From 82ba23d150bcf2c2c6204267734eba23248212ce Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Mon, 2 Dec 2024 19:01:00 -0500 Subject: [PATCH 001/717] Add ADMIN_ACTION types to manage peers and minting accounts --- src/qortalRequests/get.ts | 59 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index fa59def..c7f7046 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -3169,6 +3169,20 @@ export const adminAction = async (data, isFromExtension) => { missingFields.push(field); } }); + // For actions that require a value, check for 'value' field + const actionsRequiringValue = [ + "addpeer", + "removepeer", + "forcesync", + "addmintingaccount", + "removemintingaccount", + ]; + if ( + actionsRequiringValue.includes(data.type.toLowerCase()) && + !data.value + ) { + missingFields.push("value"); + } if (missingFields.length > 0) { const missingFieldsString = missingFields.join(", "); const errorMsg = `Missing fields: ${missingFieldsString}`; @@ -3180,6 +3194,8 @@ export const adminAction = async (data, isFromExtension) => { } let apiEndpoint = ""; + let method = "GET"; // Default method + let includeValueInBody = false; switch (data.type.toLowerCase()) { case "stop": apiEndpoint = await createEndpoint("/admin/stop"); @@ -3190,19 +3206,58 @@ export const adminAction = async (data, isFromExtension) => { case "bootstrap": apiEndpoint = await createEndpoint("/admin/bootstrap"); break; + case "addmintingaccount": + apiEndpoint = await createEndpoint("/admin/mintingaccounts"); + method = "POST"; + includeValueInBody = true; + break; + case "removemintingaccount": + apiEndpoint = await createEndpoint("/admin/mintingaccounts"); + method = "DELETE"; + includeValueInBody = true; + break; + case "forcesync": + apiEndpoint = await createEndpoint("/admin/forcesync"); + method = "POST"; + includeValueInBody = true; + break; + case "addpeer": + apiEndpoint = await createEndpoint("/peers"); + method = "POST"; + includeValueInBody = true; + break; + case "removepeer": + apiEndpoint = await createEndpoint("/peers"); + method = "DELETE"; + includeValueInBody = true; + break; default: throw new Error(`Unknown admin action type: ${data.type}`); } + // Prepare the permission prompt text + let permissionText = `Do you give this application permission to perform the admin action: ${data.type}`; + if (data.value) { + permissionText += ` with value: ${data.value}`; + } const resPermission = await getUserPermission( { - text1: `Do you give this application permission to perform a node ${data.type}?`, + text1: permissionText, }, isFromExtension ); const { accepted } = resPermission; if (accepted) { - const response = await fetch(apiEndpoint); + // Set up options for the API call + const options: RequestInit = { + method: method, + headers: {}, + }; + if (includeValueInBody) { + options.headers["Content-Type"] = "text/plain"; + options.body = data.value; + } + const response = await fetch(apiEndpoint, options); if (!response.ok) throw new Error("Failed to perform request"); let res; From 17f78c0e23e646ae46296570326802695e40bc29 Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Tue, 3 Dec 2024 03:05:38 -0500 Subject: [PATCH 002/717] added popover for reactions --- src/components/Chat/MessageItem.tsx | 81 +++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 9 deletions(-) diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index 47d4417..507fc12 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -1,8 +1,8 @@ import { Message } from "@chatscope/chat-ui-kit-react"; -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import { useInView } from "react-intersection-observer"; import { MessageDisplay } from "./MessageDisplay"; -import { Avatar, Box, ButtonBase, Typography } from "@mui/material"; +import { Avatar, Box, Button, ButtonBase, List, ListItem, ListItemText, Popover, Typography } from "@mui/material"; import { formatTimestamp } from "../../utils/time"; import { getBaseApi } from "../../background"; import { getBaseApiReact } from "../../App"; @@ -40,6 +40,8 @@ export const MessageItem = ({ triggerOnce: false, // Only trigger once when it becomes visible }); + const [anchorEl, setAnchorEl] = useState(null); + const [selectedReaction, setSelectedReaction] = useState(null); useEffect(() => { @@ -246,17 +248,15 @@ export const MessageItem = ({ // const myReaction = reactions if(numberOfReactions === 0) return null return ( - { - if(reactions[reaction] && reactions[reaction]?.find((item)=> item?.sender === myAddress)){ - handleReaction(reaction, message, false) - } else { - handleReaction(reaction, message, true) - } + }} onClick={(event) => { + event.stopPropagation(); // Prevent event bubbling + setAnchorEl(event.currentTarget); + setSelectedReaction(reaction); }}>
+ {selectedReaction && ( + { + setAnchorEl(null); + setSelectedReaction(null); + }} + anchorOrigin={{ + vertical: "top", + horizontal: "center", + }} + transformOrigin={{ + vertical: "bottom", + horizontal: "center", + }} + PaperProps={{ + style: { + backgroundColor: "#232428", + color: "white", + }, + }} + > + + + People who reacted with {selectedReaction} + + + {reactions[selectedReaction]?.map((reactionItem) => ( + + + + ))} + + + + + )} Date: Tue, 3 Dec 2024 12:46:20 +0200 Subject: [PATCH 003/717] added max width and height to reaction list --- src/components/Chat/MessageItem.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index 507fc12..bd2d86c 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -296,7 +296,11 @@ export const MessageItem = ({ People who reacted with {selectedReaction} - + {reactions[selectedReaction]?.map((reactionItem) => ( Date: Tue, 3 Dec 2024 04:08:59 -0500 Subject: [PATCH 004/717] show group chats without secret key --- src/components/Chat/ChatGroup.tsx | 1 - src/components/Group/Group.tsx | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 9d38ce8..f5039c0 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -182,7 +182,6 @@ const [messageSize, setMessageSize] = useState(0) try { if(!secretKeyRef.current){ checkForFirstSecretKeyNotification(encryptedMessages) - return } return new Promise((res, rej)=> { window.sendMessage("decryptSingle", { diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index df1f0c3..5cf00ef 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -2487,8 +2487,7 @@ export const Group = ({ handleNewEncryptionNotification={ setNewEncryptionNotification } - hide={groupSection !== "chat" || !secretKey || selectedDirect || newChat} - hideView={!(desktopViewMode === 'chat' && selectedGroup)} + hide={groupSection !== "chat" || selectedDirect || newChat} handleSecretKeyCreationInProgress={ handleSecretKeyCreationInProgress } @@ -2542,6 +2541,10 @@ export const Group = ({ Wait until an admin re-encrypts the keys. + + Only unencrypted messages will be displayed. + + Try notifying an admin from the list of admins below: From e1b6fa39f945377d43af7cb6249ce9363df3074d Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Tue, 3 Dec 2024 04:14:01 -0500 Subject: [PATCH 005/717] restore unintentionally removed code --- src/components/Group/Group.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 5cf00ef..51cdc76 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -2488,6 +2488,7 @@ export const Group = ({ setNewEncryptionNotification } hide={groupSection !== "chat" || selectedDirect || newChat} + hideView={!(desktopViewMode === 'chat' && selectedGroup)} handleSecretKeyCreationInProgress={ handleSecretKeyCreationInProgress } From 5c340b99f2fc263fdcd12a749a75bc8f389798cc Mon Sep 17 00:00:00 2001 From: PhilReact Date: Tue, 3 Dec 2024 13:00:59 +0200 Subject: [PATCH 006/717] hide chat options and editor if no secretKey --- src/components/Chat/ChatGroup.tsx | 8 +++++--- src/components/Chat/ChatList.tsx | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index f5039c0..4ff4f94 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -746,9 +746,9 @@ const clearEditorContent = () => { left: hide && '-100000px', }}> - + - + {!!secretKey && (
{ )} - + +
{messageSize > 750 && ( { {/* */}
+ )} {isOpenQManager !== null && ( { const parentRef = useRef(); const [messages, setMessages] = useState(initialMessages); @@ -406,7 +407,7 @@ export const ChatList = ({ )} - {enableMentions && ( + {enableMentions && hasSecretKey && ( Date: Tue, 3 Dec 2024 14:01:07 +0200 Subject: [PATCH 007/717] move send btn to the side, changed add group label --- src/components/Chat/ChatDirect.tsx | 39 +++++++++--------------------- src/components/Chat/ChatGroup.tsx | 37 ++++++++-------------------- src/components/Group/AddGroup.tsx | 2 +- src/components/Group/Group.tsx | 2 +- 4 files changed, 24 insertions(+), 56 deletions(-) diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index cbd5b6e..cb40066 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -580,7 +580,7 @@ useEffect(() => { backgroundColor: "#232428", minHeight: isMobile ? '0px' : '150px', display: 'flex', - flexDirection: 'column', + flexDirection: 'row', overflow: 'hidden', width: '100%', boxSizing: 'border-box', @@ -596,14 +596,17 @@ useEffect(() => { flexDirection: 'column', flexGrow: isMobile && 1, overflow: !isMobile && "auto", - flexShrink: 0 + flexShrink: 0, + width: 'calc(100% - 100px)', + justifyContent: 'flex-end' }}> {replyMessage && ( @@ -660,34 +663,14 @@ useEffect(() => { )} - {isFocusedParent && ( - { - if(isSending) return - setIsFocusedParent(false) - clearEditorContent() - // Unfocus the editor - }} - style={{ - marginTop: 'auto', - alignSelf: 'center', - cursor: isSending ? 'default' : 'pointer', - background: 'red', - flexShrink: 0, - padding: isMobile && '5px' - }} - > - - {` Close`} - - - )} + { if(messageSize > 4000) return @@ -701,7 +684,9 @@ useEffect(() => { cursor: isSending ? 'default' : 'pointer', background: isSending && 'rgba(0, 0, 0, 0.8)', flexShrink: 0, - padding: isMobile && '5px' + padding: '5px', + width: '100px', + minWidth: 'auto' }} > {isSending && ( diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 4ff4f94..b7e15d8 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -755,7 +755,7 @@ const clearEditorContent = () => { backgroundColor: "#232428", minHeight: isMobile ? '0px' : '150px', display: 'flex', - flexDirection: 'column', + flexDirection: 'row', overflow: 'hidden', width: '100%', boxSizing: 'border-box', @@ -766,12 +766,15 @@ const clearEditorContent = () => { zIndex: isFocusedParent ? 5 : 'unset', flexShrink: 0 }}> +
{replyMessage && ( { )} - {isFocusedParent && ( - { - if(isSending) return - setIsFocusedParent(false) - clearEditorContent() - // Unfocus the editor - }} - style={{ - marginTop: 'auto', - alignSelf: 'center', - cursor: isSending ? 'default' : 'pointer', - background: 'red', - flexShrink: 0, - padding: isMobile && '5px' - }} - > - - {` Close`} - - - )} + { if(messageSize > 4000) return @@ -877,7 +859,9 @@ const clearEditorContent = () => { cursor: isSending ? 'default' : 'pointer', background: isSending && 'rgba(0, 0, 0, 0.8)', flexShrink: 0, - padding: isMobile && '5px', + padding: '5px', + width: '100px', + minWidth: 'auto' }} > @@ -898,7 +882,6 @@ const clearEditorContent = () => { - {/* */}
)} {isOpenQManager !== null && ( diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index f4c6e6d..ed90c8c 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -194,7 +194,7 @@ export const AddGroup = ({ address, open, setOpen }) => { - Add Group + Group Mgmt - Add Group + Group Mgmt
)} {chatMode === "directs" && ( From dc0dbc55dff5343530a19fc441be54cd60db3256 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Tue, 3 Dec 2024 15:21:42 +0200 Subject: [PATCH 008/717] added input for qortallinks --- src/components/Apps/AppsHomeDesktop.tsx | 80 ++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/src/components/Apps/AppsHomeDesktop.tsx b/src/components/Apps/AppsHomeDesktop.tsx index 6481871..ebae36a 100644 --- a/src/components/Apps/AppsHomeDesktop.tsx +++ b/src/components/Apps/AppsHomeDesktop.tsx @@ -7,20 +7,37 @@ import { AppsContainer, AppsParent, } from "./Apps-styles"; -import { Avatar, ButtonBase } from "@mui/material"; +import { Avatar, Box, ButtonBase, Input } from "@mui/material"; import { Add } from "@mui/icons-material"; import { getBaseApiReact, isMobile } from "../../App"; import LogoSelected from "../../assets/svgs/LogoSelected.svg"; import { executeEvent } from "../../utils/events"; import { Spacer } from "../../common/Spacer"; import { SortablePinnedApps } from "./SortablePinnedApps"; - +import { extractComponents } from "../Chat/MessageDisplay"; +import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward'; export const AppsHomeDesktop = ({ setMode, myApp, myWebsite, availableQapps, }) => { + const [qortalUrl, setQortalUrl] = useState('') + + const openQortalUrl = ()=> { + try { + if(!qortalUrl) return + const res = extractComponents(qortalUrl); + if (res) { + const { service, name, identifier, path } = res; + executeEvent("addTab", { data: { service, name, identifier, path } }); + executeEvent("open-apps-mode", { }); + setQortalUrl('qortal://') + } + } catch (error) { + + } + } return ( <> + + + + { + setQortalUrl(e.target.value) + }} + disableUnderline + autoComplete='off' + autoCorrect='off' + placeholder="qortal://" + sx={{ + width: '100%', + color: 'white', + '& .MuiInput-input::placeholder': { + color: 'rgba(84, 84, 84, 0.70) !important', + fontSize: '20px', + fontStyle: 'normal', + fontWeight: 400, + lineHeight: '120%', // 24px + letterSpacing: '0.15px', + opacity: 1 + }, + '&:focus': { + outline: 'none', + }, + // Add any additional styles for the input here + }} + onKeyDown={(e) => { + if (e.key === 'Enter' && qortalUrl) { + console.log('hello') + openQortalUrl(); + } + }} + /> + openQortalUrl()}> + + + + Date: Wed, 4 Dec 2024 09:43:44 +0200 Subject: [PATCH 009/717] fix typo --- src/components/Chat/CreateCommonSecret.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Chat/CreateCommonSecret.tsx b/src/components/Chat/CreateCommonSecret.tsx index 2e2088a..c371c55 100644 --- a/src/components/Chat/CreateCommonSecret.tsx +++ b/src/components/Chat/CreateCommonSecret.tsx @@ -152,7 +152,7 @@ export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, sec maxWidth: '350px', background: '#444444' }}> - Re-encyrpt key + Re-encrypt key {noSecretKey ? ( There is no group secret key. Be the first admin to publish one! From f33b2db4d46f5ac76316ed0b920f0db408fec396 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 5 Dec 2024 14:11:29 +0200 Subject: [PATCH 010/717] fix scrolldown when new msg --- src/components/Chat/ChatList.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 8c16c93..3cae591 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -37,6 +37,7 @@ export const ChatList = ({ const hasLoadedInitialRef = useRef(false); const scrollingIntervalRef = useRef(null); + const lastSeenUnreadMessageTimestamp = useRef(null); // Initialize the virtualizer @@ -108,7 +109,7 @@ export const ChatList = ({ setTimeout(() => { const hasUnreadMessages = totalMessages.some( - (msg) => msg.unread && !msg?.chatReference && !msg?.isTemp + (msg) => msg.unread && !msg?.chatReference && !msg?.isTemp && (!msg?.chatReference && msg?.timestamp > lastSeenUnreadMessageTimestamp.current || 0) ); if (parentRef.current) { const { scrollTop, scrollHeight, clientHeight } = parentRef.current; @@ -152,6 +153,7 @@ export const ChatList = ({ })) ); setShowScrollButton(false); + lastSeenUnreadMessageTimestamp.current = Date.now() }, []); const sentNewMessageGroupFunc = useCallback(() => { @@ -385,7 +387,7 @@ export const ChatList = ({ Scroll to Unread Messages )} - {showScrollDownButton && ( + {showScrollDownButton && !showScrollButton && ( + + + + + + )} + + {isLoadingPayments && ( + + + + )} + {!isLoadingPayments && addressInfo && ( + + 20 most recent payments + + {!isLoadingPayments && payments?.length === 0 && ( + + No payments + + )} + + + + Sender + Reciver + Amount + Time + + + + {payments.map((payment, index) => ( + + + + copy address + + } + placement="bottom" + arrow + sx={{ fontSize: "24" }} + slotProps={{ + tooltip: { + sx: { + color: "#ffffff", + backgroundColor: "#444444", + }, + }, + arrow: { + sx: { + color: "#444444", + }, + }, + }} + > + { + navigator.clipboard.writeText( + payment?.creatorAddress + ); + }} + > + {formatAddress(payment?.creatorAddress)} + + + + + + copy address + + } + placement="bottom" + arrow + sx={{ fontSize: "24" }} + slotProps={{ + tooltip: { + sx: { + color: "#ffffff", + backgroundColor: "#444444", + }, + }, + arrow: { + sx: { + color: "#444444", + }, + }, + }} + > + { + navigator.clipboard.writeText(payment?.recipient); + }} + > + {formatAddress(payment?.recipient)} + + + + + + {payment?.amount} + + {formatTimestamp(payment?.timestamp)} + + ))} + +
+
+ )} + +
+
+ + ); +}; diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx index 8bcc03a..3bcb143 100644 --- a/src/components/WrapperUserAction.tsx +++ b/src/components/WrapperUserAction.tsx @@ -121,6 +121,42 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => { > Copy address + + + + +
diff --git a/src/utils/time.ts b/src/utils/time.ts index b0a27cf..c89c1cd 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -12,7 +12,7 @@ export function formatTimestamp(timestamp: number): string { } else if (elapsedTime < 1440) { return `${Math.floor(elapsedTime / 60)}h ago` } else { - return timestampMoment.format('MMM D') + return timestampMoment.format('MMM D, YYYY') } } export function formatTimestampForum(timestamp: number): string { From a3c6a7cf100490290dba3eb6beef8b47477da535 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Fri, 28 Feb 2025 17:42:13 +0200 Subject: [PATCH 014/717] autofocus --- src/components/UserLookup.tsx/UserLookup.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/UserLookup.tsx/UserLookup.tsx b/src/components/UserLookup.tsx/UserLookup.tsx index d8bb0ea..b2c6736 100644 --- a/src/components/UserLookup.tsx/UserLookup.tsx +++ b/src/components/UserLookup.tsx/UserLookup.tsx @@ -126,6 +126,7 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => { }} > setNameOrAddress(e.target.value)} size="small" From 55448ab8a7aae1555b32824e8ab686698ace0c88 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Fri, 28 Feb 2025 21:14:03 +0200 Subject: [PATCH 015/717] remove double --- src/components/WrapperUserAction.tsx | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx index 3bcb143..9aa5cbf 100644 --- a/src/components/WrapperUserAction.tsx +++ b/src/components/WrapperUserAction.tsx @@ -121,24 +121,6 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => { > Copy address - - + + + + ) +} diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index c4c7622..c2a42df 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -2528,6 +2528,7 @@ export const Group = ({ { + const [checked1, setChecked1] = React.useState(false); + const [checked2, setChecked2] = React.useState(false); + React.useEffect(() => { + if (balance && +balance >= 6) { + setChecked1(true); + } + }, [balance]); + + + React.useEffect(() => { + if (name) setChecked2(true); + }, [name]); + + + const isLoaded = React.useMemo(()=> { + if(userInfo !== null) return true + return false + }, [ userInfo]) + + const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(()=> { + if(isLoaded && checked1 && checked2) return true + return false + }, [checked1, isLoaded, checked2]) + + return ( */} - + )} + + + )} @@ -185,6 +216,9 @@ export const HomeDesktop = ({ {" "} + {!hasDoneNameAndBalanceAndIsLoaded && ( + + )} - + {hasDoneNameAndBalanceAndIsLoaded && ( + + + )} + + diff --git a/src/components/Group/ThingsToDoInitial.tsx b/src/components/Group/ThingsToDoInitial.tsx index 0a02f36..367b3b1 100644 --- a/src/components/Group/ThingsToDoInitial.tsx +++ b/src/components/Group/ThingsToDoInitial.tsx @@ -12,27 +12,17 @@ import { Box, Typography } from "@mui/material"; import { Spacer } from "../../common/Spacer"; import { isMobile } from "../../App"; import { QMailMessages } from "./QMailMessages"; +import { executeEvent } from "../../utils/events"; export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance, userInfo }) => { const [checked1, setChecked1] = React.useState(false); const [checked2, setChecked2] = React.useState(false); - const [checked3, setChecked3] = React.useState(false); + // const [checked3, setChecked3] = React.useState(false); - // const getAddressInfo = async (address) => { - // const response = await fetch(getBaseApiReact() + "/addresses/" + address); - // const data = await response.json(); - // if (data.error && data.error === 124) { - // setChecked1(false); - // } else if (data.address) { - // setChecked1(true); - // } - // }; + // React.useEffect(() => { + // if (hasGroups) setChecked3(true); + // }, [hasGroups]); - // const checkInfo = async () => { - // try { - // getAddressInfo(myAddress); - // } catch (error) {} - // }; React.useEffect(() => { if (balance && +balance >= 6) { @@ -40,9 +30,6 @@ export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance, userInf } }, [balance]); - React.useEffect(() => { - if (hasGroups) setChecked3(true); - }, [hasGroups]); React.useEffect(() => { if (name) setChecked2(true); @@ -120,11 +107,14 @@ if(!isLoaded) return null disableRipple role={undefined} dense + onClick={()=> { + executeEvent("openBuyQortInfo", {}) + }} > - { + executeEvent('openRegisterName', {}) + }} sx={{ "& .MuiTypography-root": { - fontSize: "13px", + fontSize: "1rem", fontWeight: 400, }, }} primary={`Register a name`} /> @@ -202,16 +194,7 @@ if(!isLoaded) return null - - // - // - // } + {/* - + */} )} diff --git a/src/components/RegisterName.tsx b/src/components/RegisterName.tsx new file mode 100644 index 0000000..35af458 --- /dev/null +++ b/src/components/RegisterName.tsx @@ -0,0 +1,312 @@ +import React, { useCallback, useContext, useEffect, useState } from 'react' +import { + Avatar, + Box, + Button, + ButtonBase, + Collapse, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Input, + ListItem, + ListItemAvatar, + ListItemButton, + ListItemIcon, + ListItemText, + List, + MenuItem, + Popover, + Select, + TextField, + Typography, + } from "@mui/material"; +import { Label } from './Group/AddGroup'; +import { Spacer } from '../common/Spacer'; +import { LoadingButton } from '@mui/lab'; +import { getBaseApiReact, MyContext } from '../App'; +import { getFee } from '../background'; +import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked'; +import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events'; +import { BarSpinner } from '../common/Spinners/BarSpinner/BarSpinner'; +import CheckIcon from '@mui/icons-material/Check'; +import ErrorIcon from '@mui/icons-material/Error'; + +enum Availability { + NULL = 'null', + LOADING = 'loading', + AVAILABLE = 'available', + NOT_AVAILABLE = 'not-available' +} +export const RegisterName = ({setOpenSnack, setInfoSnack, userInfo, show, setTxList, balance}) => { + const [isOpen, setIsOpen] = useState(false) + const [registerNameValue, setRegisterNameValue] = useState('') + const [isLoadingRegisterName, setIsLoadingRegisterName] = useState(false) + const [isNameAvailable, setIsNameAvailable] = useState(Availability.NULL) + const [nameFee, setNameFee] = useState(null) + + const checkIfNameExisits = async (name)=> { + if(!name?.trim()){ + setIsNameAvailable(Availability.NULL) + + return + } + setIsNameAvailable(Availability.LOADING) + try { + const res = await fetch(`${getBaseApiReact()}/names/` + name); + const data = await res.json() + if(data?.message === 'name unknown'){ + setIsNameAvailable(Availability.AVAILABLE) + } else { + setIsNameAvailable(Availability.NOT_AVAILABLE) + } + } catch (error) { + console.error(error) + } finally { + } + } + // Debounce logic + useEffect(() => { + const handler = setTimeout(() => { + checkIfNameExisits(registerNameValue); + }, 500); + + // Cleanup timeout if searchValue changes before the timeout completes + return () => { + clearTimeout(handler); + }; + }, [registerNameValue]); + + const openRegisterNameFunc = useCallback((e) => { + setIsOpen(true) + + }, [ setIsOpen]); + + useEffect(() => { + subscribeToEvent("openRegisterName", openRegisterNameFunc); + + return () => { + unsubscribeFromEvent("openRegisterName", openRegisterNameFunc); + }; + }, [openRegisterNameFunc]); + + useEffect(()=> { + const nameRegistrationFee = async ()=> { + try { + const fee = await getFee("REGISTER_NAME"); + setNameFee(fee?.fee) + } catch (error) { + console.error(error) + } + } + nameRegistrationFee() + }, []) + + const registerName = async () => { + try { + if (!userInfo?.address) throw new Error("Your address was not found"); + if(!registerNameValue) throw new Error('Enter a name') + + const fee = await getFee("REGISTER_NAME"); + await show({ + message: "Would you like to register this name?", + publishFee: fee.fee + " QORT", + }); + setIsLoadingRegisterName(true); + new Promise((res, rej) => { + window + .sendMessage("registerName", { + name: registerNameValue, + }) + .then((response) => { + if (!response?.error) { + res(response); + setIsLoadingRegisterName(false); + setInfoSnack({ + type: "success", + message: + "Successfully registered. It may take a couple of minutes for the changes to propagate", + }); + setIsOpen(false); + setRegisterNameValue(""); + setOpenSnack(true); + setTxList((prev) => [ + { + ...response, + type: "register-name", + label: `Registered name: awaiting confirmation. This may take a couple minutes.`, + labelDone: `Registered name: success!`, + done: false, + }, + ...prev.filter((item) => !item.done), + ]); + return; + } + setInfoSnack({ + type: "error", + message: response?.error, + }); + setOpenSnack(true); + rej(response.error); + }) + .catch((error) => { + setInfoSnack({ + type: "error", + message: error.message || "An error occurred", + }); + setOpenSnack(true); + rej(error); + }); + }); + } catch (error) { + if (error?.message) { + setOpenSnack(true) + setInfoSnack({ + type: "error", + message: error?.message, + }); + } + } finally { + setIsLoadingRegisterName(false); + } + }; + + return ( + + + {"Register name"} + + + + + setRegisterNameValue(e.target.value)} + value={registerNameValue} + placeholder="Choose a name" + /> + {(!balance || (nameFee && balance && balance < nameFee))&& ( + <> + + + + Your balance is {balance ?? 0} QORT. A name registration requires a {nameFee} QORT fee + + + + + )} + + {isNameAvailable === Availability.AVAILABLE && ( + + + {registerNameValue} is available + + )} + {isNameAvailable === Availability.NOT_AVAILABLE && ( + + + {registerNameValue} is unavailable + + )} + {isNameAvailable === Availability.LOADING && ( + + + Checking if name already existis + + )} + + Benefits of a name + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/src/main.tsx b/src/main.tsx index 0531491..dcaf3ca 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -24,6 +24,10 @@ const theme = createTheme({ secondary: '#b0b0b0', // Light gray for secondary text disabled: '#808080', // Gray for disabled text }, + action: { + // disabledBackground: 'set color of background here', + disabled: 'rgb(255 255 255 / 70%)' + } }, typography: { fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif', // Font family From b1b7114972a09fdbbb3bca3dc35eb0df1026e95b Mon Sep 17 00:00:00 2001 From: AlphaX-Qortal <67390536+AlphaX-Qortal@users.noreply.github.com> Date: Fri, 28 Feb 2025 20:57:32 +0100 Subject: [PATCH 018/717] Update dependencies --- package-lock.json | 18 ++++++++++-------- package.json | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index e8c249f..df0b6dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@chatscope/chat-ui-kit-react": "^2.0.3", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", - "@electron/packager": "^18.3.5", + "@electron/packager": "^18.3.6", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.0", "@evva/capacitor-secure-storage-plugin": "^3.0.1", @@ -51,7 +51,7 @@ "cordova-plugin-android-permissions": "^1.1.5", "cordova-plugin-file": "^8.1.1", "dompurify": "^3.1.6", - "electron": "^33.0.2", + "electron": "^32.3.1", "electron-builder": "^25.1.8", "emoji-picker-react": "^4.12.0", "file-saver": "^2.0.5", @@ -914,9 +914,10 @@ } }, "node_modules/@electron/packager": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@electron/packager/-/packager-18.3.5.tgz", - "integrity": "sha512-ClgTxXTt3MesWAcjIxIkgxELjTcllw1FRoVsihP7uT48kpDMqI71p4XvnMWbq8PvU57TcrKICAaLkxRhbc+/wQ==", + "version": "18.3.6", + "resolved": "https://registry.npmjs.org/@electron/packager/-/packager-18.3.6.tgz", + "integrity": "sha512-1eXHB5t+SQKvUiDpWGpvr90ZSSbXj+isrh3YbjCTjKT4bE4SQrKSBfukEAaBvp67+GXHFtCHjQgN9qSTFIge+Q==", + "license": "BSD-2-Clause", "dependencies": { "@electron/asar": "^3.2.13", "@electron/get": "^3.0.0", @@ -7243,10 +7244,11 @@ } }, "node_modules/electron": { - "version": "33.0.2", - "resolved": "https://registry.npmjs.org/electron/-/electron-33.0.2.tgz", - "integrity": "sha512-C2WksfP0COsMHbYXSJG68j6S3TjuGDrw/YT42B526yXalIlNQZ2GeAYKryg6AEMkIp3p8TUfDRD0+HyiyCt/nw==", + "version": "32.3.2", + "resolved": "https://registry.npmjs.org/electron/-/electron-32.3.2.tgz", + "integrity": "sha512-pHEfoR6I7crJabEfWdPrdKqadWp7Ch1V3+gYDsMNIZ9FB3GNy91p0Z4gkyCop2LCd4ckAT1U8Z+UexhxsbPzWQ==", "hasInstallScript": true, + "license": "MIT", "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^20.9.0", diff --git a/package.json b/package.json index dcf359a..f8ba271 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@chatscope/chat-ui-kit-react": "^2.0.3", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", - "@electron/packager": "^18.3.5", + "@electron/packager": "^18.3.6", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.0", "@evva/capacitor-secure-storage-plugin": "^3.0.1", @@ -55,7 +55,7 @@ "cordova-plugin-android-permissions": "^1.1.5", "cordova-plugin-file": "^8.1.1", "dompurify": "^3.1.6", - "electron": "^33.0.2", + "electron": "^32.3.1", "electron-builder": "^25.1.8", "emoji-picker-react": "^4.12.0", "file-saver": "^2.0.5", From 680f9fbd3e313a3689851ab29eef4c4a58e4ca7b Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sun, 2 Mar 2025 20:05:35 +0200 Subject: [PATCH 019/717] fixes --- electron/src/setup.ts | 13 ++++--- src/App.tsx | 5 +-- src/background.ts | 4 +- .../Group/ListOfGroupPromotions.tsx | 1 + src/components/MainAvatar.tsx | 39 ++++++++++++++----- src/main.tsx | 18 +++++++++ 6 files changed, 60 insertions(+), 20 deletions(-) diff --git a/electron/src/setup.ts b/electron/src/setup.ts index 6fab164..d1fb925 100644 --- a/electron/src/setup.ts +++ b/electron/src/setup.ts @@ -249,11 +249,12 @@ export class ElectronCapacitorApp { export function setupContentSecurityPolicy(customScheme: string): void { session.defaultSession.webRequest.onHeadersReceived((details: any, callback) => { const allowedSources = ["'self'", customScheme, ...domainHolder.allowedDomains]; - const connectSources = [...allowedSources]; const frameSources = [ "'self'", 'http://localhost:*', 'https://localhost:*', + 'ws://localhost:*', + 'ws://127.0.0.1:*', 'http://127.0.0.1:*', 'https://127.0.0.1:*', ...allowedSources, @@ -261,13 +262,13 @@ export function setupContentSecurityPolicy(customScheme: string): void { // Create the Content Security Policy (CSP) string const csp = ` - default-src 'self' ${allowedSources.join(' ')}; + default-src 'self' ${frameSources.join(' ')}; frame-src ${frameSources.join(' ')}; - script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline' 'unsafe-eval' ${allowedSources.join(' ')}; + script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline' 'unsafe-eval' ${frameSources.join(' ')}; object-src 'self'; - connect-src 'self' blob: ${connectSources.join(' ')}; - img-src 'self' data: blob: ${allowedSources.join(' ')}; - media-src 'self' blob: ${allowedSources.join(' ')}; + connect-src 'self' blob: ${frameSources.join(' ')}; + img-src 'self' data: blob: ${frameSources.join(' ')}; + media-src 'self' blob: ${frameSources.join(' ')}; style-src 'self' 'unsafe-inline'; font-src 'self' data:; `.replace(/\s+/g, ' ').trim(); diff --git a/src/App.tsx b/src/App.tsx index 5eca049..f6a6a7d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1443,7 +1443,7 @@ function App() { ) : ( <> - + - {"Warning"} + {"LOGOUT"} {messageUnsavedChanges.message} @@ -3336,7 +3336,6 @@ function App() { flexDirection: "column", alignItems: "center", justifyContent: "flex-start", - minHeight: "400px", maxHeight: "90vh", overflow: "auto", }} diff --git a/src/background.ts b/src/background.ts index ad8ddf3..96febcd 100644 --- a/src/background.ts +++ b/src/background.ts @@ -941,7 +941,7 @@ export async function getBalanceInfo() { const validApi = await getBaseApi(); const response = await fetch(validApi + "/addresses/balance/" + address); - if (!response?.ok) throw new Error("Cannot fetch balance"); + if (!response?.ok) throw new Error("0 QORT in your balance"); const data = await response.json(); return data; } @@ -1101,7 +1101,7 @@ export const getLastRef = async () => { const response = await fetch( validApi + "/addresses/lastreference/" + address ); - if (!response?.ok) throw new Error("Cannot fetch balance"); + if (!response?.ok) throw new Error("0 QORT in your balance"); const data = await response.text(); return data; }; diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index a983f56..afeff14 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -809,6 +809,7 @@ export const ListOfGroupPromotions = () => { value={selectedGroup} label="Groups where you are an admin" onChange={(e) => setSelectedGroup(e.target.value)} + variant="outlined" > {myGroupsWhereIAmAdmin?.map((group) => { return ( diff --git a/src/components/MainAvatar.tsx b/src/components/MainAvatar.tsx index b5f8e44..faca10a 100644 --- a/src/components/MainAvatar.tsx +++ b/src/components/MainAvatar.tsx @@ -7,8 +7,9 @@ import ImageUploader from "../common/ImageUploader"; import { getFee } from "../background"; import { fileToBase64 } from "../utils/fileReading"; import { LoadingButton } from "@mui/lab"; +import ErrorIcon from '@mui/icons-material/Error'; -export const MainAvatar = ({ myName }) => { +export const MainAvatar = ({ myName, balance, setOpenSnack, setInfoSnack }) => { const [hasAvatar, setHasAvatar] = useState(false); const [avatarFile, setAvatarFile] = useState(null); const [tempAvatar, setTempAvatar] = useState(null) @@ -52,10 +53,11 @@ const [isLoading, setIsLoading] = useState(false) checkIfAvatarExists(); }, [myName]); + const publishAvatar = async ()=> { try { const fee = await getFee('ARBITRARY') - + if(+balance < +fee.fee) throw new Error(`Publishing an Avatar requires ${fee.fee}`) await show({ message: "Would you like to publish an avatar?" , publishFee: fee.fee + ' QORT' @@ -84,7 +86,13 @@ const [isLoading, setIsLoading] = useState(false) setTempAvatar(`data:image/webp;base64,${avatarBase64}`) handleClose() } catch (error) { - + if (error?.message) { + setOpenSnack(true) + setInfoSnack({ + type: "error", + message: error?.message, + }); + } } finally { setIsLoading(false); } @@ -113,7 +121,7 @@ const [isLoading, setIsLoading] = useState(false) change avatar
- + ); } @@ -141,7 +149,7 @@ const [isLoading, setIsLoading] = useState(false) change avatar - + ); } @@ -159,13 +167,13 @@ const [isLoading, setIsLoading] = useState(false) set avatar - + ); }; -const PopoverComp = ({avatarFile, setAvatarFile, id, open, anchorEl, handleClose, publishAvatar, isLoading}) => { +const PopoverComp = ({avatarFile, setAvatarFile, id, open, anchorEl, handleClose, publishAvatar, isLoading, myName}) => { return ( {avatarFile?.name} - - + {!myName && ( + + + A registered name is required to set an avatar + + )} + + + Publish avatar diff --git a/src/main.tsx b/src/main.tsx index dcaf3ca..22bba47 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -44,6 +44,24 @@ const theme = createTheme({ color: '#b0b0b0', // Lighter text for body2, often used for secondary text }, }, + components: { + MuiOutlinedInput: { + styleOverrides: { + root: { + ".MuiOutlinedInput-notchedOutline": { + borderColor: "white", // ⚪ Default outline color + }, + }, + }, + }, + MuiSelect: { + styleOverrides: { + icon: { + color: "white", // ✅ Caret (dropdown arrow) color + }, + }, + }, + }, }); export default theme; From a219aab98bd41fbe10942a15c475c941eb14963a Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sun, 2 Mar 2025 20:22:23 +0200 Subject: [PATCH 020/717] change gateway and wallet terminology --- src/App.tsx | 2 +- src/ExtStates/NotAuthenticated.tsx | 10 ++++------ src/Wallets.tsx | 12 ++++++------ src/components/CoreSyncStatus.tsx | 2 +- src/qortalRequests/get.ts | 22 +++++++++++----------- 5 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index f6a6a7d..862a6d8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2769,7 +2769,7 @@ function App() { fontSize: "12px", }} > - {"Using gateway"} + {"Using public node"} )} diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index f693a38..89c3406 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -407,12 +407,10 @@ export const NotAuthenticated = ({ fontSize: '18px' }} > - WELCOME TO YOUR

+ WELCOME TO QORTAL WALLET + }}> QORTAL @@ -436,7 +434,7 @@ export const NotAuthenticated = ({ > setExtstate('wallets')}> {/* */} - Wallets + Accounts {/* @@ -482,7 +480,7 @@ export const NotAuthenticated = ({ } }} > - Create wallet + Create account diff --git a/src/Wallets.tsx b/src/Wallets.tsx index 8641a34..2c7b5e7 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -134,11 +134,11 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { setPassword('') setSeedError('') } else { - setSeedError('Could not create wallet.') + setSeedError('Could not create account.') } } catch (error) { - setSeedError(error?.message || 'Could not create wallet.') + setSeedError(error?.message || 'Could not create account.') } finally { setIsLoadingEncryptSeed(false) } @@ -176,19 +176,19 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { {(wallets?.length === 0 || !wallets) ? ( <> - No wallets saved + No accounts saved ): ( <> - Your saved wallets + Your saved accounts )} {rawWallet && ( - Selected Wallet: + Selected Account: {rawWallet?.name && {rawWallet.name}} {rawWallet?.address0 && ( {rawWallet?.address0} @@ -267,7 +267,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { padding: '10px' }} {...getRootProps()}> - Add wallets + Add account diff --git a/src/components/CoreSyncStatus.tsx b/src/components/CoreSyncStatus.tsx index 641996a..2334bef 100644 --- a/src/components/CoreSyncStatus.tsx +++ b/src/components/CoreSyncStatus.tsx @@ -97,7 +97,7 @@ export const CoreSyncStatus = ({imageSize, position}) => {

{message}

Block Height: {height || ''}

Connected Peers: {numberOfConnections || ''}

-

Using gateway: {isUsingGateway?.toString()}

+

Using public node: {isUsingGateway?.toString()}

diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index 956fa07..6e27f8a 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -679,7 +679,7 @@ export const decryptDataWithSharingKey = async (data, sender) => { export const getHostedData = async (data, isFromExtension) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a gateway"); + throw new Error("This action cannot be done through a public node"); } const resPermission = await getUserPermission( { @@ -715,7 +715,7 @@ export const getHostedData = async (data, isFromExtension) => { export const deleteHostedData = async (data, isFromExtension) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a gateway"); + throw new Error("This action cannot be done through a public node"); } const requiredFields = ["hostedData"]; const missingFields: string[] = []; @@ -800,7 +800,7 @@ export const decryptData = async (data) => { export const getListItems = async (data, isFromExtension) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a gateway"); + throw new Error("This action cannot be done through a public node"); } const requiredFields = ["list_name"]; const missingFields: string[] = []; @@ -857,7 +857,7 @@ export const getListItems = async (data, isFromExtension) => { export const addListItems = async (data, isFromExtension) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a gateway"); + throw new Error("This action cannot be done through a public node"); } const requiredFields = ["list_name", "items"]; const missingFields: string[] = []; @@ -915,7 +915,7 @@ export const addListItems = async (data, isFromExtension) => { export const deleteListItems = async (data, isFromExtension) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a gateway"); + throw new Error("This action cannot be done through a public node"); } const requiredFields = ["list_name"]; const missingFields: string[] = []; @@ -2575,7 +2575,7 @@ export const getForeignFee = async (data) => { export const updateForeignFee = async (data) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a gateway"); + throw new Error("This action cannot be done through a public node"); } const requiredFields = ["coin", "type", "value"]; const missingFields: string[] = []; @@ -2675,7 +2675,7 @@ export const getServerConnectionHistory = async (data) => { export const setCurrentForeignServer = async (data) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a gateway"); + throw new Error("This action cannot be done through a public node"); } const requiredFields = ["coin"]; const missingFields: string[] = []; @@ -2735,7 +2735,7 @@ export const setCurrentForeignServer = async (data) => { export const addForeignServer = async (data) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a gateway"); + throw new Error("This action cannot be done through a public node"); } const requiredFields = ["coin"]; const missingFields: string[] = []; @@ -2795,7 +2795,7 @@ export const addForeignServer = async (data) => { export const removeForeignServer = async (data) => { const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a gateway"); + throw new Error("This action cannot be done through a public node"); } const requiredFields = ["coin"]; const missingFields: string[] = []; @@ -3362,7 +3362,7 @@ export const createBuyOrder = async (data, isFromExtension) => { }, 0) )} ${` ${crosschainAtInfo?.[0]?.foreignBlockchain}`}`, - highlightedText: `Is using gateway: ${isGateway}`, + highlightedText: `Is using public node: ${isGateway}`, fee: "", foreignFee: `${sellerForeignFee[foreignBlockchain].value} ${sellerForeignFee[foreignBlockchain].ticker}`, }, @@ -3692,7 +3692,7 @@ export const adminAction = async (data, isFromExtension) => { } const isGateway = await isRunningGateway(); if (isGateway) { - throw new Error("This action cannot be done through a gateway"); + throw new Error("This action cannot be done through a public node"); } let apiEndpoint = ""; From d3af9bf726ae8a9414da9dfa561da55c018db31f Mon Sep 17 00:00:00 2001 From: PhilReact Date: Tue, 4 Mar 2025 17:27:19 +0200 Subject: [PATCH 021/717] added pwa for desktop --- package-lock.json | 3369 +++++++++++++++++++++++- package.json | 1 + public/qortal.png | Bin 0 -> 161247 bytes public/qortal192.png | Bin 0 -> 32392 bytes src/components/Group/QMailMessages.tsx | 1 - vite.config.ts | 30 +- 6 files changed, 3261 insertions(+), 140 deletions(-) create mode 100644 public/qortal.png create mode 100644 public/qortal192.png diff --git a/package-lock.json b/package-lock.json index df0b6dc..b43a338 100644 --- a/package-lock.json +++ b/package-lock.json @@ -85,6 +85,7 @@ "tippy.js": "^6.3.7", "tiptap-extension-resize-image": "^1.1.8", "ts-key-enum": "^2.0.12", + "vite-plugin-pwa": "^0.21.1", "vite-plugin-top-level-await": "^1.4.4", "vite-plugin-wasm": "^3.3.0" }, @@ -130,7 +131,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -140,9 +140,9 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz", - "integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -153,10 +153,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz", - "integrity": "sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==", - "dev": true, + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "engines": { "node": ">=6.9.0" } @@ -165,7 +164,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", - "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.0", @@ -195,18 +193,17 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz", - "integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", + "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", "dependencies": { - "@babel/parser": "^7.26.0", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -215,13 +212,23 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets": { + "node_modules/@babel/helper-annotate-as-pure": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", - "dev": true, + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "dependencies": { + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -235,11 +242,89 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "bin": { "semver": "bin/semver.js" } }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz", + "integrity": "sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.26.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.26.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz", + "integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", + "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", @@ -256,7 +341,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", @@ -269,11 +353,65 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-plugin-utils": { + "node_modules/@babel/helper-optimise-call-expression": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", - "dev": true, + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", + "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, "engines": { "node": ">=6.9.0" } @@ -298,7 +436,19 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, "engines": { "node": ">=6.9.0" } @@ -307,7 +457,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", - "dev": true, "dependencies": { "@babel/template": "^7.25.9", "@babel/types": "^7.26.0" @@ -317,11 +466,11 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", - "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", + "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.9" }, "bin": { "parser": "bin/babel-parser.js" @@ -330,6 +479,697 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", + "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.26.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", + "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-react-jsx-self": { "version": "7.24.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.1.tgz", @@ -360,6 +1200,283 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", + "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", + "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.26.8", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.26.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.26.8", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.11.0", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.40.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/runtime": { "version": "7.24.1", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", @@ -372,28 +1489,28 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", + "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -402,9 +1519,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", + "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -2501,8 +3618,6 @@ "version": "0.3.6", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "optional": true, - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -3011,6 +4126,50 @@ "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz", "integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==" }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", + "integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-virtual": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz", @@ -3027,6 +4186,43 @@ } } }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", @@ -3213,6 +4409,25 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "dependencies": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "node_modules/@surma/rollup-plugin-off-main-thread/node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, "node_modules/@swc/core": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.9.2.tgz", @@ -4202,7 +5417,7 @@ "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, + "devOptional": true, "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -4215,7 +5430,7 @@ "version": "7.6.8", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, + "devOptional": true, "dependencies": { "@babel/types": "^7.0.0" } @@ -4224,7 +5439,7 @@ "version": "7.4.4", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, + "devOptional": true, "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" @@ -4234,7 +5449,7 @@ "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", - "dev": true, + "devOptional": true, "dependencies": { "@babel/types": "^7.20.7" } @@ -4428,6 +5643,11 @@ "@types/react": "*" } }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==" + }, "node_modules/@types/responselike": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", @@ -4822,7 +6042,6 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "devOptional": true, "bin": { "acorn": "bin/acorn" }, @@ -5270,6 +6489,21 @@ "dequal": "^2.0.3" } }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -5279,6 +6513,26 @@ "node": ">=8" } }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/asmcrypto.js": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/asmcrypto.js/-/asmcrypto.js-2.3.2.tgz", @@ -5324,6 +6578,14 @@ "node": ">=0.12.0" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -5354,6 +6616,20 @@ "node": ">=0.8" } }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/axios": { "version": "1.7.7", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", @@ -5378,6 +6654,50 @@ "npm": ">=6" } }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz", + "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.3", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz", + "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.3" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/bail": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", @@ -5537,10 +6857,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", - "dev": true, + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "funding": [ { "type": "opencollective", @@ -5556,9 +6875,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -5872,15 +7191,41 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -5915,10 +7260,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001674", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001674.tgz", - "integrity": "sha512-jOsKlZVRnzfhLojb+Ykb+gyUSp9Xb57So+fAiFlLzzTKpqg8xxSav0e40c8/4F/v9N8QSvrRRaLeVzQbLqomYw==", - "dev": true, + "version": "1.0.30001702", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001702.tgz", + "integrity": "sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA==", "funding": [ { "type": "opencollective", @@ -6442,6 +7786,14 @@ "node": "^12.20.0 || >=14" } }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/compare-version": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", @@ -6549,8 +7901,7 @@ "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/copy-to-clipboard": { "version": "3.3.3", @@ -6592,6 +7943,18 @@ } } }, + "node_modules/core-js-compat": { + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", + "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", + "dependencies": { + "browserslist": "^4.24.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -6693,6 +8056,14 @@ "node": ">= 8" } }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "engines": { + "node": ">=8" + } + }, "node_modules/css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", @@ -6762,6 +8133,54 @@ "node": ">=18" } }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/dateformat": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", @@ -7224,6 +8643,19 @@ "url": "https://dotenvx.com" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -7628,10 +9060,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.49", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.49.tgz", - "integrity": "sha512-ZXfs1Of8fDb6z7WEYZjXpgIRF6MEu8JdeGA0A40aZq6OQbS+eJpnnV49epZRna2DU/YsEjSQuGtQPPtvt6J65A==", - "dev": true + "version": "1.5.112", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.112.tgz", + "integrity": "sha512-oen93kVyqSb3l+ziUgzIOlWt/oOuy4zRmpwestMn4rhFWAoFJeFuCVte9F2fASjeZZo7l/Cif9TiyrdW4CwEMA==" }, "node_modules/electron/node_modules/@electron/get": { "version": "2.0.3", @@ -7749,13 +9180,74 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", "dependencies": { - "get-intrinsic": "^1.2.4" + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } @@ -7768,6 +9260,47 @@ "node": ">= 0.4" } }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -8123,7 +9656,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -8292,6 +9824,21 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -8515,6 +10062,20 @@ } } }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -8624,6 +10185,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -8705,7 +10285,6 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -8729,15 +10308,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -8746,6 +10330,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, "node_modules/get-package-info": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz", @@ -8773,6 +10362,18 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", @@ -8786,6 +10387,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -8866,7 +10483,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "optional": true, "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -8899,11 +10515,11 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8949,6 +10565,17 @@ "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", "integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==" }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -8970,9 +10597,12 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -8981,9 +10611,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, @@ -9171,6 +10801,11 @@ "node": ">=0.10.0" } }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -9361,6 +10996,19 @@ "node": ">=8" } }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ip-address": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", @@ -9421,11 +11069,59 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -9448,12 +11144,38 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-ci": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", @@ -9476,12 +11198,29 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -9522,6 +11261,20 @@ "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -9530,6 +11283,23 @@ "node": ">=8" } }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -9569,6 +11339,22 @@ "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==" }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -9577,6 +11363,29 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -9613,12 +11422,47 @@ "peer": true }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dependencies": { + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -9640,6 +11484,51 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -9651,6 +11540,46 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-whitespace-character": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", @@ -9910,6 +11839,11 @@ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -9946,6 +11880,14 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/jssha": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.3.1.tgz", @@ -10033,6 +11975,14 @@ "url": "https://ko-fi.com/killymxi" } }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -10149,6 +12099,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -10184,6 +12139,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, "node_modules/lodash.union": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", @@ -10312,7 +12272,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, "dependencies": { "yallist": "^3.0.2" } @@ -10478,6 +12437,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mdast-util-compact": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.4.tgz", @@ -10948,10 +12915,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" }, "node_modules/nopt": { "version": "6.0.0", @@ -13405,6 +15371,17 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-is": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", @@ -13428,6 +15405,25 @@ "node": ">= 0.4" } }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -13606,6 +15602,22 @@ "node": ">=0.10.0" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -13915,6 +15927,14 @@ "node": ">=10.4.0" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.4.38", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", @@ -13970,6 +15990,17 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-bytes": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -14403,6 +16434,14 @@ "raw-loader": "^0.5.1" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/raw-loader": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", @@ -14856,20 +16895,19 @@ "redux": "^5.0.0" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -14878,6 +16916,86 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==" + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, "node_modules/remark": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/remark/-/remark-8.0.0.tgz", @@ -14997,6 +17115,14 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -15256,6 +17382,29 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -15275,6 +17424,42 @@ } ] }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -15399,6 +17584,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -15434,6 +17627,19 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", @@ -15467,6 +17673,74 @@ "suid": "bin/short-unique-id" } }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -15595,6 +17869,11 @@ "npm": ">= 3.0.0" } }, + "node_modules/smob": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", + "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==" + }, "node_modules/socks": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", @@ -15665,6 +17944,12 @@ "node": ">=0.10.0" } }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead" + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -15800,6 +18085,85 @@ "node": ">=8" } }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/stringify-entities": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz", @@ -15812,6 +18176,19 @@ "is-hexadecimal": "^1.0.0" } }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -15843,6 +18220,14 @@ "node": ">=4" } }, + "node_modules/strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", + "engines": { + "node": ">=10" + } + }, "node_modules/strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -16037,6 +18422,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "engines": { + "node": ">=8" + } + }, "node_modules/temp-file": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", @@ -16078,12 +18471,49 @@ "node": ">= 10.0.0" } }, + "node_modules/tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "dependencies": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/terser": { "version": "5.36.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", - "optional": true, - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -16100,9 +18530,7 @@ "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "optional": true, - "peer": true + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "node_modules/text-table": { "version": "0.2.0", @@ -16141,6 +18569,45 @@ "devOptional": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", + "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "dependencies": { + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinypool": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", @@ -16366,6 +18833,76 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", @@ -16390,6 +18927,23 @@ "devOptional": true, "license": "MIT" }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", @@ -16409,6 +18963,42 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "engines": { + "node": ">=4" + } + }, "node_modules/unified": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz", @@ -16445,6 +19035,17 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/unist-util-is": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", @@ -16508,11 +19109,19 @@ "node": ">=8" } }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -16720,6 +19329,35 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/vite-plugin-pwa": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.21.1.tgz", + "integrity": "sha512-rkTbKFbd232WdiRJ9R3u+hZmf5SfQljX1b45NF6oLA6DSktEKpYllgTo1l2lkiZWMWV78pABJtFjNXfBef3/3Q==", + "dependencies": { + "debug": "^4.3.6", + "pretty-bytes": "^6.1.1", + "tinyglobby": "^0.2.10", + "workbox-build": "^7.3.0", + "workbox-window": "^7.3.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vite-pwa/assets-generator": "^0.2.6", + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", + "workbox-build": "^7.3.0", + "workbox-window": "^7.3.0" + }, + "peerDependenciesMeta": { + "@vite-pwa/assets-generator": { + "optional": true + } + } + }, "node_modules/vite-plugin-top-level-await": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.4.4.tgz", @@ -16904,12 +19542,97 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/which-module": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", "dev": true }, + "node_modules/which-typed-array": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/why-is-node-running": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", @@ -16935,6 +19658,377 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/workbox-background-sync": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.3.0.tgz", + "integrity": "sha512-PCSk3eK7Mxeuyatb22pcSx9dlgWNv3+M8PqPaYDokks8Y5/FX4soaOqj3yhAZr5k6Q5JWTOMYgaJBpbw11G9Eg==", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-broadcast-update": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-7.3.0.tgz", + "integrity": "sha512-T9/F5VEdJVhwmrIAE+E/kq5at2OY6+OXXgOWQevnubal6sO92Gjo24v6dCVwQiclAF5NS3hlmsifRrpQzZCdUA==", + "dependencies": { + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-build": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-7.3.0.tgz", + "integrity": "sha512-JGL6vZTPlxnlqZRhR/K/msqg3wKP+m0wfEUVosK7gsYzSgeIxvZLi1ViJJzVL7CEeI8r7rGFV973RiEqkP3lWQ==", + "dependencies": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.24.4", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-replace": "^2.4.1", + "@rollup/plugin-terser": "^0.4.3", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "7.3.0", + "workbox-broadcast-update": "7.3.0", + "workbox-cacheable-response": "7.3.0", + "workbox-core": "7.3.0", + "workbox-expiration": "7.3.0", + "workbox-google-analytics": "7.3.0", + "workbox-navigation-preload": "7.3.0", + "workbox-precaching": "7.3.0", + "workbox-range-requests": "7.3.0", + "workbox-recipes": "7.3.0", + "workbox-routing": "7.3.0", + "workbox-strategies": "7.3.0", + "workbox-streams": "7.3.0", + "workbox-sw": "7.3.0", + "workbox-window": "7.3.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/workbox-build/node_modules/@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "dependencies": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "ajv": ">=8" + } + }, + "node_modules/workbox-build/node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, + "node_modules/workbox-build/node_modules/@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/workbox-build/node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/workbox-build/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + }, + "node_modules/workbox-build/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/workbox-build/node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" + }, + "node_modules/workbox-build/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/workbox-build/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/workbox-build/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/workbox-build/node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/workbox-build/node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/workbox-build/node_modules/rollup": { + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/workbox-build/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workbox-build/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/workbox-build/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/workbox-build/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "node_modules/workbox-build/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/workbox-cacheable-response": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-7.3.0.tgz", + "integrity": "sha512-eAFERIg6J2LuyELhLlmeRcJFa5e16Mj8kL2yCDbhWE+HUun9skRQrGIFVUagqWj4DMaaPSMWfAolM7XZZxNmxA==", + "dependencies": { + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-core": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-7.3.0.tgz", + "integrity": "sha512-Z+mYrErfh4t3zi7NVTvOuACB0A/jA3bgxUN3PwtAVHvfEsZxV9Iju580VEETug3zYJRc0Dmii/aixI/Uxj8fmw==" + }, + "node_modules/workbox-expiration": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-7.3.0.tgz", + "integrity": "sha512-lpnSSLp2BM+K6bgFCWc5bS1LR5pAwDWbcKt1iL87/eTSJRdLdAwGQznZE+1czLgn/X05YChsrEegTNxjM067vQ==", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-google-analytics": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-7.3.0.tgz", + "integrity": "sha512-ii/tSfFdhjLHZ2BrYgFNTrb/yk04pw2hasgbM70jpZfLk0vdJAXgaiMAWsoE+wfJDNWoZmBYY0hMVI0v5wWDbg==", + "dependencies": { + "workbox-background-sync": "7.3.0", + "workbox-core": "7.3.0", + "workbox-routing": "7.3.0", + "workbox-strategies": "7.3.0" + } + }, + "node_modules/workbox-navigation-preload": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-7.3.0.tgz", + "integrity": "sha512-fTJzogmFaTv4bShZ6aA7Bfj4Cewaq5rp30qcxl2iYM45YD79rKIhvzNHiFj1P+u5ZZldroqhASXwwoyusnr2cg==", + "dependencies": { + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-precaching": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-7.3.0.tgz", + "integrity": "sha512-ckp/3t0msgXclVAYaNndAGeAoWQUv7Rwc4fdhWL69CCAb2UHo3Cef0KIUctqfQj1p8h6aGyz3w8Cy3Ihq9OmIw==", + "dependencies": { + "workbox-core": "7.3.0", + "workbox-routing": "7.3.0", + "workbox-strategies": "7.3.0" + } + }, + "node_modules/workbox-range-requests": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-7.3.0.tgz", + "integrity": "sha512-EyFmM1KpDzzAouNF3+EWa15yDEenwxoeXu9bgxOEYnFfCxns7eAxA9WSSaVd8kujFFt3eIbShNqa4hLQNFvmVQ==", + "dependencies": { + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-recipes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-7.3.0.tgz", + "integrity": "sha512-BJro/MpuW35I/zjZQBcoxsctgeB+kyb2JAP5EB3EYzePg8wDGoQuUdyYQS+CheTb+GhqJeWmVs3QxLI8EBP1sg==", + "dependencies": { + "workbox-cacheable-response": "7.3.0", + "workbox-core": "7.3.0", + "workbox-expiration": "7.3.0", + "workbox-precaching": "7.3.0", + "workbox-routing": "7.3.0", + "workbox-strategies": "7.3.0" + } + }, + "node_modules/workbox-routing": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-7.3.0.tgz", + "integrity": "sha512-ZUlysUVn5ZUzMOmQN3bqu+gK98vNfgX/gSTZ127izJg/pMMy4LryAthnYtjuqcjkN4HEAx1mdgxNiKJMZQM76A==", + "dependencies": { + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-strategies": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-7.3.0.tgz", + "integrity": "sha512-tmZydug+qzDFATwX7QiEL5Hdf7FrkhjaF9db1CbB39sDmEZJg3l9ayDvPxy8Y18C3Y66Nrr9kkN1f/RlkDgllg==", + "dependencies": { + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-streams": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-7.3.0.tgz", + "integrity": "sha512-SZnXucyg8x2Y61VGtDjKPO5EgPUG5NDn/v86WYHX+9ZqvAsGOytP0Jxp1bl663YUuMoXSAtsGLL+byHzEuMRpw==", + "dependencies": { + "workbox-core": "7.3.0", + "workbox-routing": "7.3.0" + } + }, + "node_modules/workbox-sw": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-7.3.0.tgz", + "integrity": "sha512-aCUyoAZU9IZtH05mn0ACUpyHzPs0lMeJimAYkQkBsOWiqaJLgusfDCR+yllkPkFRxWpZKF8vSvgHYeG7LwhlmA==" + }, + "node_modules/workbox-window": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-7.3.0.tgz", + "integrity": "sha512-qW8PDy16OV1UBaUNGlTVcepzrlzyzNW/ZJvFQQs2j2TzGsg6IKjcpZC1RSquqQnTOafl5pCj5bGfAHlCjOOjdA==", + "dependencies": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "7.3.0" + } + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -17131,8 +20225,7 @@ "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/yaml": { "version": "1.10.2", diff --git a/package.json b/package.json index f8ba271..f5b6940 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "tippy.js": "^6.3.7", "tiptap-extension-resize-image": "^1.1.8", "ts-key-enum": "^2.0.12", + "vite-plugin-pwa": "^0.21.1", "vite-plugin-top-level-await": "^1.4.4", "vite-plugin-wasm": "^3.3.0" }, diff --git a/public/qortal.png b/public/qortal.png new file mode 100644 index 0000000000000000000000000000000000000000..005d3535846ab77f76db7b6aec684bf5a3774ef7 GIT binary patch literal 161247 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

BswZD%>-Qh0e*HWnw}=TA5fR0u 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 W*uIab3dxrM^ra-PE>|XF8uWj6>rp}g literal 0 HcmV?d00001 diff --git a/public/qortal192.png b/public/qortal192.png new file mode 100644 index 0000000000000000000000000000000000000000..1c68ff312708765bccc2116a727799b9a65153f7 GIT binary patch literal 32392 zcmV*GKxw~;P)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00006VoOIv0RI600RN!9r;`8x z010qNS#tmY4#WTe4#WYKD-Ig~000McNliru=>r502_sFUfNKB%0=;@vSaechcOYMuz*7ir>k`Syqm+ zS`>zE(J4r=*LlCcm$vkZH|NI zP4H9)lHC9-X+x8@N@~t@`>Vbcf}&kTLwo+D5KFTSVlEiXhnS@&WzuM+MU^Hb%Y4FF*15x zWa{qkoDK5Nj?fspn+%M8D9#g{3J$|n<^udfi12Fqi46=8m;-O+$*B|4-G%QI$C^n<2v;Rw4)f{Kv72cGJ<^&j|=8le-fzONrzQ|?ysvDAFy2VeQ;BHK*% z%JH>wyF1^Qz5|I~?q5U6y4McBw>5s$Dyo|I2 zz${28-Y@vw-MAN#mTw7s7cj)$qK2mQ#a4Vvqhmb{-`7MNOW!#8K_88@Zjb8c-7oI~ zVH4fFk#vlGs%U$e2>$?L=?cfrA4RVK0D3t|L_t(|+U>n}oE*n_=ly-EduBEPyI3q1 zi<|?&3@}q9B}U~eS>Y@Pos*p2dwu6SzdqmTobG(``I3FIeU^3SAX}DXD^nCHiliuJ z5+IRtUSP4n=H1zu>8^VJ=v3X)vjJQZl*9+0!OnE3uJAlh{hm3Nb{)mG8*!VrpxhN`zlpY`e?)ckAAwH-k6h{s|45L!)D`&Qr+@$4+`djcOp1jn}`k^G}Qdi)IjhZ{&2Hqe|3*Ja& z;2TA!xawY~b;}>9LeI}3g_U4jj0OzG7$g(~5kbV6r<|timD_etq4kC)T^%@j^c&?d zZ}J@1{?<;;Jo5KoE?nv+{!r2DGr$~CY5rTNj66LoFl|L>DqyNL7%AgSY6J%)7!qT&XY|BVx;F6X>c~r9ZSJ~vZ1Te6wDkN8 z!-qZ$j9%&{|4`8D0`M|PIh|bo{tFy@;N2{|>Prmn{_t|AW%G}V+xb&SVS_Q6SmU=h z<6p57Z~Zd#{5B}K;QKHzgmdIH&Yt7AgOyki41n>*jX!?BUpeT zg?U^5lN5V?72MUJ6-I-j@K-OyyZaid*KNjcpBI-R1h_x>Ezs7AUcC%|*#gYu7&0<} z_#mPXapsBBe6uRF-#L2xo8nh59GkcDUrzY7L2mr_yXk%GpD?=bQg_Y|9K8;KW=zev zrOWUtFVMX3$Cx<(_q2BZodTNNU?P54kkyQq;vBhQyu(%_aFSp?w@g*R{v*l=e^ZHs~8`H!OSb6 zuUj9$opsC6u3M*NrqenC;1)1T7NOTJLpKQw52Ge)AuLjksN!02THjJ=S-DPE`%l$I zzi_6s;67^QA>7tpboHlrJ(@f{y)FRLP;2)ufhmIWsSG{UYW&Lk-L@2(3fA#u8Jit90cAEkNM zGdcK0r_i+NhC=&IzpsitzlM}Hf_5+l9MCH}@$bHx+Rc~aFKo9MT@uE&j^%!!u|Nng zuNA#^IsTFk%;XqqWC9ZVpaZ9Ln<{kNF>&S_o?p3eq;=WPSNtjzJ6=Lp`!V|U_(2}M zE&v&7YX6rkzwsMXhM!>B9XlC6{SWJ!7QW}#RB8F|ifX>qKoR2u2GiAwe#2&JZ@LC= zc{j$%=}w!sLk7=GP<4v9( ztd8tIRchbl)y7WXw%mcP?gR9t?&8-Jy)FPDiCW91pBi9r&ws_Q4dG4f>8K3u{c+W_ z`VYmO|308ShC6LV^tM&h?%j&Fc_pS)K;q1XdiD-@+62rm!8Hx|-MWcHYT{3LuHa&N zy3y;Fp-ToC9zn`pWFHhz#ZAg>eQTj*#cEv}IQ}yu|Je89AAJXJa)@I4JMb$zgZjKq zuL}SaXzKiVboDfqb7!dxJk;bgEqROEdgbqn+xd&&EC(${8z>5Ta}VBq*HGKG2|d3h zW+Tbf?3T|*B>qIgKnfLQYz3R?8wi7GDh2U_Ro!X(0ELLttemE8%9(ffjz7GAo?q!Z zzF^Dm4OfPrWAV+~89)61MxVOWoqx&cM_K?xT@L0vrFmCT9sQQLO&hkAI`8^zDfIjX zs&o~`cg5(yt+`}A`tB?7?zjSf(E^N;M2XsrOja1C@%f|#K^mF}sj^ca1@q^j*Dc5I zX~#^CqejMU`(U9s&2Msw3vR6r@2Gj@(?|Tt6IH*`M|00dsEr&3MlSVGTr&C*6@cZy z2uzlwd1uEZ&Sixg^;yCiy6Z=~{rlemQzU2}VXj`#=y;BC5DtoR)jt(&8)pN?(6< z==;5ew#$5P;tYkht?24UfJZO&z`W}8BP0NyM7cX~THXRwXW{ zSg#uWh(v;~r`XnpS3XNi&)XS0{i$Nxl3%$)6?=XKXWrcynhYAWhJwPZ?ZUrx6aLyI zXx9w{B7!@nzSE2WgOJVfPcBIKGfaNYy95D@X>6PtrXDc+yb%y>khA@`&+MkK?<}fp z5TyXYU_e*)>)P<2*2a%~zIFMJof$vSFV+2_I%ZG8KCefm#i9Ifa1|1;IW?=SxRAHVbor)l+n z6nEZRfR-5Ubd)f+uE&4tR=f?%&;>VOQi-+2iBEMUuiv)IlQ~Pl+@Zf~}u`~bOZSDD`8=TVePvEq@9Zb71A&g5-ZCpncStbGT3Mp|GznV;^XPuI%z_ zL;uU0IR1sEg|}ZQ_diPWqRSXPx(lPf@G6h(D@Q*B0#KmPauX|VdyL+v-$&1_U*g;| zKfS?eTKTi$c77C`B_>Q$27~Eo!`!?Mf9o3jc`d`q|xv2XT}DI4yGWB6$v)h zO~yK2S4Nh?GHnu*I6L$2VfLeFK{ct>P=`kX3==N_ZbI*-~&JMhI`T>VP3t0D-F-Cj8gkLRF8-1a>GPL)no#r+F zNu2rbF)%O2`E4%Tx(5BOZTMS)^o5jb2kJ3;n6eQ34mnMda}m^$%)K`HQIcGmwh^og zMXcvZ9kh}>$Yi@d>mtU}d>|>`aWIQI(HocJw-t2zIL*s3-Y@cloOb;KDgZ^=7XKpJ8=*FFmde0)nxxSAR=4e{PpLxZ z&w{grXf19@VXj(&dFR#WTQ=d(Z>}%S4&7#q+O!=TWp|p`a21ijqhn=srl5Z|;d82B zS2ntq3HwUUC%IH+^~sVc7BDLo?yj$+$3CWpV~IK_3>H+A0iNmX3@Z$y>07!xi> zRKS`Jc*j-fyRX1s(ix~@lW6RK-)x4f4{YTzOv00r`fbk&(Q$J4IrW^p2yl+pPRoK= z&z>TnHXVrx3~t+L)Y^<$vlM@I7uugh21k*a37_M3h|_eNDlE8d^88~pf3o-3yw(45 zQr9METIl0VYzDLQQZvb{=m$suZf4!R-)6M;8!Wu$OH|H3xT>`9oxkQZul_xpmOH>S zM`_9|YJqz;q2GBe{<_5&M}=@F)TEHhm{};3KpNY{XrGfh?Vboma-vQU$PprP1R~$> znfaPn2c}UOc^_p}F{+2v5YsU~fJIB@Va7*M!)5RRQKD2~sW>h7Ii;?vz4FQa+UUMh zh1M;8b@Wl3<{L1zgO{3FW=yYn0T6c`UF-fkMxUlusZkx-zQC^xe9&oL_lM$kd=R9= zglWpmE5MB_G4I`qzGfAsr4*H7mzZKqk(^7S-%|-feB81Fs27G@TOm_^GBZfMd0}5O*KKccTUIUA)xi@V{My(1 z4}RtQcoY2;+uwp;sRFOHmzQN?Y!9=Uw>+s?hZ-24@v$ z3DOq?u2>B3y%ztD&FGGnu#CvK%~7Y47KGqS_+NIFmi0HX0#d2Xagp_cWovq4ON#Y4 z`(x7l7-N4#P?m!3Bp?#nbaau){ zZcuL9J^Q}+ttP*E{^) zVc>d5WM@ccwRU22NIqjyFBFkSHs4;z*e2oMdZx}?l%3t;^U*eq$+1*ftu5#c%kWos zV*E*DU@Wo^M8sXFoaQ@Kq2p$6;%K=xaq^f`?5OJ6Fr@`=!yA9%)ncDn(Q8Toq;NIs z?mWxHg@NumGz)2!6@Aw_3?}8MabrcA>LRW_ijdSNvAK=XJ4975r$gdm?X2)YI-aX86n6*iF715xhM75 zlpt8UK}6SO&gYS7@FP#gq62l2V+g!Brhf5z>e`%{roN!ey_p#!IcAXagSb!brSSYo zoM8`gc``lYQrFer1T(6+c6DXb&)UNjUKm2*6czZTk0k6MI>(>Az7Kc(l9kxxGK5 zn%4Y*IBoAX&=#dHu%L+Cxeoc@R?O8aFiizZLrbXXqJhd0f{0&YDG2`N2aYU`$Q&j| zc^)^RRPWoeVj>W>4caPm@C76Zn z=*w2%_p}D~!B82|f+)v971t`K^(}7m^3DEaZ*Oh*^S!Egh4ID@qMEP5cvk}lUR7>< z6$RjvGY* zHxdZa-|HA%+0U4Y>GPgXEO20g_`%YJ=q+LVU~mW-tDy+euElA((sAeATWGtf%dhqy zS^KW<4vn68oSvJ0m(gSY6IjBlH6y$V0^m|?y9>Yi1&Zx=Q5pW4a!VVpFLvDW+p4(a z*F_aK8>5{dO&QqGiTdEx$Xm9cm$au0!C3R#BSj7b%DE=iZqfCY_KI(h9#ZGX&lH`R z5t0cYDl0#IqM-}Y`W@9R5-6LmjnpT;rTAVYFvZw)Z=m6mdrjPy+`8EeZ(kOFF_|t8 zt}{(V%<3Ne%{^$>LoSRUlRkn%l-n*&)2&XSlouUgp(C_TZGR#>fKDXI?QLeT4*|mH8`w6R&&>r)4Al z_`VLmHuxc@Y0V!hxBY`aN3_ct?FG~wt5F}_hS|0f(_GA0g2@b!(v6v>{@U8Je`{bS+l$3%? z89{Bp^eMlW#rWxjJZsLjg~Jg{R|op?75F_ZFgl71l_9VXT&0Q|l-u^!Li36>+8aE1 z$6tQq?CFP}!5jMys`-tt*s{zy7l7u@4}exU%`2I_aDT~dUVWcCf9vllcfl`%vocO! zC@ExXH_nHzLGId&UeJopE)@?4q)toalI^u=06((YKBJPeJP{l<@khn?N|xuz&Ioy) zl@wQ{v}S`A+006|ya*~uO0en;k9{bc)*w1&?t z*0R2c317j5!M5a*6^NM|8w9n)_AZ5gO}HM?U@|aQWnvE|@yVy1?WrJwWKKuRCIzNk zDQO4Zrd%DCJ(s^#gvF31=h6@wWiWk%VGr-9k1W?eFDXB8&;`$zvlnn5-%H_{lQ<*V ziXUj-_><4*+Tf?Fqx-+ywD8u^^0@~ow7rqZ3;!MTIp%`qKmgiU{)Q9u@BA2xuKg+9PyzE89lC7HvStbh-_7+7{Qc4W@oF+ zS3}Nn%q(0?6=v$6FZpGsS|OP(R0=#Fb)*;f;awDVpGQqfvOd{(){C`SUZ(0 zBXtPFhAVbnPaVWof@qN8D$+NBI$uUjdPvcMf=WZMtWA)8Mh<*w1ZNW4R#Hug`^@sF zWo;#siC5Hr>8}GCYQ8jVU_F%1*d;1^TkZ_-$dD#}videUvmO7xUCr zC4J0FAk1`EF0_eG$1uh|XmZQR|D9v|M8UhEF`TFNQ+WI^?%4|95~PzG(3O4K8~wA| z#EH+97Tj=d^u*_ITGrx^{0$dF7bO54>;5~n@qN_B4=yaW-}o!4u;}NExQnrNS!3lq z)ZOcmTh?OcHBSk0O+%rGBE4yDH&I{5I6tR3#@L*Z+vIePk4Z6i%v>gzwzGLc(KRtx z$?}S^eC{=a92$lvj-mFRM#jbwtU~`Z+p1YgoAz`4(rkzE%Sz^H?;{I}VQ>Lv25f zjEo`@_LDLgM}nt&1~Gxp)EJQF5?sC>}jgVq$lQqt5 z^CND4o)DxOZ;frvg;@cxPfMdRGuL}funfbrTw=#sqG!b7+6V$o#rWCjR0J6vM?G_h;&=Aro~OR{x&U!>rwFug*Kq?wuJdgRuJ*X!R zA|oS!h9dYY=25$D8MP~x;xFz%x3{1>TG2fn!Hf1fT&R>$NeRBD*x=TZC5;rN1d zwP9Kcsjje%<=C)oXc0Fi+we|7CEI|n`vi++5D8|N*bvSby)&tcKwa*UO{F4cT@U`| zZp`Q~>g-rBYbs}j;PfxL{9k|n{PRD7@lVc}zhKq^P^5X$+cCO!w<<3ADZs@TXS|6ZzJWu- z@YMszgL_bC`U9=+7<5lFwQWnOU9}Xyvo&!2LGn-oxGrYleEb!i=r)1ziSWH&3K^Tk z95{#z&M@noXIgPoHQ!G}zvzIw??TNY$tuneJQzL9zNZSm3Xc z9md+*(3^VT?0KBC6Norsj9(f!@aeDMw3SWm7l3cgn4@PY0G<5Ub6;oQ|M~Vuq|kXM z2AE$$ePkPYNqc}(4-6yUI*RN&kF*w{y_8N1j=b!dsnBFTbzBX0dKQRJ4Ib)7?XT$#DcJkL7M71vcweBcjMOPavX%D`S)zDwujoNuC zXy}tikT z#Uk{!rTD8CVBBJG`TSPAZR_z@4pP{A2KV?Fs^%b*70fe-PzTRaym2l14QucgwZ`rN z6aOFgn$^r0&t+W&IkSKjpoFyXl!2X%#;sm?5O2>^cabev22}4D&LhVuJbesjcobD~ zD7M|g&<#t2Lb}`0Z6%y?84}%t`cbwFD9IdLpMkasJ$0Z zWkGO&5g4ApJu!m1vIl=_H-6WAP+>~I;!dia^YK=nr?Be`?zt*TJ1{hc`SwoK?$Z=+ z-+;e$IsUw2679|~UtPls0pAbC*MKRwn1UNICgPM3Re)Dg0P6&{jl!@JjSob|8BOkeWh#joIIa zIWdS_yBvA*2F$tz7)N432zXa4<2lr4QyI)DK}kF`CJqv0&P7aN!k1Chxijqsg*sA_ zJRMPBY8nm>!FP_ru2ZP;1fanY{O)GF%ewKGFF==E%l)ch@B*@9KhBObs6m5J$UN9X zkDN!I7^84_5NBHt-sN5Bc`aZ9c%)bNQ0?x(JJLsCUq9}Uj~EAY@&bHe7-z>R)SEWq zZC->fI!Ve%WqTqr=*$JuiM_rb#y4xB-1l>qJ< z1I;enxPr;cyTkFd4DUR~m<7#F02+{lt^^=p@&QsXB3Vqu;yp`=}$8ziD z&InTtYQ~w6nptx^bWF$i$vCELJOk>)2=2qjC_H-tH8zGQov_2^F8q5oQN3yzUdc`F zYslx*wk$<+A^=gsumEU-ZYjZ?S0Gocz6RVEF|Hd1`94h0S9ztzdjK8fLZ)rO? zg`i#Y+wg8!k6ty1v$vPRi3ybFAQO|A#}AMY=?)Z|kEf zDRm^40%2y1Zw+ox4*?mh;5^w&;nBmmX9p3yc&u66ih1KYs<*DA*3ptuNCDOFdT}vy zF#;g<(a^+T5>fGQ-Q9tF;6`NYD)hJaAlv(pa`^IvqvtXIcL<(8jdSk?^koat1qt`} ziAGD3>34=RTbwhJg+-PiBt~syNZ2D3it@OJdEp%9;iGW$EaFuIHYf=G%K3O(7UOr% zPe)gM56<+WoPhs~N+;cU=xG*q^x&I}cU8hlZY{cKT6t6X%hZQ*z62sXR4l{KX!veA*6aUR< z{8V5|0h2z?i~SV7a|HL`S;VgrZlD5f1-M}awYxS_UD1h7*$O6I)*R3*1t9;acEC5@ zpo$>t(IA)&OOX{_m=}*@9@v8%8by4CR6We|CoqTlQ8%qd?pcRk(uPqwB^gmIHsjzp0Dbx-N8UDK+Y5d=%Mt2^U!n=Mk{>lZILb!yqr5W$4)%YvB@b;djaAX*#tOEOB=Sk$) zd5T+C;lF7;-irB{5Na5R_b#gMw|qpMaxv5y!WtKUW;7468+$AuP@kwmx8z*CfrO3LdI=~fXzA((TMn8#0J9zBAb8z9&S z-(Z>*{+b2UHZR6s(2}%6z*HuYV<&K)J&bdB3{`V2@6KF}R#q4^R7YWV8TZ5x&XwJG zTf6ZW%?A|-o>?#t@5T-2RfD*DdMTVPBc20eWz1vyarU0UxqSov&8zS_o6zwNazVoy zF@Iz~WL=u|v$07gTLm)V;cV~6edsXm(LO}i0`HDC&|bpaw36Cgo2V`c6DLzU+F6XB z@kBMYbzf00Py-`027Z>S%%mAKHgR=$>GCe*$8W`4cM9{sUS!vWz-{XJm|eY?qZd%u zuSD)$k6zu5QG`D3aF9ZGXZAVMRXqkoBFLDBe)b&Zp(DuAK17E&-xc)o(4W89_UD7q zUf|DM`2GN zZhsAh3;n~Ge|iDu#Zx$M+DPr19=sN(9&Jc{9diBpaX`kpPe43_IxvL$&{5o%0+(?F zcT7>>s-^h%Y^J)g8_!h<<2P`})^|rE33#==K#^vS^5-+@md4((nFw|Z#y3H*=gQT{ z+8)fahcOTC58S3W$V3J6=t0`0aj>uYA;cWlps}&*)@QE@F?s& zja14+X{EZWgtw&|Z`DF{(emdL;rxR*FP_1lc(`#GYi;yG;xd8-<9D`Q-Qud z)VD_|9vH;GZZY2aPE4s3^i83F-mr|yk_Gq&&QLgT0cVUr2^~BOCofQVVL8rS8>nsU zM7yB%^ail4>gwE@fSfJkJaU4<6Gw4|h7oi)D;QYYfxdea)$3N`wG@MX4bO)KHsCWW zo9{$oc`1K|d%{iB#K zy$Cycaqr!T-qwR|cEcdAt&+^twt)aX$njD1gU8|7Ow3c$aoF)>{$vAAI3S+ zj~FjZR58%mgt>h!wcFQITQmea0voo*$TvN$4tZQ*&wlTLf4}) znC=ebeb>X*mFNd{BQNwJWe_cx1AUm2gUIvCaPHfP-mn1U27Q?Lq)o~Tl_+OH&P`$- z3vqs5i1Wo@N(z5X2i|32-i{R)Fx5%q*h$nghj5OJp{ni_aA!Kre=)|eke&;&_$2zR zh}@=ZAICj5gu1o|@9IV91+78+1;KQ+Q@gnhfAs+FuG19GRD$&f!{eBTcH`_ijq}Eh zRBu|2*O53O;wNRtGdw0WAGLoF_rW6+_Vgl^ava4q&B1o|wR<;HUE76rB!%-O%MWRW zu^(hhLf?y-{1EN^zapU!9v^XxgygNKo0=fJN8*Ej~fw3XVH#rR7)Fm8hL z(H`dP8PxL!aCQ&ijA#A%jbKmq_iULyH*{@vaujg1-MeX_5NepgbI2nj6b}sGU%wcC z(?WD>OQ39&gI>OfYF9h{u|C|r=Wqu+#JHH=LHNRR6nC7&y?Yb2E4uNUocx7L5!^XF zhWp)PxKAI)85sq1phFa}t}}?@Ub`HxxsWh!oLgiXwzf{|+svPp|Cd2?7YQ~3V$!=Y z$8|RKvs|R9!)1WR>H?!RQYs)fZh%coF^})ZJaPneW|BCX{Iy;1VlU3U8{vj!=y`=C zu}%c48unbkeESIOI*n8&6P&-WgtvJS-kOEzQo({dCdk`)=>YBvr*Q`eaeii-=Z2X7 z;*H?O*QQy+&iAbM*I1mP%ji?5Q0InF`@7NC_u#EsfGHFLrYaSKx4bOt#NXdb;ou<7 zIEV*kPcL%p0;OwL;@`QR+PV&WWx13^V7Q7r-HZG1Vcatpz<9CxF^gI-cdVs)$9ifD zT7rJfv(X~kEe9^=YW?VhQLh~vAl3gm@+q5)+LwGG2pbm92GQR*vSy&Y6?xlMxOx@( z;XUxgNz|wT6db#N`9IGh&z;7(Zv%Q$R}h&!Jq8aS!#sT)868a+zs+x3(;qL9Fa4bx_E>|$$KZvvMEb0yG@NQj& z*HMD1j~p08efKEtzB4GVLIgcciwoB;r*`*7s;d{Gl_ZQWR%j^f8=12fcz2w%7lB5@6XJCTKrXWL@DE4tD1TL_%>g6Zz0);SM< z^#$DBXDFPlptOq_7=^FDg!}YyRA(!cJ*000DNiQZny$dch3NY>Q@LtskhG>VZg6V{ z>PWYxcjd66JeKW|YL*?*pJqMzOlgj?L5UZphgl2But@gGuszus!4`tJqe252g6$I> zg+=EW0Xr-ucI!S89tyP^kgX-D`Xy|8@kL0=#8{9c^h z{m7Wa=?nFG+Uoo%zel7}j9)K8_1>KHlOmMw5Qu|LU0ZJ$H#!#109Qqw*Dc0dX1Pt9O8A$rVsb$n#ix!_9Msrv&oM?L^NMie3cR~FQCrr5mJF1! zyU3Pxi}~XKY6|7#Ac?3T`&=qr;^Jve0%3JtsmRi|>Ki+|q@J;2K_J|aDFul_gU@ou z#jt2mJMw{>Fx?%Pzj+C%MEzv&m$g#8Ze_6fZd8wnF=YQCoaYXsPL)x*P}c>L8IW{c zr?v+q*8J)AC9T)c-h{kq4c=QfQ|oTWcm_GtkMs4NIM1ENnbf33$6op=IkSN^5Ou$k znwiwGAShxcFJQKh;2yhxbInq`tGk0m`;pspSr^qCK|OX1cOn{7gIe5%|IfBkxpHZ4 za$O#Oq_up=yp0Um6!S-nZ%fWh7;|PcH#0zJw4~}CC$U*|myDJYTUUltbtEGu1%QZ+ zyNpp}l!9wlqaQqiIzAfQTvYg%_XO(_j0v(ePaa1-u?y$mV2JbWY|Sj*ulMMrLsTn`*-8)A3*7}3QeA^rWAs7J+r^^ z1g44}>_r|Orf~2A&W+2cUEUoh*_ovasjVBq**Op^2X)gL#bj+C%oF|+nkJ;@#KVzT0%}`NUCe3EC@7U5J5Tw|;4yLJu zVH7b2-QJAX)tYqN>618LeG;e7tAjh~uFHD?M${^q;=X+5kJ7i*hEC+YS5UiQ6*Z@b z9uH$4(auxGV5ES5!)ElROJr2Ym6Sn zoEpa&7)R+Mm1}}`FryUS%1-=)L%5USSS@J9C_${WiMmiw%ny=g{Ja^~a{6VRCAkT3 zqUy`2yJ%)K=aJwfMImd*m_(1o?f?@*mOz8p?!Jt#JCjqCV#bu)4CrXWgv;lQ_E9^J z;Pll(=Fd{T_a=K@8lKOGJqC^RwBf#GBmUhRsmyOfmkmssV4Z{pbTmB@jJ{_uEv zZAD+b66c}axKA8I4Oe49NLV;41bOXgbS}@cB!I@BIE(we779z|Q(4*+K+lerAU$Mq zGPz4gjYoQ8WBad3JUltP8KIlFWp!Ir+Eva#s9d@vGeAzGh!#+Unm)B%TqFo8=>ZeE z$}$$S?Jderc+1huMJE{??IXiw>a$n!em8PbHq!o5SFXVzZ6yk~uR`9rh1&9k)GA=c z0*!Ap7@t50vW8s1gP;t0@gmGe-+;bxEzbQrad(_SDv`4^y2*w^^&X(~0NGB~JRwP} zRsZ}ciu>17TiS}oM;r%API#xp$|~ND#?tW7Ob{KFQm^Ao2{wZt=P@=FSM1 z(^Lq%u2BOcL5LfKja1XV;%L=ZeGU;RAxPbm?ODm`o+&Y7a^uXpHH-bNJ$787aP2Y* z@7;oT#Zs!;4d9MX!2G@>&?ow&1R$)u63`msIQSb^qSq|O-+l!5TcLemY)GRB9$?qa zOi}x+L2Ta_iAV){hfsPsT7|nrT#`UiN~R zWZUxe{`zqqzvDXewpBO}?#BJz5!6WVg1ID@YAPEb z-Jj|2k~uzQvy*YOygA8>PV8&NmX0J}E7LrQZs`G0p5{xnER8mt?!HAKQ z%+R!tJL#~r$EcpG6Cp@0F_tTJCL|4yN!|H)GOq(i290WIqWP9pG`;Ui^oB*$v|uVG zh5~V*#v}}Hvd~A9b_d1~hz7fU1Po-Bbz^>_6Mgd zUh$PCy*Ws|=Fd1Lu-clz`e6a}jGz5(yGG||)}OO%(CAz2&XUA$AM2Wo;6~n}ddwa$ zcAVRvf>PmgtayRRD`Q5KVLmg}6%t=px?(9UKXx^8{R+IIi>{iKOEBdDi$0ddH}-G) zp%DXU0wJ*PjiFKKEpi2NNtnN`9jWWn zT@x{X3gMme^%#<3W*3VuCl9u1P1FxqQUDHrY)L3eFh17mHl5ux5Vm#Y9V7W-`?=~m-V49ln?!FRr z#ZuHCeVfAW0W#Zh=R8xWpCxP^{xY`r##YXeP%2T9@?NKKujaiJl=&RM^*J@}Du&5;(EaXSHL$s+u&R(u_T zqo@ih`G^WTC3@+bt?q>6T?M&MAZ_iZqe{s^`x*_pvlY|bWbs9w3)h~{X2$5bRkXO) zW`1Lxmm+EuJ@4=MV`& z8UrS-vE*FJzifEbg`l$iB;GfUA;aaM_Z&$pPFoP1;1df)^pVW>_+Arygp>fKu2W(i z4`-JC0o0eC#C`r8l6vLCG&p`1N2jES`D5m_JUexaZxSA{#xeB??FWf6OB+p=jVdz> znk59XEgbu>tr4QdE!IohLXM4$FELS?KyHdAh5W~87}V@tlFn`1tS4Vz7V+4KF8j|i)JDQumX8Jx!F?BRP(Jv!O-cK~3WT0+* z+!lyXT=MdNOx@Gxgf3P9>T6&kh7MR%g=x%D3Pdva#xi6jrRD3rMbQu?5|TjQ`nbc) zs+*@yq@gGn45cgAp?>`qye5S_b_oA#dypfen4tlxUme4H{tSit*5cf?0=>93ac4$Q zJ^U*XoC$+su^Tk%1F>cSTk|`FP|Jr?eK-&A!u{S!+(ADmTq(G+8~vUw)UH^Jesn*D zKYtokv0IXz1uoJ96y0kn9Z4~)B!;jBg`!N~^e`l_HoPGo*Sg^{_0OMvtf!|r+88V9 z&m@RN3~_>!fv}tSZ{#Ws&f4MmWaF#cq&e!h@0&dUnknP~!J)Ky73C!zc%DYR?Hb&z zt1$QPMt}PdYM_cf-ACpB4&glAi~F{9sGFBy+Df4^2DY8FO%kGNn{F-B^oXE3B)l0iG{#O_(~;-d_rYu zshO+3U@E~dz5(9^JGXQ%Kt6U0>c%ygukM5=Pa~6M{M{$ehtK0YxdQc$b(qT+;uqWi z%X^`MSAtOn;}BnmWdxub7$oTC|aQ8aAcWkD%d;!`6 zDmuYhYW8Yj)?HL^gqeAP6<9e#tWwqu=#HUyKAu$+#L!8 ze+oNUE<2Fu6HI=(Gx!vgmGpghLqQ`Z*DjCw%TI)&vX#p~hoEGqbgym}NcE>muR68i z<76cVT7g#po?kxF!dm~mJf^)paumFc;+^mC!1(G9v`Mx6)ZbYpRNJvZPLk(KLbm2O zkYf3?)A_w#fA;g_uD0er1-}7CeB7&yrUGO3&0@+3*ggQyZi@ z*pG=0SO`*<$abUBKGV%ngaYF~^iqc?tsn)4v@E>d9EZXx2RYyLbH^r+LGABpU9_o* zrC1@d!_;Zit?jg~yHMLA8F1EQ5~?tc=*@iN%^9GO6<JGKLK)#>KO?Q7GGOuCR-FzUH_FnAHdWQb%$>#-;DG7U3VsK z#H)J|lF)rtB2uCMR;(BOo@!#pWkWS{;^(npdY*IlxR&UYzLrvWlT#*RXNwJIpBvmO zYg*QclChG_8F?~}Zq3if&$N#?#Kzc!Gukr|{Um$+pv>4Q=RxiMlJ;W$pbxce&UkL4 zJZ*Xt-VLUrI>R*ah#VSH*K34bCTUM*>E(DQaL-dp_qUUKyWud%-%#$%KhH@lnK;?^ zh0vJV6x=;wS@eg-3@biwMMOef^^m@7ojyzEejm6bstO&2N15~4{j`fEoHEPG4WZSK zOQdCws$Yj^lSiDa4E!A1ClB5i?AsF(>4vgC@vHzn^L?Pwt5#{`DPv*kRVf0IoC8ic zgWxy$CUJ1O`RL@n)s(Iu2Om@A2P?o{&QZZUemn+3yvLi2WJtk3#__uc@&o5W;HHJ~ar(&iG*Z=MKxC|RPivgU-E!Xd( z%dSq%S1xRI#6lQeJlVG||4iM*5iwC=3e^p%$Y=kqkH&oRA*!NI!d+lB+6|b*6|Bk#;fOnov*>2~KoapD|$Ql~wnRDIsZCvdbtKkt8dcHNvg8WsR4ap5D0MQD6< z!v)3fd#z!~-Q5(l-SH477XM{zGd0^C#a8Zxvj6?^CJxO>_j@lHok*&%y) zBH~a{v^KtiruIxmNMl|Ckzl8yuwTNd&I6{15$8W-s(%{W+S64VN=IV)ZR7Q_KJB&F z*z}0)EFxt&`}mOtQAaT6>g^PN7q& z8KEd%ebZrTw8K(5a?t0mj}_~3(O~OvF-2aTMN0QlY!%~iR6na&lDG{pvpI%wK#;s= zamau1o?~87h9Nhhd2K}1Rp2mneQ|pJP z`N~Whxh|eT*~d>72az~^s0`mua5+0#9>zGpMpn!iX{h39f#*bO??+kWp392RC8 z=K4DhAe4)RC@(5F3C!P8c>0QoFuor&#!P1uom ziG&n-;FQ50u6Bt3f#^Jp%-F&L!d}L(zNA@z8>torx<76cKPF98bXHQ&Q;@>U{Ap+6 z23D{X*q@8VbiBVjUevP1AGFL$r}_gyTgcsrlV^~RHgrp>KTR$Grx^mAt~ zw`IW&N+GIs_CH3t0!;Mj{Fmm5%5Xg4d^0(+t)2bS9sSB(p=MaKB1_T#hZt30<{97Zj>Rib<>Zq5XF*&yhZ?oSTIq!~;EL~4HDBk~UzwFKMe z7X%y%!n2>Io{XYfyC&LLz5R`%-nW@5MExfx@OT?==upA07OE;!(pZu@o(MIIyKYNJ ztSG2+emanzwGH2h}RIf9@s^%964yF99lu9@h^!12rmz z_(fL0qlvD(p(_(qGL-n^0je9N`P30?6v~0v7Zd&`e6hSzWF7M^42=sxHMjafLwFHK z0{Na56!l0SGSmVm@!`~*Pf@q5tcXUG2JLTudZ*W^{Ob!k&+(5BP@dZr@uQmpw!9%Q z!zzRu$oWu6$=6h$E?YsE%}s-PrCkNMW-bjyrwtNX`wNGic|-Sj)LIJ*`Hf-;&TBSM zi02BRCo1XjYiawW=;6zx)#`)iAK79Am58du6IAsE?w5)u&XR25Zvs+Ez)u+j#llH7 z`gOqp6*zFQvLwwjg&%s^0H>%a(?Qr7ml1Z+yRjTUt{{}rv5zHQchk#!_YQm^h9&l@ zhK=}KesJDD3rn5rinq71RWa-?r#SHN^6k=D=qby(reljnX5B|xqdRpz&NylP1)7^7jQIw&8EUiS*!0o( zFZn*Do>MnPv*1L0MwMNi*L>U5JUd2}zT4DA+WUJcgsgtJ)r@*Q*Y>Qk$z88Z?!;Uw z;EIHTI2z?6buV#kKMQt3ZZ-<7n2`*;q)MqTpK?aDKIdq)^(lOS zS3D2$lKI6|SrZ?=&J#%4M|+K$Ou{ehfExZFPvQ|p-TkqSe)u;%v#2z}O8uzLiYp7v ziVuv9*WC5$- zvabqNG8)*Fvvb4~ObN7`x5*8S?@vTxuwE>Svj0+=%X#hwsDk0AgR z*83FTxD8`-KQ%JBd#@D~!v5N?wp4#5El6BJQ^7p3layze^VZy1c4nG15I7e;QZVJ@UT%Fo+i)$P zv3%zP&bAe4_pLEinjf6fvG15NrwHyG0-OXwDBZ)Em6N@CDUyI>p->Wv?*G*ivahkfsQL^R1{&YBm*BC$%o{ zO9tbnr%tC4$WG@i*H4*#DGQ1y5tpMGV0DD?^HxQa*6@e~!!?TePdvKP)BHJRXssu0 z1|@GXT5riT{o}SPVgLNhi*RHc%3PNA(_dhobG~`zTx0Fay8s01hs{K&tb%SpQV2;g z@mNJhMN+0hQNY;`LOri(Mm#-dOTcFM?KpqWuSv`n8m75Ae4G6&weNH*Ri0WZ=f(jZ z_U89^S;pBI*~q>eun2e`KbKhdVX!z^EB{&kBRffOAZheTz`hS_Gh)7r}4W(6!MfjzeNNsLnrofPhJB>SQL*<}7%&lqt*}*aZ*WiA`mOKqYJ(CT?%C`Q9Iy>q`VKlY6?al!9|#=&x_`D+t*6s zS%0RRkOVP}IwwZ;y8s!vUHS>Cbg$L2s=PZAUuLRJN3u$CZg-&V6iAqV(z&0XkD2~k zFvpe_m+Nh+1Hw15PDKFmY|aS=+`5vZcP2iM8hhRBT!0xT?=1L_fcf+C4<&#w|~Y9@ZHCl>J$_Lyk*HnnMbDrwn|BRZVJU9bpp zeNT*=Q+U`v%J$~Ga>O@R{yoXxRHlWCWbTXTM0Wh)*TL!>>vXVlj{;h~ad>mUXHW77 zaqHHY0lD!nLr@d<%AB>5la~a!=n$B%d#3zieyK^xxX1lg?UMKOlY3$teOsuzxls`k zRWBWO9@D#~E(dFE5u#T)8%7OO3HuSIU6e&$*aM~%aYQKpaia|?|D{xij;@4!6!SsvcsphDq3o@yNbPdH97 zd{8U)2ry?8EADs}u4#x~fE=j__OSCztrkjXvGSoDdf9z8MWd}w;d0CF&!cVB+KxT9KSbr6j+*DQUfJG9-}V@c=Dtw=b=vm}0RaE~Oq^0&~$ zsm|tNQpYdCFR*poSZAy&v`$%cn@_a!SF*3Mzt(?*Xf8(JY|yVfyUh zsQ;M+fId$+TjWj$eg~eNjh!V~v%yEI6=LLd-!0A*&s0}wp65~RMglE49U}%Mgv1s9f=I5; zqoLN{QR6iNlyv9&9Z?CTp9c`(o3qPju zJxw)_HHOfGhv*btB_Gg(DtO|_*HMy~s+{m{%af}kJs%b-i(Zs?L&ZXLB!fBZlbvBy zP=;5^E5%sJR9BeVGfuV3$AWE7(Z|}Gx6_EMl{3w|lpm%kXOKnz7!;5CdvVSUNssC% zmdCUVU@wa%_Vg!gjH`nL1e>tCPiS1bFxQ(YAC9oBA?~W|YgfYJBv2ZReB(t-%b#DV z4exYF`X7K=9VW5;FOH7vSSM4obJ?#roQ3@H-Ui}_n-dlIa8q4dK1SA#qJ&o|mvIG$ z{u`3a)zXBV(U_@EK?zg5e&ttrWyUg~dLGvPW>cEl8NQ|IPR)@wxpMv#cl5K`70Z?T zA01Tk;yy4>s={G%r8Zk1DzzA6Z`!>M>+(`Kst29X&iGZhRESF+sTBGlVjeY;Z2J5Q zXP=XtKjI`|9FD^~E-31t+Fcyyh`LsES7C-)B8)o@86PF!)$6&8KKsuU&0z2r!hZ!w z8lp&kkWc9q5`O2w1Wt_jHQDOKocv=?t9F?R2-c=B#EuRlpg^P6MZy1iW<_Yl{Lo1@ zC5p88pwy=)ktZDl@63L-SwH%v9q#>IymP)l3ez9u$6Shb(9gucUxgdT2{%L?=3k_P zsJ=*>wNXMjL+kz6{1!@E!^_0~Ee`#~$Uv%_+U*#_%askj|VF87T67?;d zDaGK(TTn`;K*V_fW;yP8X{lM!bgn zVEV&>*1v0lZ&8BkA?D&w#}}2hWw{h=+J54DLmzt565bM;nQ*>C1>cXS6;WIa$7||x zr!Ds5A=~D!3Cr02GiDQJ?Y>p@efu(eH@t%b<`sMje-~1X`Z#mfCk4LhC0C8kRd0N^u}=uS{f<-1yx4?m1vITMA61GuAEE_ ztV=L;faESU&u%7(TWdL06IbkE zWtU#1m=%4Po=^N|zYX>z+mWBvb*i+I>^1FTnM_GW44YtFf7ABL&F=qh3Wj1JFKn&1 z(T-lWCp|?i?D-CPl04qY9mJBDklRA|J+3d8RyZeW)dt-{EiYCv)LjvMA=Jr&N)K5! zejipAf5%W3?FAkns0k3a*dAkVbp?XKf_W+v@k9?vLs5MR^)U)a^>-W7aJvpG1=~daKOVWl%N4 z&h*%6H4$|Ad*$TSUleOMSiwB9igs4+_T2_DhA0ZiE-W!b4ZD3mQyFnekG@VrT#`ch z^)OII+dsvc{*Qlbl7NiYdhgc<`w7CYkw-=rwl>r~qCo`*Xs^rgvml`Zn4OfJ9>Q>tdNyXlAH50yASx!~^D zl`3zGqon!-GAgiPEtR+$(n5o3I^**2B`68i2BA>2h^;^QsX`;8NbO;_>MY8~apxem z(&bXgB#)PtqLG3_=HsmNkC{Y+TgWqsw#+c4(u93!=AH^#d;UX98bdXHcVtVtbZRCI z^er({RmVBiRuG%T4IJdMrw^FLmYW!un1@h!lmt>{7~%F-)K)K|0H=av6ZYwwg4^6G z4uv(fQZ4jXUB`HZtW(Xqku-U|6Z(Qt9ZzBX&Jl$Iicd5MrJR+BL;Mls zC-4f)xP>Ohng8{K@p55oB#?&2|Cw$K5sJt;Q zY_y}^xoGW60C%kf_*BTvqxf9~UuQC?gk2J26>&-PW#t)vv~`)*(6Ku@t=L0m#6rsv zW3@Tdltc`MSL;^na6Y5jXZ58eeXxd#`{u{~?Y|6}(SCm0^PN-J@%ivtlX<4_xYxUo zK!$vaczREL4kDSWQHM5i-Vg~OPHVm8q(}DeLkk#|r6QRs-c!vwXSauAKk}8m<#CJA z;TO{|AbKFO+Z+aMQCl8cJDB`dQyihj>>E+=*OS;>9*9@vtSU)xx5~M`tYMJVKQno9 ztwG;h%vo)intO4edm_g*PlZ4@OB=L-<@C(3cdSr&RS*YG^uy133sj1DOv}KNxDxbm zR$kk{O`#U7(JgDtM#R_yUeXl#`>g5lmI!nGM{|i@oW2KwIbUd_0+pwd2-A{#HWXRQrpEn=csA9*J9QKUEpD zbqt8DzVhxI0kekixFVT5pAG8d8cY%)Ad7#yyJ+3?^~rs30)z*!Eyhx z`gNHyE_X7);YOSc2me;pUS{Poo?5ehzuzzYfEO8efUI@WZ?`s61BxSOZD@oE^ElD5 z-?a5QUbFLV0#fRI4oHZL^n4X7ZxC0zd}O^9;27ytpRsAeIqDS7c+5xe8Kp7jp_Yi~ z_vt!fDTwR%7e~Kx+u18pmO=NC_dIvO$8ae;Peyr6yX1%7iM9)tHn2wxV`%vM3k0;P6kEVEc>)bM^roFJ+t7x+jC3S2sM zR@vbZ_UT1_Ui)<}?gYEA0a z^_c^B`vrc!Rp+@^o9PPidEwK7B5z!rc9o#SuQYKj9QU_Wmr zhd#;oIn+8;`tyqu*GO@ir~GWtqO4_6mJX=Dt8hBo)MkY zJtr|TB2$6gJ8VW63l5r7zHK%UpjGvw6Hro&uZQAYzb5(B_WY5{#5hy^Ia(cSgkeNH zrM9~}nbk-)ESxIIJj{c7J}E*uE6>13&IWtJD>t zmJsO=5OMT%9lmzN1PKbq{BVxc09aV}Nm*u^Hz280N-6A={Q*IGW!{xmXSVC@veI-n4T*0~~% z26bqrlxgP+lQ%PKC|y=XF$(FGb%%o_q*g+qQb5bqzwSinKF5#pH9xrzH^~v6k@E6y zuGXS@`l#%d(5H>Jl^#~S{pyuH-pczaZ_(-P_=I2qn+#mcCdEY8Ug`^M+duqGxR7(0F7KRteDE57vS^p0w8M8YE4X~m2jVI@KA~1o>n`9#2_IUJZ`Ar=#Y2*)kWEi&TT^cb=58|^FqfU%lfARBfXBWs=yD!5i4G8Iq4J=si%&a-|RQ;8eu6_3&ZFPO_idA#lCN zWtaoASLj>apAE=tJ+@?rf*K%IBA|b7y?0_?5k%gk@4a{A0F@ zygSsnb(PTMJNpG5w(bk@y1yI>NsZ&x?}C}^G)lf}YvkAXsK3uFgnIdo-v6bb7R9Z< zRbKcOe%kM{>O|J~!7wr6uWQwNr?gA{nrJ5R61u4mTHQK+KilAtt*g+NNa!SYbOGEUGjaxie#<k}HIrX**dwEYm-AVz zY59{z4SeW>s>8m<-|)hxl6XOnkkyI7(7y*4JeYmWoRbR^T$xsJM#HkZ#fR-%LTg*^ zkMqez&-WwID@jxa4Mbxq4dlJItgY*4V;L{KW(+W6u zA}ZFVH>Ra0Wf%ypAs*;Z=NkTQoLPmmU2Cw)%!igb>|fd}doUWUMiA*1hmc+tllJ|D z!+A=Pz~`$HnBD_xXcmNS-rl_oyjVk1M*8*bO(FAo?o-I4&Sum|OVyqPuSo8X>|;F^ z@H^8SUNo`XBXepW64)p1AJ1{G_||^qa{zWk%3Vh49daxjv2`LP{LA0ABBW-dU{t@N ze|#KPoeGKd-=d*alq$c4 zEyg!@4l}vSZtv%9=Z8OAVrSfPHQrfT^id>A2%WIJn~W8W3Q!0%-xK^OQr-I?8-S6E z=?`c<+kd86{8JcEBjrFMFmV{1QFUieKsfq*+|n)Z+_7`?ci^n+*!8I}7$lQO`9i1* zG)erx2}^tp7$6DIL!|z}13m&!C%75c53^F`v$eWB2XsNemFUjd1G^4JU|`KcaCsF& zS`+Hg-{7hdyPJ->N}pvk(2G_843SvVBA&;-M~sRyiLL_R3@GAfwF&FFB)?!Hx#^OoS*G9d>3&g)lu`^(?j9{Q?f(`+O9Kus${FfSc;&3zj1n}2vua7 z3kCl1fI!~s7D0gn`+N;AQ7n(WG6j$R_L>v(+CrjXE$)WYD6Asf^)^B4s!8wUtOcn; zbO2)hA766D*cQt;V_w(=FXWsbVUe(9W?33`_GVXKiyj=LEG^HN2&ug7!-fG0?kNwn zZBx{)$Vrb3(Ab4NGhIe3$ae2BmFT6~()%pl~!4uy49P?Tz2z6n>ag!isRT3 zsh7(gVn$Z%FEr+mIH=YAt1L_4&f!cB!eb~>zIasYfW|!C>_US&Ac32%pyjz!$l(OZ7BE2($S{r#X)!2&;4NQ`gOOd*@IKlc zq}^(-6S8_^*jiCD%lS6|`SxiuE2V1jF0?*AK_H$W?T`D>?Y0nd;BCFJ!sR*Pvornq z8f&&nWF#hO(;NiLy+#&^QO-xIF*-qWAW%+xcEfgV+rhb8J1EXBOzc;&4e0ENZ$TmK zQi1s)$pVSN4Jq~;=;iFZ>`{K;ufhELUu%e@_~Oz-(T2`|KfDXjXb+P_LEJGZ$Rh6`UBXXRRTm@ zvmmk54L12~88hs=TX{(FYMKU2CX7@T;ARVK?Jgqz;AU) zYiA}u`mD9h!E)FjJ&Q43Ou@A?DH)Jq2X5uLStnLDk+hlwYP zX@gpBEq+XB#%>Z(k?L3?APUP?!yW9H?#N};4D`1x-e6<0wh$fH^KQ0MZTsmIY4*gR z0UCW?4}GPF;^qAHddGxZkh#J+Bqy=afFsyBFoO(<67yFBKJEo$7?dF5A;(r==(}iJ z8u89`HZ~;pkpn}vwyx|6F1kTd0kA8n<}h!lt9Cj_JBc)P$0W%(Wg1OnYozXEHIsON z@Mi|SMCV%84{`F+R0W7EsdTx0J?@*%ZjgAJbSv-k-)Eyux(?><@c+BIz4DOxZAYV| zmXKjC1nn$v?EFFb5A=Ryk90`icr-^EzT@hX=$-k6{Je&MY6RPXrQoAKuKOvvIQVS8 zw?gBGCB>8g20of%{009I`LD))vS);PAXyJTM||i>V63FTx2czvCNyUH;>~$}m7;TDm>?*OMF&6h zRi8O!dU9~}O#E(QXI(@5knK4x^zrt%sv`eI#}G9UiYLeBcn?E|ks`b-{>kHkmbRSu z05(9Y>oED;`T#7Jcsr`w^)VIzCF&TxYPpMMClHvs5j>S-YX+(x?ly;S=>1+`M?5H; zJm1ksCH|%`rv{hnofX-g7SEmO*8uKmeew9F7yhylA+HF`IJN0=M$`U; za3Bb92lDNo+osBM!`ey+9n*eR77Vz%HRug#AbQ6^k@_@F0YBLA=zdp%6@Mv6#qk8$ zg)|63b{e{7Yb8tKRd#4x-o&^gMAlPYE2RszRc>9j6`kKdc@2d$ToDMr@P_UCb4s=D ze#??@Chhf~pEiBvsa|M7eNvMQ-wB|~s0kybl5b71ry5syoPGfmXGV&G$G0B#c?*PN z?tguHq8#6S!S)pX(t!F}f)^I&bqS+L+|3i`Az)v*@bZ4w)o{t=y#=zZbQt5T8veQI zZiKE4)a+5$6k18~d|+|xs2?%r8A$f8G}Yae+B{VyW5|9f@_D3>1LNIhJ~jyfH)9ZdL!wX^vO^0v&Ng$Gk2hV;N*A`*`mW`*&sN|pX+eRDi}T;THjMg~G-Vj;VNkA27C1MdxHp@4i! zxBOLbA`8cRYUc-WTEsJ|@%IxSD8%f>WmEAoo)Sd#bU@OAtD4u$ro?{cZ)oim1{B9l$*PV>UV~URd-9qR^sor>8AuqU|%c7|zBhL!EFba|{)7W!PVIgOT z)Fn#&4=v6@(QTkZ??PyBUhw>A*8s?%{IMf=*1`1UcTMNBgcdM=429ydbU`!>xX9DH zDWGZG!G(gxzqi9|kTx4%F{xfy{(Hs52Qm8MdTjSmp8XEMTuk;@MNekQ)ngZS+=3GZHL3_o;g9&p7ZK(gi?`TBm@Jl)sXb9-sg4CTR zT(0&{Kx28Ja={sr7W=fWQx$QFmXHUQ-GyVX@v4^bVwPXfuXi=i-PkN8n;d={-wEZq zv*K5YwnpSrs{s`oQ{(_k1dtn~cm}Sh0V+&IFbG;90&sUD&7;cw}+`R44-vIxcg>o|2FUF%lZp3$#TKXfw&qpZyEq zPu(1mx;GZoN_HCCyaXh0oK?Tu=;K=dRNk*XtZG7oaU7v#F8i-! zP6cGmF{l-sP^Yz_xoyAwf?NEXWAYc;|kZAL-9GR#kZB~YGzDg5T~ecEo? z-Egy>d^Mj2&~(6?2}%6GeF>v1UjoU#bC*45JL3_AS($lGlc9HNv>` zS7v}ylqz)KCDRPNTM_&x44mRRzQl0T|9Wx8yUFQ2F~D%<4I4L!v7cEi`=%g8?5^xI z9li2k)_@`Ln6|N|GH_y`-fw>&g^S2zC?_5obznzX`M0)*so+CLyJAqZlGHTi74TFu zQA3v0?rx?Wj#2NCq%j>$Y)zu$WAdD5Z^K|YadS_Hg<&N@QW9LZyM`p?; zh=DWs)$_RAH|S!$BzOS$!+adqFo9*3*xjNzUSuQ1^9Pj;0ozwO!JfZ$dv{;MJlZJX zO!_~rTk&DTMVskeBSbe){(K(A9c@60HHjYh*>M>+Rg0T=xVTND3%Gi*%oU>+A{ATsdl$r^iM7 z;cAg+5YLV*7)$!ICp;{xfM49NrrJ70@ijoS0T$3=$b#!G*7Ab>tdZZv(es9%zAb zUMY(1)m0I6NM)f2ZBI~gDk>E9y!}UaK*A6q_t(CJH-ViH4vc`WPG4{34vPLzJKfcz zCxPTZ{y{0SJ96wu^b=VrBa+v{mYKhm0%W;j8*&Lv34Q$I{(dTn%8xuAFx2qn{3>u3 zZ~@09v$(^o3DlZHTflEyp}*eMk$75uL-f>_)u$Qb&_XObp0r0NXk*334;b$bqgF#$&`)E}vF|&JZHiSH0h1@f8~)ap zK~N+XPljQNPG+s-Cm(3VdFBYIbh3KuUur$8FY$o*=5+j>C)o%QT?nl1fu@m|5{D&J z;MH{Enp{>o45_JNg>-2;4qRhZ`p~t1w*leFMFn+Eo z{`TR&QxHHO0i|dJAdr&Vw9$Ox8JaOxRd4c~zcHFN?^6#s`ZF{#pyBqw>KHUJymAtAm z=YL78qu6|X67Vy)%)TEsL{&>(kT$Y{zc=j+7#+8N1=~U6IEo26Jo^-Hn%%6zv~dCa z*xNgB@~K0pN5f1t`j~XQNTp7MmzbgR4MlHM8N~fSDbH2on&m&l4p%RFnQ-x8~Y^2`qkp4Cf5#5_5eIwE!Je-i%WoDTuqC zX8u^U3p4mGlGAY{^BuXkqWrWlu@I!1zuEnQ2~``!58BOQw8VeLpC;a7pPxrpdS4EY zET=U*33$IFWDcFW<>?|kL~#L{30teQh-eF^_Zf>p^_YOzz2{FRjV>Sn^vxGeA$+%g zsqH=O2ZXB0K9rL60f`+NkWTL<_u0$Z!pla)%EJcw0K>z@!_Use%g)88%grysB`m_j z&C10k!o^iZ$Pw^=4RCg~cChvT{|{JK%o>LdVER8ZcsV%RczRhtlh*&ANqG7H_ayC| z8Q-B}NdFt-W^F4X?d@gn>H(wd?dj#+eKvlyK`3$ pU02tyFftY{J{HtW+BPoMHXb$}%=7-ikll;qT9>pz)C{12y+^)CPb literal 0 HcmV?d00001 diff --git a/src/components/Group/QMailMessages.tsx b/src/components/Group/QMailMessages.tsx index ee62a4c..b4bfd4a 100644 --- a/src/components/Group/QMailMessages.tsx +++ b/src/components/Group/QMailMessages.tsx @@ -18,7 +18,6 @@ import { mailsAtom, qMailLastEnteredTimestampAtom } from '../../atoms/global'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import MarkEmailUnreadIcon from '@mui/icons-material/MarkEmailUnread'; -import { last } from 'slate'; export const isLessThanOneWeekOld = (timestamp) => { // Current time in milliseconds const now = Date.now(); diff --git a/vite.config.ts b/vite.config.ts index 5a7bb91..e515bea 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -5,6 +5,7 @@ import react from '@vitejs/plugin-react'; import fixReactVirtualized from 'esbuild-plugin-react-virtualized' import wasm from 'vite-plugin-wasm'; import topLevelAwait from 'vite-plugin-top-level-await'; +import { VitePWA } from 'vite-plugin-pwa'; export default defineConfig({ @@ -14,7 +15,34 @@ export default defineConfig({ setupFiles: ['./src/test/setup.ts'] }, assetsInclude: ['**/*.wasm'], - plugins: [react(), wasm(), topLevelAwait()], + plugins: [react(), wasm(), topLevelAwait(), VitePWA({ + registerType: 'prompt', + manifest: { + name: 'Qortal Hub', + short_name: 'Hub', + description: 'Your easy access to the Qortal blockchain', + start_url: '/', + display: 'standalone', + theme_color: '#ffffff', + background_color: '#ffffff', + icons: [ + { + src: '/qortal192.png', + sizes: '192x192', + type: 'image/png', + }, + { + src: '/qortal.png', + sizes: '512x512', + type: 'image/png', + }, + ], + }, + workbox: { + maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, // 5MB limit + disableDevLogs: true, // Suppresses logs in development + }, + })], optimizeDeps: { esbuildOptions: { plugins: [fixReactVirtualized], From 36d34a631daab24734d8b2e7f0a6860a802bfd30 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 5 Mar 2025 00:26:10 +0200 Subject: [PATCH 022/717] save new wallet --- src/App.tsx | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/App.tsx b/src/App.tsx index 862a6d8..78fd701 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -88,10 +88,12 @@ import { cleanUrl, getFee, getProtocol, + getWallets, groupApi, groupApiLocal, groupApiSocket, groupApiSocketLocal, + storeWallets, } from "./background"; import { executeEvent, @@ -1007,6 +1009,26 @@ function App() { } }; + const saveWalletToLocalStorage = async (newWallet)=> { + try { + getWallets().then((res)=> { + + if(res && Array.isArray(res)){ + const wallets = [...res, newWallet] + storeWallets(wallets) + } else { + storeWallets([newWallet]) + } + setIsLoading(false) + }).catch((error)=> { + console.error(error) + setIsLoading(false) + }) + } catch (error) { + console.error(error) + } + } + const createAccountFunc = async () => { try { if (!walletToBeDownloadedPassword) { @@ -1043,6 +1065,7 @@ function App() { .then((response) => { if (response && !response.error) { setRawWallet(wallet); + saveWalletToLocalStorage(wallet) setWalletToBeDownloaded({ wallet, qortAddress: wallet.address0, From 2ffc62688c9f47af84f3d37a6c172955b556b928 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 5 Mar 2025 02:19:08 +0200 Subject: [PATCH 023/717] add hide to seedphrase --- src/App.tsx | 15 +++++++- src/Wallets.tsx | 20 +++++++--- src/atoms/global.ts | 10 ----- src/components/Apps/AppsCategory.tsx | 2 - src/components/Apps/AppsCategoryDesktop.tsx | 2 - src/components/Apps/AppsLibrary.tsx | 2 - src/components/Apps/AppsLibraryDesktop.tsx | 2 - src/components/Chat/MessageItem.tsx | 2 +- src/components/Group/Group.tsx | 37 +++++++++++-------- .../PasswordField/PasswordField.tsx | 11 ++++-- src/components/WrapperUserAction.tsx | 7 +++- 11 files changed, 62 insertions(+), 48 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 78fd701..c6ecb71 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -401,6 +401,7 @@ function App() { message: messageQortalRequestExtension, } = useModal(); + const [isRunningPublicNode, setIsRunningPublicNode] = useState(false) const [infoSnack, setInfoSnack] = useState(null); const [openSnack, setOpenSnack] = useState(false); @@ -450,6 +451,14 @@ function App() { } }, []); + useEffect(()=> { + isRunningGateway().then((res)=> { + setIsRunningPublicNode(res) + }).catch((error)=> { + console.error(error) + }) + }, [extState]) + useEffect(()=> { if(!shownTutorialsInitiated) return if(extState === 'not-authenticated'){ @@ -1868,7 +1877,8 @@ function App() { isUserBlocked, addToBlockList, removeBlockFromList, - getAllBlockedUsers + getAllBlockedUsers, + isRunningPublicNode }} > @@ -2046,7 +2056,8 @@ function App() { isUserBlocked, addToBlockList, removeBlockFromList, - getAllBlockedUsers + getAllBlockedUsers, + isRunningPublicNode }} > { /> - setSeedValue(e.target.value)} - /> - + setSeedValue(e.target.value)} + autoComplete="off" + sx={{ + width: '100%' + }} + /> + { value={password} onChange={(e) => setPassword(e.target.value)} autoComplete="off" + sx={{ + width: '100%' + }} /> diff --git a/src/atoms/global.ts b/src/atoms/global.ts index cb49bf7..95b7099 100644 --- a/src/atoms/global.ts +++ b/src/atoms/global.ts @@ -12,18 +12,12 @@ export const sortablePinnedAppsAtom = atom({ }, { name: 'Q-Share', service: 'APP' - }, { - name: 'qombo', - service: 'APP' }, { name: 'Q-Fund', service: 'APP' }, { name: 'Q-Shop', service: 'APP' - },{ - name: 'Qombo', - service: 'APP' }, { name: 'Q-Trade', @@ -33,10 +27,6 @@ export const sortablePinnedAppsAtom = atom({ name: 'Q-Support', service: 'APP' }, - { - name: 'NodeInfo', - service: 'APP' - }, { name: 'Q-Manager', service: 'APP' diff --git a/src/components/Apps/AppsCategory.tsx b/src/components/Apps/AppsCategory.tsx index 5045f1c..725ff1d 100644 --- a/src/components/Apps/AppsCategory.tsx +++ b/src/components/Apps/AppsCategory.tsx @@ -36,12 +36,10 @@ const officialAppList = [ "q-share", "q-support", "q-mail", - "qombo", "q-fund", "q-shop", "q-trade", "q-support", - "nodeinfo", "q-manager" ]; diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index ab4fef1..d41207a 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -44,12 +44,10 @@ const officialAppList = [ "q-share", "q-support", "q-mail", - "qombo", "q-fund", "q-shop", "q-trade", "q-support", - "nodeinfo", "q-manager" ]; diff --git a/src/components/Apps/AppsLibrary.tsx b/src/components/Apps/AppsLibrary.tsx index ba42c3e..26e7d81 100644 --- a/src/components/Apps/AppsLibrary.tsx +++ b/src/components/Apps/AppsLibrary.tsx @@ -38,12 +38,10 @@ const officialAppList = [ "q-share", "q-support", "q-mail", - "qombo", "q-fund", "q-shop", "q-trade", "q-support", - "nodeinfo", "q-manager" ]; diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index 8da2e28..b836ac8 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -53,12 +53,10 @@ const officialAppList = [ "q-share", "q-support", "q-mail", - "qombo", "q-fund", "q-shop", "q-trade", "q-support", - "nodeinfo", "q-manager", "q-mintership" ]; diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index 5123ee2..284ad96 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -188,7 +188,7 @@ const onSeenFunc = useCallback(()=> { - + (null); const [timestampEnterData, setTimestampEnterData] = useState({}); const [chatMode, setChatMode] = useState("groups"); @@ -457,6 +457,8 @@ export const Group = ({ return null }, [selectedGroup]) + + const setSelectedGroupId = useSetRecoilState(selectedGroupIdAtom) const toggleSideViewDirects = ()=> { @@ -2021,21 +2023,24 @@ export const Group = ({ /> Group Mgmt - { - setIsOpenBlockedUserModal(true); - }} - sx={{ - minWidth: 'unset', - padding: '10px' - }} - > - - + {!isRunningPublicNode && ( + { + setIsOpenBlockedUserModal(true); + }} + sx={{ + minWidth: 'unset', + padding: '10px' + }} + > + + + )} + )} {chatMode === "directs" && ( diff --git a/src/components/PasswordField/PasswordField.tsx b/src/components/PasswordField/PasswordField.tsx index 4ff4a80..89bc8b4 100644 --- a/src/components/PasswordField/PasswordField.tsx +++ b/src/components/PasswordField/PasswordField.tsx @@ -1,6 +1,7 @@ -import { Button, InputAdornment, TextField, TextFieldProps, styled } from "@mui/material"; +import { Button, ButtonBase, InputAdornment, TextField, TextFieldProps, styled } from "@mui/material"; import { forwardRef, useState } from 'react' - +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; +import VisibilityIcon from '@mui/icons-material/Visibility'; export const CustomInput = styled(TextField)({ width: "183px", // Adjust the width as needed borderRadius: "5px", @@ -51,7 +52,11 @@ export const PasswordField = forwardRef( ({ .. { setCanViewPassword((prevState) => !prevState) }}> - {canViewPassword ? : } + {canViewPassword ? : } ) }} diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx index 9aa5cbf..a3bcc3f 100644 --- a/src/components/WrapperUserAction.tsx +++ b/src/components/WrapperUserAction.tsx @@ -4,6 +4,7 @@ import { executeEvent } from '../utils/events'; import { MyContext } from '../App'; export const WrapperUserAction = ({ children, address, name, disabled }) => { + const {isRunningPublicNode} = useContext(MyContext) const [anchorEl, setAnchorEl] = useState(null); // Handle child element click to open Popover @@ -138,8 +139,10 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => { User lookup - - + {!isRunningPublicNode && ( + + + )} )} From 45b32befb4370f42b84d23fde97c9c6770400cca Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 5 Mar 2025 03:20:31 +0200 Subject: [PATCH 024/717] fix --- src/App.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index c6ecb71..47b42f0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2862,7 +2862,7 @@ function App() { fontWeight: 600, }} > - Download Wallet + Download Account @@ -2894,11 +2894,11 @@ function App() { onClick={async () => { await saveFileToDiskFunc(); await showInfo({ - message: `Keep your wallet file secure.`, + message: `Keep your account file secure.`, }); }} > - Download wallet + Download account )} From 2a64a73f0bdf6f4a1537796687faa3f397c6a330 Mon Sep 17 00:00:00 2001 From: AlphaX-Qortal <67390536+AlphaX-Qortal@users.noreply.github.com> Date: Tue, 4 Mar 2025 11:49:39 +0100 Subject: [PATCH 025/717] Added node info and status qortal requests --- .../Apps/useQortalMessageListener.tsx | 149 +++++++++++++----- src/qortalRequests.ts | 42 ++++- src/qortalRequests/get.ts | 62 ++++++++ 3 files changed, 215 insertions(+), 38 deletions(-) diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index 635aba6..e3f5c52 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -175,52 +175,127 @@ export function openIndexedDB() { export const listOfAllQortalRequests = [ - 'GET_USER_ACCOUNT', 'DECRYPT_DATA', 'SEND_COIN', 'GET_LIST_ITEMS', - 'ADD_LIST_ITEMS', 'DELETE_LIST_ITEM', 'VOTE_ON_POLL', 'CREATE_POLL', - 'SEND_CHAT_MESSAGE', 'JOIN_GROUP', 'DEPLOY_AT', 'GET_USER_WALLET', - 'GET_WALLET_BALANCE', 'GET_USER_WALLET_INFO', 'GET_CROSSCHAIN_SERVER_INFO', - 'GET_TX_ACTIVITY_SUMMARY', 'GET_FOREIGN_FEE', 'UPDATE_FOREIGN_FEE', - 'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER', - 'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER', 'CREATE_TRADE_SELL_ORDER', 'CANCEL_TRADE_SELL_ORDER', 'IS_USING_PUBLIC_NODE', 'ADMIN_ACTION', 'SIGN_TRANSACTION', 'OPEN_NEW_TAB', 'CREATE_AND_COPY_EMBED_LINK', 'DECRYPT_QORTAL_GROUP_DATA', 'DECRYPT_DATA_WITH_SHARING_KEY', 'DELETE_HOSTED_DATA', 'GET_HOSTED_DATA', 'PUBLISH_MULTIPLE_QDN_RESOURCES', + 'GET_USER_ACCOUNT', + 'DECRYPT_DATA', + 'SEND_COIN', + 'GET_LIST_ITEMS', + 'ADD_LIST_ITEMS', + 'DELETE_LIST_ITEM', + 'VOTE_ON_POLL', + 'CREATE_POLL', + 'SEND_CHAT_MESSAGE', + 'JOIN_GROUP', + 'DEPLOY_AT', + 'GET_USER_WALLET', + 'GET_WALLET_BALANCE', + 'GET_USER_WALLET_INFO', + 'GET_CROSSCHAIN_SERVER_INFO', + 'GET_TX_ACTIVITY_SUMMARY', + 'GET_FOREIGN_FEE', + 'UPDATE_FOREIGN_FEE', + 'GET_SERVER_CONNECTION_HISTORY', + 'SET_CURRENT_FOREIGN_SERVER', + 'ADD_FOREIGN_SERVER', + 'REMOVE_FOREIGN_SERVER', + 'GET_DAY_SUMMARY', + 'CREATE_TRADE_BUY_ORDER', + 'CREATE_TRADE_SELL_ORDER', + 'CANCEL_TRADE_SELL_ORDER', + 'IS_USING_PUBLIC_NODE', + 'ADMIN_ACTION', + 'SIGN_TRANSACTION', + 'OPEN_NEW_TAB', + 'CREATE_AND_COPY_EMBED_LINK', + 'DECRYPT_QORTAL_GROUP_DATA', + 'DECRYPT_DATA_WITH_SHARING_KEY', + 'DELETE_HOSTED_DATA', + 'GET_HOSTED_DATA', + 'PUBLISH_MULTIPLE_QDN_RESOURCES', 'PUBLISH_QDN_RESOURCE', 'ENCRYPT_DATA', 'ENCRYPT_DATA_WITH_SHARING_KEY', 'ENCRYPT_QORTAL_GROUP_DATA', 'SAVE_FILE', 'GET_ACCOUNT_DATA', - 'GET_ACCOUNT_NAMES', - 'SEARCH_NAMES', - 'GET_NAME_DATA', - 'GET_QDN_RESOURCE_URL', - 'LINK_TO_QDN_RESOURCE', - 'LIST_QDN_RESOURCES', - 'SEARCH_QDN_RESOURCES', - 'FETCH_QDN_RESOURCE', - 'GET_QDN_RESOURCE_STATUS', - 'GET_QDN_RESOURCE_PROPERTIES', - 'GET_QDN_RESOURCE_METADATA', - 'SEARCH_CHAT_MESSAGES', - 'LIST_GROUPS', - 'GET_BALANCE', - 'GET_AT', - 'GET_AT_DATA', - 'LIST_ATS', - 'FETCH_BLOCK', - 'FETCH_BLOCK_RANGE', - 'SEARCH_TRANSACTIONS', - 'GET_PRICE', - 'SHOW_ACTIONS', - 'GET_USER_WALLET_TRANSACTIONS' + 'GET_ACCOUNT_NAMES', + 'SEARCH_NAMES', + 'GET_NAME_DATA', + 'GET_QDN_RESOURCE_URL', + 'LINK_TO_QDN_RESOURCE', + 'LIST_QDN_RESOURCES', + 'SEARCH_QDN_RESOURCES', + 'FETCH_QDN_RESOURCE', + 'GET_QDN_RESOURCE_STATUS', + 'GET_QDN_RESOURCE_PROPERTIES', + 'GET_QDN_RESOURCE_METADATA', + 'SEARCH_CHAT_MESSAGES', + 'LIST_GROUPS', + 'GET_BALANCE', + 'GET_AT', + 'GET_AT_DATA', + 'LIST_ATS', + 'FETCH_BLOCK', + 'FETCH_BLOCK_RANGE', + 'SEARCH_TRANSACTIONS', + 'GET_PRICE', + 'SHOW_ACTIONS', + 'GET_USER_WALLET_TRANSACTIONS', + 'GET_NODE_INFO', + 'GET_NODE_STATUS' ] export const UIQortalRequests = [ - 'GET_USER_ACCOUNT', 'DECRYPT_DATA', 'SEND_COIN', 'GET_LIST_ITEMS', - 'ADD_LIST_ITEMS', 'DELETE_LIST_ITEM', 'VOTE_ON_POLL', 'CREATE_POLL', - 'SEND_CHAT_MESSAGE', 'JOIN_GROUP', 'DEPLOY_AT', 'GET_USER_WALLET', - 'GET_WALLET_BALANCE', 'GET_USER_WALLET_INFO', 'GET_CROSSCHAIN_SERVER_INFO', - 'GET_TX_ACTIVITY_SUMMARY', 'GET_FOREIGN_FEE', 'UPDATE_FOREIGN_FEE', - 'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER', - 'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER', 'CREATE_TRADE_SELL_ORDER', 'CANCEL_TRADE_SELL_ORDER', 'IS_USING_PUBLIC_NODE', 'ADMIN_ACTION', 'SIGN_TRANSACTION', 'OPEN_NEW_TAB', 'CREATE_AND_COPY_EMBED_LINK', 'DECRYPT_QORTAL_GROUP_DATA', 'DECRYPT_DATA_WITH_SHARING_KEY', 'DELETE_HOSTED_DATA', 'GET_HOSTED_DATA', 'SHOW_ACTIONS', 'REGISTER_NAME', 'UPDATE_NAME', 'LEAVE_GROUP', 'INVITE_TO_GROUP', 'KICK_FROM_GROUP', 'BAN_FROM_GROUP', 'CANCEL_GROUP_BAN', 'ADD_GROUP_ADMIN', 'REMOVE_GROUP_ADMIN', 'DECRYPT_AESGCM', 'CANCEL_GROUP_INVITE', 'CREATE_GROUP', 'GET_USER_WALLET_TRANSACTIONS' + 'GET_USER_ACCOUNT', + 'DECRYPT_DATA', + 'SEND_COIN', + 'GET_LIST_ITEMS', + 'ADD_LIST_ITEMS', + 'DELETE_LIST_ITEM', + 'VOTE_ON_POLL', + 'CREATE_POLL', + 'SEND_CHAT_MESSAGE', + 'JOIN_GROUP', + 'DEPLOY_AT', + 'GET_USER_WALLET', + 'GET_WALLET_BALANCE', + 'GET_USER_WALLET_INFO', + 'GET_CROSSCHAIN_SERVER_INFO', + 'GET_TX_ACTIVITY_SUMMARY', + 'GET_FOREIGN_FEE', + 'UPDATE_FOREIGN_FEE', + 'GET_SERVER_CONNECTION_HISTORY', + 'SET_CURRENT_FOREIGN_SERVER', + 'ADD_FOREIGN_SERVER', + 'REMOVE_FOREIGN_SERVER', + 'GET_DAY_SUMMARY', + 'CREATE_TRADE_BUY_ORDER', + 'CREATE_TRADE_SELL_ORDER', + 'CANCEL_TRADE_SELL_ORDER', + 'IS_USING_PUBLIC_NODE', + 'ADMIN_ACTION', + 'SIGN_TRANSACTION', + 'OPEN_NEW_TAB', + 'CREATE_AND_COPY_EMBED_LINK', + 'DECRYPT_QORTAL_GROUP_DATA', + 'DECRYPT_DATA_WITH_SHARING_KEY', + 'DELETE_HOSTED_DATA', + 'GET_HOSTED_DATA', + 'SHOW_ACTIONS', + 'REGISTER_NAME', + 'UPDATE_NAME', + 'LEAVE_GROUP', + 'INVITE_TO_GROUP', + 'KICK_FROM_GROUP', + 'BAN_FROM_GROUP', + 'CANCEL_GROUP_BAN', + 'ADD_GROUP_ADMIN', + 'REMOVE_GROUP_ADMIN', + 'DECRYPT_AESGCM', + 'CANCEL_GROUP_INVITE', + 'CREATE_GROUP', + 'GET_USER_WALLET_TRANSACTIONS', + 'GET_NODE_INFO', + 'GET_NODE_STATUS' ]; // TODO listOfAllQortalRequests diff --git a/src/qortalRequests.ts b/src/qortalRequests.ts index 0397538..b2848c4 100644 --- a/src/qortalRequests.ts +++ b/src/qortalRequests.ts @@ -1,6 +1,6 @@ import { gateways, getApiKeyFromStorage } from "./background"; import { listOfAllQortalRequests } from "./components/Apps/useQortalMessageListener"; -import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banFromGroupRequest, cancelGroupBanRequest, cancelGroupInviteRequest, cancelSellOrder, createAndCopyEmbedLink, createBuyOrder, createGroupRequest, createPoll, createSellOrder, decryptAESGCMRequest, decryptData, decryptDataWithSharingKey, decryptQortalGroupData, deleteHostedData, deleteListItems, deployAt, encryptData, encryptDataWithSharingKey, encryptQortalGroupData, getCrossChainServerInfo, getDaySummary, getForeignFee, getHostedData, getListItems, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getUserWalletTransactions, getWalletBalance, inviteToGroupRequest, joinGroup, kickFromGroupRequest, leaveGroupRequest, openNewTab, publishMultipleQDNResources, publishQDNResource, registerNameRequest, removeForeignServer, removeGroupAdminRequest, saveFile, sendChatMessage, sendCoin, setCurrentForeignServer, signTransaction, updateForeignFee, updateNameRequest, voteOnPoll } from "./qortalRequests/get"; +import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banFromGroupRequest, cancelGroupBanRequest, cancelGroupInviteRequest, cancelSellOrder, createAndCopyEmbedLink, createBuyOrder, createGroupRequest, createPoll, createSellOrder, decryptAESGCMRequest, decryptData, decryptDataWithSharingKey, decryptQortalGroupData, deleteHostedData, deleteListItems, deployAt, encryptData, encryptDataWithSharingKey, encryptQortalGroupData, getCrossChainServerInfo, getDaySummary, getNodeInfo, getNodeStatus, getForeignFee, getHostedData, getListItems, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getUserWalletTransactions, getWalletBalance, inviteToGroupRequest, joinGroup, kickFromGroupRequest, leaveGroupRequest, openNewTab, publishMultipleQDNResources, publishQDNResource, registerNameRequest, removeForeignServer, removeGroupAdminRequest, saveFile, sendChatMessage, sendCoin, setCurrentForeignServer, signTransaction, updateForeignFee, updateNameRequest, voteOnPoll } from "./qortalRequests/get"; import { getData, storeData } from "./utils/chromeStorage"; @@ -635,6 +635,46 @@ export const isRunningGateway = async ()=> { break; } + case "GET_NODE_INFO": { + try { + const res = await getNodeInfo(request.payload); + event.source.postMessage({ + requestId: request.requestId, + action: request.action, + payload: res, + type: "backgroundMessageResponse", + }, event.origin); + } catch (error) { + event.source.postMessage({ + requestId: request.requestId, + action: request.action, + error: error.message, + type: "backgroundMessageResponse", + }, event.origin); + } + break; + } + + case "GET_NODE_STATUS": { + try { + const res = await getNodeStatus(request.payload); + event.source.postMessage({ + requestId: request.requestId, + action: request.action, + payload: res, + type: "backgroundMessageResponse", + }, event.origin); + } catch (error) { + event.source.postMessage({ + requestId: request.requestId, + action: request.action, + error: error.message, + type: "backgroundMessageResponse", + }, event.origin); + } + break; + } + case "SEND_COIN": { try { const res = await sendCoin(request.payload, isFromExtension); diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index 6e27f8a..66e3006 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -2883,6 +2883,68 @@ export const getDaySummary = async () => { } }; +export const getNodeInfo = async () => { + const url = `/admin/info`; // Simplified endpoint URL + + try { + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL + const response = await fetch(endpoint, { + method: "GET", + headers: { + Accept: "*/*", + }, + }); + + if (!response.ok) throw new Error("Failed to retrieve node info"); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response + } catch (error) { + throw new Error(error?.message || "Error in retrieving node info"); + } +}; + +export const getNodeStatus = async () => { + const url = `/admin/status`; // Simplified endpoint URL + + try { + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL + const response = await fetch(endpoint, { + method: "GET", + headers: { + Accept: "*/*", + }, + }); + + if (!response.ok) throw new Error("Failed to retrieve node status"); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response + } catch (error) { + throw new Error(error?.message || "Error in retrieving node status"); + } +}; + export const sendCoin = async (data, isFromExtension) => { const requiredFields = ["coin", "amount"]; const missingFields: string[] = []; From 548e325aa25b3891ec61b9589378bc96722b52c8 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 6 Mar 2025 17:46:38 +0200 Subject: [PATCH 026/717] added payment component --- src/App.tsx | 142 +-------------- .../Apps/useQortalMessageListener.tsx | 13 +- src/components/QortPayment.tsx | 167 ++++++++++++++++++ 3 files changed, 188 insertions(+), 134 deletions(-) create mode 100644 src/components/QortPayment.tsx diff --git a/src/App.tsx b/src/App.tsx index 47b42f0..221b8cb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -153,6 +153,7 @@ import { DrawerUserLookup } from "./components/Drawer/DrawerUserLookup"; import { UserLookup } from "./components/UserLookup.tsx/UserLookup"; import { RegisterName } from "./components/RegisterName"; import { BuyQortInformation } from "./components/BuyQortInformation"; +import { QortPayment } from "./components/QortPayment"; type extStates = | "not-authenticated" @@ -351,6 +352,8 @@ function App() { const [authenticatePassword, setAuthenticatePassword] = useState(""); const [sendqortState, setSendqortState] = useState(null); const [isLoading, setIsLoading] = useState(false); + const [isLoadingSendCoin, setIsLoadingSendCoin] = useState(false); + const [ walletToBeDownloadedPasswordConfirm, setWalletToBeDownloadedPasswordConfirm, @@ -775,52 +778,7 @@ function App() { setLtcBalanceLoading(false); }); }; - const sendCoinFunc = async() => { - try { - setSendPaymentError(""); - setSendPaymentSuccess(""); - if (!paymentTo) { - setSendPaymentError("Please enter a recipient"); - return; - } - if (!paymentAmount) { - setSendPaymentError("Please enter an amount greater than 0"); - return; - } - if (!paymentPassword) { - setSendPaymentError("Please enter your wallet password"); - return; - } - const fee = await getFee('PAYMENT') - - await show({ - message: `Would you like to transfer ${Number(paymentAmount)} QORT?` , - paymentFee: fee.fee + ' QORT' - }) - setIsLoading(true); - window - .sendMessage("sendCoin", { - amount: Number(paymentAmount), - receiver: paymentTo.trim(), - password: paymentPassword, - }) - .then((response) => { - if (response?.error) { - setSendPaymentError(response.error); - } else { - setIsOpenSendQort(false); - setIsOpenSendQortSuccess(true); - } - setIsLoading(false); - }) - .catch((error) => { - console.error("Failed to send coin:", error); - setIsLoading(false); - }); - } catch (error) { - // error - } - }; + const clearAllStates = () => { setRequestConnection(null); @@ -2119,94 +2077,12 @@ function App() { /> - - - Transfer QORT - - - - Balance: - - - {balance?.toFixed(2)} QORT - - - - - - To - - setPaymentTo(e.target.value)} - autoComplete="off" + { + setIsOpenSendQort(false); + setIsOpenSendQortSuccess(true); + }} + defaultPaymentTo={paymentTo} /> - - - Amount - - - setPaymentAmount(+e)} - /> - - - Confirm Wallet Password - - - setPaymentPassword(e.target.value)} - autoComplete="off" - /> - - - {sendPaymentError} - {/* {sendPaymentSuccess} */} - - { - sendCoinFunc(); - }} - > - Send - )} diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index e3f5c52..508d5bc 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -239,6 +239,18 @@ export const listOfAllQortalRequests = [ 'SEARCH_TRANSACTIONS', 'GET_PRICE', 'SHOW_ACTIONS', + 'REGISTER_NAME', + 'UPDATE_NAME', + 'LEAVE_GROUP', + 'INVITE_TO_GROUP', + 'KICK_FROM_GROUP', + 'BAN_FROM_GROUP', + 'CANCEL_GROUP_BAN', + 'ADD_GROUP_ADMIN', + 'REMOVE_GROUP_ADMIN', + 'DECRYPT_AESGCM', + 'CANCEL_GROUP_INVITE', + 'CREATE_GROUP', 'GET_USER_WALLET_TRANSACTIONS', 'GET_NODE_INFO', 'GET_NODE_STATUS' @@ -297,7 +309,6 @@ export const UIQortalRequests = [ 'GET_NODE_INFO', 'GET_NODE_STATUS' ]; -// TODO listOfAllQortalRequests diff --git a/src/components/QortPayment.tsx b/src/components/QortPayment.tsx new file mode 100644 index 0000000..6add9b2 --- /dev/null +++ b/src/components/QortPayment.tsx @@ -0,0 +1,167 @@ +import { Box, CircularProgress } from '@mui/material'; +import React, { useEffect, useState } from 'react' +import { CustomButton, CustomInput, CustomLabel, TextP } from '../App-styles'; +import { Spacer } from '../common/Spacer'; +import BoundedNumericTextField from '../common/BoundedNumericTextField'; +import { PasswordField } from './PasswordField/PasswordField'; +import { ErrorText } from './ErrorText/ErrorText'; +import { getFee } from '../background'; + +export const QortPayment = ({balance, show, onSuccess, defaultPaymentTo}) => { + const [paymentTo, setPaymentTo] = useState(defaultPaymentTo); + const [paymentAmount, setPaymentAmount] = useState(0); + const [paymentPassword, setPaymentPassword] = useState(""); + const [sendPaymentError, setSendPaymentError] = useState(""); + const [sendPaymentSuccess, setSendPaymentSuccess] = useState(""); + const [isLoadingSendCoin, setIsLoadingSendCoin] = useState(false); + + + + const sendCoinFunc = async() => { + try { + setSendPaymentError(""); + setSendPaymentSuccess(""); + if (!paymentTo) { + setSendPaymentError("Please enter a recipient"); + return; + } + if (!paymentAmount) { + setSendPaymentError("Please enter an amount greater than 0"); + return; + } + if (!paymentPassword) { + setSendPaymentError("Please enter your wallet password"); + return; + } + const fee = await getFee('PAYMENT') + + await show({ + message: `Would you like to transfer ${Number(paymentAmount)} QORT?` , + paymentFee: fee.fee + ' QORT' + }) + setIsLoadingSendCoin(true); + window + .sendMessage("sendCoin", { + amount: Number(paymentAmount), + receiver: paymentTo.trim(), + password: paymentPassword, + }) + .then((response) => { + if (response?.error) { + setSendPaymentError(response.error); + } else { + onSuccess() + + } + setIsLoadingSendCoin(false); + }) + .catch((error) => { + console.error("Failed to send coin:", error); + setIsLoadingSendCoin(false); + }); + } catch (error) { + // error + } + }; + return ( + <> + + + Transfer QORT + + + + Balance: + + + {balance?.toFixed(2)} QORT + + + + + + To + + setPaymentTo(e.target.value)} + autoComplete="off" + /> + + + Amount + + + setPaymentAmount(+e)} + /> + + + Confirm Wallet Password + + + setPaymentPassword(e.target.value)} + autoComplete="off" + /> + + + {sendPaymentError} + {/* {sendPaymentSuccess} */} + + { + if(isLoadingSendCoin) return + sendCoinFunc(); + }} + > + {isLoadingSendCoin && ( + + )} + Send + + + ) +} From 2dfe2c0cee6b8b6991af5bca4d460c2083bd2b23 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Fri, 7 Mar 2025 01:11:00 +0200 Subject: [PATCH 027/717] fix retrieving settings from localstorage --- src/App.tsx | 2 +- src/useRetrieveDataLocalStorage.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 221b8cb..3520489 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -428,7 +428,7 @@ function App() { const [creationStep, setCreationStep] = useState(1) const {getIndividualUserInfo} = useHandleUserInfo() const qortalRequestCheckbox1Ref = useRef(null); - useRetrieveDataLocalStorage(); + useRetrieveDataLocalStorage(userInfo?.address); useQortalGetSaveSettings(userInfo?.name, extState === "authenticated"); const [fullScreen, setFullScreen] = useRecoilState(fullScreenAtom); const [isEnabledDevMode, setIsEnabledDevMode] = diff --git a/src/useRetrieveDataLocalStorage.tsx b/src/useRetrieveDataLocalStorage.tsx index 2a248bd..fafcd5c 100644 --- a/src/useRetrieveDataLocalStorage.tsx +++ b/src/useRetrieveDataLocalStorage.tsx @@ -15,7 +15,7 @@ function fetchFromLocalStorage(key) { } } -export const useRetrieveDataLocalStorage = () => { +export const useRetrieveDataLocalStorage = (address) => { const setSortablePinnedApps = useSetRecoilState(sortablePinnedAppsAtom); const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom); const setIsUsingImportExportSettings = useSetRecoilState(isUsingImportExportSettingsAtom) @@ -50,6 +50,6 @@ export const useRetrieveDataLocalStorage = () => { getSortablePinnedApps() getSortablePinnedAppsImportExport() - }, [getSortablePinnedApps]) + }, [getSortablePinnedApps, address]) } From 2c40559044d39563c5b3e76524db17c6fcb0e0ff Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 8 Mar 2025 03:24:58 +0200 Subject: [PATCH 028/717] added payment notification --- src/App.tsx | 9 ++ src/atoms/global.ts | 5 + src/background.ts | 142 ++++++++++++++++++++- src/components/GeneralNotifications.tsx | 132 +++++++++++++++++++ src/hooks/useHandlePaymentNotification.tsx | 116 +++++++++++++++++ 5 files changed, 402 insertions(+), 2 deletions(-) create mode 100644 src/components/GeneralNotifications.tsx create mode 100644 src/hooks/useHandlePaymentNotification.tsx diff --git a/src/App.tsx b/src/App.tsx index 3520489..777c25e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -120,6 +120,7 @@ import { hasSettingsChangedAtom, isDisabledEditorEnterAtom, isUsingImportExportSettingsAtom, + lastPaymentSeenTimestampAtom, mailsAtom, oldPinnedAppsAtom, qMailLastEnteredTimestampAtom, @@ -154,6 +155,7 @@ import { UserLookup } from "./components/UserLookup.tsx/UserLookup"; import { RegisterName } from "./components/RegisterName"; import { BuyQortInformation } from "./components/BuyQortInformation"; import { QortPayment } from "./components/QortPayment"; +import { GeneralNotifications } from "./components/GeneralNotifications"; type extStates = | "not-authenticated" @@ -508,6 +510,7 @@ function App() { const resetAtomQMailLastEnteredTimestampAtom = useResetRecoilState(qMailLastEnteredTimestampAtom) const resetAtomMailsAtom = useResetRecoilState(mailsAtom) const resetGroupPropertiesAtom = useResetRecoilState(groupsPropertiesAtom) + const resetLastPaymentSeenTimestampAtom = useResetRecoilState(lastPaymentSeenTimestampAtom) const resetAllRecoil = () => { resetAtomSortablePinnedAppsAtom(); resetAtomCanSaveSettingToQdnAtom(); @@ -518,6 +521,7 @@ function App() { resetAtomQMailLastEnteredTimestampAtom() resetAtomMailsAtom() resetGroupPropertiesAtom() + resetLastPaymentSeenTimestampAtom() }; useEffect(() => { @@ -1802,6 +1806,11 @@ function App() { + + {extState === 'authenticated' && ( + + )} + ({ + key: 'lastPaymentSeenTimestampAtom', + default: null, +}); + export const mailsAtom = atom({ key: 'mailsAtom', default: [], diff --git a/src/background.ts b/src/background.ts index 96febcd..c43dacf 100644 --- a/src/background.ts +++ b/src/background.ts @@ -262,11 +262,12 @@ export const getForeignKey = async (foreignBlockchain)=> { export const pauseAllQueues = () => controlAllQueues("pause"); export const resumeAllQueues = () => controlAllQueues("resume"); -const checkDifference = (createdTimestamp) => { +export const checkDifference = (createdTimestamp, diff = timeDifferenceForNotificationChatsBackground) => { return ( - Date.now() - createdTimestamp < timeDifferenceForNotificationChatsBackground + Date.now() - createdTimestamp < diff ); }; + export const getApiKeyFromStorage = async (): Promise => { return getData("apiKey").catch(() => null); }; @@ -518,6 +519,7 @@ const handleNotificationDirect = async (directs) => { `_from=${newestLatestTimestamp.address}`); const notification = new window.Notification(title, { body, + icon: window.location.origin + "/qortal192.png", data: { id: notificationId }, }); @@ -559,6 +561,7 @@ const handleNotificationDirect = async (directs) => { const notification = new window.Notification(title, { body, + icon: window.location.origin + "/qortal192.png", data: { id: notificationId }, }); @@ -746,6 +749,7 @@ const handleNotification = async (groups) => { const notification = new window.Notification(title, { body, + icon: window.location.origin + "/qortal192.png", data: { id: notificationId }, }); @@ -788,6 +792,7 @@ const handleNotification = async (groups) => { // Create and show the notification immediately const notification = new window.Notification(title, { body, + icon: window.location.origin + "/qortal192.png", data: { id: notificationId }, }); @@ -2739,6 +2744,31 @@ export async function addTimestampGroupAnnouncement({ }); } +export async function getTimestampLatestPayment() { + const wallet = await getSaveWallet(); + const address = wallet.address0; + const key = `latest-payment-${address}`; + const res = await getData(key).catch(() => null); + if (res) { + const parsedData = res; + return parsedData; + } else return 0 +} + +export async function addTimestampLatestPayment(timestamp) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + + return await new Promise((resolve, reject) => { + storeData(`latest-payment-${address}`, timestamp) + .then(() => resolve(true)) + .catch((error) => { + reject(new Error(error.message || "Error saving data")); + }); + }); +} + + export async function addEnteredQmailTimestamp() { const wallet = await getSaveWallet(); const address = wallet.address0; @@ -3273,6 +3303,7 @@ export const checkNewMessages = async () => { // Create and show the notification const notification = new window.Notification(title, { body, + icon: window.location.origin + "/qortal192.png", data: { id: notificationId }, }); @@ -3302,6 +3333,95 @@ export const checkNewMessages = async () => { } }; +export const checkPaymentsForNotifications = async (address) => { + try { + const isDisableNotifications = + (await getUserSettings({ key: "disable-push-notifications" })) || false; + if(isDisableNotifications) return + let latestPayment = null + const savedtimestamp = await getTimestampLatestPayment(); + + const url = await createEndpoint( + `/transactions/search?txType=PAYMENT&address=${address}&confirmationStatus=CONFIRMED&limit=5&reverse=true` + ); + + const response = await fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + const responseData = await response.json(); + + const latestTx = responseData.filter( + (tx) => tx?.creatorAddress !== address && tx?.recipient === address + )[0]; + if (!latestTx) { + return; // continue to the next group + } + if ( + checkDifference(latestTx.timestamp) && + (!savedtimestamp || + latestTx.timestamp > + savedtimestamp) + ) { + if(latestTx.timestamp){ + latestPayment = latestTx + await addTimestampLatestPayment(latestTx.timestamp); + } + + // save new timestamp + } + + + + if ( + latestPayment + ) { + // Create a unique notification ID with type and group announcement details + const notificationId = + encodeURIComponent("payment_notification_" + + Date.now() + + "_type=payment-announcement"); + + const title = "New payment!"; + const body = `You have received a new payment of ${latestPayment?.amount} QORT`; + + // Create and show the notification + const notification = new window.Notification(title, { + body, + icon: window.location.origin + "/qortal192.png", + data: { id: notificationId }, + }); + + // Handle notification click with specific actions based on `notificationId` + notification.onclick = () => { + handleNotificationClick(notificationId); + notification.close(); // Clean up the notification on click + }; + + // Automatically close the notification after 5 seconds if it’s not clicked + setTimeout(() => { + notification.close(); + }, 10000); // Close after 5 seconds + + const targetOrigin = window.location.origin; + + window.postMessage( + { + action: "SET_PAYMENT_ANNOUNCEMENT", + payload: latestPayment, + }, + targetOrigin + ); + } + + } catch (error) { + console.error(error) + } +}; + const checkActiveChatsForNotifications = async () => { try { checkGroupList(); @@ -3437,6 +3557,7 @@ export const checkThreads = async (bringBack) => { // Create and show the notification const notification = new window.Notification(title, { body, + icon: window.location.origin + "/qortal192.png", data: { id: notificationId }, }); @@ -3494,6 +3615,7 @@ export const checkThreads = async (bringBack) => { // }); let notificationCheckInterval +let paymentsCheckInterval const createNotificationCheck = () => { // Check if an interval already exists before creating it @@ -3513,6 +3635,22 @@ const createNotificationCheck = () => { } }, 10 * 60 * 1000); // 10 minutes } + + if (!paymentsCheckInterval) { + paymentsCheckInterval = setInterval(async () => { + try { + // This would replace the Chrome alarm callback + const wallet = await getSaveWallet(); + const address = wallet?.address0; + if (!address) return; + + checkPaymentsForNotifications(address); + + } catch (error) { + console.error('Error checking payments:', error); + } + }, 3 * 60 * 1000); // 3 minutes + } }; // Call this function when initializing your app diff --git a/src/components/GeneralNotifications.tsx b/src/components/GeneralNotifications.tsx new file mode 100644 index 0000000..532c0e6 --- /dev/null +++ b/src/components/GeneralNotifications.tsx @@ -0,0 +1,132 @@ +import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; + +import { + Box, + ButtonBase, + Card, + MenuItem, + Popover, + Tooltip, + Typography, +} from "@mui/material"; +import NotificationsIcon from "@mui/icons-material/Notifications"; +import AccountBalanceWalletIcon from "@mui/icons-material/AccountBalanceWallet"; +import { formatDate } from "../utils/time"; +import { useHandlePaymentNotification } from "../hooks/useHandlePaymentNotification"; + +export const GeneralNotifications = ({ address }) => { + const [anchorEl, setAnchorEl] = useState(null); + const {latestTx, + getNameOrAddressOfSenderMiddle, + hasNewPayment, setLastEnteredTimestampPayment, nameAddressOfSender} = useHandlePaymentNotification(address) + + const handlePopupClick = (event) => { + event.stopPropagation(); // Prevent parent onClick from firing + setAnchorEl(event.currentTarget); + }; + + return ( + <> + { + handlePopupClick(e); + + + }} + style={{}} + > + + + + { + if(hasNewPayment){ + setLastEnteredTimestampPayment(Date.now()) + } + setAnchorEl(null) + + }} // Close popover on click outside + > + + {!hasNewPayment && No new notifications} + {hasNewPayment && ( + { + // executeEvent("addTab", { data: { service: 'APP', name: 'q-mail' } }); + // executeEvent("open-apps-mode", { }); + }} + > + + + {" "} + {formatDate(latestTx?.timestamp)} + + + + {latestTx?.amount} + + {nameAddressOfSender.current[latestTx?.creatorAddress] || getNameOrAddressOfSenderMiddle(latestTx?.creatorAddress)} + + + + )} + + + + ); +}; diff --git a/src/hooks/useHandlePaymentNotification.tsx b/src/hooks/useHandlePaymentNotification.tsx new file mode 100644 index 0000000..cfed663 --- /dev/null +++ b/src/hooks/useHandlePaymentNotification.tsx @@ -0,0 +1,116 @@ +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { getBaseApiReact } from '../App'; +import { getData, storeData } from '../utils/chromeStorage'; +import { checkDifference, getNameInfoForOthers } from '../background'; +import { useRecoilState } from 'recoil'; +import { lastPaymentSeenTimestampAtom } from '../atoms/global'; + +export const useHandlePaymentNotification = (address) => { + const [latestTx, setLatestTx] = useState(null); + + const nameAddressOfSender = useRef({}) + const isFetchingName = useRef({}) + + + const [lastEnteredTimestampPayment, setLastEnteredTimestampPayment] = + useRecoilState(lastPaymentSeenTimestampAtom); + + useEffect(() => { + if (lastEnteredTimestampPayment && address) { + storeData(`last-seen-payment-${address}`, Date.now()).catch((error) => { + console.error(error); + }); + } + }, [lastEnteredTimestampPayment, address]); + + const getNameOrAddressOfSender = useCallback(async(senderAddress)=> { + if(isFetchingName.current[senderAddress]) return senderAddress + try { + isFetchingName.current[senderAddress] = true + const res = await getNameInfoForOthers(senderAddress) + nameAddressOfSender.current[senderAddress] = res || senderAddress + } catch (error) { + console.error(error) + } finally { + isFetchingName.current[senderAddress] = false + } + + }, []) + + const getNameOrAddressOfSenderMiddle = useCallback(async(senderAddress)=> { + getNameOrAddressOfSender(senderAddress) + return senderAddress + + }, [getNameOrAddressOfSender]) + + const hasNewPayment = useMemo(() => { + if (!latestTx) return false; + if (!checkDifference(latestTx?.timestamp)) return false; + if ( + !lastEnteredTimestampPayment || + lastEnteredTimestampPayment < latestTx?.timestamp + ) + return true; + + return false; + }, [lastEnteredTimestampPayment, latestTx]); + + const getLastSeenData = useCallback(async () => { + try { + if (!address) return; + const key = `last-seen-payment-${address}`; + + const res = await getData(key).catch(() => null); + if (res) { + setLastEnteredTimestampPayment(res); + } + + const response = await fetch( + `${getBaseApiReact()}/transactions/search?txType=PAYMENT&address=${address}&confirmationStatus=CONFIRMED&limit=5&reverse=true` + ); + + const responseData = await response.json(); + + const latestTx = responseData.filter( + (tx) => tx?.creatorAddress !== address && tx?.recipient === address + )[0]; + if (!latestTx) { + return; // continue to the next group + } + + setLatestTx(latestTx); + } catch (error) { + console.error(error); + } + }, [address, setLastEnteredTimestampPayment]); + + + useEffect(() => { + getLastSeenData(); + // Handler function for incoming messages + const messageHandler = (event) => { + if (event.origin !== window.location.origin) { + return; + } + const message = event.data; + if (message?.action === "SET_PAYMENT_ANNOUNCEMENT" && message?.payload) { + setLatestTx(message.payload); + } + }; + + // Attach the event listener + window.addEventListener("message", messageHandler); + + // Clean up the event listener on component unmount + return () => { + window.removeEventListener("message", messageHandler); + }; + }, [getLastSeenData]); + return { + latestTx, + getNameOrAddressOfSenderMiddle, + hasNewPayment, + setLastEnteredTimestampPayment, + nameAddressOfSender + } +} From d8d6650edcce9b683d493adc32bb97fc44fa124b Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 8 Mar 2025 05:28:49 +0200 Subject: [PATCH 029/717] disable dev mode option when on web --- src/components/Chat/MessageDisplay.tsx | 6 +++++- src/components/Group/Settings.tsx | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/Chat/MessageDisplay.tsx b/src/components/Chat/MessageDisplay.tsx index a686a00..52d0d93 100644 --- a/src/components/Chat/MessageDisplay.tsx +++ b/src/components/Chat/MessageDisplay.tsx @@ -98,7 +98,11 @@ export const MessageDisplay = ({ htmlContent, isReply }) => { const target = e.target; if (target.tagName === 'A') { const href = target.getAttribute('href'); - window.electronAPI.openExternal(href); + if(window?.electronAPI){ + window.electronAPI.openExternal(href); + } else { + window.open(href, '_system'); + } } else if (target.getAttribute('data-url')) { const url = target.getAttribute('data-url'); diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index f14483c..2aba6f6 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -186,7 +186,8 @@ export const Settings = ({ } label="Disable all push notifications" /> - + )} From d62415e697eb42eec003f7d55bc6086b9e12195a Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 8 Mar 2025 20:10:18 +0200 Subject: [PATCH 030/717] added wallets embed --- src/App.tsx | 106 ++++-------- src/atoms/global.ts | 4 + src/components/Apps/AppViewer.tsx | 4 +- src/components/Apps/AppViewerContainer.tsx | 4 +- src/components/Apps/AppsCategory.tsx | 3 +- src/components/Apps/AppsCategoryDesktop.tsx | 3 +- src/components/Apps/AppsLibrary.tsx | 3 +- src/components/Apps/AppsLibraryDesktop.tsx | 3 +- .../Apps/useQortalMessageListener.tsx | 6 +- src/components/Explore/Explore.tsx | 30 ++++ src/components/GeneralNotifications.tsx | 28 ++- src/components/Group/Group.tsx | 2 + src/components/Group/WalletsAppWrapper.tsx | 163 ++++++++++++++++++ src/hooks/useHandlePaymentNotification.tsx | 16 ++ src/messaging/messagesToBackground.tsx | 4 +- src/qortalRequests.ts | 3 +- src/qortalRequests/get.ts | 5 +- 17 files changed, 299 insertions(+), 88 deletions(-) create mode 100644 src/components/Group/WalletsAppWrapper.tsx diff --git a/src/App.tsx b/src/App.tsx index 777c25e..6678580 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -47,6 +47,7 @@ import Info from "./assets/svgs/Info.svg"; import CloseIcon from "@mui/icons-material/Close"; import './utils/seedPhrase/RandomSentenceGenerator'; import EngineeringIcon from '@mui/icons-material/Engineering'; +import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; import { createAccount, generateRandomSentence, @@ -1698,13 +1699,46 @@ function App() { /> + + { + executeEvent('openWalletsApp', {}) + }} + > + WALLETS} + placement="left" + arrow + sx={{ fontSize: "24" }} + slotProps={{ + tooltip: { + sx: { + color: "#ffffff", + backgroundColor: "#444444", + }, + }, + arrow: { + sx: { + color: "#444444", + }, + }, + }} + > + + + + {desktopViewMode !== 'home' && ( <> WALLET} + title={YOUR ACCOUNT} placement="left" arrow sx={{ fontSize: "24" }} @@ -1733,75 +1767,7 @@ function App() { )} - {/* {authenticatedMode === "qort" && ( - LITECOIN WALLET} - placement="left" - arrow - sx={{ fontSize: "24" }} - slotProps={{ - tooltip: { - sx: { - color: "#ffffff", - backgroundColor: "#444444", - }, - }, - arrow: { - sx: { - color: "#444444", - }, - }, - }} - > - { - if(desktopViewMode !== 'home'){ - setIsOpenDrawerProfile((prev)=> !prev) - } - setAuthenticatedMode("ltc"); - }} - src={ltcLogo} - style={{ - cursor: "pointer", - width: "20px", - height: "auto", - }} - /> - - )} - {authenticatedMode === "ltc" && ( - QORTAL WALLET} - placement="left" - arrow - sx={{ fontSize: "24" }} - slotProps={{ - tooltip: { - sx: { - color: "#ffffff", - backgroundColor: "#444444", - }, - }, - arrow: { - sx: { - color: "#444444", - }, - }, - }} - > - { - setAuthenticatedMode("qort"); - }} - src={qortLogo} - style={{ - cursor: "pointer", - width: "20px", - height: "auto", - }} - /> - - )} */} + diff --git a/src/atoms/global.ts b/src/atoms/global.ts index f1946ef..d546bf0 100644 --- a/src/atoms/global.ts +++ b/src/atoms/global.ts @@ -38,6 +38,10 @@ export const sortablePinnedAppsAtom = atom({ { name: 'Q-Mintership', service: 'APP' + }, + { + name: 'Q-Wallets', + service: 'APP' } ], }); diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 902c6d8..cd9cf94 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -11,11 +11,11 @@ import { useQortalMessageListener } from "./useQortalMessageListener"; -export const AppViewer = React.forwardRef(({ app , hide, isDevMode}, iframeRef) => { +export const AppViewer = React.forwardRef(({ app , hide, isDevMode, skipAuth}, iframeRef) => { const { rootHeight } = useContext(MyContext); // const iframeRef = useRef(null); const { document, window: frameWindow } = useFrame(); - const {path, history, changeCurrentIndex, resetHistory} = useQortalMessageListener(frameWindow, iframeRef, app?.tabId, isDevMode, app?.name, app?.service) + const {path, history, changeCurrentIndex, resetHistory} = useQortalMessageListener(frameWindow, iframeRef, app?.tabId, isDevMode, app?.name, app?.service, skipAuth) const [url, setUrl] = useState('') diff --git a/src/components/Apps/AppViewerContainer.tsx b/src/components/Apps/AppViewerContainer.tsx index 674a9a7..f258848 100644 --- a/src/components/Apps/AppViewerContainer.tsx +++ b/src/components/Apps/AppViewerContainer.tsx @@ -3,7 +3,7 @@ import { AppViewer } from './AppViewer'; import Frame from 'react-frame-component'; import { MyContext, isMobile } from '../../App'; -const AppViewerContainer = React.forwardRef(({ app, isSelected, hide, isDevMode, customHeight }, ref) => { +const AppViewerContainer = React.forwardRef(({ app, isSelected, hide, isDevMode, customHeight, skipAuth }, ref) => { const { rootHeight } = useContext(MyContext); @@ -42,7 +42,7 @@ const AppViewerContainer = React.forwardRef(({ app, isSelected, hide, isDevMode, overflow: 'hidden', }} > - + ); }); diff --git a/src/components/Apps/AppsCategory.tsx b/src/components/Apps/AppsCategory.tsx index 725ff1d..d1a1420 100644 --- a/src/components/Apps/AppsCategory.tsx +++ b/src/components/Apps/AppsCategory.tsx @@ -40,7 +40,8 @@ const officialAppList = [ "q-shop", "q-trade", "q-support", - "q-manager" + "q-manager", + "q-wallets" ]; const ScrollerStyled = styled('div')({ diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index d41207a..f6ba48d 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -48,7 +48,8 @@ const officialAppList = [ "q-shop", "q-trade", "q-support", - "q-manager" + "q-manager", + "q-wallets" ]; const ScrollerStyled = styled("div")({ diff --git a/src/components/Apps/AppsLibrary.tsx b/src/components/Apps/AppsLibrary.tsx index 26e7d81..f9b58ea 100644 --- a/src/components/Apps/AppsLibrary.tsx +++ b/src/components/Apps/AppsLibrary.tsx @@ -42,7 +42,8 @@ const officialAppList = [ "q-shop", "q-trade", "q-support", - "q-manager" + "q-manager", + "q-wallets" ]; const ScrollerStyled = styled('div')({ diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index b836ac8..de356d4 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -58,7 +58,8 @@ const officialAppList = [ "q-trade", "q-support", "q-manager", - "q-mintership" + "q-mintership", + "q-wallets" ]; const ScrollerStyled = styled("div")({ diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index 508d5bc..6ff79ab 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -507,7 +507,7 @@ export const UIQortalRequests = [ return obj; // Updated object with references to stored files } -export const useQortalMessageListener = (frameWindow, iframeRef, tabId, isDevMode, appName, appService) => { +export const useQortalMessageListener = (frameWindow, iframeRef, tabId, isDevMode, appName, appService, skipAuth) => { const [path, setPath] = useState('') const [history, setHistory] = useState({ customQDNHistoryPaths: [], @@ -564,7 +564,7 @@ isDOMContentLoaded: false const sendMessageToRuntime = (message, eventPort) => { window.sendMessage(message.action, message.payload, 300000, message.isExtension, { name: appName, service: appService - }) + }, skipAuth) .then((response) => { if (response.error) { eventPort.postMessage({ @@ -633,6 +633,8 @@ isDOMContentLoaded: false window.sendMessage("addEnteredQmailTimestamp").catch((error) => { // error }); + } else if(appName?.toLowerCase() === 'q-wallets'){ + executeEvent('setLastEnteredTimestampPaymentEvent', {}) } } else if(event?.data?.action === 'NAVIGATION_HISTORY'){ if(event?.data?.payload?.isDOMContentLoaded){ diff --git a/src/components/Explore/Explore.tsx b/src/components/Explore/Explore.tsx index 7cc96a0..61037d7 100644 --- a/src/components/Explore/Explore.tsx +++ b/src/components/Explore/Explore.tsx @@ -4,6 +4,9 @@ import ChatIcon from "@mui/icons-material/Chat"; import qTradeLogo from "../../assets/Icons/q-trade-logo.webp"; import AppsIcon from "@mui/icons-material/Apps"; import { executeEvent } from "../../utils/events"; +import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; + + export const Explore = ({setDesktopViewMode}) => { return ( { General Chat + { + executeEvent("openWalletsApp", { + + }); + }} + > + + + Wallets + + ); }; diff --git a/src/components/GeneralNotifications.tsx b/src/components/GeneralNotifications.tsx index 532c0e6..c7336dc 100644 --- a/src/components/GeneralNotifications.tsx +++ b/src/components/GeneralNotifications.tsx @@ -13,6 +13,7 @@ import NotificationsIcon from "@mui/icons-material/Notifications"; import AccountBalanceWalletIcon from "@mui/icons-material/AccountBalanceWallet"; import { formatDate } from "../utils/time"; import { useHandlePaymentNotification } from "../hooks/useHandlePaymentNotification"; +import { executeEvent } from "../utils/events"; export const GeneralNotifications = ({ address }) => { const [anchorEl, setAnchorEl] = useState(null); @@ -35,11 +36,31 @@ export const GeneralNotifications = ({ address }) => { }} style={{}} > + PAYMENT NOTIFICATION} + placement="left" + arrow + sx={{ fontSize: "24" }} + slotProps={{ + tooltip: { + sx: { + color: "#ffffff", + backgroundColor: "#444444", + }, + }, + arrow: { + sx: { + color: "#444444", + }, + }, + }} + > + { width: "100%", alignItems: "flex-start", textWrap: "auto", - cursor: 'default' }} - onClick={(e) => { - // executeEvent("addTab", { data: { service: 'APP', name: 'q-mail' } }); - // executeEvent("open-apps-mode", { }); + onClick={() => { + setAnchorEl(null) + executeEvent('openWalletsApp', {}) }} > { @@ -2579,6 +2580,7 @@ export const Group = ({ message: "Setting up groups... please wait.", }} /> + ); diff --git a/src/components/Group/WalletsAppWrapper.tsx b/src/components/Group/WalletsAppWrapper.tsx new file mode 100644 index 0000000..00218a7 --- /dev/null +++ b/src/components/Group/WalletsAppWrapper.tsx @@ -0,0 +1,163 @@ +import { Box, ButtonBase, Divider, Typography } from "@mui/material"; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import CloseIcon from "@mui/icons-material/Close"; +import AppViewerContainer from "../Apps/AppViewerContainer"; +import { + executeEvent, + subscribeToEvent, + unsubscribeFromEvent, +} from "../../utils/events"; +import { useRecoilState } from "recoil"; +import { navigationControllerAtom } from "../../atoms/global"; +import { AppsNavBarLeft, AppsNavBarParent } from "../Apps/Apps-styles"; +import NavBack from "../../assets/svgs/NavBack.svg"; +import RefreshIcon from "@mui/icons-material/Refresh"; + +export const WalletsAppWrapper = () => { + const iframeRef = useRef(null); + const [isOpen, setIsOpen] = useState(false); + const [navigationController, setNavigationController] = useRecoilState( + navigationControllerAtom + ); + const [selectedTab, setSelectedTab] = useState({ + tabId: "5558589", + name: "Q-Wallets", + service: "APP", + path: '/qortal' + }); + + const isDisableBackButton = useMemo(() => { + if (selectedTab && navigationController[selectedTab?.tabId]?.hasBack) + return false; + if (selectedTab && !navigationController[selectedTab?.tabId]?.hasBack) + return true; + return false; + }, [navigationController, selectedTab]); + + const openWalletsAppFunc = useCallback( + (e) => { + setIsOpen(true); + }, + [setIsOpen] + ); + + useEffect(() => { + subscribeToEvent("openWalletsApp", openWalletsAppFunc); + + return () => { + unsubscribeFromEvent("openWalletsApp", openWalletsAppFunc); + }; + }, [openWalletsAppFunc]); + + const handleClose = ()=> { + setIsOpen(false); + iframeRef.current = null + } + + return ( + <> + {isOpen && ( + + + + Q-Wallets + + + + + + + + + { + executeEvent(`navigateBackApp-${selectedTab?.tabId}`, {}); + }} + disabled={isDisableBackButton} + sx={{ + opacity: !isDisableBackButton ? 1 : 0.1, + cursor: !isDisableBackButton ? "pointer" : "default", + }} + > + + + { + if (selectedTab?.refreshFunc) { + selectedTab.refreshFunc(selectedTab?.tabId); + + } else { + executeEvent("refreshApp", { + tabId: selectedTab?.tabId, + }); + } + + + }}> + + + + + + + )} + + ); +}; diff --git a/src/hooks/useHandlePaymentNotification.tsx b/src/hooks/useHandlePaymentNotification.tsx index cfed663..b5df816 100644 --- a/src/hooks/useHandlePaymentNotification.tsx +++ b/src/hooks/useHandlePaymentNotification.tsx @@ -4,6 +4,7 @@ import { getData, storeData } from '../utils/chromeStorage'; import { checkDifference, getNameInfoForOthers } from '../background'; import { useRecoilState } from 'recoil'; import { lastPaymentSeenTimestampAtom } from '../atoms/global'; +import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events'; export const useHandlePaymentNotification = (address) => { const [latestTx, setLatestTx] = useState(null); @@ -106,6 +107,21 @@ export const useHandlePaymentNotification = (address) => { window.removeEventListener("message", messageHandler); }; }, [getLastSeenData]); + + const setLastEnteredTimestampPaymentEventFunc = useCallback( + (e) => { + setLastEnteredTimestampPayment(Date.now) + }, + [setLastEnteredTimestampPayment] + ); + + useEffect(() => { + subscribeToEvent("setLastEnteredTimestampPaymentEvent", setLastEnteredTimestampPaymentEventFunc); + + return () => { + unsubscribeFromEvent("setLastEnteredTimestampPaymentEvent", setLastEnteredTimestampPaymentEventFunc); + }; + }, [setLastEnteredTimestampPaymentEventFunc]); return { latestTx, getNameOrAddressOfSenderMiddle, diff --git a/src/messaging/messagesToBackground.tsx b/src/messaging/messagesToBackground.tsx index 7da16d4..0ba8f71 100644 --- a/src/messaging/messagesToBackground.tsx +++ b/src/messaging/messagesToBackground.tsx @@ -24,13 +24,13 @@ window.addEventListener("message", (event) => { } }); -export const sendMessageBackground = (action, data = {}, timeout = 180000, isExtension, appInfo) => { +export const sendMessageBackground = (action, data = {}, timeout = 180000, isExtension, appInfo, skipAuth) => { return new Promise((resolve, reject) => { const requestId = generateRequestId(); // Unique ID for each request callbackMap.set(requestId, { resolve, reject }); // Store both resolve and reject callbacks const targetOrigin = window.location.origin // Send the message with `backgroundMessage` type - window.postMessage({ type: "backgroundMessage", action, requestId, payload: data, isExtension, appInfo }, targetOrigin); + window.postMessage({ type: "backgroundMessage", action, requestId, payload: data, isExtension, appInfo, skipAuth }, targetOrigin); // Set up a timeout to automatically reject if no response is received const timeoutId = setTimeout(() => { diff --git a/src/qortalRequests.ts b/src/qortalRequests.ts index b2848c4..18457da 100644 --- a/src/qortalRequests.ts +++ b/src/qortalRequests.ts @@ -70,6 +70,7 @@ export const isRunningGateway = async ()=> { // Ensure the message is from a trusted source const isFromExtension = request?.isExtension; const appInfo = request?.appInfo; + const skipAuth = request?.skipAuth || false if (request?.type !== "backgroundMessage") return; // Only process messages of type 'backgroundMessage' @@ -77,7 +78,7 @@ export const isRunningGateway = async ()=> { switch (request.action) { case "GET_USER_ACCOUNT": { try { - const res = await getUserAccount({isFromExtension, appInfo}); + const res = await getUserAccount({isFromExtension, appInfo, skipAuth}); event.source.postMessage({ requestId: request.requestId, action: request.action, diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index 66e3006..30fd805 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -371,7 +371,7 @@ async function getUserPermission(payload, isFromExtension) { }); } -export const getUserAccount = async ({ isFromExtension, appInfo }) => { +export const getUserAccount = async ({ isFromExtension, appInfo, skipAuth }) => { try { const value = (await getPermission(`qAPPAutoAuth-${appInfo?.name}`)) || false; @@ -379,6 +379,9 @@ export const getUserAccount = async ({ isFromExtension, appInfo }) => { if (value) { skip = true; } + if(skipAuth){ + skip = true + } let resPermission; if (!skip) { resPermission = await getUserPermission( From 15f15f8172a6159829250ab12a0a04a03a7a5e55 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 10 Mar 2025 18:12:41 +0200 Subject: [PATCH 031/717] fix qmail notification --- src/components/Group/QMailMessages.tsx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/Group/QMailMessages.tsx b/src/components/Group/QMailMessages.tsx index b4bfd4a..c2d7370 100644 --- a/src/components/Group/QMailMessages.tsx +++ b/src/components/Group/QMailMessages.tsx @@ -103,16 +103,16 @@ export const QMailMessages = ({userName, userAddress}) => { }, [getMails, userName, userAddress]); - const anyUnread = useMemo(()=> { - let unread = false - - mails.forEach((mail)=> { - if(lastEnteredTimestamp && isLessThanOneWeekOld(mail?.created)){ - unread = true - } - }) - return unread - }, [mails, lastEnteredTimestamp]) + const anyUnread = useMemo(()=> { + let unread = false + + mails.forEach((mail)=> { + if(!lastEnteredTimestamp && isLessThanOneWeekOld(mail?.created) || (lastEnteredTimestamp && isLessThanOneWeekOld(mail?.created) && lastEnteredTimestamp < mail?.created)){ + unread = true + } + }) + return unread + }, [mails, lastEnteredTimestamp]) return ( { Latest Q-Mails {isExpanded ? : ( )} From f65e4e92ced97399a4fc02db63221d59157513b3 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 10 Mar 2025 19:45:05 +0200 Subject: [PATCH 032/717] fix showing edited messages --- src/components/Chat/ChatList.tsx | 4 ++++ src/components/Chat/MessageItem.tsx | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 9d5e42b..ef32e7e 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -245,6 +245,7 @@ export const ChatList = ({ if (chatReferences?.[reply?.signature]?.edit) { reply.decryptedData = chatReferences[reply?.signature]?.edit; reply.text = chatReferences[reply?.signature]?.edit?.message; + reply.editTimestamp = chatReferences[reply?.signature]?.edit?.timestamp } } @@ -271,10 +272,12 @@ export const ChatList = ({ if (chatReferences[message.signature]?.edit?.message && message?.text) { message.text = chatReferences[message.signature]?.edit?.message; message.isEdit = true + message.editTimestamp = chatReferences[message.signature]?.edit?.timestamp } if (chatReferences[message.signature]?.edit?.messageText && message?.messageText) { message.messageText = chatReferences[message.signature]?.edit?.messageText; message.isEdit = true + message.editTimestamp = chatReferences[message.signature]?.edit?.timestamp } } @@ -360,6 +363,7 @@ export const ChatList = ({ reactions={reactions} isUpdating={isUpdating} isPrivate={isPrivate} + /> diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index 284ad96..06db7f8 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -102,7 +102,7 @@ const htmlText = useMemo(()=> { ]) } -}, []) +}, [message?.editTimestamp]) @@ -118,7 +118,7 @@ const htmlReply = useMemo(()=> { ]) } -}, []) +}, [reply?.editTimestamp]) const userAvatarUrl = useMemo(()=> { return message?.senderName ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${ From 881d848d4ded5913d317352345afe039da41859a Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 2 Apr 2025 05:58:17 +0300 Subject: [PATCH 033/717] added pdf viewer and optimizations --- public/pdfjs/build/pdf.mjs | 21576 ++++++ public/pdfjs/build/pdf.sandbox.mjs | 242 + public/pdfjs/build/pdf.worker.mjs | 56979 ++++++++++++++++ public/pdfjs/web/debugger.css | 111 + public/pdfjs/web/debugger.mjs | 623 + public/pdfjs/web/images/altText_add.svg | 3 + .../pdfjs/web/images/altText_disclaimer.svg | 3 + public/pdfjs/web/images/altText_done.svg | 3 + public/pdfjs/web/images/altText_spinner.svg | 30 + public/pdfjs/web/images/altText_warning.svg | 3 + public/pdfjs/web/images/annotation-check.svg | 11 + .../pdfjs/web/images/annotation-comment.svg | 16 + public/pdfjs/web/images/annotation-help.svg | 26 + public/pdfjs/web/images/annotation-insert.svg | 10 + public/pdfjs/web/images/annotation-key.svg | 11 + .../web/images/annotation-newparagraph.svg | 11 + public/pdfjs/web/images/annotation-noicon.svg | 7 + public/pdfjs/web/images/annotation-note.svg | 42 + .../pdfjs/web/images/annotation-paperclip.svg | 6 + .../pdfjs/web/images/annotation-paragraph.svg | 16 + .../pdfjs/web/images/annotation-pushpin.svg | 7 + .../web/images/cursor-editorFreeHighlight.svg | 6 + .../web/images/cursor-editorFreeText.svg | 3 + public/pdfjs/web/images/cursor-editorInk.svg | 4 + .../web/images/cursor-editorTextHighlight.svg | 8 + .../web/images/editor-toolbar-delete.svg | 5 + .../pdfjs/web/images/findbarButton-next.svg | 3 + .../web/images/findbarButton-previous.svg | 3 + .../web/images/gv-toolbarButton-download.svg | 3 + public/pdfjs/web/images/loading-icon.gif | Bin 0 -> 2545 bytes public/pdfjs/web/images/loading.svg | 1 + .../web/images/messageBar_closingButton.svg | 3 + .../pdfjs/web/images/messageBar_warning.svg | 3 + ...ondaryToolbarButton-documentProperties.svg | 3 + .../secondaryToolbarButton-firstPage.svg | 3 + .../secondaryToolbarButton-handTool.svg | 3 + .../secondaryToolbarButton-lastPage.svg | 3 + .../secondaryToolbarButton-rotateCcw.svg | 3 + .../secondaryToolbarButton-rotateCw.svg | 3 + ...econdaryToolbarButton-scrollHorizontal.svg | 3 + .../secondaryToolbarButton-scrollPage.svg | 3 + .../secondaryToolbarButton-scrollVertical.svg | 3 + .../secondaryToolbarButton-scrollWrapped.svg | 3 + .../secondaryToolbarButton-selectTool.svg | 3 + .../secondaryToolbarButton-spreadEven.svg | 3 + .../secondaryToolbarButton-spreadNone.svg | 3 + .../secondaryToolbarButton-spreadOdd.svg | 3 + .../web/images/toolbarButton-bookmark.svg | 3 + .../toolbarButton-currentOutlineItem.svg | 3 + .../web/images/toolbarButton-download.svg | 4 + .../images/toolbarButton-editorFreeText.svg | 5 + .../images/toolbarButton-editorHighlight.svg | 6 + .../web/images/toolbarButton-editorInk.svg | 4 + .../web/images/toolbarButton-editorStamp.svg | 8 + .../web/images/toolbarButton-menuArrow.svg | 3 + .../web/images/toolbarButton-openFile.svg | 3 + .../web/images/toolbarButton-pageDown.svg | 3 + .../pdfjs/web/images/toolbarButton-pageUp.svg | 3 + .../images/toolbarButton-presentationMode.svg | 3 + .../pdfjs/web/images/toolbarButton-print.svg | 3 + .../pdfjs/web/images/toolbarButton-search.svg | 3 + .../toolbarButton-secondaryToolbarToggle.svg | 3 + .../images/toolbarButton-sidebarToggle.svg | 3 + .../images/toolbarButton-viewAttachments.svg | 3 + .../web/images/toolbarButton-viewLayers.svg | 3 + .../web/images/toolbarButton-viewOutline.svg | 3 + .../images/toolbarButton-viewThumbnail.svg | 3 + .../pdfjs/web/images/toolbarButton-zoomIn.svg | 3 + .../web/images/toolbarButton-zoomOut.svg | 3 + .../pdfjs/web/images/treeitem-collapsed.svg | 1 + public/pdfjs/web/images/treeitem-expanded.svg | 1 + public/pdfjs/web/locale/ach/viewer.ftl | 225 + public/pdfjs/web/locale/af/viewer.ftl | 212 + public/pdfjs/web/locale/an/viewer.ftl | 257 + public/pdfjs/web/locale/ar/viewer.ftl | 425 + public/pdfjs/web/locale/ast/viewer.ftl | 201 + public/pdfjs/web/locale/az/viewer.ftl | 257 + public/pdfjs/web/locale/be/viewer.ftl | 518 + public/pdfjs/web/locale/bg/viewer.ftl | 418 + public/pdfjs/web/locale/bn/viewer.ftl | 247 + public/pdfjs/web/locale/bo/viewer.ftl | 247 + public/pdfjs/web/locale/br/viewer.ftl | 340 + public/pdfjs/web/locale/brx/viewer.ftl | 218 + public/pdfjs/web/locale/bs/viewer.ftl | 223 + public/pdfjs/web/locale/ca/viewer.ftl | 313 + public/pdfjs/web/locale/cak/viewer.ftl | 291 + public/pdfjs/web/locale/ckb/viewer.ftl | 242 + public/pdfjs/web/locale/cs/viewer.ftl | 521 + public/pdfjs/web/locale/cy/viewer.ftl | 527 + public/pdfjs/web/locale/da/viewer.ftl | 515 + public/pdfjs/web/locale/de/viewer.ftl | 515 + public/pdfjs/web/locale/dsb/viewer.ftl | 521 + public/pdfjs/web/locale/el/viewer.ftl | 515 + public/pdfjs/web/locale/en-CA/viewer.ftl | 515 + public/pdfjs/web/locale/en-GB/viewer.ftl | 515 + public/pdfjs/web/locale/en-US/viewer.ftl | 526 + public/pdfjs/web/locale/eo/viewer.ftl | 515 + public/pdfjs/web/locale/es-AR/viewer.ftl | 515 + public/pdfjs/web/locale/es-CL/viewer.ftl | 515 + public/pdfjs/web/locale/es-ES/viewer.ftl | 515 + public/pdfjs/web/locale/es-MX/viewer.ftl | 515 + public/pdfjs/web/locale/et/viewer.ftl | 268 + public/pdfjs/web/locale/eu/viewer.ftl | 515 + public/pdfjs/web/locale/fa/viewer.ftl | 348 + public/pdfjs/web/locale/ff/viewer.ftl | 247 + public/pdfjs/web/locale/fi/viewer.ftl | 515 + public/pdfjs/web/locale/fr/viewer.ftl | 511 + public/pdfjs/web/locale/fur/viewer.ftl | 485 + public/pdfjs/web/locale/fy-NL/viewer.ftl | 515 + public/pdfjs/web/locale/ga-IE/viewer.ftl | 213 + public/pdfjs/web/locale/gd/viewer.ftl | 313 + public/pdfjs/web/locale/gl/viewer.ftl | 385 + public/pdfjs/web/locale/gn/viewer.ftl | 515 + public/pdfjs/web/locale/gu-IN/viewer.ftl | 247 + public/pdfjs/web/locale/he/viewer.ftl | 515 + public/pdfjs/web/locale/hi-IN/viewer.ftl | 267 + public/pdfjs/web/locale/hr/viewer.ftl | 473 + public/pdfjs/web/locale/hsb/viewer.ftl | 521 + public/pdfjs/web/locale/hu/viewer.ftl | 515 + public/pdfjs/web/locale/hy-AM/viewer.ftl | 272 + public/pdfjs/web/locale/hye/viewer.ftl | 268 + public/pdfjs/web/locale/ia/viewer.ftl | 515 + public/pdfjs/web/locale/id/viewer.ftl | 374 + public/pdfjs/web/locale/is/viewer.ftl | 515 + public/pdfjs/web/locale/it/viewer.ftl | 515 + public/pdfjs/web/locale/ja/viewer.ftl | 503 + public/pdfjs/web/locale/ka/viewer.ftl | 515 + public/pdfjs/web/locale/kab/viewer.ftl | 438 + public/pdfjs/web/locale/kk/viewer.ftl | 515 + public/pdfjs/web/locale/km/viewer.ftl | 223 + public/pdfjs/web/locale/kn/viewer.ftl | 213 + public/pdfjs/web/locale/ko/viewer.ftl | 503 + public/pdfjs/web/locale/lij/viewer.ftl | 247 + public/pdfjs/web/locale/lo/viewer.ftl | 313 + public/pdfjs/web/locale/locale.json | 1 + public/pdfjs/web/locale/lt/viewer.ftl | 268 + public/pdfjs/web/locale/ltg/viewer.ftl | 246 + public/pdfjs/web/locale/lv/viewer.ftl | 247 + public/pdfjs/web/locale/meh/viewer.ftl | 87 + public/pdfjs/web/locale/mk/viewer.ftl | 215 + public/pdfjs/web/locale/mr/viewer.ftl | 239 + public/pdfjs/web/locale/ms/viewer.ftl | 247 + public/pdfjs/web/locale/my/viewer.ftl | 206 + public/pdfjs/web/locale/nb-NO/viewer.ftl | 495 + public/pdfjs/web/locale/ne-NP/viewer.ftl | 234 + public/pdfjs/web/locale/nl/viewer.ftl | 515 + public/pdfjs/web/locale/nn-NO/viewer.ftl | 498 + public/pdfjs/web/locale/oc/viewer.ftl | 409 + public/pdfjs/web/locale/pa-IN/viewer.ftl | 515 + public/pdfjs/web/locale/pl/viewer.ftl | 518 + public/pdfjs/web/locale/pt-BR/viewer.ftl | 515 + public/pdfjs/web/locale/pt-PT/viewer.ftl | 515 + public/pdfjs/web/locale/rm/viewer.ftl | 515 + public/pdfjs/web/locale/ro/viewer.ftl | 251 + public/pdfjs/web/locale/ru/viewer.ftl | 518 + public/pdfjs/web/locale/sat/viewer.ftl | 325 + public/pdfjs/web/locale/sc/viewer.ftl | 367 + public/pdfjs/web/locale/scn/viewer.ftl | 74 + public/pdfjs/web/locale/sco/viewer.ftl | 264 + public/pdfjs/web/locale/si/viewer.ftl | 271 + public/pdfjs/web/locale/sk/viewer.ftl | 521 + public/pdfjs/web/locale/skr/viewer.ftl | 498 + public/pdfjs/web/locale/sl/viewer.ftl | 521 + public/pdfjs/web/locale/son/viewer.ftl | 206 + public/pdfjs/web/locale/sq/viewer.ftl | 506 + public/pdfjs/web/locale/sr/viewer.ftl | 421 + public/pdfjs/web/locale/sv-SE/viewer.ftl | 515 + public/pdfjs/web/locale/szl/viewer.ftl | 257 + public/pdfjs/web/locale/ta/viewer.ftl | 223 + public/pdfjs/web/locale/te/viewer.ftl | 239 + public/pdfjs/web/locale/tg/viewer.ftl | 515 + public/pdfjs/web/locale/th/viewer.ftl | 503 + public/pdfjs/web/locale/tl/viewer.ftl | 257 + public/pdfjs/web/locale/tr/viewer.ftl | 515 + public/pdfjs/web/locale/trs/viewer.ftl | 197 + public/pdfjs/web/locale/uk/viewer.ftl | 518 + public/pdfjs/web/locale/ur/viewer.ftl | 248 + public/pdfjs/web/locale/uz/viewer.ftl | 187 + public/pdfjs/web/locale/vi/viewer.ftl | 503 + public/pdfjs/web/locale/wo/viewer.ftl | 127 + public/pdfjs/web/locale/xh/viewer.ftl | 212 + public/pdfjs/web/locale/zh-CN/viewer.ftl | 503 + public/pdfjs/web/locale/zh-TW/viewer.ftl | 503 + .../web/standard_fonts/FoxitDingbats.pfb | Bin 0 -> 29513 bytes .../pdfjs/web/standard_fonts/FoxitFixed.pfb | Bin 0 -> 17597 bytes .../web/standard_fonts/FoxitFixedBold.pfb | Bin 0 -> 18055 bytes .../standard_fonts/FoxitFixedBoldItalic.pfb | Bin 0 -> 19151 bytes .../web/standard_fonts/FoxitFixedItalic.pfb | Bin 0 -> 18746 bytes .../pdfjs/web/standard_fonts/FoxitSerif.pfb | Bin 0 -> 19469 bytes .../web/standard_fonts/FoxitSerifBold.pfb | Bin 0 -> 19395 bytes .../standard_fonts/FoxitSerifBoldItalic.pfb | Bin 0 -> 20733 bytes .../web/standard_fonts/FoxitSerifItalic.pfb | Bin 0 -> 21227 bytes .../pdfjs/web/standard_fonts/FoxitSymbol.pfb | Bin 0 -> 16729 bytes public/pdfjs/web/standard_fonts/LICENSE_FOXIT | 27 + .../web/standard_fonts/LICENSE_LIBERATION | 102 + .../standard_fonts/LiberationSans-Bold.ttf | Bin 0 -> 137052 bytes .../LiberationSans-BoldItalic.ttf | Bin 0 -> 135124 bytes .../standard_fonts/LiberationSans-Italic.ttf | Bin 0 -> 162036 bytes .../standard_fonts/LiberationSans-Regular.ttf | Bin 0 -> 139512 bytes public/pdfjs/web/viewer.css | 5193 ++ public/pdfjs/web/viewer.html | 637 + public/pdfjs/web/viewer.mjs | 15365 +++++ src/App.tsx | 2 + src/common/PdfViewer.tsx | 80 + .../Apps/useQortalMessageListener.tsx | 6 +- src/components/Chat/MessageItem.tsx | 6 + src/components/Group/ManageMembers.tsx | 10 +- src/qortalRequests.ts | 25 +- src/qortalRequests/get.ts | 32 +- 209 files changed, 143803 insertions(+), 8 deletions(-) create mode 100644 public/pdfjs/build/pdf.mjs create mode 100644 public/pdfjs/build/pdf.sandbox.mjs create mode 100644 public/pdfjs/build/pdf.worker.mjs create mode 100644 public/pdfjs/web/debugger.css create mode 100644 public/pdfjs/web/debugger.mjs create mode 100644 public/pdfjs/web/images/altText_add.svg create mode 100644 public/pdfjs/web/images/altText_disclaimer.svg create mode 100644 public/pdfjs/web/images/altText_done.svg create mode 100644 public/pdfjs/web/images/altText_spinner.svg create mode 100644 public/pdfjs/web/images/altText_warning.svg create mode 100644 public/pdfjs/web/images/annotation-check.svg create mode 100644 public/pdfjs/web/images/annotation-comment.svg create mode 100644 public/pdfjs/web/images/annotation-help.svg create mode 100644 public/pdfjs/web/images/annotation-insert.svg create mode 100644 public/pdfjs/web/images/annotation-key.svg create mode 100644 public/pdfjs/web/images/annotation-newparagraph.svg create mode 100644 public/pdfjs/web/images/annotation-noicon.svg create mode 100644 public/pdfjs/web/images/annotation-note.svg create mode 100644 public/pdfjs/web/images/annotation-paperclip.svg create mode 100644 public/pdfjs/web/images/annotation-paragraph.svg create mode 100644 public/pdfjs/web/images/annotation-pushpin.svg create mode 100644 public/pdfjs/web/images/cursor-editorFreeHighlight.svg create mode 100644 public/pdfjs/web/images/cursor-editorFreeText.svg create mode 100644 public/pdfjs/web/images/cursor-editorInk.svg create mode 100644 public/pdfjs/web/images/cursor-editorTextHighlight.svg create mode 100644 public/pdfjs/web/images/editor-toolbar-delete.svg create mode 100644 public/pdfjs/web/images/findbarButton-next.svg create mode 100644 public/pdfjs/web/images/findbarButton-previous.svg create mode 100644 public/pdfjs/web/images/gv-toolbarButton-download.svg create mode 100644 public/pdfjs/web/images/loading-icon.gif create mode 100644 public/pdfjs/web/images/loading.svg create mode 100644 public/pdfjs/web/images/messageBar_closingButton.svg create mode 100644 public/pdfjs/web/images/messageBar_warning.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-documentProperties.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-firstPage.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-handTool.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-lastPage.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-rotateCcw.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-rotateCw.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-scrollHorizontal.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-scrollPage.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-scrollVertical.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-scrollWrapped.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-selectTool.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-spreadEven.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-spreadNone.svg create mode 100644 public/pdfjs/web/images/secondaryToolbarButton-spreadOdd.svg create mode 100644 public/pdfjs/web/images/toolbarButton-bookmark.svg create mode 100644 public/pdfjs/web/images/toolbarButton-currentOutlineItem.svg create mode 100644 public/pdfjs/web/images/toolbarButton-download.svg create mode 100644 public/pdfjs/web/images/toolbarButton-editorFreeText.svg create mode 100644 public/pdfjs/web/images/toolbarButton-editorHighlight.svg create mode 100644 public/pdfjs/web/images/toolbarButton-editorInk.svg create mode 100644 public/pdfjs/web/images/toolbarButton-editorStamp.svg create mode 100644 public/pdfjs/web/images/toolbarButton-menuArrow.svg create mode 100644 public/pdfjs/web/images/toolbarButton-openFile.svg create mode 100644 public/pdfjs/web/images/toolbarButton-pageDown.svg create mode 100644 public/pdfjs/web/images/toolbarButton-pageUp.svg create mode 100644 public/pdfjs/web/images/toolbarButton-presentationMode.svg create mode 100644 public/pdfjs/web/images/toolbarButton-print.svg create mode 100644 public/pdfjs/web/images/toolbarButton-search.svg create mode 100644 public/pdfjs/web/images/toolbarButton-secondaryToolbarToggle.svg create mode 100644 public/pdfjs/web/images/toolbarButton-sidebarToggle.svg create mode 100644 public/pdfjs/web/images/toolbarButton-viewAttachments.svg create mode 100644 public/pdfjs/web/images/toolbarButton-viewLayers.svg create mode 100644 public/pdfjs/web/images/toolbarButton-viewOutline.svg create mode 100644 public/pdfjs/web/images/toolbarButton-viewThumbnail.svg create mode 100644 public/pdfjs/web/images/toolbarButton-zoomIn.svg create mode 100644 public/pdfjs/web/images/toolbarButton-zoomOut.svg create mode 100644 public/pdfjs/web/images/treeitem-collapsed.svg create mode 100644 public/pdfjs/web/images/treeitem-expanded.svg create mode 100644 public/pdfjs/web/locale/ach/viewer.ftl create mode 100644 public/pdfjs/web/locale/af/viewer.ftl create mode 100644 public/pdfjs/web/locale/an/viewer.ftl create mode 100644 public/pdfjs/web/locale/ar/viewer.ftl create mode 100644 public/pdfjs/web/locale/ast/viewer.ftl create mode 100644 public/pdfjs/web/locale/az/viewer.ftl create mode 100644 public/pdfjs/web/locale/be/viewer.ftl create mode 100644 public/pdfjs/web/locale/bg/viewer.ftl create mode 100644 public/pdfjs/web/locale/bn/viewer.ftl create mode 100644 public/pdfjs/web/locale/bo/viewer.ftl create mode 100644 public/pdfjs/web/locale/br/viewer.ftl create mode 100644 public/pdfjs/web/locale/brx/viewer.ftl create mode 100644 public/pdfjs/web/locale/bs/viewer.ftl create mode 100644 public/pdfjs/web/locale/ca/viewer.ftl create mode 100644 public/pdfjs/web/locale/cak/viewer.ftl create mode 100644 public/pdfjs/web/locale/ckb/viewer.ftl create mode 100644 public/pdfjs/web/locale/cs/viewer.ftl create mode 100644 public/pdfjs/web/locale/cy/viewer.ftl create mode 100644 public/pdfjs/web/locale/da/viewer.ftl create mode 100644 public/pdfjs/web/locale/de/viewer.ftl create mode 100644 public/pdfjs/web/locale/dsb/viewer.ftl create mode 100644 public/pdfjs/web/locale/el/viewer.ftl create mode 100644 public/pdfjs/web/locale/en-CA/viewer.ftl create mode 100644 public/pdfjs/web/locale/en-GB/viewer.ftl create mode 100644 public/pdfjs/web/locale/en-US/viewer.ftl create mode 100644 public/pdfjs/web/locale/eo/viewer.ftl create mode 100644 public/pdfjs/web/locale/es-AR/viewer.ftl create mode 100644 public/pdfjs/web/locale/es-CL/viewer.ftl create mode 100644 public/pdfjs/web/locale/es-ES/viewer.ftl create mode 100644 public/pdfjs/web/locale/es-MX/viewer.ftl create mode 100644 public/pdfjs/web/locale/et/viewer.ftl create mode 100644 public/pdfjs/web/locale/eu/viewer.ftl create mode 100644 public/pdfjs/web/locale/fa/viewer.ftl create mode 100644 public/pdfjs/web/locale/ff/viewer.ftl create mode 100644 public/pdfjs/web/locale/fi/viewer.ftl create mode 100644 public/pdfjs/web/locale/fr/viewer.ftl create mode 100644 public/pdfjs/web/locale/fur/viewer.ftl create mode 100644 public/pdfjs/web/locale/fy-NL/viewer.ftl create mode 100644 public/pdfjs/web/locale/ga-IE/viewer.ftl create mode 100644 public/pdfjs/web/locale/gd/viewer.ftl create mode 100644 public/pdfjs/web/locale/gl/viewer.ftl create mode 100644 public/pdfjs/web/locale/gn/viewer.ftl create mode 100644 public/pdfjs/web/locale/gu-IN/viewer.ftl create mode 100644 public/pdfjs/web/locale/he/viewer.ftl create mode 100644 public/pdfjs/web/locale/hi-IN/viewer.ftl create mode 100644 public/pdfjs/web/locale/hr/viewer.ftl create mode 100644 public/pdfjs/web/locale/hsb/viewer.ftl create mode 100644 public/pdfjs/web/locale/hu/viewer.ftl create mode 100644 public/pdfjs/web/locale/hy-AM/viewer.ftl create mode 100644 public/pdfjs/web/locale/hye/viewer.ftl create mode 100644 public/pdfjs/web/locale/ia/viewer.ftl create mode 100644 public/pdfjs/web/locale/id/viewer.ftl create mode 100644 public/pdfjs/web/locale/is/viewer.ftl create mode 100644 public/pdfjs/web/locale/it/viewer.ftl create mode 100644 public/pdfjs/web/locale/ja/viewer.ftl create mode 100644 public/pdfjs/web/locale/ka/viewer.ftl create mode 100644 public/pdfjs/web/locale/kab/viewer.ftl create mode 100644 public/pdfjs/web/locale/kk/viewer.ftl create mode 100644 public/pdfjs/web/locale/km/viewer.ftl create mode 100644 public/pdfjs/web/locale/kn/viewer.ftl create mode 100644 public/pdfjs/web/locale/ko/viewer.ftl create mode 100644 public/pdfjs/web/locale/lij/viewer.ftl create mode 100644 public/pdfjs/web/locale/lo/viewer.ftl create mode 100644 public/pdfjs/web/locale/locale.json create mode 100644 public/pdfjs/web/locale/lt/viewer.ftl create mode 100644 public/pdfjs/web/locale/ltg/viewer.ftl create mode 100644 public/pdfjs/web/locale/lv/viewer.ftl create mode 100644 public/pdfjs/web/locale/meh/viewer.ftl create mode 100644 public/pdfjs/web/locale/mk/viewer.ftl create mode 100644 public/pdfjs/web/locale/mr/viewer.ftl create mode 100644 public/pdfjs/web/locale/ms/viewer.ftl create mode 100644 public/pdfjs/web/locale/my/viewer.ftl create mode 100644 public/pdfjs/web/locale/nb-NO/viewer.ftl create mode 100644 public/pdfjs/web/locale/ne-NP/viewer.ftl create mode 100644 public/pdfjs/web/locale/nl/viewer.ftl create mode 100644 public/pdfjs/web/locale/nn-NO/viewer.ftl create mode 100644 public/pdfjs/web/locale/oc/viewer.ftl create mode 100644 public/pdfjs/web/locale/pa-IN/viewer.ftl create mode 100644 public/pdfjs/web/locale/pl/viewer.ftl create mode 100644 public/pdfjs/web/locale/pt-BR/viewer.ftl create mode 100644 public/pdfjs/web/locale/pt-PT/viewer.ftl create mode 100644 public/pdfjs/web/locale/rm/viewer.ftl create mode 100644 public/pdfjs/web/locale/ro/viewer.ftl create mode 100644 public/pdfjs/web/locale/ru/viewer.ftl create mode 100644 public/pdfjs/web/locale/sat/viewer.ftl create mode 100644 public/pdfjs/web/locale/sc/viewer.ftl create mode 100644 public/pdfjs/web/locale/scn/viewer.ftl create mode 100644 public/pdfjs/web/locale/sco/viewer.ftl create mode 100644 public/pdfjs/web/locale/si/viewer.ftl create mode 100644 public/pdfjs/web/locale/sk/viewer.ftl create mode 100644 public/pdfjs/web/locale/skr/viewer.ftl create mode 100644 public/pdfjs/web/locale/sl/viewer.ftl create mode 100644 public/pdfjs/web/locale/son/viewer.ftl create mode 100644 public/pdfjs/web/locale/sq/viewer.ftl create mode 100644 public/pdfjs/web/locale/sr/viewer.ftl create mode 100644 public/pdfjs/web/locale/sv-SE/viewer.ftl create mode 100644 public/pdfjs/web/locale/szl/viewer.ftl create mode 100644 public/pdfjs/web/locale/ta/viewer.ftl create mode 100644 public/pdfjs/web/locale/te/viewer.ftl create mode 100644 public/pdfjs/web/locale/tg/viewer.ftl create mode 100644 public/pdfjs/web/locale/th/viewer.ftl create mode 100644 public/pdfjs/web/locale/tl/viewer.ftl create mode 100644 public/pdfjs/web/locale/tr/viewer.ftl create mode 100644 public/pdfjs/web/locale/trs/viewer.ftl create mode 100644 public/pdfjs/web/locale/uk/viewer.ftl create mode 100644 public/pdfjs/web/locale/ur/viewer.ftl create mode 100644 public/pdfjs/web/locale/uz/viewer.ftl create mode 100644 public/pdfjs/web/locale/vi/viewer.ftl create mode 100644 public/pdfjs/web/locale/wo/viewer.ftl create mode 100644 public/pdfjs/web/locale/xh/viewer.ftl create mode 100644 public/pdfjs/web/locale/zh-CN/viewer.ftl create mode 100644 public/pdfjs/web/locale/zh-TW/viewer.ftl create mode 100644 public/pdfjs/web/standard_fonts/FoxitDingbats.pfb create mode 100644 public/pdfjs/web/standard_fonts/FoxitFixed.pfb create mode 100644 public/pdfjs/web/standard_fonts/FoxitFixedBold.pfb create mode 100644 public/pdfjs/web/standard_fonts/FoxitFixedBoldItalic.pfb create mode 100644 public/pdfjs/web/standard_fonts/FoxitFixedItalic.pfb create mode 100644 public/pdfjs/web/standard_fonts/FoxitSerif.pfb create mode 100644 public/pdfjs/web/standard_fonts/FoxitSerifBold.pfb create mode 100644 public/pdfjs/web/standard_fonts/FoxitSerifBoldItalic.pfb create mode 100644 public/pdfjs/web/standard_fonts/FoxitSerifItalic.pfb create mode 100644 public/pdfjs/web/standard_fonts/FoxitSymbol.pfb create mode 100644 public/pdfjs/web/standard_fonts/LICENSE_FOXIT create mode 100644 public/pdfjs/web/standard_fonts/LICENSE_LIBERATION create mode 100644 public/pdfjs/web/standard_fonts/LiberationSans-Bold.ttf create mode 100644 public/pdfjs/web/standard_fonts/LiberationSans-BoldItalic.ttf create mode 100644 public/pdfjs/web/standard_fonts/LiberationSans-Italic.ttf create mode 100644 public/pdfjs/web/standard_fonts/LiberationSans-Regular.ttf create mode 100644 public/pdfjs/web/viewer.css create mode 100644 public/pdfjs/web/viewer.html create mode 100644 public/pdfjs/web/viewer.mjs create mode 100644 src/common/PdfViewer.tsx diff --git a/public/pdfjs/build/pdf.mjs b/public/pdfjs/build/pdf.mjs new file mode 100644 index 0000000..0d201af --- /dev/null +++ b/public/pdfjs/build/pdf.mjs @@ -0,0 +1,21576 @@ +/** + * @licstart The following is the entire license notice for the + * JavaScript code in this page + * + * Copyright 2024 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @licend The above is the entire license notice for the + * JavaScript code in this page + */ + +/******/ // The require scope +/******/ var __webpack_require__ = {}; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = globalThis.pdfjsLib = {}; + +// EXPORTS +__webpack_require__.d(__webpack_exports__, { + AbortException: () => (/* reexport */ AbortException), + AnnotationEditorLayer: () => (/* reexport */ AnnotationEditorLayer), + AnnotationEditorParamsType: () => (/* reexport */ AnnotationEditorParamsType), + AnnotationEditorType: () => (/* reexport */ AnnotationEditorType), + AnnotationEditorUIManager: () => (/* reexport */ AnnotationEditorUIManager), + AnnotationLayer: () => (/* reexport */ AnnotationLayer), + AnnotationMode: () => (/* reexport */ AnnotationMode), + ColorPicker: () => (/* reexport */ ColorPicker), + DOMSVGFactory: () => (/* reexport */ DOMSVGFactory), + DrawLayer: () => (/* reexport */ DrawLayer), + FeatureTest: () => (/* reexport */ util_FeatureTest), + GlobalWorkerOptions: () => (/* reexport */ GlobalWorkerOptions), + ImageKind: () => (/* reexport */ util_ImageKind), + InvalidPDFException: () => (/* reexport */ InvalidPDFException), + MissingPDFException: () => (/* reexport */ MissingPDFException), + OPS: () => (/* reexport */ OPS), + OutputScale: () => (/* reexport */ OutputScale), + PDFDataRangeTransport: () => (/* reexport */ PDFDataRangeTransport), + PDFDateString: () => (/* reexport */ PDFDateString), + PDFWorker: () => (/* reexport */ PDFWorker), + PasswordResponses: () => (/* reexport */ PasswordResponses), + PermissionFlag: () => (/* reexport */ PermissionFlag), + PixelsPerInch: () => (/* reexport */ PixelsPerInch), + RenderingCancelledException: () => (/* reexport */ RenderingCancelledException), + TextLayer: () => (/* reexport */ TextLayer), + TouchManager: () => (/* reexport */ TouchManager), + UnexpectedResponseException: () => (/* reexport */ UnexpectedResponseException), + Util: () => (/* reexport */ Util), + VerbosityLevel: () => (/* reexport */ VerbosityLevel), + XfaLayer: () => (/* reexport */ XfaLayer), + build: () => (/* reexport */ build), + createValidAbsoluteUrl: () => (/* reexport */ createValidAbsoluteUrl), + fetchData: () => (/* reexport */ fetchData), + getDocument: () => (/* reexport */ getDocument), + getFilenameFromUrl: () => (/* reexport */ getFilenameFromUrl), + getPdfFilenameFromUrl: () => (/* reexport */ getPdfFilenameFromUrl), + getXfaPageViewport: () => (/* reexport */ getXfaPageViewport), + isDataScheme: () => (/* reexport */ isDataScheme), + isPdfFile: () => (/* reexport */ isPdfFile), + noContextMenu: () => (/* reexport */ noContextMenu), + normalizeUnicode: () => (/* reexport */ normalizeUnicode), + setLayerDimensions: () => (/* reexport */ setLayerDimensions), + shadow: () => (/* reexport */ shadow), + stopEvent: () => (/* reexport */ stopEvent), + version: () => (/* reexport */ version) +}); + +;// ./src/shared/util.js +const isNodeJS = typeof process === "object" && process + "" === "[object process]" && !process.versions.nw && !(process.versions.electron && process.type && process.type !== "browser"); +const IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; +const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; +const MAX_IMAGE_SIZE_TO_CACHE = 10e6; +const LINE_FACTOR = 1.35; +const LINE_DESCENT_FACTOR = 0.35; +const BASELINE_FACTOR = LINE_DESCENT_FACTOR / LINE_FACTOR; +const RenderingIntentFlag = { + ANY: 0x01, + DISPLAY: 0x02, + PRINT: 0x04, + SAVE: 0x08, + ANNOTATIONS_FORMS: 0x10, + ANNOTATIONS_STORAGE: 0x20, + ANNOTATIONS_DISABLE: 0x40, + IS_EDITING: 0x80, + OPLIST: 0x100 +}; +const AnnotationMode = { + DISABLE: 0, + ENABLE: 1, + ENABLE_FORMS: 2, + ENABLE_STORAGE: 3 +}; +const AnnotationEditorPrefix = "pdfjs_internal_editor_"; +const AnnotationEditorType = { + DISABLE: -1, + NONE: 0, + FREETEXT: 3, + HIGHLIGHT: 9, + STAMP: 13, + INK: 15 +}; +const AnnotationEditorParamsType = { + RESIZE: 1, + CREATE: 2, + FREETEXT_SIZE: 11, + FREETEXT_COLOR: 12, + FREETEXT_OPACITY: 13, + INK_COLOR: 21, + INK_THICKNESS: 22, + INK_OPACITY: 23, + HIGHLIGHT_COLOR: 31, + HIGHLIGHT_DEFAULT_COLOR: 32, + HIGHLIGHT_THICKNESS: 33, + HIGHLIGHT_FREE: 34, + HIGHLIGHT_SHOW_ALL: 35, + DRAW_STEP: 41 +}; +const PermissionFlag = { + PRINT: 0x04, + MODIFY_CONTENTS: 0x08, + COPY: 0x10, + MODIFY_ANNOTATIONS: 0x20, + FILL_INTERACTIVE_FORMS: 0x100, + COPY_FOR_ACCESSIBILITY: 0x200, + ASSEMBLE: 0x400, + PRINT_HIGH_QUALITY: 0x800 +}; +const TextRenderingMode = { + FILL: 0, + STROKE: 1, + FILL_STROKE: 2, + INVISIBLE: 3, + FILL_ADD_TO_PATH: 4, + STROKE_ADD_TO_PATH: 5, + FILL_STROKE_ADD_TO_PATH: 6, + ADD_TO_PATH: 7, + FILL_STROKE_MASK: 3, + ADD_TO_PATH_FLAG: 4 +}; +const util_ImageKind = { + GRAYSCALE_1BPP: 1, + RGB_24BPP: 2, + RGBA_32BPP: 3 +}; +const AnnotationType = { + TEXT: 1, + LINK: 2, + FREETEXT: 3, + LINE: 4, + SQUARE: 5, + CIRCLE: 6, + POLYGON: 7, + POLYLINE: 8, + HIGHLIGHT: 9, + UNDERLINE: 10, + SQUIGGLY: 11, + STRIKEOUT: 12, + STAMP: 13, + CARET: 14, + INK: 15, + POPUP: 16, + FILEATTACHMENT: 17, + SOUND: 18, + MOVIE: 19, + WIDGET: 20, + SCREEN: 21, + PRINTERMARK: 22, + TRAPNET: 23, + WATERMARK: 24, + THREED: 25, + REDACT: 26 +}; +const AnnotationReplyType = { + GROUP: "Group", + REPLY: "R" +}; +const AnnotationFlag = { + INVISIBLE: 0x01, + HIDDEN: 0x02, + PRINT: 0x04, + NOZOOM: 0x08, + NOROTATE: 0x10, + NOVIEW: 0x20, + READONLY: 0x40, + LOCKED: 0x80, + TOGGLENOVIEW: 0x100, + LOCKEDCONTENTS: 0x200 +}; +const AnnotationFieldFlag = { + READONLY: 0x0000001, + REQUIRED: 0x0000002, + NOEXPORT: 0x0000004, + MULTILINE: 0x0001000, + PASSWORD: 0x0002000, + NOTOGGLETOOFF: 0x0004000, + RADIO: 0x0008000, + PUSHBUTTON: 0x0010000, + COMBO: 0x0020000, + EDIT: 0x0040000, + SORT: 0x0080000, + FILESELECT: 0x0100000, + MULTISELECT: 0x0200000, + DONOTSPELLCHECK: 0x0400000, + DONOTSCROLL: 0x0800000, + COMB: 0x1000000, + RICHTEXT: 0x2000000, + RADIOSINUNISON: 0x2000000, + COMMITONSELCHANGE: 0x4000000 +}; +const AnnotationBorderStyleType = { + SOLID: 1, + DASHED: 2, + BEVELED: 3, + INSET: 4, + UNDERLINE: 5 +}; +const AnnotationActionEventType = { + E: "Mouse Enter", + X: "Mouse Exit", + D: "Mouse Down", + U: "Mouse Up", + Fo: "Focus", + Bl: "Blur", + PO: "PageOpen", + PC: "PageClose", + PV: "PageVisible", + PI: "PageInvisible", + K: "Keystroke", + F: "Format", + V: "Validate", + C: "Calculate" +}; +const DocumentActionEventType = { + WC: "WillClose", + WS: "WillSave", + DS: "DidSave", + WP: "WillPrint", + DP: "DidPrint" +}; +const PageActionEventType = { + O: "PageOpen", + C: "PageClose" +}; +const VerbosityLevel = { + ERRORS: 0, + WARNINGS: 1, + INFOS: 5 +}; +const OPS = { + dependency: 1, + setLineWidth: 2, + setLineCap: 3, + setLineJoin: 4, + setMiterLimit: 5, + setDash: 6, + setRenderingIntent: 7, + setFlatness: 8, + setGState: 9, + save: 10, + restore: 11, + transform: 12, + moveTo: 13, + lineTo: 14, + curveTo: 15, + curveTo2: 16, + curveTo3: 17, + closePath: 18, + rectangle: 19, + stroke: 20, + closeStroke: 21, + fill: 22, + eoFill: 23, + fillStroke: 24, + eoFillStroke: 25, + closeFillStroke: 26, + closeEOFillStroke: 27, + endPath: 28, + clip: 29, + eoClip: 30, + beginText: 31, + endText: 32, + setCharSpacing: 33, + setWordSpacing: 34, + setHScale: 35, + setLeading: 36, + setFont: 37, + setTextRenderingMode: 38, + setTextRise: 39, + moveText: 40, + setLeadingMoveText: 41, + setTextMatrix: 42, + nextLine: 43, + showText: 44, + showSpacedText: 45, + nextLineShowText: 46, + nextLineSetSpacingShowText: 47, + setCharWidth: 48, + setCharWidthAndBounds: 49, + setStrokeColorSpace: 50, + setFillColorSpace: 51, + setStrokeColor: 52, + setStrokeColorN: 53, + setFillColor: 54, + setFillColorN: 55, + setStrokeGray: 56, + setFillGray: 57, + setStrokeRGBColor: 58, + setFillRGBColor: 59, + setStrokeCMYKColor: 60, + setFillCMYKColor: 61, + shadingFill: 62, + beginInlineImage: 63, + beginImageData: 64, + endInlineImage: 65, + paintXObject: 66, + markPoint: 67, + markPointProps: 68, + beginMarkedContent: 69, + beginMarkedContentProps: 70, + endMarkedContent: 71, + beginCompat: 72, + endCompat: 73, + paintFormXObjectBegin: 74, + paintFormXObjectEnd: 75, + beginGroup: 76, + endGroup: 77, + beginAnnotation: 80, + endAnnotation: 81, + paintImageMaskXObject: 83, + paintImageMaskXObjectGroup: 84, + paintImageXObject: 85, + paintInlineImageXObject: 86, + paintInlineImageXObjectGroup: 87, + paintImageXObjectRepeat: 88, + paintImageMaskXObjectRepeat: 89, + paintSolidColorImageMask: 90, + constructPath: 91, + setStrokeTransparent: 92, + setFillTransparent: 93 +}; +const PasswordResponses = { + NEED_PASSWORD: 1, + INCORRECT_PASSWORD: 2 +}; +let verbosity = VerbosityLevel.WARNINGS; +function setVerbosityLevel(level) { + if (Number.isInteger(level)) { + verbosity = level; + } +} +function getVerbosityLevel() { + return verbosity; +} +function info(msg) { + if (verbosity >= VerbosityLevel.INFOS) { + console.log(`Info: ${msg}`); + } +} +function warn(msg) { + if (verbosity >= VerbosityLevel.WARNINGS) { + console.log(`Warning: ${msg}`); + } +} +function unreachable(msg) { + throw new Error(msg); +} +function assert(cond, msg) { + if (!cond) { + unreachable(msg); + } +} +function _isValidProtocol(url) { + switch (url?.protocol) { + case "http:": + case "https:": + case "ftp:": + case "mailto:": + case "tel:": + return true; + default: + return false; + } +} +function createValidAbsoluteUrl(url, baseUrl = null, options = null) { + if (!url) { + return null; + } + try { + if (options && typeof url === "string") { + if (options.addDefaultProtocol && url.startsWith("www.")) { + const dots = url.match(/\./g); + if (dots?.length >= 2) { + url = `http://${url}`; + } + } + if (options.tryConvertEncoding) { + try { + url = stringToUTF8String(url); + } catch {} + } + } + const absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url); + if (_isValidProtocol(absoluteUrl)) { + return absoluteUrl; + } + } catch {} + return null; +} +function shadow(obj, prop, value, nonSerializable = false) { + Object.defineProperty(obj, prop, { + value, + enumerable: !nonSerializable, + configurable: true, + writable: false + }); + return value; +} +const BaseException = function BaseExceptionClosure() { + function BaseException(message, name) { + this.message = message; + this.name = name; + } + BaseException.prototype = new Error(); + BaseException.constructor = BaseException; + return BaseException; +}(); +class PasswordException extends BaseException { + constructor(msg, code) { + super(msg, "PasswordException"); + this.code = code; + } +} +class UnknownErrorException extends BaseException { + constructor(msg, details) { + super(msg, "UnknownErrorException"); + this.details = details; + } +} +class InvalidPDFException extends BaseException { + constructor(msg) { + super(msg, "InvalidPDFException"); + } +} +class MissingPDFException extends BaseException { + constructor(msg) { + super(msg, "MissingPDFException"); + } +} +class UnexpectedResponseException extends BaseException { + constructor(msg, status) { + super(msg, "UnexpectedResponseException"); + this.status = status; + } +} +class FormatError extends BaseException { + constructor(msg) { + super(msg, "FormatError"); + } +} +class AbortException extends BaseException { + constructor(msg) { + super(msg, "AbortException"); + } +} +function bytesToString(bytes) { + if (typeof bytes !== "object" || bytes?.length === undefined) { + unreachable("Invalid argument for bytesToString"); + } + const length = bytes.length; + const MAX_ARGUMENT_COUNT = 8192; + if (length < MAX_ARGUMENT_COUNT) { + return String.fromCharCode.apply(null, bytes); + } + const strBuf = []; + for (let i = 0; i < length; i += MAX_ARGUMENT_COUNT) { + const chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length); + const chunk = bytes.subarray(i, chunkEnd); + strBuf.push(String.fromCharCode.apply(null, chunk)); + } + return strBuf.join(""); +} +function stringToBytes(str) { + if (typeof str !== "string") { + unreachable("Invalid argument for stringToBytes"); + } + const length = str.length; + const bytes = new Uint8Array(length); + for (let i = 0; i < length; ++i) { + bytes[i] = str.charCodeAt(i) & 0xff; + } + return bytes; +} +function string32(value) { + return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff); +} +function objectSize(obj) { + return Object.keys(obj).length; +} +function objectFromMap(map) { + const obj = Object.create(null); + for (const [key, value] of map) { + obj[key] = value; + } + return obj; +} +function isLittleEndian() { + const buffer8 = new Uint8Array(4); + buffer8[0] = 1; + const view32 = new Uint32Array(buffer8.buffer, 0, 1); + return view32[0] === 1; +} +function isEvalSupported() { + try { + new Function(""); + return true; + } catch { + return false; + } +} +class util_FeatureTest { + static get isLittleEndian() { + return shadow(this, "isLittleEndian", isLittleEndian()); + } + static get isEvalSupported() { + return shadow(this, "isEvalSupported", isEvalSupported()); + } + static get isOffscreenCanvasSupported() { + return shadow(this, "isOffscreenCanvasSupported", typeof OffscreenCanvas !== "undefined"); + } + static get isImageDecoderSupported() { + return shadow(this, "isImageDecoderSupported", typeof ImageDecoder !== "undefined"); + } + static get platform() { + if (typeof navigator !== "undefined" && typeof navigator?.platform === "string") { + return shadow(this, "platform", { + isMac: navigator.platform.includes("Mac"), + isWindows: navigator.platform.includes("Win"), + isFirefox: typeof navigator?.userAgent === "string" && navigator.userAgent.includes("Firefox") + }); + } + return shadow(this, "platform", { + isMac: false, + isWindows: false, + isFirefox: false + }); + } + static get isCSSRoundSupported() { + return shadow(this, "isCSSRoundSupported", globalThis.CSS?.supports?.("width: round(1.5px, 1px)")); + } +} +const hexNumbers = Array.from(Array(256).keys(), n => n.toString(16).padStart(2, "0")); +class Util { + static makeHexColor(r, g, b) { + return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`; + } + static scaleMinMax(transform, minMax) { + let temp; + if (transform[0]) { + if (transform[0] < 0) { + temp = minMax[0]; + minMax[0] = minMax[2]; + minMax[2] = temp; + } + minMax[0] *= transform[0]; + minMax[2] *= transform[0]; + if (transform[3] < 0) { + temp = minMax[1]; + minMax[1] = minMax[3]; + minMax[3] = temp; + } + minMax[1] *= transform[3]; + minMax[3] *= transform[3]; + } else { + temp = minMax[0]; + minMax[0] = minMax[1]; + minMax[1] = temp; + temp = minMax[2]; + minMax[2] = minMax[3]; + minMax[3] = temp; + if (transform[1] < 0) { + temp = minMax[1]; + minMax[1] = minMax[3]; + minMax[3] = temp; + } + minMax[1] *= transform[1]; + minMax[3] *= transform[1]; + if (transform[2] < 0) { + temp = minMax[0]; + minMax[0] = minMax[2]; + minMax[2] = temp; + } + minMax[0] *= transform[2]; + minMax[2] *= transform[2]; + } + minMax[0] += transform[4]; + minMax[1] += transform[5]; + minMax[2] += transform[4]; + minMax[3] += transform[5]; + } + static transform(m1, m2) { + return [m1[0] * m2[0] + m1[2] * m2[1], m1[1] * m2[0] + m1[3] * m2[1], m1[0] * m2[2] + m1[2] * m2[3], m1[1] * m2[2] + m1[3] * m2[3], m1[0] * m2[4] + m1[2] * m2[5] + m1[4], m1[1] * m2[4] + m1[3] * m2[5] + m1[5]]; + } + static applyTransform(p, m) { + const xt = p[0] * m[0] + p[1] * m[2] + m[4]; + const yt = p[0] * m[1] + p[1] * m[3] + m[5]; + return [xt, yt]; + } + static applyInverseTransform(p, m) { + const d = m[0] * m[3] - m[1] * m[2]; + const xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d; + const yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d; + return [xt, yt]; + } + static getAxialAlignedBoundingBox(r, m) { + const p1 = this.applyTransform(r, m); + const p2 = this.applyTransform(r.slice(2, 4), m); + const p3 = this.applyTransform([r[0], r[3]], m); + const p4 = this.applyTransform([r[2], r[1]], m); + return [Math.min(p1[0], p2[0], p3[0], p4[0]), Math.min(p1[1], p2[1], p3[1], p4[1]), Math.max(p1[0], p2[0], p3[0], p4[0]), Math.max(p1[1], p2[1], p3[1], p4[1])]; + } + static inverseTransform(m) { + const d = m[0] * m[3] - m[1] * m[2]; + return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d]; + } + static singularValueDecompose2dScale(m) { + const transpose = [m[0], m[2], m[1], m[3]]; + const a = m[0] * transpose[0] + m[1] * transpose[2]; + const b = m[0] * transpose[1] + m[1] * transpose[3]; + const c = m[2] * transpose[0] + m[3] * transpose[2]; + const d = m[2] * transpose[1] + m[3] * transpose[3]; + const first = (a + d) / 2; + const second = Math.sqrt((a + d) ** 2 - 4 * (a * d - c * b)) / 2; + const sx = first + second || 1; + const sy = first - second || 1; + return [Math.sqrt(sx), Math.sqrt(sy)]; + } + static normalizeRect(rect) { + const r = rect.slice(0); + if (rect[0] > rect[2]) { + r[0] = rect[2]; + r[2] = rect[0]; + } + if (rect[1] > rect[3]) { + r[1] = rect[3]; + r[3] = rect[1]; + } + return r; + } + static intersect(rect1, rect2) { + const xLow = Math.max(Math.min(rect1[0], rect1[2]), Math.min(rect2[0], rect2[2])); + const xHigh = Math.min(Math.max(rect1[0], rect1[2]), Math.max(rect2[0], rect2[2])); + if (xLow > xHigh) { + return null; + } + const yLow = Math.max(Math.min(rect1[1], rect1[3]), Math.min(rect2[1], rect2[3])); + const yHigh = Math.min(Math.max(rect1[1], rect1[3]), Math.max(rect2[1], rect2[3])); + if (yLow > yHigh) { + return null; + } + return [xLow, yLow, xHigh, yHigh]; + } + static #getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, t, minMax) { + if (t <= 0 || t >= 1) { + return; + } + const mt = 1 - t; + const tt = t * t; + const ttt = tt * t; + const x = mt * (mt * (mt * x0 + 3 * t * x1) + 3 * tt * x2) + ttt * x3; + const y = mt * (mt * (mt * y0 + 3 * t * y1) + 3 * tt * y2) + ttt * y3; + minMax[0] = Math.min(minMax[0], x); + minMax[1] = Math.min(minMax[1], y); + minMax[2] = Math.max(minMax[2], x); + minMax[3] = Math.max(minMax[3], y); + } + static #getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, a, b, c, minMax) { + if (Math.abs(a) < 1e-12) { + if (Math.abs(b) >= 1e-12) { + this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, -c / b, minMax); + } + return; + } + const delta = b ** 2 - 4 * c * a; + if (delta < 0) { + return; + } + const sqrtDelta = Math.sqrt(delta); + const a2 = 2 * a; + this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b + sqrtDelta) / a2, minMax); + this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b - sqrtDelta) / a2, minMax); + } + static bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax) { + if (minMax) { + minMax[0] = Math.min(minMax[0], x0, x3); + minMax[1] = Math.min(minMax[1], y0, y3); + minMax[2] = Math.max(minMax[2], x0, x3); + minMax[3] = Math.max(minMax[3], y0, y3); + } else { + minMax = [Math.min(x0, x3), Math.min(y0, y3), Math.max(x0, x3), Math.max(y0, y3)]; + } + this.#getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, 3 * (-x0 + 3 * (x1 - x2) + x3), 6 * (x0 - 2 * x1 + x2), 3 * (x1 - x0), minMax); + this.#getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, 3 * (-y0 + 3 * (y1 - y2) + y3), 6 * (y0 - 2 * y1 + y2), 3 * (y1 - y0), minMax); + return minMax; + } +} +const PDFStringTranslateTable = (/* unused pure expression or super */ null && ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2d8, 0x2c7, 0x2c6, 0x2d9, 0x2dd, 0x2db, 0x2da, 0x2dc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x192, 0x2044, 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018, 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x141, 0x152, 0x160, 0x178, 0x17d, 0x131, 0x142, 0x153, 0x161, 0x17e, 0, 0x20ac])); +function stringToPDFString(str) { + if (str[0] >= "\xEF") { + let encoding; + if (str[0] === "\xFE" && str[1] === "\xFF") { + encoding = "utf-16be"; + if (str.length % 2 === 1) { + str = str.slice(0, -1); + } + } else if (str[0] === "\xFF" && str[1] === "\xFE") { + encoding = "utf-16le"; + if (str.length % 2 === 1) { + str = str.slice(0, -1); + } + } else if (str[0] === "\xEF" && str[1] === "\xBB" && str[2] === "\xBF") { + encoding = "utf-8"; + } + if (encoding) { + try { + const decoder = new TextDecoder(encoding, { + fatal: true + }); + const buffer = stringToBytes(str); + const decoded = decoder.decode(buffer); + if (!decoded.includes("\x1b")) { + return decoded; + } + return decoded.replaceAll(/\x1b[^\x1b]*(?:\x1b|$)/g, ""); + } catch (ex) { + warn(`stringToPDFString: "${ex}".`); + } + } + } + const strBuf = []; + for (let i = 0, ii = str.length; i < ii; i++) { + const charCode = str.charCodeAt(i); + if (charCode === 0x1b) { + while (++i < ii && str.charCodeAt(i) !== 0x1b) {} + continue; + } + const code = PDFStringTranslateTable[charCode]; + strBuf.push(code ? String.fromCharCode(code) : str.charAt(i)); + } + return strBuf.join(""); +} +function stringToUTF8String(str) { + return decodeURIComponent(escape(str)); +} +function utf8StringToString(str) { + return unescape(encodeURIComponent(str)); +} +function isArrayEqual(arr1, arr2) { + if (arr1.length !== arr2.length) { + return false; + } + for (let i = 0, ii = arr1.length; i < ii; i++) { + if (arr1[i] !== arr2[i]) { + return false; + } + } + return true; +} +function getModificationDate(date = new Date()) { + const buffer = [date.getUTCFullYear().toString(), (date.getUTCMonth() + 1).toString().padStart(2, "0"), date.getUTCDate().toString().padStart(2, "0"), date.getUTCHours().toString().padStart(2, "0"), date.getUTCMinutes().toString().padStart(2, "0"), date.getUTCSeconds().toString().padStart(2, "0")]; + return buffer.join(""); +} +let NormalizeRegex = null; +let NormalizationMap = null; +function normalizeUnicode(str) { + if (!NormalizeRegex) { + NormalizeRegex = /([\u00a0\u00b5\u037e\u0eb3\u2000-\u200a\u202f\u2126\ufb00-\ufb04\ufb06\ufb20-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufba1\ufba4-\ufba9\ufbae-\ufbb1\ufbd3-\ufbdc\ufbde-\ufbe7\ufbea-\ufbf8\ufbfc-\ufbfd\ufc00-\ufc5d\ufc64-\ufcf1\ufcf5-\ufd3d\ufd88\ufdf4\ufdfa-\ufdfb\ufe71\ufe77\ufe79\ufe7b\ufe7d]+)|(\ufb05+)/gu; + NormalizationMap = new Map([["ſt", "ſt"]]); + } + return str.replaceAll(NormalizeRegex, (_, p1, p2) => p1 ? p1.normalize("NFKC") : NormalizationMap.get(p2)); +} +function getUuid() { + if (typeof crypto.randomUUID === "function") { + return crypto.randomUUID(); + } + const buf = new Uint8Array(32); + crypto.getRandomValues(buf); + return bytesToString(buf); +} +const AnnotationPrefix = "pdfjs_internal_id_"; +function toHexUtil(arr) { + if (Uint8Array.prototype.toHex) { + return arr.toHex(); + } + return Array.from(arr, num => hexNumbers[num]).join(""); +} +function toBase64Util(arr) { + if (Uint8Array.prototype.toBase64) { + return arr.toBase64(); + } + return btoa(bytesToString(arr)); +} +function fromBase64Util(str) { + if (Uint8Array.fromBase64) { + return Uint8Array.fromBase64(str); + } + return stringToBytes(atob(str)); +} +if (typeof Promise.try !== "function") { + Promise.try = function (fn, ...args) { + return new Promise(resolve => { + resolve(fn(...args)); + }); + }; +} + +;// ./src/display/display_utils.js + +const SVG_NS = "http://www.w3.org/2000/svg"; +class PixelsPerInch { + static CSS = 96.0; + static PDF = 72.0; + static PDF_TO_CSS_UNITS = this.CSS / this.PDF; +} +async function fetchData(url, type = "text") { + if (isValidFetchUrl(url, document.baseURI)) { + const response = await fetch(url); + if (!response.ok) { + throw new Error(response.statusText); + } + switch (type) { + case "arraybuffer": + return response.arrayBuffer(); + case "blob": + return response.blob(); + case "json": + return response.json(); + } + return response.text(); + } + return new Promise((resolve, reject) => { + const request = new XMLHttpRequest(); + request.open("GET", url, true); + request.responseType = type; + request.onreadystatechange = () => { + if (request.readyState !== XMLHttpRequest.DONE) { + return; + } + if (request.status === 200 || request.status === 0) { + switch (type) { + case "arraybuffer": + case "blob": + case "json": + resolve(request.response); + return; + } + resolve(request.responseText); + return; + } + reject(new Error(request.statusText)); + }; + request.send(null); + }); +} +class PageViewport { + constructor({ + viewBox, + userUnit, + scale, + rotation, + offsetX = 0, + offsetY = 0, + dontFlip = false + }) { + this.viewBox = viewBox; + this.userUnit = userUnit; + this.scale = scale; + this.rotation = rotation; + this.offsetX = offsetX; + this.offsetY = offsetY; + scale *= userUnit; + const centerX = (viewBox[2] + viewBox[0]) / 2; + const centerY = (viewBox[3] + viewBox[1]) / 2; + let rotateA, rotateB, rotateC, rotateD; + rotation %= 360; + if (rotation < 0) { + rotation += 360; + } + switch (rotation) { + case 180: + rotateA = -1; + rotateB = 0; + rotateC = 0; + rotateD = 1; + break; + case 90: + rotateA = 0; + rotateB = 1; + rotateC = 1; + rotateD = 0; + break; + case 270: + rotateA = 0; + rotateB = -1; + rotateC = -1; + rotateD = 0; + break; + case 0: + rotateA = 1; + rotateB = 0; + rotateC = 0; + rotateD = -1; + break; + default: + throw new Error("PageViewport: Invalid rotation, must be a multiple of 90 degrees."); + } + if (dontFlip) { + rotateC = -rotateC; + rotateD = -rotateD; + } + let offsetCanvasX, offsetCanvasY; + let width, height; + if (rotateA === 0) { + offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX; + offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY; + width = (viewBox[3] - viewBox[1]) * scale; + height = (viewBox[2] - viewBox[0]) * scale; + } else { + offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX; + offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY; + width = (viewBox[2] - viewBox[0]) * scale; + height = (viewBox[3] - viewBox[1]) * scale; + } + this.transform = [rotateA * scale, rotateB * scale, rotateC * scale, rotateD * scale, offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY]; + this.width = width; + this.height = height; + } + get rawDims() { + const { + userUnit, + viewBox + } = this; + const dims = viewBox.map(x => x * userUnit); + return shadow(this, "rawDims", { + pageWidth: dims[2] - dims[0], + pageHeight: dims[3] - dims[1], + pageX: dims[0], + pageY: dims[1] + }); + } + clone({ + scale = this.scale, + rotation = this.rotation, + offsetX = this.offsetX, + offsetY = this.offsetY, + dontFlip = false + } = {}) { + return new PageViewport({ + viewBox: this.viewBox.slice(), + userUnit: this.userUnit, + scale, + rotation, + offsetX, + offsetY, + dontFlip + }); + } + convertToViewportPoint(x, y) { + return Util.applyTransform([x, y], this.transform); + } + convertToViewportRectangle(rect) { + const topLeft = Util.applyTransform([rect[0], rect[1]], this.transform); + const bottomRight = Util.applyTransform([rect[2], rect[3]], this.transform); + return [topLeft[0], topLeft[1], bottomRight[0], bottomRight[1]]; + } + convertToPdfPoint(x, y) { + return Util.applyInverseTransform([x, y], this.transform); + } +} +class RenderingCancelledException extends BaseException { + constructor(msg, extraDelay = 0) { + super(msg, "RenderingCancelledException"); + this.extraDelay = extraDelay; + } +} +function isDataScheme(url) { + const ii = url.length; + let i = 0; + while (i < ii && url[i].trim() === "") { + i++; + } + return url.substring(i, i + 5).toLowerCase() === "data:"; +} +function isPdfFile(filename) { + return typeof filename === "string" && /\.pdf$/i.test(filename); +} +function getFilenameFromUrl(url) { + [url] = url.split(/[#?]/, 1); + return url.substring(url.lastIndexOf("/") + 1); +} +function getPdfFilenameFromUrl(url, defaultFilename = "document.pdf") { + if (typeof url !== "string") { + return defaultFilename; + } + if (isDataScheme(url)) { + warn('getPdfFilenameFromUrl: ignore "data:"-URL for performance reasons.'); + return defaultFilename; + } + const reURI = /^(?:(?:[^:]+:)?\/\/[^/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/; + const reFilename = /[^/?#=]+\.pdf\b(?!.*\.pdf\b)/i; + const splitURI = reURI.exec(url); + let suggestedFilename = reFilename.exec(splitURI[1]) || reFilename.exec(splitURI[2]) || reFilename.exec(splitURI[3]); + if (suggestedFilename) { + suggestedFilename = suggestedFilename[0]; + if (suggestedFilename.includes("%")) { + try { + suggestedFilename = reFilename.exec(decodeURIComponent(suggestedFilename))[0]; + } catch {} + } + } + return suggestedFilename || defaultFilename; +} +class StatTimer { + started = Object.create(null); + times = []; + time(name) { + if (name in this.started) { + warn(`Timer is already running for ${name}`); + } + this.started[name] = Date.now(); + } + timeEnd(name) { + if (!(name in this.started)) { + warn(`Timer has not been started for ${name}`); + } + this.times.push({ + name, + start: this.started[name], + end: Date.now() + }); + delete this.started[name]; + } + toString() { + const outBuf = []; + let longest = 0; + for (const { + name + } of this.times) { + longest = Math.max(name.length, longest); + } + for (const { + name, + start, + end + } of this.times) { + outBuf.push(`${name.padEnd(longest)} ${end - start}ms\n`); + } + return outBuf.join(""); + } +} +function isValidFetchUrl(url, baseUrl) { + try { + const { + protocol + } = baseUrl ? new URL(url, baseUrl) : new URL(url); + return protocol === "http:" || protocol === "https:"; + } catch { + return false; + } +} +function noContextMenu(e) { + e.preventDefault(); +} +function stopEvent(e) { + e.preventDefault(); + e.stopPropagation(); +} +function deprecated(details) { + console.log("Deprecated API usage: " + details); +} +class PDFDateString { + static #regex; + static toDateObject(input) { + if (!input || typeof input !== "string") { + return null; + } + this.#regex ||= new RegExp("^D:" + "(\\d{4})" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "([Z|+|-])?" + "(\\d{2})?" + "'?" + "(\\d{2})?" + "'?"); + const matches = this.#regex.exec(input); + if (!matches) { + return null; + } + const year = parseInt(matches[1], 10); + let month = parseInt(matches[2], 10); + month = month >= 1 && month <= 12 ? month - 1 : 0; + let day = parseInt(matches[3], 10); + day = day >= 1 && day <= 31 ? day : 1; + let hour = parseInt(matches[4], 10); + hour = hour >= 0 && hour <= 23 ? hour : 0; + let minute = parseInt(matches[5], 10); + minute = minute >= 0 && minute <= 59 ? minute : 0; + let second = parseInt(matches[6], 10); + second = second >= 0 && second <= 59 ? second : 0; + const universalTimeRelation = matches[7] || "Z"; + let offsetHour = parseInt(matches[8], 10); + offsetHour = offsetHour >= 0 && offsetHour <= 23 ? offsetHour : 0; + let offsetMinute = parseInt(matches[9], 10) || 0; + offsetMinute = offsetMinute >= 0 && offsetMinute <= 59 ? offsetMinute : 0; + if (universalTimeRelation === "-") { + hour += offsetHour; + minute += offsetMinute; + } else if (universalTimeRelation === "+") { + hour -= offsetHour; + minute -= offsetMinute; + } + return new Date(Date.UTC(year, month, day, hour, minute, second)); + } +} +function getXfaPageViewport(xfaPage, { + scale = 1, + rotation = 0 +}) { + const { + width, + height + } = xfaPage.attributes.style; + const viewBox = [0, 0, parseInt(width), parseInt(height)]; + return new PageViewport({ + viewBox, + userUnit: 1, + scale, + rotation + }); +} +function getRGB(color) { + if (color.startsWith("#")) { + const colorRGB = parseInt(color.slice(1), 16); + return [(colorRGB & 0xff0000) >> 16, (colorRGB & 0x00ff00) >> 8, colorRGB & 0x0000ff]; + } + if (color.startsWith("rgb(")) { + return color.slice(4, -1).split(",").map(x => parseInt(x)); + } + if (color.startsWith("rgba(")) { + return color.slice(5, -1).split(",").map(x => parseInt(x)).slice(0, 3); + } + warn(`Not a valid color format: "${color}"`); + return [0, 0, 0]; +} +function getColorValues(colors) { + const span = document.createElement("span"); + span.style.visibility = "hidden"; + document.body.append(span); + for (const name of colors.keys()) { + span.style.color = name; + const computedColor = window.getComputedStyle(span).color; + colors.set(name, getRGB(computedColor)); + } + span.remove(); +} +function getCurrentTransform(ctx) { + const { + a, + b, + c, + d, + e, + f + } = ctx.getTransform(); + return [a, b, c, d, e, f]; +} +function getCurrentTransformInverse(ctx) { + const { + a, + b, + c, + d, + e, + f + } = ctx.getTransform().invertSelf(); + return [a, b, c, d, e, f]; +} +function setLayerDimensions(div, viewport, mustFlip = false, mustRotate = true) { + if (viewport instanceof PageViewport) { + const { + pageWidth, + pageHeight + } = viewport.rawDims; + const { + style + } = div; + const useRound = util_FeatureTest.isCSSRoundSupported; + const w = `var(--scale-factor) * ${pageWidth}px`, + h = `var(--scale-factor) * ${pageHeight}px`; + const widthStr = useRound ? `round(down, ${w}, var(--scale-round-x, 1px))` : `calc(${w})`, + heightStr = useRound ? `round(down, ${h}, var(--scale-round-y, 1px))` : `calc(${h})`; + if (!mustFlip || viewport.rotation % 180 === 0) { + style.width = widthStr; + style.height = heightStr; + } else { + style.width = heightStr; + style.height = widthStr; + } + } + if (mustRotate) { + div.setAttribute("data-main-rotation", viewport.rotation); + } +} +class OutputScale { + constructor() { + const pixelRatio = window.devicePixelRatio || 1; + this.sx = pixelRatio; + this.sy = pixelRatio; + } + get scaled() { + return this.sx !== 1 || this.sy !== 1; + } + get symmetric() { + return this.sx === this.sy; + } +} + +;// ./src/display/editor/toolbar.js + +class EditorToolbar { + #toolbar = null; + #colorPicker = null; + #editor; + #buttons = null; + #altText = null; + static #l10nRemove = null; + constructor(editor) { + this.#editor = editor; + EditorToolbar.#l10nRemove ||= Object.freeze({ + freetext: "pdfjs-editor-remove-freetext-button", + highlight: "pdfjs-editor-remove-highlight-button", + ink: "pdfjs-editor-remove-ink-button", + stamp: "pdfjs-editor-remove-stamp-button" + }); + } + render() { + const editToolbar = this.#toolbar = document.createElement("div"); + editToolbar.classList.add("editToolbar", "hidden"); + editToolbar.setAttribute("role", "toolbar"); + const signal = this.#editor._uiManager._signal; + editToolbar.addEventListener("contextmenu", noContextMenu, { + signal + }); + editToolbar.addEventListener("pointerdown", EditorToolbar.#pointerDown, { + signal + }); + const buttons = this.#buttons = document.createElement("div"); + buttons.className = "buttons"; + editToolbar.append(buttons); + const position = this.#editor.toolbarPosition; + if (position) { + const { + style + } = editToolbar; + const x = this.#editor._uiManager.direction === "ltr" ? 1 - position[0] : position[0]; + style.insetInlineEnd = `${100 * x}%`; + style.top = `calc(${100 * position[1]}% + var(--editor-toolbar-vert-offset))`; + } + this.#addDeleteButton(); + return editToolbar; + } + get div() { + return this.#toolbar; + } + static #pointerDown(e) { + e.stopPropagation(); + } + #focusIn(e) { + this.#editor._focusEventsAllowed = false; + stopEvent(e); + } + #focusOut(e) { + this.#editor._focusEventsAllowed = true; + stopEvent(e); + } + #addListenersToElement(element) { + const signal = this.#editor._uiManager._signal; + element.addEventListener("focusin", this.#focusIn.bind(this), { + capture: true, + signal + }); + element.addEventListener("focusout", this.#focusOut.bind(this), { + capture: true, + signal + }); + element.addEventListener("contextmenu", noContextMenu, { + signal + }); + } + hide() { + this.#toolbar.classList.add("hidden"); + this.#colorPicker?.hideDropdown(); + } + show() { + this.#toolbar.classList.remove("hidden"); + this.#altText?.shown(); + } + #addDeleteButton() { + const { + editorType, + _uiManager + } = this.#editor; + const button = document.createElement("button"); + button.className = "delete"; + button.tabIndex = 0; + button.setAttribute("data-l10n-id", EditorToolbar.#l10nRemove[editorType]); + this.#addListenersToElement(button); + button.addEventListener("click", e => { + _uiManager.delete(); + }, { + signal: _uiManager._signal + }); + this.#buttons.append(button); + } + get #divider() { + const divider = document.createElement("div"); + divider.className = "divider"; + return divider; + } + async addAltText(altText) { + const button = await altText.render(); + this.#addListenersToElement(button); + this.#buttons.prepend(button, this.#divider); + this.#altText = altText; + } + addColorPicker(colorPicker) { + this.#colorPicker = colorPicker; + const button = colorPicker.renderButton(); + this.#addListenersToElement(button); + this.#buttons.prepend(button, this.#divider); + } + remove() { + this.#toolbar.remove(); + this.#colorPicker?.destroy(); + this.#colorPicker = null; + } +} +class HighlightToolbar { + #buttons = null; + #toolbar = null; + #uiManager; + constructor(uiManager) { + this.#uiManager = uiManager; + } + #render() { + const editToolbar = this.#toolbar = document.createElement("div"); + editToolbar.className = "editToolbar"; + editToolbar.setAttribute("role", "toolbar"); + editToolbar.addEventListener("contextmenu", noContextMenu, { + signal: this.#uiManager._signal + }); + const buttons = this.#buttons = document.createElement("div"); + buttons.className = "buttons"; + editToolbar.append(buttons); + this.#addHighlightButton(); + return editToolbar; + } + #getLastPoint(boxes, isLTR) { + let lastY = 0; + let lastX = 0; + for (const box of boxes) { + const y = box.y + box.height; + if (y < lastY) { + continue; + } + const x = box.x + (isLTR ? box.width : 0); + if (y > lastY) { + lastX = x; + lastY = y; + continue; + } + if (isLTR) { + if (x > lastX) { + lastX = x; + } + } else if (x < lastX) { + lastX = x; + } + } + return [isLTR ? 1 - lastX : lastX, lastY]; + } + show(parent, boxes, isLTR) { + const [x, y] = this.#getLastPoint(boxes, isLTR); + const { + style + } = this.#toolbar ||= this.#render(); + parent.append(this.#toolbar); + style.insetInlineEnd = `${100 * x}%`; + style.top = `calc(${100 * y}% + var(--editor-toolbar-vert-offset))`; + } + hide() { + this.#toolbar.remove(); + } + #addHighlightButton() { + const button = document.createElement("button"); + button.className = "highlightButton"; + button.tabIndex = 0; + button.setAttribute("data-l10n-id", `pdfjs-highlight-floating-button1`); + const span = document.createElement("span"); + button.append(span); + span.className = "visuallyHidden"; + span.setAttribute("data-l10n-id", "pdfjs-highlight-floating-button-label"); + const signal = this.#uiManager._signal; + button.addEventListener("contextmenu", noContextMenu, { + signal + }); + button.addEventListener("click", () => { + this.#uiManager.highlightSelection("floating_button"); + }, { + signal + }); + this.#buttons.append(button); + } +} + +;// ./src/display/editor/tools.js + + + +function bindEvents(obj, element, names) { + for (const name of names) { + element.addEventListener(name, obj[name].bind(obj)); + } +} +function opacityToHex(opacity) { + return Math.round(Math.min(255, Math.max(1, 255 * opacity))).toString(16).padStart(2, "0"); +} +class IdManager { + #id = 0; + get id() { + return `${AnnotationEditorPrefix}${this.#id++}`; + } +} +class ImageManager { + #baseId = getUuid(); + #id = 0; + #cache = null; + static get _isSVGFittingCanvas() { + const svg = `data:image/svg+xml;charset=UTF-8,`; + const canvas = new OffscreenCanvas(1, 3); + const ctx = canvas.getContext("2d", { + willReadFrequently: true + }); + const image = new Image(); + image.src = svg; + const promise = image.decode().then(() => { + ctx.drawImage(image, 0, 0, 1, 1, 0, 0, 1, 3); + return new Uint32Array(ctx.getImageData(0, 0, 1, 1).data.buffer)[0] === 0; + }); + return shadow(this, "_isSVGFittingCanvas", promise); + } + async #get(key, rawData) { + this.#cache ||= new Map(); + let data = this.#cache.get(key); + if (data === null) { + return null; + } + if (data?.bitmap) { + data.refCounter += 1; + return data; + } + try { + data ||= { + bitmap: null, + id: `image_${this.#baseId}_${this.#id++}`, + refCounter: 0, + isSvg: false + }; + let image; + if (typeof rawData === "string") { + data.url = rawData; + image = await fetchData(rawData, "blob"); + } else if (rawData instanceof File) { + image = data.file = rawData; + } else if (rawData instanceof Blob) { + image = rawData; + } + if (image.type === "image/svg+xml") { + const mustRemoveAspectRatioPromise = ImageManager._isSVGFittingCanvas; + const fileReader = new FileReader(); + const imageElement = new Image(); + const imagePromise = new Promise((resolve, reject) => { + imageElement.onload = () => { + data.bitmap = imageElement; + data.isSvg = true; + resolve(); + }; + fileReader.onload = async () => { + const url = data.svgUrl = fileReader.result; + imageElement.src = (await mustRemoveAspectRatioPromise) ? `${url}#svgView(preserveAspectRatio(none))` : url; + }; + imageElement.onerror = fileReader.onerror = reject; + }); + fileReader.readAsDataURL(image); + await imagePromise; + } else { + data.bitmap = await createImageBitmap(image); + } + data.refCounter = 1; + } catch (e) { + warn(e); + data = null; + } + this.#cache.set(key, data); + if (data) { + this.#cache.set(data.id, data); + } + return data; + } + async getFromFile(file) { + const { + lastModified, + name, + size, + type + } = file; + return this.#get(`${lastModified}_${name}_${size}_${type}`, file); + } + async getFromUrl(url) { + return this.#get(url, url); + } + async getFromBlob(id, blobPromise) { + const blob = await blobPromise; + return this.#get(id, blob); + } + async getFromId(id) { + this.#cache ||= new Map(); + const data = this.#cache.get(id); + if (!data) { + return null; + } + if (data.bitmap) { + data.refCounter += 1; + return data; + } + if (data.file) { + return this.getFromFile(data.file); + } + if (data.blobPromise) { + const { + blobPromise + } = data; + delete data.blobPromise; + return this.getFromBlob(data.id, blobPromise); + } + return this.getFromUrl(data.url); + } + getFromCanvas(id, canvas) { + this.#cache ||= new Map(); + let data = this.#cache.get(id); + if (data?.bitmap) { + data.refCounter += 1; + return data; + } + const offscreen = new OffscreenCanvas(canvas.width, canvas.height); + const ctx = offscreen.getContext("2d"); + ctx.drawImage(canvas, 0, 0); + data = { + bitmap: offscreen.transferToImageBitmap(), + id: `image_${this.#baseId}_${this.#id++}`, + refCounter: 1, + isSvg: false + }; + this.#cache.set(id, data); + this.#cache.set(data.id, data); + return data; + } + getSvgUrl(id) { + const data = this.#cache.get(id); + if (!data?.isSvg) { + return null; + } + return data.svgUrl; + } + deleteId(id) { + this.#cache ||= new Map(); + const data = this.#cache.get(id); + if (!data) { + return; + } + data.refCounter -= 1; + if (data.refCounter !== 0) { + return; + } + const { + bitmap + } = data; + if (!data.url && !data.file) { + const canvas = new OffscreenCanvas(bitmap.width, bitmap.height); + const ctx = canvas.getContext("bitmaprenderer"); + ctx.transferFromImageBitmap(bitmap); + data.blobPromise = canvas.convertToBlob(); + } + bitmap.close?.(); + data.bitmap = null; + } + isValidId(id) { + return id.startsWith(`image_${this.#baseId}_`); + } +} +class CommandManager { + #commands = []; + #locked = false; + #maxSize; + #position = -1; + constructor(maxSize = 128) { + this.#maxSize = maxSize; + } + add({ + cmd, + undo, + post, + mustExec, + type = NaN, + overwriteIfSameType = false, + keepUndo = false + }) { + if (mustExec) { + cmd(); + } + if (this.#locked) { + return; + } + const save = { + cmd, + undo, + post, + type + }; + if (this.#position === -1) { + if (this.#commands.length > 0) { + this.#commands.length = 0; + } + this.#position = 0; + this.#commands.push(save); + return; + } + if (overwriteIfSameType && this.#commands[this.#position].type === type) { + if (keepUndo) { + save.undo = this.#commands[this.#position].undo; + } + this.#commands[this.#position] = save; + return; + } + const next = this.#position + 1; + if (next === this.#maxSize) { + this.#commands.splice(0, 1); + } else { + this.#position = next; + if (next < this.#commands.length) { + this.#commands.splice(next); + } + } + this.#commands.push(save); + } + undo() { + if (this.#position === -1) { + return; + } + this.#locked = true; + const { + undo, + post + } = this.#commands[this.#position]; + undo(); + post?.(); + this.#locked = false; + this.#position -= 1; + } + redo() { + if (this.#position < this.#commands.length - 1) { + this.#position += 1; + this.#locked = true; + const { + cmd, + post + } = this.#commands[this.#position]; + cmd(); + post?.(); + this.#locked = false; + } + } + hasSomethingToUndo() { + return this.#position !== -1; + } + hasSomethingToRedo() { + return this.#position < this.#commands.length - 1; + } + cleanType(type) { + if (this.#position === -1) { + return; + } + for (let i = this.#position; i >= 0; i--) { + if (this.#commands[i].type !== type) { + this.#commands.splice(i + 1, this.#position - i); + this.#position = i; + return; + } + } + this.#commands.length = 0; + this.#position = -1; + } + destroy() { + this.#commands = null; + } +} +class KeyboardManager { + constructor(callbacks) { + this.buffer = []; + this.callbacks = new Map(); + this.allKeys = new Set(); + const { + isMac + } = util_FeatureTest.platform; + for (const [keys, callback, options = {}] of callbacks) { + for (const key of keys) { + const isMacKey = key.startsWith("mac+"); + if (isMac && isMacKey) { + this.callbacks.set(key.slice(4), { + callback, + options + }); + this.allKeys.add(key.split("+").at(-1)); + } else if (!isMac && !isMacKey) { + this.callbacks.set(key, { + callback, + options + }); + this.allKeys.add(key.split("+").at(-1)); + } + } + } + } + #serialize(event) { + if (event.altKey) { + this.buffer.push("alt"); + } + if (event.ctrlKey) { + this.buffer.push("ctrl"); + } + if (event.metaKey) { + this.buffer.push("meta"); + } + if (event.shiftKey) { + this.buffer.push("shift"); + } + this.buffer.push(event.key); + const str = this.buffer.join("+"); + this.buffer.length = 0; + return str; + } + exec(self, event) { + if (!this.allKeys.has(event.key)) { + return; + } + const info = this.callbacks.get(this.#serialize(event)); + if (!info) { + return; + } + const { + callback, + options: { + bubbles = false, + args = [], + checker = null + } + } = info; + if (checker && !checker(self, event)) { + return; + } + callback.bind(self, ...args, event)(); + if (!bubbles) { + stopEvent(event); + } + } +} +class ColorManager { + static _colorsMapping = new Map([["CanvasText", [0, 0, 0]], ["Canvas", [255, 255, 255]]]); + get _colors() { + const colors = new Map([["CanvasText", null], ["Canvas", null]]); + getColorValues(colors); + return shadow(this, "_colors", colors); + } + convert(color) { + const rgb = getRGB(color); + if (!window.matchMedia("(forced-colors: active)").matches) { + return rgb; + } + for (const [name, RGB] of this._colors) { + if (RGB.every((x, i) => x === rgb[i])) { + return ColorManager._colorsMapping.get(name); + } + } + return rgb; + } + getHexCode(name) { + const rgb = this._colors.get(name); + if (!rgb) { + return name; + } + return Util.makeHexColor(...rgb); + } +} +class AnnotationEditorUIManager { + #abortController = new AbortController(); + #activeEditor = null; + #allEditors = new Map(); + #allLayers = new Map(); + #altTextManager = null; + #annotationStorage = null; + #changedExistingAnnotations = null; + #commandManager = new CommandManager(); + #copyPasteAC = null; + #currentDrawingSession = null; + #currentPageIndex = 0; + #deletedAnnotationsElementIds = new Set(); + #draggingEditors = null; + #editorTypes = null; + #editorsToRescale = new Set(); + _editorUndoBar = null; + #enableHighlightFloatingButton = false; + #enableUpdatedAddImage = false; + #enableNewAltTextWhenAddingImage = false; + #filterFactory = null; + #focusMainContainerTimeoutId = null; + #focusManagerAC = null; + #highlightColors = null; + #highlightWhenShiftUp = false; + #highlightToolbar = null; + #idManager = new IdManager(); + #isEnabled = false; + #isWaiting = false; + #keyboardManagerAC = null; + #lastActiveElement = null; + #mainHighlightColorPicker = null; + #mlManager = null; + #mode = AnnotationEditorType.NONE; + #selectedEditors = new Set(); + #selectedTextNode = null; + #pageColors = null; + #showAllStates = null; + #previousStates = { + isEditing: false, + isEmpty: true, + hasSomethingToUndo: false, + hasSomethingToRedo: false, + hasSelectedEditor: false, + hasSelectedText: false + }; + #translation = [0, 0]; + #translationTimeoutId = null; + #container = null; + #viewer = null; + #updateModeCapability = null; + static TRANSLATE_SMALL = 1; + static TRANSLATE_BIG = 10; + static get _keyboardManager() { + const proto = AnnotationEditorUIManager.prototype; + const arrowChecker = self => self.#container.contains(document.activeElement) && document.activeElement.tagName !== "BUTTON" && self.hasSomethingToControl(); + const textInputChecker = (_self, { + target: el + }) => { + if (el instanceof HTMLInputElement) { + const { + type + } = el; + return type !== "text" && type !== "number"; + } + return true; + }; + const small = this.TRANSLATE_SMALL; + const big = this.TRANSLATE_BIG; + return shadow(this, "_keyboardManager", new KeyboardManager([[["ctrl+a", "mac+meta+a"], proto.selectAll, { + checker: textInputChecker + }], [["ctrl+z", "mac+meta+z"], proto.undo, { + checker: textInputChecker + }], [["ctrl+y", "ctrl+shift+z", "mac+meta+shift+z", "ctrl+shift+Z", "mac+meta+shift+Z"], proto.redo, { + checker: textInputChecker + }], [["Backspace", "alt+Backspace", "ctrl+Backspace", "shift+Backspace", "mac+Backspace", "mac+alt+Backspace", "mac+ctrl+Backspace", "Delete", "ctrl+Delete", "shift+Delete", "mac+Delete"], proto.delete, { + checker: textInputChecker + }], [["Enter", "mac+Enter"], proto.addNewEditorFromKeyboard, { + checker: (self, { + target: el + }) => !(el instanceof HTMLButtonElement) && self.#container.contains(el) && !self.isEnterHandled + }], [[" ", "mac+ "], proto.addNewEditorFromKeyboard, { + checker: (self, { + target: el + }) => !(el instanceof HTMLButtonElement) && self.#container.contains(document.activeElement) + }], [["Escape", "mac+Escape"], proto.unselectAll], [["ArrowLeft", "mac+ArrowLeft"], proto.translateSelectedEditors, { + args: [-small, 0], + checker: arrowChecker + }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], proto.translateSelectedEditors, { + args: [-big, 0], + checker: arrowChecker + }], [["ArrowRight", "mac+ArrowRight"], proto.translateSelectedEditors, { + args: [small, 0], + checker: arrowChecker + }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], proto.translateSelectedEditors, { + args: [big, 0], + checker: arrowChecker + }], [["ArrowUp", "mac+ArrowUp"], proto.translateSelectedEditors, { + args: [0, -small], + checker: arrowChecker + }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], proto.translateSelectedEditors, { + args: [0, -big], + checker: arrowChecker + }], [["ArrowDown", "mac+ArrowDown"], proto.translateSelectedEditors, { + args: [0, small], + checker: arrowChecker + }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], proto.translateSelectedEditors, { + args: [0, big], + checker: arrowChecker + }]])); + } + constructor(container, viewer, altTextManager, eventBus, pdfDocument, pageColors, highlightColors, enableHighlightFloatingButton, enableUpdatedAddImage, enableNewAltTextWhenAddingImage, mlManager, editorUndoBar, supportsPinchToZoom) { + const signal = this._signal = this.#abortController.signal; + this.#container = container; + this.#viewer = viewer; + this.#altTextManager = altTextManager; + this._eventBus = eventBus; + eventBus._on("editingaction", this.onEditingAction.bind(this), { + signal + }); + eventBus._on("pagechanging", this.onPageChanging.bind(this), { + signal + }); + eventBus._on("scalechanging", this.onScaleChanging.bind(this), { + signal + }); + eventBus._on("rotationchanging", this.onRotationChanging.bind(this), { + signal + }); + eventBus._on("setpreference", this.onSetPreference.bind(this), { + signal + }); + eventBus._on("switchannotationeditorparams", evt => this.updateParams(evt.type, evt.value), { + signal + }); + this.#addSelectionListener(); + this.#addDragAndDropListeners(); + this.#addKeyboardManager(); + this.#annotationStorage = pdfDocument.annotationStorage; + this.#filterFactory = pdfDocument.filterFactory; + this.#pageColors = pageColors; + this.#highlightColors = highlightColors || null; + this.#enableHighlightFloatingButton = enableHighlightFloatingButton; + this.#enableUpdatedAddImage = enableUpdatedAddImage; + this.#enableNewAltTextWhenAddingImage = enableNewAltTextWhenAddingImage; + this.#mlManager = mlManager || null; + this.viewParameters = { + realScale: PixelsPerInch.PDF_TO_CSS_UNITS, + rotation: 0 + }; + this.isShiftKeyDown = false; + this._editorUndoBar = editorUndoBar || null; + this._supportsPinchToZoom = supportsPinchToZoom !== false; + } + destroy() { + this.#updateModeCapability?.resolve(); + this.#updateModeCapability = null; + this.#abortController?.abort(); + this.#abortController = null; + this._signal = null; + for (const layer of this.#allLayers.values()) { + layer.destroy(); + } + this.#allLayers.clear(); + this.#allEditors.clear(); + this.#editorsToRescale.clear(); + this.#activeEditor = null; + this.#selectedEditors.clear(); + this.#commandManager.destroy(); + this.#altTextManager?.destroy(); + this.#highlightToolbar?.hide(); + this.#highlightToolbar = null; + if (this.#focusMainContainerTimeoutId) { + clearTimeout(this.#focusMainContainerTimeoutId); + this.#focusMainContainerTimeoutId = null; + } + if (this.#translationTimeoutId) { + clearTimeout(this.#translationTimeoutId); + this.#translationTimeoutId = null; + } + this._editorUndoBar?.destroy(); + } + combinedSignal(ac) { + return AbortSignal.any([this._signal, ac.signal]); + } + get mlManager() { + return this.#mlManager; + } + get useNewAltTextFlow() { + return this.#enableUpdatedAddImage; + } + get useNewAltTextWhenAddingImage() { + return this.#enableNewAltTextWhenAddingImage; + } + get hcmFilter() { + return shadow(this, "hcmFilter", this.#pageColors ? this.#filterFactory.addHCMFilter(this.#pageColors.foreground, this.#pageColors.background) : "none"); + } + get direction() { + return shadow(this, "direction", getComputedStyle(this.#container).direction); + } + get highlightColors() { + return shadow(this, "highlightColors", this.#highlightColors ? new Map(this.#highlightColors.split(",").map(pair => pair.split("=").map(x => x.trim()))) : null); + } + get highlightColorNames() { + return shadow(this, "highlightColorNames", this.highlightColors ? new Map(Array.from(this.highlightColors, e => e.reverse())) : null); + } + setCurrentDrawingSession(layer) { + if (layer) { + this.unselectAll(); + this.disableUserSelect(true); + } else { + this.disableUserSelect(false); + } + this.#currentDrawingSession = layer; + } + setMainHighlightColorPicker(colorPicker) { + this.#mainHighlightColorPicker = colorPicker; + } + editAltText(editor, firstTime = false) { + this.#altTextManager?.editAltText(this, editor, firstTime); + } + switchToMode(mode, callback) { + this._eventBus.on("annotationeditormodechanged", callback, { + once: true, + signal: this._signal + }); + this._eventBus.dispatch("showannotationeditorui", { + source: this, + mode + }); + } + setPreference(name, value) { + this._eventBus.dispatch("setpreference", { + source: this, + name, + value + }); + } + onSetPreference({ + name, + value + }) { + switch (name) { + case "enableNewAltTextWhenAddingImage": + this.#enableNewAltTextWhenAddingImage = value; + break; + } + } + onPageChanging({ + pageNumber + }) { + this.#currentPageIndex = pageNumber - 1; + } + focusMainContainer() { + this.#container.focus(); + } + findParent(x, y) { + for (const layer of this.#allLayers.values()) { + const { + x: layerX, + y: layerY, + width, + height + } = layer.div.getBoundingClientRect(); + if (x >= layerX && x <= layerX + width && y >= layerY && y <= layerY + height) { + return layer; + } + } + return null; + } + disableUserSelect(value = false) { + this.#viewer.classList.toggle("noUserSelect", value); + } + addShouldRescale(editor) { + this.#editorsToRescale.add(editor); + } + removeShouldRescale(editor) { + this.#editorsToRescale.delete(editor); + } + onScaleChanging({ + scale + }) { + this.commitOrRemove(); + this.viewParameters.realScale = scale * PixelsPerInch.PDF_TO_CSS_UNITS; + for (const editor of this.#editorsToRescale) { + editor.onScaleChanging(); + } + this.#currentDrawingSession?.onScaleChanging(); + } + onRotationChanging({ + pagesRotation + }) { + this.commitOrRemove(); + this.viewParameters.rotation = pagesRotation; + } + #getAnchorElementForSelection({ + anchorNode + }) { + return anchorNode.nodeType === Node.TEXT_NODE ? anchorNode.parentElement : anchorNode; + } + #getLayerForTextLayer(textLayer) { + const { + currentLayer + } = this; + if (currentLayer.hasTextLayer(textLayer)) { + return currentLayer; + } + for (const layer of this.#allLayers.values()) { + if (layer.hasTextLayer(textLayer)) { + return layer; + } + } + return null; + } + highlightSelection(methodOfCreation = "") { + const selection = document.getSelection(); + if (!selection || selection.isCollapsed) { + return; + } + const { + anchorNode, + anchorOffset, + focusNode, + focusOffset + } = selection; + const text = selection.toString(); + const anchorElement = this.#getAnchorElementForSelection(selection); + const textLayer = anchorElement.closest(".textLayer"); + const boxes = this.getSelectionBoxes(textLayer); + if (!boxes) { + return; + } + selection.empty(); + const layer = this.#getLayerForTextLayer(textLayer); + const isNoneMode = this.#mode === AnnotationEditorType.NONE; + const callback = () => { + layer?.createAndAddNewEditor({ + x: 0, + y: 0 + }, false, { + methodOfCreation, + boxes, + anchorNode, + anchorOffset, + focusNode, + focusOffset, + text + }); + if (isNoneMode) { + this.showAllEditors("highlight", true, true); + } + }; + if (isNoneMode) { + this.switchToMode(AnnotationEditorType.HIGHLIGHT, callback); + return; + } + callback(); + } + #displayHighlightToolbar() { + const selection = document.getSelection(); + if (!selection || selection.isCollapsed) { + return; + } + const anchorElement = this.#getAnchorElementForSelection(selection); + const textLayer = anchorElement.closest(".textLayer"); + const boxes = this.getSelectionBoxes(textLayer); + if (!boxes) { + return; + } + this.#highlightToolbar ||= new HighlightToolbar(this); + this.#highlightToolbar.show(textLayer, boxes, this.direction === "ltr"); + } + addToAnnotationStorage(editor) { + if (!editor.isEmpty() && this.#annotationStorage && !this.#annotationStorage.has(editor.id)) { + this.#annotationStorage.setValue(editor.id, editor); + } + } + #selectionChange() { + const selection = document.getSelection(); + if (!selection || selection.isCollapsed) { + if (this.#selectedTextNode) { + this.#highlightToolbar?.hide(); + this.#selectedTextNode = null; + this.#dispatchUpdateStates({ + hasSelectedText: false + }); + } + return; + } + const { + anchorNode + } = selection; + if (anchorNode === this.#selectedTextNode) { + return; + } + const anchorElement = this.#getAnchorElementForSelection(selection); + const textLayer = anchorElement.closest(".textLayer"); + if (!textLayer) { + if (this.#selectedTextNode) { + this.#highlightToolbar?.hide(); + this.#selectedTextNode = null; + this.#dispatchUpdateStates({ + hasSelectedText: false + }); + } + return; + } + this.#highlightToolbar?.hide(); + this.#selectedTextNode = anchorNode; + this.#dispatchUpdateStates({ + hasSelectedText: true + }); + if (this.#mode !== AnnotationEditorType.HIGHLIGHT && this.#mode !== AnnotationEditorType.NONE) { + return; + } + if (this.#mode === AnnotationEditorType.HIGHLIGHT) { + this.showAllEditors("highlight", true, true); + } + this.#highlightWhenShiftUp = this.isShiftKeyDown; + if (!this.isShiftKeyDown) { + const activeLayer = this.#mode === AnnotationEditorType.HIGHLIGHT ? this.#getLayerForTextLayer(textLayer) : null; + activeLayer?.toggleDrawing(); + const ac = new AbortController(); + const signal = this.combinedSignal(ac); + const pointerup = e => { + if (e.type === "pointerup" && e.button !== 0) { + return; + } + ac.abort(); + activeLayer?.toggleDrawing(true); + if (e.type === "pointerup") { + this.#onSelectEnd("main_toolbar"); + } + }; + window.addEventListener("pointerup", pointerup, { + signal + }); + window.addEventListener("blur", pointerup, { + signal + }); + } + } + #onSelectEnd(methodOfCreation = "") { + if (this.#mode === AnnotationEditorType.HIGHLIGHT) { + this.highlightSelection(methodOfCreation); + } else if (this.#enableHighlightFloatingButton) { + this.#displayHighlightToolbar(); + } + } + #addSelectionListener() { + document.addEventListener("selectionchange", this.#selectionChange.bind(this), { + signal: this._signal + }); + } + #addFocusManager() { + if (this.#focusManagerAC) { + return; + } + this.#focusManagerAC = new AbortController(); + const signal = this.combinedSignal(this.#focusManagerAC); + window.addEventListener("focus", this.focus.bind(this), { + signal + }); + window.addEventListener("blur", this.blur.bind(this), { + signal + }); + } + #removeFocusManager() { + this.#focusManagerAC?.abort(); + this.#focusManagerAC = null; + } + blur() { + this.isShiftKeyDown = false; + if (this.#highlightWhenShiftUp) { + this.#highlightWhenShiftUp = false; + this.#onSelectEnd("main_toolbar"); + } + if (!this.hasSelection) { + return; + } + const { + activeElement + } = document; + for (const editor of this.#selectedEditors) { + if (editor.div.contains(activeElement)) { + this.#lastActiveElement = [editor, activeElement]; + editor._focusEventsAllowed = false; + break; + } + } + } + focus() { + if (!this.#lastActiveElement) { + return; + } + const [lastEditor, lastActiveElement] = this.#lastActiveElement; + this.#lastActiveElement = null; + lastActiveElement.addEventListener("focusin", () => { + lastEditor._focusEventsAllowed = true; + }, { + once: true, + signal: this._signal + }); + lastActiveElement.focus(); + } + #addKeyboardManager() { + if (this.#keyboardManagerAC) { + return; + } + this.#keyboardManagerAC = new AbortController(); + const signal = this.combinedSignal(this.#keyboardManagerAC); + window.addEventListener("keydown", this.keydown.bind(this), { + signal + }); + window.addEventListener("keyup", this.keyup.bind(this), { + signal + }); + } + #removeKeyboardManager() { + this.#keyboardManagerAC?.abort(); + this.#keyboardManagerAC = null; + } + #addCopyPasteListeners() { + if (this.#copyPasteAC) { + return; + } + this.#copyPasteAC = new AbortController(); + const signal = this.combinedSignal(this.#copyPasteAC); + document.addEventListener("copy", this.copy.bind(this), { + signal + }); + document.addEventListener("cut", this.cut.bind(this), { + signal + }); + document.addEventListener("paste", this.paste.bind(this), { + signal + }); + } + #removeCopyPasteListeners() { + this.#copyPasteAC?.abort(); + this.#copyPasteAC = null; + } + #addDragAndDropListeners() { + const signal = this._signal; + document.addEventListener("dragover", this.dragOver.bind(this), { + signal + }); + document.addEventListener("drop", this.drop.bind(this), { + signal + }); + } + addEditListeners() { + this.#addKeyboardManager(); + this.#addCopyPasteListeners(); + } + removeEditListeners() { + this.#removeKeyboardManager(); + this.#removeCopyPasteListeners(); + } + dragOver(event) { + for (const { + type + } of event.dataTransfer.items) { + for (const editorType of this.#editorTypes) { + if (editorType.isHandlingMimeForPasting(type)) { + event.dataTransfer.dropEffect = "copy"; + event.preventDefault(); + return; + } + } + } + } + drop(event) { + for (const item of event.dataTransfer.items) { + for (const editorType of this.#editorTypes) { + if (editorType.isHandlingMimeForPasting(item.type)) { + editorType.paste(item, this.currentLayer); + event.preventDefault(); + return; + } + } + } + } + copy(event) { + event.preventDefault(); + this.#activeEditor?.commitOrRemove(); + if (!this.hasSelection) { + return; + } + const editors = []; + for (const editor of this.#selectedEditors) { + const serialized = editor.serialize(true); + if (serialized) { + editors.push(serialized); + } + } + if (editors.length === 0) { + return; + } + event.clipboardData.setData("application/pdfjs", JSON.stringify(editors)); + } + cut(event) { + this.copy(event); + this.delete(); + } + async paste(event) { + event.preventDefault(); + const { + clipboardData + } = event; + for (const item of clipboardData.items) { + for (const editorType of this.#editorTypes) { + if (editorType.isHandlingMimeForPasting(item.type)) { + editorType.paste(item, this.currentLayer); + return; + } + } + } + let data = clipboardData.getData("application/pdfjs"); + if (!data) { + return; + } + try { + data = JSON.parse(data); + } catch (ex) { + warn(`paste: "${ex.message}".`); + return; + } + if (!Array.isArray(data)) { + return; + } + this.unselectAll(); + const layer = this.currentLayer; + try { + const newEditors = []; + for (const editor of data) { + const deserializedEditor = await layer.deserialize(editor); + if (!deserializedEditor) { + return; + } + newEditors.push(deserializedEditor); + } + const cmd = () => { + for (const editor of newEditors) { + this.#addEditorToLayer(editor); + } + this.#selectEditors(newEditors); + }; + const undo = () => { + for (const editor of newEditors) { + editor.remove(); + } + }; + this.addCommands({ + cmd, + undo, + mustExec: true + }); + } catch (ex) { + warn(`paste: "${ex.message}".`); + } + } + keydown(event) { + if (!this.isShiftKeyDown && event.key === "Shift") { + this.isShiftKeyDown = true; + } + if (this.#mode !== AnnotationEditorType.NONE && !this.isEditorHandlingKeyboard) { + AnnotationEditorUIManager._keyboardManager.exec(this, event); + } + } + keyup(event) { + if (this.isShiftKeyDown && event.key === "Shift") { + this.isShiftKeyDown = false; + if (this.#highlightWhenShiftUp) { + this.#highlightWhenShiftUp = false; + this.#onSelectEnd("main_toolbar"); + } + } + } + onEditingAction({ + name + }) { + switch (name) { + case "undo": + case "redo": + case "delete": + case "selectAll": + this[name](); + break; + case "highlightSelection": + this.highlightSelection("context_menu"); + break; + } + } + #dispatchUpdateStates(details) { + const hasChanged = Object.entries(details).some(([key, value]) => this.#previousStates[key] !== value); + if (hasChanged) { + this._eventBus.dispatch("annotationeditorstateschanged", { + source: this, + details: Object.assign(this.#previousStates, details) + }); + if (this.#mode === AnnotationEditorType.HIGHLIGHT && details.hasSelectedEditor === false) { + this.#dispatchUpdateUI([[AnnotationEditorParamsType.HIGHLIGHT_FREE, true]]); + } + } + } + #dispatchUpdateUI(details) { + this._eventBus.dispatch("annotationeditorparamschanged", { + source: this, + details + }); + } + setEditingState(isEditing) { + if (isEditing) { + this.#addFocusManager(); + this.#addCopyPasteListeners(); + this.#dispatchUpdateStates({ + isEditing: this.#mode !== AnnotationEditorType.NONE, + isEmpty: this.#isEmpty(), + hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(), + hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(), + hasSelectedEditor: false + }); + } else { + this.#removeFocusManager(); + this.#removeCopyPasteListeners(); + this.#dispatchUpdateStates({ + isEditing: false + }); + this.disableUserSelect(false); + } + } + registerEditorTypes(types) { + if (this.#editorTypes) { + return; + } + this.#editorTypes = types; + for (const editorType of this.#editorTypes) { + this.#dispatchUpdateUI(editorType.defaultPropertiesToUpdate); + } + } + getId() { + return this.#idManager.id; + } + get currentLayer() { + return this.#allLayers.get(this.#currentPageIndex); + } + getLayer(pageIndex) { + return this.#allLayers.get(pageIndex); + } + get currentPageIndex() { + return this.#currentPageIndex; + } + addLayer(layer) { + this.#allLayers.set(layer.pageIndex, layer); + if (this.#isEnabled) { + layer.enable(); + } else { + layer.disable(); + } + } + removeLayer(layer) { + this.#allLayers.delete(layer.pageIndex); + } + async updateMode(mode, editId = null, isFromKeyboard = false) { + if (this.#mode === mode) { + return; + } + if (this.#updateModeCapability) { + await this.#updateModeCapability.promise; + if (!this.#updateModeCapability) { + return; + } + } + this.#updateModeCapability = Promise.withResolvers(); + this.#mode = mode; + if (mode === AnnotationEditorType.NONE) { + this.setEditingState(false); + this.#disableAll(); + this._editorUndoBar?.hide(); + this.#updateModeCapability.resolve(); + return; + } + this.setEditingState(true); + await this.#enableAll(); + this.unselectAll(); + for (const layer of this.#allLayers.values()) { + layer.updateMode(mode); + } + if (!editId) { + if (isFromKeyboard) { + this.addNewEditorFromKeyboard(); + } + this.#updateModeCapability.resolve(); + return; + } + for (const editor of this.#allEditors.values()) { + if (editor.annotationElementId === editId) { + this.setSelected(editor); + editor.enterInEditMode(); + } else { + editor.unselect(); + } + } + this.#updateModeCapability.resolve(); + } + addNewEditorFromKeyboard() { + if (this.currentLayer.canCreateNewEmptyEditor()) { + this.currentLayer.addNewEditor(); + } + } + updateToolbar(mode) { + if (mode === this.#mode) { + return; + } + this._eventBus.dispatch("switchannotationeditormode", { + source: this, + mode + }); + } + updateParams(type, value) { + if (!this.#editorTypes) { + return; + } + switch (type) { + case AnnotationEditorParamsType.CREATE: + this.currentLayer.addNewEditor(); + return; + case AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR: + this.#mainHighlightColorPicker?.updateColor(value); + break; + case AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL: + this._eventBus.dispatch("reporttelemetry", { + source: this, + details: { + type: "editing", + data: { + type: "highlight", + action: "toggle_visibility" + } + } + }); + (this.#showAllStates ||= new Map()).set(type, value); + this.showAllEditors("highlight", value); + break; + } + for (const editor of this.#selectedEditors) { + editor.updateParams(type, value); + } + for (const editorType of this.#editorTypes) { + editorType.updateDefaultParams(type, value); + } + } + showAllEditors(type, visible, updateButton = false) { + for (const editor of this.#allEditors.values()) { + if (editor.editorType === type) { + editor.show(visible); + } + } + const state = this.#showAllStates?.get(AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL) ?? true; + if (state !== visible) { + this.#dispatchUpdateUI([[AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL, visible]]); + } + } + enableWaiting(mustWait = false) { + if (this.#isWaiting === mustWait) { + return; + } + this.#isWaiting = mustWait; + for (const layer of this.#allLayers.values()) { + if (mustWait) { + layer.disableClick(); + } else { + layer.enableClick(); + } + layer.div.classList.toggle("waiting", mustWait); + } + } + async #enableAll() { + if (!this.#isEnabled) { + this.#isEnabled = true; + const promises = []; + for (const layer of this.#allLayers.values()) { + promises.push(layer.enable()); + } + await Promise.all(promises); + for (const editor of this.#allEditors.values()) { + editor.enable(); + } + } + } + #disableAll() { + this.unselectAll(); + if (this.#isEnabled) { + this.#isEnabled = false; + for (const layer of this.#allLayers.values()) { + layer.disable(); + } + for (const editor of this.#allEditors.values()) { + editor.disable(); + } + } + } + getEditors(pageIndex) { + const editors = []; + for (const editor of this.#allEditors.values()) { + if (editor.pageIndex === pageIndex) { + editors.push(editor); + } + } + return editors; + } + getEditor(id) { + return this.#allEditors.get(id); + } + addEditor(editor) { + this.#allEditors.set(editor.id, editor); + } + removeEditor(editor) { + if (editor.div.contains(document.activeElement)) { + if (this.#focusMainContainerTimeoutId) { + clearTimeout(this.#focusMainContainerTimeoutId); + } + this.#focusMainContainerTimeoutId = setTimeout(() => { + this.focusMainContainer(); + this.#focusMainContainerTimeoutId = null; + }, 0); + } + this.#allEditors.delete(editor.id); + this.unselect(editor); + if (!editor.annotationElementId || !this.#deletedAnnotationsElementIds.has(editor.annotationElementId)) { + this.#annotationStorage?.remove(editor.id); + } + } + addDeletedAnnotationElement(editor) { + this.#deletedAnnotationsElementIds.add(editor.annotationElementId); + this.addChangedExistingAnnotation(editor); + editor.deleted = true; + } + isDeletedAnnotationElement(annotationElementId) { + return this.#deletedAnnotationsElementIds.has(annotationElementId); + } + removeDeletedAnnotationElement(editor) { + this.#deletedAnnotationsElementIds.delete(editor.annotationElementId); + this.removeChangedExistingAnnotation(editor); + editor.deleted = false; + } + #addEditorToLayer(editor) { + const layer = this.#allLayers.get(editor.pageIndex); + if (layer) { + layer.addOrRebuild(editor); + } else { + this.addEditor(editor); + this.addToAnnotationStorage(editor); + } + } + setActiveEditor(editor) { + if (this.#activeEditor === editor) { + return; + } + this.#activeEditor = editor; + if (editor) { + this.#dispatchUpdateUI(editor.propertiesToUpdate); + } + } + get #lastSelectedEditor() { + let ed = null; + for (ed of this.#selectedEditors) {} + return ed; + } + updateUI(editor) { + if (this.#lastSelectedEditor === editor) { + this.#dispatchUpdateUI(editor.propertiesToUpdate); + } + } + updateUIForDefaultProperties(editorType) { + this.#dispatchUpdateUI(editorType.defaultPropertiesToUpdate); + } + toggleSelected(editor) { + if (this.#selectedEditors.has(editor)) { + this.#selectedEditors.delete(editor); + editor.unselect(); + this.#dispatchUpdateStates({ + hasSelectedEditor: this.hasSelection + }); + return; + } + this.#selectedEditors.add(editor); + editor.select(); + this.#dispatchUpdateUI(editor.propertiesToUpdate); + this.#dispatchUpdateStates({ + hasSelectedEditor: true + }); + } + setSelected(editor) { + this.#currentDrawingSession?.commitOrRemove(); + for (const ed of this.#selectedEditors) { + if (ed !== editor) { + ed.unselect(); + } + } + this.#selectedEditors.clear(); + this.#selectedEditors.add(editor); + editor.select(); + this.#dispatchUpdateUI(editor.propertiesToUpdate); + this.#dispatchUpdateStates({ + hasSelectedEditor: true + }); + } + isSelected(editor) { + return this.#selectedEditors.has(editor); + } + get firstSelectedEditor() { + return this.#selectedEditors.values().next().value; + } + unselect(editor) { + editor.unselect(); + this.#selectedEditors.delete(editor); + this.#dispatchUpdateStates({ + hasSelectedEditor: this.hasSelection + }); + } + get hasSelection() { + return this.#selectedEditors.size !== 0; + } + get isEnterHandled() { + return this.#selectedEditors.size === 1 && this.firstSelectedEditor.isEnterHandled; + } + undo() { + this.#commandManager.undo(); + this.#dispatchUpdateStates({ + hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(), + hasSomethingToRedo: true, + isEmpty: this.#isEmpty() + }); + this._editorUndoBar?.hide(); + } + redo() { + this.#commandManager.redo(); + this.#dispatchUpdateStates({ + hasSomethingToUndo: true, + hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(), + isEmpty: this.#isEmpty() + }); + } + addCommands(params) { + this.#commandManager.add(params); + this.#dispatchUpdateStates({ + hasSomethingToUndo: true, + hasSomethingToRedo: false, + isEmpty: this.#isEmpty() + }); + } + cleanUndoStack(type) { + this.#commandManager.cleanType(type); + } + #isEmpty() { + if (this.#allEditors.size === 0) { + return true; + } + if (this.#allEditors.size === 1) { + for (const editor of this.#allEditors.values()) { + return editor.isEmpty(); + } + } + return false; + } + delete() { + this.commitOrRemove(); + const drawingEditor = this.currentLayer?.endDrawingSession(true); + if (!this.hasSelection && !drawingEditor) { + return; + } + const editors = drawingEditor ? [drawingEditor] : [...this.#selectedEditors]; + const cmd = () => { + this._editorUndoBar?.show(undo, editors.length === 1 ? editors[0].editorType : editors.length); + for (const editor of editors) { + editor.remove(); + } + }; + const undo = () => { + for (const editor of editors) { + this.#addEditorToLayer(editor); + } + }; + this.addCommands({ + cmd, + undo, + mustExec: true + }); + } + commitOrRemove() { + this.#activeEditor?.commitOrRemove(); + } + hasSomethingToControl() { + return this.#activeEditor || this.hasSelection; + } + #selectEditors(editors) { + for (const editor of this.#selectedEditors) { + editor.unselect(); + } + this.#selectedEditors.clear(); + for (const editor of editors) { + if (editor.isEmpty()) { + continue; + } + this.#selectedEditors.add(editor); + editor.select(); + } + this.#dispatchUpdateStates({ + hasSelectedEditor: this.hasSelection + }); + } + selectAll() { + for (const editor of this.#selectedEditors) { + editor.commit(); + } + this.#selectEditors(this.#allEditors.values()); + } + unselectAll() { + if (this.#activeEditor) { + this.#activeEditor.commitOrRemove(); + if (this.#mode !== AnnotationEditorType.NONE) { + return; + } + } + if (this.#currentDrawingSession?.commitOrRemove()) { + return; + } + if (!this.hasSelection) { + return; + } + for (const editor of this.#selectedEditors) { + editor.unselect(); + } + this.#selectedEditors.clear(); + this.#dispatchUpdateStates({ + hasSelectedEditor: false + }); + } + translateSelectedEditors(x, y, noCommit = false) { + if (!noCommit) { + this.commitOrRemove(); + } + if (!this.hasSelection) { + return; + } + this.#translation[0] += x; + this.#translation[1] += y; + const [totalX, totalY] = this.#translation; + const editors = [...this.#selectedEditors]; + const TIME_TO_WAIT = 1000; + if (this.#translationTimeoutId) { + clearTimeout(this.#translationTimeoutId); + } + this.#translationTimeoutId = setTimeout(() => { + this.#translationTimeoutId = null; + this.#translation[0] = this.#translation[1] = 0; + this.addCommands({ + cmd: () => { + for (const editor of editors) { + if (this.#allEditors.has(editor.id)) { + editor.translateInPage(totalX, totalY); + } + } + }, + undo: () => { + for (const editor of editors) { + if (this.#allEditors.has(editor.id)) { + editor.translateInPage(-totalX, -totalY); + } + } + }, + mustExec: false + }); + }, TIME_TO_WAIT); + for (const editor of editors) { + editor.translateInPage(x, y); + } + } + setUpDragSession() { + if (!this.hasSelection) { + return; + } + this.disableUserSelect(true); + this.#draggingEditors = new Map(); + for (const editor of this.#selectedEditors) { + this.#draggingEditors.set(editor, { + savedX: editor.x, + savedY: editor.y, + savedPageIndex: editor.pageIndex, + newX: 0, + newY: 0, + newPageIndex: -1 + }); + } + } + endDragSession() { + if (!this.#draggingEditors) { + return false; + } + this.disableUserSelect(false); + const map = this.#draggingEditors; + this.#draggingEditors = null; + let mustBeAddedInUndoStack = false; + for (const [{ + x, + y, + pageIndex + }, value] of map) { + value.newX = x; + value.newY = y; + value.newPageIndex = pageIndex; + mustBeAddedInUndoStack ||= x !== value.savedX || y !== value.savedY || pageIndex !== value.savedPageIndex; + } + if (!mustBeAddedInUndoStack) { + return false; + } + const move = (editor, x, y, pageIndex) => { + if (this.#allEditors.has(editor.id)) { + const parent = this.#allLayers.get(pageIndex); + if (parent) { + editor._setParentAndPosition(parent, x, y); + } else { + editor.pageIndex = pageIndex; + editor.x = x; + editor.y = y; + } + } + }; + this.addCommands({ + cmd: () => { + for (const [editor, { + newX, + newY, + newPageIndex + }] of map) { + move(editor, newX, newY, newPageIndex); + } + }, + undo: () => { + for (const [editor, { + savedX, + savedY, + savedPageIndex + }] of map) { + move(editor, savedX, savedY, savedPageIndex); + } + }, + mustExec: true + }); + return true; + } + dragSelectedEditors(tx, ty) { + if (!this.#draggingEditors) { + return; + } + for (const editor of this.#draggingEditors.keys()) { + editor.drag(tx, ty); + } + } + rebuild(editor) { + if (editor.parent === null) { + const parent = this.getLayer(editor.pageIndex); + if (parent) { + parent.changeParent(editor); + parent.addOrRebuild(editor); + } else { + this.addEditor(editor); + this.addToAnnotationStorage(editor); + editor.rebuild(); + } + } else { + editor.parent.addOrRebuild(editor); + } + } + get isEditorHandlingKeyboard() { + return this.getActive()?.shouldGetKeyboardEvents() || this.#selectedEditors.size === 1 && this.firstSelectedEditor.shouldGetKeyboardEvents(); + } + isActive(editor) { + return this.#activeEditor === editor; + } + getActive() { + return this.#activeEditor; + } + getMode() { + return this.#mode; + } + get imageManager() { + return shadow(this, "imageManager", new ImageManager()); + } + getSelectionBoxes(textLayer) { + if (!textLayer) { + return null; + } + const selection = document.getSelection(); + for (let i = 0, ii = selection.rangeCount; i < ii; i++) { + if (!textLayer.contains(selection.getRangeAt(i).commonAncestorContainer)) { + return null; + } + } + const { + x: layerX, + y: layerY, + width: parentWidth, + height: parentHeight + } = textLayer.getBoundingClientRect(); + let rotator; + switch (textLayer.getAttribute("data-main-rotation")) { + case "90": + rotator = (x, y, w, h) => ({ + x: (y - layerY) / parentHeight, + y: 1 - (x + w - layerX) / parentWidth, + width: h / parentHeight, + height: w / parentWidth + }); + break; + case "180": + rotator = (x, y, w, h) => ({ + x: 1 - (x + w - layerX) / parentWidth, + y: 1 - (y + h - layerY) / parentHeight, + width: w / parentWidth, + height: h / parentHeight + }); + break; + case "270": + rotator = (x, y, w, h) => ({ + x: 1 - (y + h - layerY) / parentHeight, + y: (x - layerX) / parentWidth, + width: h / parentHeight, + height: w / parentWidth + }); + break; + default: + rotator = (x, y, w, h) => ({ + x: (x - layerX) / parentWidth, + y: (y - layerY) / parentHeight, + width: w / parentWidth, + height: h / parentHeight + }); + break; + } + const boxes = []; + for (let i = 0, ii = selection.rangeCount; i < ii; i++) { + const range = selection.getRangeAt(i); + if (range.collapsed) { + continue; + } + for (const { + x, + y, + width, + height + } of range.getClientRects()) { + if (width === 0 || height === 0) { + continue; + } + boxes.push(rotator(x, y, width, height)); + } + } + return boxes.length === 0 ? null : boxes; + } + addChangedExistingAnnotation({ + annotationElementId, + id + }) { + (this.#changedExistingAnnotations ||= new Map()).set(annotationElementId, id); + } + removeChangedExistingAnnotation({ + annotationElementId + }) { + this.#changedExistingAnnotations?.delete(annotationElementId); + } + renderAnnotationElement(annotation) { + const editorId = this.#changedExistingAnnotations?.get(annotation.data.id); + if (!editorId) { + return; + } + const editor = this.#annotationStorage.getRawValue(editorId); + if (!editor) { + return; + } + if (this.#mode === AnnotationEditorType.NONE && !editor.hasBeenModified) { + return; + } + editor.renderAnnotationElement(annotation); + } +} + +;// ./src/display/editor/alt_text.js + +class AltText { + #altText = null; + #altTextDecorative = false; + #altTextButton = null; + #altTextButtonLabel = null; + #altTextTooltip = null; + #altTextTooltipTimeout = null; + #altTextWasFromKeyBoard = false; + #badge = null; + #editor = null; + #guessedText = null; + #textWithDisclaimer = null; + #useNewAltTextFlow = false; + static #l10nNewButton = null; + static _l10n = null; + constructor(editor) { + this.#editor = editor; + this.#useNewAltTextFlow = editor._uiManager.useNewAltTextFlow; + AltText.#l10nNewButton ||= Object.freeze({ + added: "pdfjs-editor-new-alt-text-added-button", + "added-label": "pdfjs-editor-new-alt-text-added-button-label", + missing: "pdfjs-editor-new-alt-text-missing-button", + "missing-label": "pdfjs-editor-new-alt-text-missing-button-label", + review: "pdfjs-editor-new-alt-text-to-review-button", + "review-label": "pdfjs-editor-new-alt-text-to-review-button-label" + }); + } + static initialize(l10n) { + AltText._l10n ??= l10n; + } + async render() { + const altText = this.#altTextButton = document.createElement("button"); + altText.className = "altText"; + altText.tabIndex = "0"; + const label = this.#altTextButtonLabel = document.createElement("span"); + altText.append(label); + if (this.#useNewAltTextFlow) { + altText.classList.add("new"); + altText.setAttribute("data-l10n-id", AltText.#l10nNewButton.missing); + label.setAttribute("data-l10n-id", AltText.#l10nNewButton["missing-label"]); + } else { + altText.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button"); + label.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button-label"); + } + const signal = this.#editor._uiManager._signal; + altText.addEventListener("contextmenu", noContextMenu, { + signal + }); + altText.addEventListener("pointerdown", event => event.stopPropagation(), { + signal + }); + const onClick = event => { + event.preventDefault(); + this.#editor._uiManager.editAltText(this.#editor); + if (this.#useNewAltTextFlow) { + this.#editor._reportTelemetry({ + action: "pdfjs.image.alt_text.image_status_label_clicked", + data: { + label: this.#label + } + }); + } + }; + altText.addEventListener("click", onClick, { + capture: true, + signal + }); + altText.addEventListener("keydown", event => { + if (event.target === altText && event.key === "Enter") { + this.#altTextWasFromKeyBoard = true; + onClick(event); + } + }, { + signal + }); + await this.#setState(); + return altText; + } + get #label() { + return this.#altText && "added" || this.#altText === null && this.guessedText && "review" || "missing"; + } + finish() { + if (!this.#altTextButton) { + return; + } + this.#altTextButton.focus({ + focusVisible: this.#altTextWasFromKeyBoard + }); + this.#altTextWasFromKeyBoard = false; + } + isEmpty() { + if (this.#useNewAltTextFlow) { + return this.#altText === null; + } + return !this.#altText && !this.#altTextDecorative; + } + hasData() { + if (this.#useNewAltTextFlow) { + return this.#altText !== null || !!this.#guessedText; + } + return this.isEmpty(); + } + get guessedText() { + return this.#guessedText; + } + async setGuessedText(guessedText) { + if (this.#altText !== null) { + return; + } + this.#guessedText = guessedText; + this.#textWithDisclaimer = await AltText._l10n.get("pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer", { + generatedAltText: guessedText + }); + this.#setState(); + } + toggleAltTextBadge(visibility = false) { + if (!this.#useNewAltTextFlow || this.#altText) { + this.#badge?.remove(); + this.#badge = null; + return; + } + if (!this.#badge) { + const badge = this.#badge = document.createElement("div"); + badge.className = "noAltTextBadge"; + this.#editor.div.append(badge); + } + this.#badge.classList.toggle("hidden", !visibility); + } + serialize(isForCopying) { + let altText = this.#altText; + if (!isForCopying && this.#guessedText === altText) { + altText = this.#textWithDisclaimer; + } + return { + altText, + decorative: this.#altTextDecorative, + guessedText: this.#guessedText, + textWithDisclaimer: this.#textWithDisclaimer + }; + } + get data() { + return { + altText: this.#altText, + decorative: this.#altTextDecorative + }; + } + set data({ + altText, + decorative, + guessedText, + textWithDisclaimer, + cancel = false + }) { + if (guessedText) { + this.#guessedText = guessedText; + this.#textWithDisclaimer = textWithDisclaimer; + } + if (this.#altText === altText && this.#altTextDecorative === decorative) { + return; + } + if (!cancel) { + this.#altText = altText; + this.#altTextDecorative = decorative; + } + this.#setState(); + } + toggle(enabled = false) { + if (!this.#altTextButton) { + return; + } + if (!enabled && this.#altTextTooltipTimeout) { + clearTimeout(this.#altTextTooltipTimeout); + this.#altTextTooltipTimeout = null; + } + this.#altTextButton.disabled = !enabled; + } + shown() { + this.#editor._reportTelemetry({ + action: "pdfjs.image.alt_text.image_status_label_displayed", + data: { + label: this.#label + } + }); + } + destroy() { + this.#altTextButton?.remove(); + this.#altTextButton = null; + this.#altTextButtonLabel = null; + this.#altTextTooltip = null; + this.#badge?.remove(); + this.#badge = null; + } + async #setState() { + const button = this.#altTextButton; + if (!button) { + return; + } + if (this.#useNewAltTextFlow) { + button.classList.toggle("done", !!this.#altText); + button.setAttribute("data-l10n-id", AltText.#l10nNewButton[this.#label]); + this.#altTextButtonLabel?.setAttribute("data-l10n-id", AltText.#l10nNewButton[`${this.#label}-label`]); + if (!this.#altText) { + this.#altTextTooltip?.remove(); + return; + } + } else { + if (!this.#altText && !this.#altTextDecorative) { + button.classList.remove("done"); + this.#altTextTooltip?.remove(); + return; + } + button.classList.add("done"); + button.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-edit-button"); + } + let tooltip = this.#altTextTooltip; + if (!tooltip) { + this.#altTextTooltip = tooltip = document.createElement("span"); + tooltip.className = "tooltip"; + tooltip.setAttribute("role", "tooltip"); + tooltip.id = `alt-text-tooltip-${this.#editor.id}`; + const DELAY_TO_SHOW_TOOLTIP = 100; + const signal = this.#editor._uiManager._signal; + signal.addEventListener("abort", () => { + clearTimeout(this.#altTextTooltipTimeout); + this.#altTextTooltipTimeout = null; + }, { + once: true + }); + button.addEventListener("mouseenter", () => { + this.#altTextTooltipTimeout = setTimeout(() => { + this.#altTextTooltipTimeout = null; + this.#altTextTooltip.classList.add("show"); + this.#editor._reportTelemetry({ + action: "alt_text_tooltip" + }); + }, DELAY_TO_SHOW_TOOLTIP); + }, { + signal + }); + button.addEventListener("mouseleave", () => { + if (this.#altTextTooltipTimeout) { + clearTimeout(this.#altTextTooltipTimeout); + this.#altTextTooltipTimeout = null; + } + this.#altTextTooltip?.classList.remove("show"); + }, { + signal + }); + } + if (this.#altTextDecorative) { + tooltip.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-decorative-tooltip"); + } else { + tooltip.removeAttribute("data-l10n-id"); + tooltip.textContent = this.#altText; + } + if (!tooltip.parentNode) { + button.append(tooltip); + } + const element = this.#editor.getImageForAltText(); + element?.setAttribute("aria-describedby", tooltip.id); + } +} + +;// ./src/display/touch_manager.js + + +class TouchManager { + #container; + #isPinching = false; + #isPinchingStopped = null; + #isPinchingDisabled; + #onPinchStart; + #onPinching; + #onPinchEnd; + #signal; + #touchInfo = null; + #touchManagerAC; + #touchMoveAC = null; + constructor({ + container, + isPinchingDisabled = null, + isPinchingStopped = null, + onPinchStart = null, + onPinching = null, + onPinchEnd = null, + signal + }) { + this.#container = container; + this.#isPinchingStopped = isPinchingStopped; + this.#isPinchingDisabled = isPinchingDisabled; + this.#onPinchStart = onPinchStart; + this.#onPinching = onPinching; + this.#onPinchEnd = onPinchEnd; + this.#touchManagerAC = new AbortController(); + this.#signal = AbortSignal.any([signal, this.#touchManagerAC.signal]); + container.addEventListener("touchstart", this.#onTouchStart.bind(this), { + passive: false, + signal: this.#signal + }); + } + get MIN_TOUCH_DISTANCE_TO_PINCH() { + return shadow(this, "MIN_TOUCH_DISTANCE_TO_PINCH", 35 / (window.devicePixelRatio || 1)); + } + #onTouchStart(evt) { + if (this.#isPinchingDisabled?.() || evt.touches.length < 2) { + return; + } + if (!this.#touchMoveAC) { + this.#touchMoveAC = new AbortController(); + const signal = AbortSignal.any([this.#signal, this.#touchMoveAC.signal]); + const container = this.#container; + const opt = { + signal, + passive: false + }; + container.addEventListener("touchmove", this.#onTouchMove.bind(this), opt); + container.addEventListener("touchend", this.#onTouchEnd.bind(this), opt); + container.addEventListener("touchcancel", this.#onTouchEnd.bind(this), opt); + this.#onPinchStart?.(); + } + stopEvent(evt); + if (evt.touches.length !== 2 || this.#isPinchingStopped?.()) { + this.#touchInfo = null; + return; + } + let [touch0, touch1] = evt.touches; + if (touch0.identifier > touch1.identifier) { + [touch0, touch1] = [touch1, touch0]; + } + this.#touchInfo = { + touch0X: touch0.screenX, + touch0Y: touch0.screenY, + touch1X: touch1.screenX, + touch1Y: touch1.screenY + }; + } + #onTouchMove(evt) { + if (!this.#touchInfo || evt.touches.length !== 2) { + return; + } + let [touch0, touch1] = evt.touches; + if (touch0.identifier > touch1.identifier) { + [touch0, touch1] = [touch1, touch0]; + } + const { + screenX: screen0X, + screenY: screen0Y + } = touch0; + const { + screenX: screen1X, + screenY: screen1Y + } = touch1; + const touchInfo = this.#touchInfo; + const { + touch0X: pTouch0X, + touch0Y: pTouch0Y, + touch1X: pTouch1X, + touch1Y: pTouch1Y + } = touchInfo; + const prevGapX = pTouch1X - pTouch0X; + const prevGapY = pTouch1Y - pTouch0Y; + const currGapX = screen1X - screen0X; + const currGapY = screen1Y - screen0Y; + const distance = Math.hypot(currGapX, currGapY) || 1; + const pDistance = Math.hypot(prevGapX, prevGapY) || 1; + if (!this.#isPinching && Math.abs(pDistance - distance) <= TouchManager.MIN_TOUCH_DISTANCE_TO_PINCH) { + return; + } + touchInfo.touch0X = screen0X; + touchInfo.touch0Y = screen0Y; + touchInfo.touch1X = screen1X; + touchInfo.touch1Y = screen1Y; + evt.preventDefault(); + if (!this.#isPinching) { + this.#isPinching = true; + return; + } + const origin = [(screen0X + screen1X) / 2, (screen0Y + screen1Y) / 2]; + this.#onPinching?.(origin, pDistance, distance); + } + #onTouchEnd(evt) { + this.#touchMoveAC.abort(); + this.#touchMoveAC = null; + this.#onPinchEnd?.(); + if (!this.#touchInfo) { + return; + } + evt.preventDefault(); + this.#touchInfo = null; + this.#isPinching = false; + } + destroy() { + this.#touchManagerAC?.abort(); + this.#touchManagerAC = null; + } +} + +;// ./src/display/editor/editor.js + + + + + + +class AnnotationEditor { + #accessibilityData = null; + #allResizerDivs = null; + #altText = null; + #disabled = false; + #dragPointerId = null; + #dragPointerType = ""; + #keepAspectRatio = false; + #resizersDiv = null; + #lastPointerCoords = null; + #savedDimensions = null; + #focusAC = null; + #focusedResizerName = ""; + #hasBeenClicked = false; + #initialRect = null; + #isEditing = false; + #isInEditMode = false; + #isResizerEnabledForKeyboard = false; + #moveInDOMTimeout = null; + #prevDragX = 0; + #prevDragY = 0; + #telemetryTimeouts = null; + #touchManager = null; + _editToolbar = null; + _initialOptions = Object.create(null); + _initialData = null; + _isVisible = true; + _uiManager = null; + _focusEventsAllowed = true; + static _l10n = null; + static _l10nResizer = null; + #isDraggable = false; + #zIndex = AnnotationEditor._zIndex++; + static _borderLineWidth = -1; + static _colorManager = new ColorManager(); + static _zIndex = 1; + static _telemetryTimeout = 1000; + static get _resizerKeyboardManager() { + const resize = AnnotationEditor.prototype._resizeWithKeyboard; + const small = AnnotationEditorUIManager.TRANSLATE_SMALL; + const big = AnnotationEditorUIManager.TRANSLATE_BIG; + return shadow(this, "_resizerKeyboardManager", new KeyboardManager([[["ArrowLeft", "mac+ArrowLeft"], resize, { + args: [-small, 0] + }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], resize, { + args: [-big, 0] + }], [["ArrowRight", "mac+ArrowRight"], resize, { + args: [small, 0] + }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], resize, { + args: [big, 0] + }], [["ArrowUp", "mac+ArrowUp"], resize, { + args: [0, -small] + }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], resize, { + args: [0, -big] + }], [["ArrowDown", "mac+ArrowDown"], resize, { + args: [0, small] + }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], resize, { + args: [0, big] + }], [["Escape", "mac+Escape"], AnnotationEditor.prototype._stopResizingWithKeyboard]])); + } + constructor(parameters) { + this.parent = parameters.parent; + this.id = parameters.id; + this.width = this.height = null; + this.pageIndex = parameters.parent.pageIndex; + this.name = parameters.name; + this.div = null; + this._uiManager = parameters.uiManager; + this.annotationElementId = null; + this._willKeepAspectRatio = false; + this._initialOptions.isCentered = parameters.isCentered; + this._structTreeParentId = null; + const { + rotation, + rawDims: { + pageWidth, + pageHeight, + pageX, + pageY + } + } = this.parent.viewport; + this.rotation = rotation; + this.pageRotation = (360 + rotation - this._uiManager.viewParameters.rotation) % 360; + this.pageDimensions = [pageWidth, pageHeight]; + this.pageTranslation = [pageX, pageY]; + const [width, height] = this.parentDimensions; + this.x = parameters.x / width; + this.y = parameters.y / height; + this.isAttachedToDOM = false; + this.deleted = false; + } + get editorType() { + return Object.getPrototypeOf(this).constructor._type; + } + static get isDrawer() { + return false; + } + static get _defaultLineColor() { + return shadow(this, "_defaultLineColor", this._colorManager.getHexCode("CanvasText")); + } + static deleteAnnotationElement(editor) { + const fakeEditor = new FakeEditor({ + id: editor.parent.getNextId(), + parent: editor.parent, + uiManager: editor._uiManager + }); + fakeEditor.annotationElementId = editor.annotationElementId; + fakeEditor.deleted = true; + fakeEditor._uiManager.addToAnnotationStorage(fakeEditor); + } + static initialize(l10n, _uiManager) { + AnnotationEditor._l10n ??= l10n; + AnnotationEditor._l10nResizer ||= Object.freeze({ + topLeft: "pdfjs-editor-resizer-top-left", + topMiddle: "pdfjs-editor-resizer-top-middle", + topRight: "pdfjs-editor-resizer-top-right", + middleRight: "pdfjs-editor-resizer-middle-right", + bottomRight: "pdfjs-editor-resizer-bottom-right", + bottomMiddle: "pdfjs-editor-resizer-bottom-middle", + bottomLeft: "pdfjs-editor-resizer-bottom-left", + middleLeft: "pdfjs-editor-resizer-middle-left" + }); + if (AnnotationEditor._borderLineWidth !== -1) { + return; + } + const style = getComputedStyle(document.documentElement); + AnnotationEditor._borderLineWidth = parseFloat(style.getPropertyValue("--outline-width")) || 0; + } + static updateDefaultParams(_type, _value) {} + static get defaultPropertiesToUpdate() { + return []; + } + static isHandlingMimeForPasting(mime) { + return false; + } + static paste(item, parent) { + unreachable("Not implemented"); + } + get propertiesToUpdate() { + return []; + } + get _isDraggable() { + return this.#isDraggable; + } + set _isDraggable(value) { + this.#isDraggable = value; + this.div?.classList.toggle("draggable", value); + } + get isEnterHandled() { + return true; + } + center() { + const [pageWidth, pageHeight] = this.pageDimensions; + switch (this.parentRotation) { + case 90: + this.x -= this.height * pageHeight / (pageWidth * 2); + this.y += this.width * pageWidth / (pageHeight * 2); + break; + case 180: + this.x += this.width / 2; + this.y += this.height / 2; + break; + case 270: + this.x += this.height * pageHeight / (pageWidth * 2); + this.y -= this.width * pageWidth / (pageHeight * 2); + break; + default: + this.x -= this.width / 2; + this.y -= this.height / 2; + break; + } + this.fixAndSetPosition(); + } + addCommands(params) { + this._uiManager.addCommands(params); + } + get currentLayer() { + return this._uiManager.currentLayer; + } + setInBackground() { + this.div.style.zIndex = 0; + } + setInForeground() { + this.div.style.zIndex = this.#zIndex; + } + setParent(parent) { + if (parent !== null) { + this.pageIndex = parent.pageIndex; + this.pageDimensions = parent.pageDimensions; + } else { + this.#stopResizing(); + } + this.parent = parent; + } + focusin(event) { + if (!this._focusEventsAllowed) { + return; + } + if (!this.#hasBeenClicked) { + this.parent.setSelected(this); + } else { + this.#hasBeenClicked = false; + } + } + focusout(event) { + if (!this._focusEventsAllowed) { + return; + } + if (!this.isAttachedToDOM) { + return; + } + const target = event.relatedTarget; + if (target?.closest(`#${this.id}`)) { + return; + } + event.preventDefault(); + if (!this.parent?.isMultipleSelection) { + this.commitOrRemove(); + } + } + commitOrRemove() { + if (this.isEmpty()) { + this.remove(); + } else { + this.commit(); + } + } + commit() { + this.addToAnnotationStorage(); + } + addToAnnotationStorage() { + this._uiManager.addToAnnotationStorage(this); + } + setAt(x, y, tx, ty) { + const [width, height] = this.parentDimensions; + [tx, ty] = this.screenToPageTranslation(tx, ty); + this.x = (x + tx) / width; + this.y = (y + ty) / height; + this.fixAndSetPosition(); + } + #translate([width, height], x, y) { + [x, y] = this.screenToPageTranslation(x, y); + this.x += x / width; + this.y += y / height; + this._onTranslating(this.x, this.y); + this.fixAndSetPosition(); + } + translate(x, y) { + this.#translate(this.parentDimensions, x, y); + } + translateInPage(x, y) { + this.#initialRect ||= [this.x, this.y, this.width, this.height]; + this.#translate(this.pageDimensions, x, y); + this.div.scrollIntoView({ + block: "nearest" + }); + } + drag(tx, ty) { + this.#initialRect ||= [this.x, this.y, this.width, this.height]; + const { + div, + parentDimensions: [parentWidth, parentHeight] + } = this; + this.x += tx / parentWidth; + this.y += ty / parentHeight; + if (this.parent && (this.x < 0 || this.x > 1 || this.y < 0 || this.y > 1)) { + const { + x, + y + } = this.div.getBoundingClientRect(); + if (this.parent.findNewParent(this, x, y)) { + this.x -= Math.floor(this.x); + this.y -= Math.floor(this.y); + } + } + let { + x, + y + } = this; + const [bx, by] = this.getBaseTranslation(); + x += bx; + y += by; + const { + style + } = div; + style.left = `${(100 * x).toFixed(2)}%`; + style.top = `${(100 * y).toFixed(2)}%`; + this._onTranslating(x, y); + div.scrollIntoView({ + block: "nearest" + }); + } + _onTranslating(x, y) {} + _onTranslated(x, y) {} + get _hasBeenMoved() { + return !!this.#initialRect && (this.#initialRect[0] !== this.x || this.#initialRect[1] !== this.y); + } + get _hasBeenResized() { + return !!this.#initialRect && (this.#initialRect[2] !== this.width || this.#initialRect[3] !== this.height); + } + getBaseTranslation() { + const [parentWidth, parentHeight] = this.parentDimensions; + const { + _borderLineWidth + } = AnnotationEditor; + const x = _borderLineWidth / parentWidth; + const y = _borderLineWidth / parentHeight; + switch (this.rotation) { + case 90: + return [-x, y]; + case 180: + return [x, y]; + case 270: + return [x, -y]; + default: + return [-x, -y]; + } + } + get _mustFixPosition() { + return true; + } + fixAndSetPosition(rotation = this.rotation) { + const { + div: { + style + }, + pageDimensions: [pageWidth, pageHeight] + } = this; + let { + x, + y, + width, + height + } = this; + width *= pageWidth; + height *= pageHeight; + x *= pageWidth; + y *= pageHeight; + if (this._mustFixPosition) { + switch (rotation) { + case 0: + x = Math.max(0, Math.min(pageWidth - width, x)); + y = Math.max(0, Math.min(pageHeight - height, y)); + break; + case 90: + x = Math.max(0, Math.min(pageWidth - height, x)); + y = Math.min(pageHeight, Math.max(width, y)); + break; + case 180: + x = Math.min(pageWidth, Math.max(width, x)); + y = Math.min(pageHeight, Math.max(height, y)); + break; + case 270: + x = Math.min(pageWidth, Math.max(height, x)); + y = Math.max(0, Math.min(pageHeight - width, y)); + break; + } + } + this.x = x /= pageWidth; + this.y = y /= pageHeight; + const [bx, by] = this.getBaseTranslation(); + x += bx; + y += by; + style.left = `${(100 * x).toFixed(2)}%`; + style.top = `${(100 * y).toFixed(2)}%`; + this.moveInDOM(); + } + static #rotatePoint(x, y, angle) { + switch (angle) { + case 90: + return [y, -x]; + case 180: + return [-x, -y]; + case 270: + return [-y, x]; + default: + return [x, y]; + } + } + screenToPageTranslation(x, y) { + return AnnotationEditor.#rotatePoint(x, y, this.parentRotation); + } + pageTranslationToScreen(x, y) { + return AnnotationEditor.#rotatePoint(x, y, 360 - this.parentRotation); + } + #getRotationMatrix(rotation) { + switch (rotation) { + case 90: + { + const [pageWidth, pageHeight] = this.pageDimensions; + return [0, -pageWidth / pageHeight, pageHeight / pageWidth, 0]; + } + case 180: + return [-1, 0, 0, -1]; + case 270: + { + const [pageWidth, pageHeight] = this.pageDimensions; + return [0, pageWidth / pageHeight, -pageHeight / pageWidth, 0]; + } + default: + return [1, 0, 0, 1]; + } + } + get parentScale() { + return this._uiManager.viewParameters.realScale; + } + get parentRotation() { + return (this._uiManager.viewParameters.rotation + this.pageRotation) % 360; + } + get parentDimensions() { + const { + parentScale, + pageDimensions: [pageWidth, pageHeight] + } = this; + return [pageWidth * parentScale, pageHeight * parentScale]; + } + setDims(width, height) { + const [parentWidth, parentHeight] = this.parentDimensions; + const { + style + } = this.div; + style.width = `${(100 * width / parentWidth).toFixed(2)}%`; + if (!this.#keepAspectRatio) { + style.height = `${(100 * height / parentHeight).toFixed(2)}%`; + } + } + fixDims() { + const { + style + } = this.div; + const { + height, + width + } = style; + const widthPercent = width.endsWith("%"); + const heightPercent = !this.#keepAspectRatio && height.endsWith("%"); + if (widthPercent && heightPercent) { + return; + } + const [parentWidth, parentHeight] = this.parentDimensions; + if (!widthPercent) { + style.width = `${(100 * parseFloat(width) / parentWidth).toFixed(2)}%`; + } + if (!this.#keepAspectRatio && !heightPercent) { + style.height = `${(100 * parseFloat(height) / parentHeight).toFixed(2)}%`; + } + } + getInitialTranslation() { + return [0, 0]; + } + #createResizers() { + if (this.#resizersDiv) { + return; + } + this.#resizersDiv = document.createElement("div"); + this.#resizersDiv.classList.add("resizers"); + const classes = this._willKeepAspectRatio ? ["topLeft", "topRight", "bottomRight", "bottomLeft"] : ["topLeft", "topMiddle", "topRight", "middleRight", "bottomRight", "bottomMiddle", "bottomLeft", "middleLeft"]; + const signal = this._uiManager._signal; + for (const name of classes) { + const div = document.createElement("div"); + this.#resizersDiv.append(div); + div.classList.add("resizer", name); + div.setAttribute("data-resizer-name", name); + div.addEventListener("pointerdown", this.#resizerPointerdown.bind(this, name), { + signal + }); + div.addEventListener("contextmenu", noContextMenu, { + signal + }); + div.tabIndex = -1; + } + this.div.prepend(this.#resizersDiv); + } + #resizerPointerdown(name, event) { + event.preventDefault(); + const { + isMac + } = util_FeatureTest.platform; + if (event.button !== 0 || event.ctrlKey && isMac) { + return; + } + this.#altText?.toggle(false); + const savedDraggable = this._isDraggable; + this._isDraggable = false; + this.#lastPointerCoords = [event.screenX, event.screenY]; + const ac = new AbortController(); + const signal = this._uiManager.combinedSignal(ac); + this.parent.togglePointerEvents(false); + window.addEventListener("pointermove", this.#resizerPointermove.bind(this, name), { + passive: true, + capture: true, + signal + }); + window.addEventListener("touchmove", stopEvent, { + passive: false, + signal + }); + window.addEventListener("contextmenu", noContextMenu, { + signal + }); + this.#savedDimensions = { + savedX: this.x, + savedY: this.y, + savedWidth: this.width, + savedHeight: this.height + }; + const savedParentCursor = this.parent.div.style.cursor; + const savedCursor = this.div.style.cursor; + this.div.style.cursor = this.parent.div.style.cursor = window.getComputedStyle(event.target).cursor; + const pointerUpCallback = () => { + ac.abort(); + this.parent.togglePointerEvents(true); + this.#altText?.toggle(true); + this._isDraggable = savedDraggable; + this.parent.div.style.cursor = savedParentCursor; + this.div.style.cursor = savedCursor; + this.#addResizeToUndoStack(); + }; + window.addEventListener("pointerup", pointerUpCallback, { + signal + }); + window.addEventListener("blur", pointerUpCallback, { + signal + }); + } + #resize(x, y, width, height) { + this.width = width; + this.height = height; + this.x = x; + this.y = y; + const [parentWidth, parentHeight] = this.parentDimensions; + this.setDims(parentWidth * width, parentHeight * height); + this.fixAndSetPosition(); + this._onResized(); + } + _onResized() {} + #addResizeToUndoStack() { + if (!this.#savedDimensions) { + return; + } + const { + savedX, + savedY, + savedWidth, + savedHeight + } = this.#savedDimensions; + this.#savedDimensions = null; + const newX = this.x; + const newY = this.y; + const newWidth = this.width; + const newHeight = this.height; + if (newX === savedX && newY === savedY && newWidth === savedWidth && newHeight === savedHeight) { + return; + } + this.addCommands({ + cmd: this.#resize.bind(this, newX, newY, newWidth, newHeight), + undo: this.#resize.bind(this, savedX, savedY, savedWidth, savedHeight), + mustExec: true + }); + } + static _round(x) { + return Math.round(x * 10000) / 10000; + } + #resizerPointermove(name, event) { + const [parentWidth, parentHeight] = this.parentDimensions; + const savedX = this.x; + const savedY = this.y; + const savedWidth = this.width; + const savedHeight = this.height; + const minWidth = AnnotationEditor.MIN_SIZE / parentWidth; + const minHeight = AnnotationEditor.MIN_SIZE / parentHeight; + const rotationMatrix = this.#getRotationMatrix(this.rotation); + const transf = (x, y) => [rotationMatrix[0] * x + rotationMatrix[2] * y, rotationMatrix[1] * x + rotationMatrix[3] * y]; + const invRotationMatrix = this.#getRotationMatrix(360 - this.rotation); + const invTransf = (x, y) => [invRotationMatrix[0] * x + invRotationMatrix[2] * y, invRotationMatrix[1] * x + invRotationMatrix[3] * y]; + let getPoint; + let getOpposite; + let isDiagonal = false; + let isHorizontal = false; + switch (name) { + case "topLeft": + isDiagonal = true; + getPoint = (w, h) => [0, 0]; + getOpposite = (w, h) => [w, h]; + break; + case "topMiddle": + getPoint = (w, h) => [w / 2, 0]; + getOpposite = (w, h) => [w / 2, h]; + break; + case "topRight": + isDiagonal = true; + getPoint = (w, h) => [w, 0]; + getOpposite = (w, h) => [0, h]; + break; + case "middleRight": + isHorizontal = true; + getPoint = (w, h) => [w, h / 2]; + getOpposite = (w, h) => [0, h / 2]; + break; + case "bottomRight": + isDiagonal = true; + getPoint = (w, h) => [w, h]; + getOpposite = (w, h) => [0, 0]; + break; + case "bottomMiddle": + getPoint = (w, h) => [w / 2, h]; + getOpposite = (w, h) => [w / 2, 0]; + break; + case "bottomLeft": + isDiagonal = true; + getPoint = (w, h) => [0, h]; + getOpposite = (w, h) => [w, 0]; + break; + case "middleLeft": + isHorizontal = true; + getPoint = (w, h) => [0, h / 2]; + getOpposite = (w, h) => [w, h / 2]; + break; + } + const point = getPoint(savedWidth, savedHeight); + const oppositePoint = getOpposite(savedWidth, savedHeight); + let transfOppositePoint = transf(...oppositePoint); + const oppositeX = AnnotationEditor._round(savedX + transfOppositePoint[0]); + const oppositeY = AnnotationEditor._round(savedY + transfOppositePoint[1]); + let ratioX = 1; + let ratioY = 1; + let deltaX, deltaY; + if (!event.fromKeyboard) { + const { + screenX, + screenY + } = event; + const [lastScreenX, lastScreenY] = this.#lastPointerCoords; + [deltaX, deltaY] = this.screenToPageTranslation(screenX - lastScreenX, screenY - lastScreenY); + this.#lastPointerCoords[0] = screenX; + this.#lastPointerCoords[1] = screenY; + } else { + ({ + deltaX, + deltaY + } = event); + } + [deltaX, deltaY] = invTransf(deltaX / parentWidth, deltaY / parentHeight); + if (isDiagonal) { + const oldDiag = Math.hypot(savedWidth, savedHeight); + ratioX = ratioY = Math.max(Math.min(Math.hypot(oppositePoint[0] - point[0] - deltaX, oppositePoint[1] - point[1] - deltaY) / oldDiag, 1 / savedWidth, 1 / savedHeight), minWidth / savedWidth, minHeight / savedHeight); + } else if (isHorizontal) { + ratioX = Math.max(minWidth, Math.min(1, Math.abs(oppositePoint[0] - point[0] - deltaX))) / savedWidth; + } else { + ratioY = Math.max(minHeight, Math.min(1, Math.abs(oppositePoint[1] - point[1] - deltaY))) / savedHeight; + } + const newWidth = AnnotationEditor._round(savedWidth * ratioX); + const newHeight = AnnotationEditor._round(savedHeight * ratioY); + transfOppositePoint = transf(...getOpposite(newWidth, newHeight)); + const newX = oppositeX - transfOppositePoint[0]; + const newY = oppositeY - transfOppositePoint[1]; + this.#initialRect ||= [this.x, this.y, this.width, this.height]; + this.width = newWidth; + this.height = newHeight; + this.x = newX; + this.y = newY; + this.setDims(parentWidth * newWidth, parentHeight * newHeight); + this.fixAndSetPosition(); + this._onResizing(); + } + _onResizing() {} + altTextFinish() { + this.#altText?.finish(); + } + async addEditToolbar() { + if (this._editToolbar || this.#isInEditMode) { + return this._editToolbar; + } + this._editToolbar = new EditorToolbar(this); + this.div.append(this._editToolbar.render()); + if (this.#altText) { + await this._editToolbar.addAltText(this.#altText); + } + return this._editToolbar; + } + removeEditToolbar() { + if (!this._editToolbar) { + return; + } + this._editToolbar.remove(); + this._editToolbar = null; + this.#altText?.destroy(); + } + addContainer(container) { + const editToolbarDiv = this._editToolbar?.div; + if (editToolbarDiv) { + editToolbarDiv.before(container); + } else { + this.div.append(container); + } + } + getClientDimensions() { + return this.div.getBoundingClientRect(); + } + async addAltTextButton() { + if (this.#altText) { + return; + } + AltText.initialize(AnnotationEditor._l10n); + this.#altText = new AltText(this); + if (this.#accessibilityData) { + this.#altText.data = this.#accessibilityData; + this.#accessibilityData = null; + } + await this.addEditToolbar(); + } + get altTextData() { + return this.#altText?.data; + } + set altTextData(data) { + if (!this.#altText) { + return; + } + this.#altText.data = data; + } + get guessedAltText() { + return this.#altText?.guessedText; + } + async setGuessedAltText(text) { + await this.#altText?.setGuessedText(text); + } + serializeAltText(isForCopying) { + return this.#altText?.serialize(isForCopying); + } + hasAltText() { + return !!this.#altText && !this.#altText.isEmpty(); + } + hasAltTextData() { + return this.#altText?.hasData() ?? false; + } + render() { + this.div = document.createElement("div"); + this.div.setAttribute("data-editor-rotation", (360 - this.rotation) % 360); + this.div.className = this.name; + this.div.setAttribute("id", this.id); + this.div.tabIndex = this.#disabled ? -1 : 0; + if (!this._isVisible) { + this.div.classList.add("hidden"); + } + this.setInForeground(); + this.#addFocusListeners(); + const [parentWidth, parentHeight] = this.parentDimensions; + if (this.parentRotation % 180 !== 0) { + this.div.style.maxWidth = `${(100 * parentHeight / parentWidth).toFixed(2)}%`; + this.div.style.maxHeight = `${(100 * parentWidth / parentHeight).toFixed(2)}%`; + } + const [tx, ty] = this.getInitialTranslation(); + this.translate(tx, ty); + bindEvents(this, this.div, ["pointerdown"]); + if (this.isResizable && this._uiManager._supportsPinchToZoom) { + this.#touchManager ||= new TouchManager({ + container: this.div, + isPinchingDisabled: () => !this.isSelected, + onPinchStart: this.#touchPinchStartCallback.bind(this), + onPinching: this.#touchPinchCallback.bind(this), + onPinchEnd: this.#touchPinchEndCallback.bind(this), + signal: this._uiManager._signal + }); + } + this._uiManager._editorUndoBar?.hide(); + return this.div; + } + #touchPinchStartCallback() { + this.#savedDimensions = { + savedX: this.x, + savedY: this.y, + savedWidth: this.width, + savedHeight: this.height + }; + this.#altText?.toggle(false); + this.parent.togglePointerEvents(false); + } + #touchPinchCallback(_origin, prevDistance, distance) { + const slowDownFactor = 0.7; + let factor = slowDownFactor * (distance / prevDistance) + 1 - slowDownFactor; + if (factor === 1) { + return; + } + const rotationMatrix = this.#getRotationMatrix(this.rotation); + const transf = (x, y) => [rotationMatrix[0] * x + rotationMatrix[2] * y, rotationMatrix[1] * x + rotationMatrix[3] * y]; + const [parentWidth, parentHeight] = this.parentDimensions; + const savedX = this.x; + const savedY = this.y; + const savedWidth = this.width; + const savedHeight = this.height; + const minWidth = AnnotationEditor.MIN_SIZE / parentWidth; + const minHeight = AnnotationEditor.MIN_SIZE / parentHeight; + factor = Math.max(Math.min(factor, 1 / savedWidth, 1 / savedHeight), minWidth / savedWidth, minHeight / savedHeight); + const newWidth = AnnotationEditor._round(savedWidth * factor); + const newHeight = AnnotationEditor._round(savedHeight * factor); + if (newWidth === savedWidth && newHeight === savedHeight) { + return; + } + this.#initialRect ||= [savedX, savedY, savedWidth, savedHeight]; + const transfCenterPoint = transf(savedWidth / 2, savedHeight / 2); + const centerX = AnnotationEditor._round(savedX + transfCenterPoint[0]); + const centerY = AnnotationEditor._round(savedY + transfCenterPoint[1]); + const newTransfCenterPoint = transf(newWidth / 2, newHeight / 2); + this.x = centerX - newTransfCenterPoint[0]; + this.y = centerY - newTransfCenterPoint[1]; + this.width = newWidth; + this.height = newHeight; + this.setDims(parentWidth * newWidth, parentHeight * newHeight); + this.fixAndSetPosition(); + this._onResizing(); + } + #touchPinchEndCallback() { + this.#altText?.toggle(true); + this.parent.togglePointerEvents(true); + this.#addResizeToUndoStack(); + } + pointerdown(event) { + const { + isMac + } = util_FeatureTest.platform; + if (event.button !== 0 || event.ctrlKey && isMac) { + event.preventDefault(); + return; + } + this.#hasBeenClicked = true; + if (this._isDraggable) { + this.#setUpDragSession(event); + return; + } + this.#selectOnPointerEvent(event); + } + get isSelected() { + return this._uiManager.isSelected(this); + } + #selectOnPointerEvent(event) { + const { + isMac + } = util_FeatureTest.platform; + if (event.ctrlKey && !isMac || event.shiftKey || event.metaKey && isMac) { + this.parent.toggleSelected(this); + } else { + this.parent.setSelected(this); + } + } + #setUpDragSession(event) { + const { + isSelected + } = this; + this._uiManager.setUpDragSession(); + let hasDraggingStarted = false; + const ac = new AbortController(); + const signal = this._uiManager.combinedSignal(ac); + const opts = { + capture: true, + passive: false, + signal + }; + const cancelDrag = e => { + ac.abort(); + this.#dragPointerId = null; + this.#hasBeenClicked = false; + if (!this._uiManager.endDragSession()) { + this.#selectOnPointerEvent(e); + } + if (hasDraggingStarted) { + this._onStopDragging(); + } + }; + if (isSelected) { + this.#prevDragX = event.clientX; + this.#prevDragY = event.clientY; + this.#dragPointerId = event.pointerId; + this.#dragPointerType = event.pointerType; + window.addEventListener("pointermove", e => { + if (!hasDraggingStarted) { + hasDraggingStarted = true; + this._onStartDragging(); + } + const { + clientX: x, + clientY: y, + pointerId + } = e; + if (pointerId !== this.#dragPointerId) { + stopEvent(e); + return; + } + const [tx, ty] = this.screenToPageTranslation(x - this.#prevDragX, y - this.#prevDragY); + this.#prevDragX = x; + this.#prevDragY = y; + this._uiManager.dragSelectedEditors(tx, ty); + }, opts); + window.addEventListener("touchmove", stopEvent, opts); + window.addEventListener("pointerdown", e => { + if (e.pointerType === this.#dragPointerType) { + if (this.#touchManager || e.isPrimary) { + cancelDrag(e); + } + } + stopEvent(e); + }, opts); + } + const pointerUpCallback = e => { + if (!this.#dragPointerId || this.#dragPointerId === e.pointerId) { + cancelDrag(e); + return; + } + stopEvent(e); + }; + window.addEventListener("pointerup", pointerUpCallback, { + signal + }); + window.addEventListener("blur", pointerUpCallback, { + signal + }); + } + _onStartDragging() {} + _onStopDragging() {} + moveInDOM() { + if (this.#moveInDOMTimeout) { + clearTimeout(this.#moveInDOMTimeout); + } + this.#moveInDOMTimeout = setTimeout(() => { + this.#moveInDOMTimeout = null; + this.parent?.moveEditorInDOM(this); + }, 0); + } + _setParentAndPosition(parent, x, y) { + parent.changeParent(this); + this.x = x; + this.y = y; + this.fixAndSetPosition(); + this._onTranslated(); + } + getRect(tx, ty, rotation = this.rotation) { + const scale = this.parentScale; + const [pageWidth, pageHeight] = this.pageDimensions; + const [pageX, pageY] = this.pageTranslation; + const shiftX = tx / scale; + const shiftY = ty / scale; + const x = this.x * pageWidth; + const y = this.y * pageHeight; + const width = this.width * pageWidth; + const height = this.height * pageHeight; + switch (rotation) { + case 0: + return [x + shiftX + pageX, pageHeight - y - shiftY - height + pageY, x + shiftX + width + pageX, pageHeight - y - shiftY + pageY]; + case 90: + return [x + shiftY + pageX, pageHeight - y + shiftX + pageY, x + shiftY + height + pageX, pageHeight - y + shiftX + width + pageY]; + case 180: + return [x - shiftX - width + pageX, pageHeight - y + shiftY + pageY, x - shiftX + pageX, pageHeight - y + shiftY + height + pageY]; + case 270: + return [x - shiftY - height + pageX, pageHeight - y - shiftX - width + pageY, x - shiftY + pageX, pageHeight - y - shiftX + pageY]; + default: + throw new Error("Invalid rotation"); + } + } + getRectInCurrentCoords(rect, pageHeight) { + const [x1, y1, x2, y2] = rect; + const width = x2 - x1; + const height = y2 - y1; + switch (this.rotation) { + case 0: + return [x1, pageHeight - y2, width, height]; + case 90: + return [x1, pageHeight - y1, height, width]; + case 180: + return [x2, pageHeight - y1, width, height]; + case 270: + return [x2, pageHeight - y2, height, width]; + default: + throw new Error("Invalid rotation"); + } + } + onceAdded(focus) {} + isEmpty() { + return false; + } + enableEditMode() { + this.#isInEditMode = true; + } + disableEditMode() { + this.#isInEditMode = false; + } + isInEditMode() { + return this.#isInEditMode; + } + shouldGetKeyboardEvents() { + return this.#isResizerEnabledForKeyboard; + } + needsToBeRebuilt() { + return this.div && !this.isAttachedToDOM; + } + get isOnScreen() { + const { + top, + left, + bottom, + right + } = this.getClientDimensions(); + const { + innerHeight, + innerWidth + } = window; + return left < innerWidth && right > 0 && top < innerHeight && bottom > 0; + } + #addFocusListeners() { + if (this.#focusAC || !this.div) { + return; + } + this.#focusAC = new AbortController(); + const signal = this._uiManager.combinedSignal(this.#focusAC); + this.div.addEventListener("focusin", this.focusin.bind(this), { + signal + }); + this.div.addEventListener("focusout", this.focusout.bind(this), { + signal + }); + } + rebuild() { + this.#addFocusListeners(); + } + rotate(_angle) {} + resize() {} + serializeDeleted() { + return { + id: this.annotationElementId, + deleted: true, + pageIndex: this.pageIndex, + popupRef: this._initialData?.popupRef || "" + }; + } + serialize(isForCopying = false, context = null) { + unreachable("An editor must be serializable"); + } + static async deserialize(data, parent, uiManager) { + const editor = new this.prototype.constructor({ + parent, + id: parent.getNextId(), + uiManager + }); + editor.rotation = data.rotation; + editor.#accessibilityData = data.accessibilityData; + const [pageWidth, pageHeight] = editor.pageDimensions; + const [x, y, width, height] = editor.getRectInCurrentCoords(data.rect, pageHeight); + editor.x = x / pageWidth; + editor.y = y / pageHeight; + editor.width = width / pageWidth; + editor.height = height / pageHeight; + return editor; + } + get hasBeenModified() { + return !!this.annotationElementId && (this.deleted || this.serialize() !== null); + } + remove() { + this.#focusAC?.abort(); + this.#focusAC = null; + if (!this.isEmpty()) { + this.commit(); + } + if (this.parent) { + this.parent.remove(this); + } else { + this._uiManager.removeEditor(this); + } + if (this.#moveInDOMTimeout) { + clearTimeout(this.#moveInDOMTimeout); + this.#moveInDOMTimeout = null; + } + this.#stopResizing(); + this.removeEditToolbar(); + if (this.#telemetryTimeouts) { + for (const timeout of this.#telemetryTimeouts.values()) { + clearTimeout(timeout); + } + this.#telemetryTimeouts = null; + } + this.parent = null; + this.#touchManager?.destroy(); + this.#touchManager = null; + } + get isResizable() { + return false; + } + makeResizable() { + if (this.isResizable) { + this.#createResizers(); + this.#resizersDiv.classList.remove("hidden"); + bindEvents(this, this.div, ["keydown"]); + } + } + get toolbarPosition() { + return null; + } + keydown(event) { + if (!this.isResizable || event.target !== this.div || event.key !== "Enter") { + return; + } + this._uiManager.setSelected(this); + this.#savedDimensions = { + savedX: this.x, + savedY: this.y, + savedWidth: this.width, + savedHeight: this.height + }; + const children = this.#resizersDiv.children; + if (!this.#allResizerDivs) { + this.#allResizerDivs = Array.from(children); + const boundResizerKeydown = this.#resizerKeydown.bind(this); + const boundResizerBlur = this.#resizerBlur.bind(this); + const signal = this._uiManager._signal; + for (const div of this.#allResizerDivs) { + const name = div.getAttribute("data-resizer-name"); + div.setAttribute("role", "spinbutton"); + div.addEventListener("keydown", boundResizerKeydown, { + signal + }); + div.addEventListener("blur", boundResizerBlur, { + signal + }); + div.addEventListener("focus", this.#resizerFocus.bind(this, name), { + signal + }); + div.setAttribute("data-l10n-id", AnnotationEditor._l10nResizer[name]); + } + } + const first = this.#allResizerDivs[0]; + let firstPosition = 0; + for (const div of children) { + if (div === first) { + break; + } + firstPosition++; + } + const nextFirstPosition = (360 - this.rotation + this.parentRotation) % 360 / 90 * (this.#allResizerDivs.length / 4); + if (nextFirstPosition !== firstPosition) { + if (nextFirstPosition < firstPosition) { + for (let i = 0; i < firstPosition - nextFirstPosition; i++) { + this.#resizersDiv.append(this.#resizersDiv.firstChild); + } + } else if (nextFirstPosition > firstPosition) { + for (let i = 0; i < nextFirstPosition - firstPosition; i++) { + this.#resizersDiv.firstChild.before(this.#resizersDiv.lastChild); + } + } + let i = 0; + for (const child of children) { + const div = this.#allResizerDivs[i++]; + const name = div.getAttribute("data-resizer-name"); + child.setAttribute("data-l10n-id", AnnotationEditor._l10nResizer[name]); + } + } + this.#setResizerTabIndex(0); + this.#isResizerEnabledForKeyboard = true; + this.#resizersDiv.firstChild.focus({ + focusVisible: true + }); + event.preventDefault(); + event.stopImmediatePropagation(); + } + #resizerKeydown(event) { + AnnotationEditor._resizerKeyboardManager.exec(this, event); + } + #resizerBlur(event) { + if (this.#isResizerEnabledForKeyboard && event.relatedTarget?.parentNode !== this.#resizersDiv) { + this.#stopResizing(); + } + } + #resizerFocus(name) { + this.#focusedResizerName = this.#isResizerEnabledForKeyboard ? name : ""; + } + #setResizerTabIndex(value) { + if (!this.#allResizerDivs) { + return; + } + for (const div of this.#allResizerDivs) { + div.tabIndex = value; + } + } + _resizeWithKeyboard(x, y) { + if (!this.#isResizerEnabledForKeyboard) { + return; + } + this.#resizerPointermove(this.#focusedResizerName, { + deltaX: x, + deltaY: y, + fromKeyboard: true + }); + } + #stopResizing() { + this.#isResizerEnabledForKeyboard = false; + this.#setResizerTabIndex(-1); + this.#addResizeToUndoStack(); + } + _stopResizingWithKeyboard() { + this.#stopResizing(); + this.div.focus(); + } + select() { + this.makeResizable(); + this.div?.classList.add("selectedEditor"); + if (!this._editToolbar) { + this.addEditToolbar().then(() => { + if (this.div?.classList.contains("selectedEditor")) { + this._editToolbar?.show(); + } + }); + return; + } + this._editToolbar?.show(); + this.#altText?.toggleAltTextBadge(false); + } + unselect() { + this.#resizersDiv?.classList.add("hidden"); + this.div?.classList.remove("selectedEditor"); + if (this.div?.contains(document.activeElement)) { + this._uiManager.currentLayer.div.focus({ + preventScroll: true + }); + } + this._editToolbar?.hide(); + this.#altText?.toggleAltTextBadge(true); + } + updateParams(type, value) {} + disableEditing() {} + enableEditing() {} + enterInEditMode() {} + getImageForAltText() { + return null; + } + get contentDiv() { + return this.div; + } + get isEditing() { + return this.#isEditing; + } + set isEditing(value) { + this.#isEditing = value; + if (!this.parent) { + return; + } + if (value) { + this.parent.setSelected(this); + this.parent.setActiveEditor(this); + } else { + this.parent.setActiveEditor(null); + } + } + setAspectRatio(width, height) { + this.#keepAspectRatio = true; + const aspectRatio = width / height; + const { + style + } = this.div; + style.aspectRatio = aspectRatio; + style.height = "auto"; + } + static get MIN_SIZE() { + return 16; + } + static canCreateNewEmptyEditor() { + return true; + } + get telemetryInitialData() { + return { + action: "added" + }; + } + get telemetryFinalData() { + return null; + } + _reportTelemetry(data, mustWait = false) { + if (mustWait) { + this.#telemetryTimeouts ||= new Map(); + const { + action + } = data; + let timeout = this.#telemetryTimeouts.get(action); + if (timeout) { + clearTimeout(timeout); + } + timeout = setTimeout(() => { + this._reportTelemetry(data); + this.#telemetryTimeouts.delete(action); + if (this.#telemetryTimeouts.size === 0) { + this.#telemetryTimeouts = null; + } + }, AnnotationEditor._telemetryTimeout); + this.#telemetryTimeouts.set(action, timeout); + return; + } + data.type ||= this.editorType; + this._uiManager._eventBus.dispatch("reporttelemetry", { + source: this, + details: { + type: "editing", + data + } + }); + } + show(visible = this._isVisible) { + this.div.classList.toggle("hidden", !visible); + this._isVisible = visible; + } + enable() { + if (this.div) { + this.div.tabIndex = 0; + } + this.#disabled = false; + } + disable() { + if (this.div) { + this.div.tabIndex = -1; + } + this.#disabled = true; + } + renderAnnotationElement(annotation) { + let content = annotation.container.querySelector(".annotationContent"); + if (!content) { + content = document.createElement("div"); + content.classList.add("annotationContent", this.editorType); + annotation.container.prepend(content); + } else if (content.nodeName === "CANVAS") { + const canvas = content; + content = document.createElement("div"); + content.classList.add("annotationContent", this.editorType); + canvas.before(content); + } + return content; + } + resetAnnotationElement(annotation) { + const { + firstChild + } = annotation.container; + if (firstChild?.nodeName === "DIV" && firstChild.classList.contains("annotationContent")) { + firstChild.remove(); + } + } +} +class FakeEditor extends AnnotationEditor { + constructor(params) { + super(params); + this.annotationElementId = params.annotationElementId; + this.deleted = true; + } + serialize() { + return this.serializeDeleted(); + } +} + +;// ./src/shared/murmurhash3.js +const SEED = 0xc3d2e1f0; +const MASK_HIGH = 0xffff0000; +const MASK_LOW = 0xffff; +class MurmurHash3_64 { + constructor(seed) { + this.h1 = seed ? seed & 0xffffffff : SEED; + this.h2 = seed ? seed & 0xffffffff : SEED; + } + update(input) { + let data, length; + if (typeof input === "string") { + data = new Uint8Array(input.length * 2); + length = 0; + for (let i = 0, ii = input.length; i < ii; i++) { + const code = input.charCodeAt(i); + if (code <= 0xff) { + data[length++] = code; + } else { + data[length++] = code >>> 8; + data[length++] = code & 0xff; + } + } + } else if (ArrayBuffer.isView(input)) { + data = input.slice(); + length = data.byteLength; + } else { + throw new Error("Invalid data format, must be a string or TypedArray."); + } + const blockCounts = length >> 2; + const tailLength = length - blockCounts * 4; + const dataUint32 = new Uint32Array(data.buffer, 0, blockCounts); + let k1 = 0, + k2 = 0; + let h1 = this.h1, + h2 = this.h2; + const C1 = 0xcc9e2d51, + C2 = 0x1b873593; + const C1_LOW = C1 & MASK_LOW, + C2_LOW = C2 & MASK_LOW; + for (let i = 0; i < blockCounts; i++) { + if (i & 1) { + k1 = dataUint32[i]; + k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW; + k1 = k1 << 15 | k1 >>> 17; + k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW; + h1 ^= k1; + h1 = h1 << 13 | h1 >>> 19; + h1 = h1 * 5 + 0xe6546b64; + } else { + k2 = dataUint32[i]; + k2 = k2 * C1 & MASK_HIGH | k2 * C1_LOW & MASK_LOW; + k2 = k2 << 15 | k2 >>> 17; + k2 = k2 * C2 & MASK_HIGH | k2 * C2_LOW & MASK_LOW; + h2 ^= k2; + h2 = h2 << 13 | h2 >>> 19; + h2 = h2 * 5 + 0xe6546b64; + } + } + k1 = 0; + switch (tailLength) { + case 3: + k1 ^= data[blockCounts * 4 + 2] << 16; + case 2: + k1 ^= data[blockCounts * 4 + 1] << 8; + case 1: + k1 ^= data[blockCounts * 4]; + k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW; + k1 = k1 << 15 | k1 >>> 17; + k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW; + if (blockCounts & 1) { + h1 ^= k1; + } else { + h2 ^= k1; + } + } + this.h1 = h1; + this.h2 = h2; + } + hexdigest() { + let h1 = this.h1, + h2 = this.h2; + h1 ^= h2 >>> 1; + h1 = h1 * 0xed558ccd & MASK_HIGH | h1 * 0x8ccd & MASK_LOW; + h2 = h2 * 0xff51afd7 & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16; + h1 ^= h2 >>> 1; + h1 = h1 * 0x1a85ec53 & MASK_HIGH | h1 * 0xec53 & MASK_LOW; + h2 = h2 * 0xc4ceb9fe & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16; + h1 ^= h2 >>> 1; + return (h1 >>> 0).toString(16).padStart(8, "0") + (h2 >>> 0).toString(16).padStart(8, "0"); + } +} + +;// ./src/display/annotation_storage.js + + + +const SerializableEmpty = Object.freeze({ + map: null, + hash: "", + transfer: undefined +}); +class AnnotationStorage { + #modified = false; + #modifiedIds = null; + #storage = new Map(); + constructor() { + this.onSetModified = null; + this.onResetModified = null; + this.onAnnotationEditor = null; + } + getValue(key, defaultValue) { + const value = this.#storage.get(key); + if (value === undefined) { + return defaultValue; + } + return Object.assign(defaultValue, value); + } + getRawValue(key) { + return this.#storage.get(key); + } + remove(key) { + this.#storage.delete(key); + if (this.#storage.size === 0) { + this.resetModified(); + } + if (typeof this.onAnnotationEditor === "function") { + for (const value of this.#storage.values()) { + if (value instanceof AnnotationEditor) { + return; + } + } + this.onAnnotationEditor(null); + } + } + setValue(key, value) { + const obj = this.#storage.get(key); + let modified = false; + if (obj !== undefined) { + for (const [entry, val] of Object.entries(value)) { + if (obj[entry] !== val) { + modified = true; + obj[entry] = val; + } + } + } else { + modified = true; + this.#storage.set(key, value); + } + if (modified) { + this.#setModified(); + } + if (value instanceof AnnotationEditor && typeof this.onAnnotationEditor === "function") { + this.onAnnotationEditor(value.constructor._type); + } + } + has(key) { + return this.#storage.has(key); + } + getAll() { + return this.#storage.size > 0 ? objectFromMap(this.#storage) : null; + } + setAll(obj) { + for (const [key, val] of Object.entries(obj)) { + this.setValue(key, val); + } + } + get size() { + return this.#storage.size; + } + #setModified() { + if (!this.#modified) { + this.#modified = true; + if (typeof this.onSetModified === "function") { + this.onSetModified(); + } + } + } + resetModified() { + if (this.#modified) { + this.#modified = false; + if (typeof this.onResetModified === "function") { + this.onResetModified(); + } + } + } + get print() { + return new PrintAnnotationStorage(this); + } + get serializable() { + if (this.#storage.size === 0) { + return SerializableEmpty; + } + const map = new Map(), + hash = new MurmurHash3_64(), + transfer = []; + const context = Object.create(null); + let hasBitmap = false; + for (const [key, val] of this.#storage) { + const serialized = val instanceof AnnotationEditor ? val.serialize(false, context) : val; + if (serialized) { + map.set(key, serialized); + hash.update(`${key}:${JSON.stringify(serialized)}`); + hasBitmap ||= !!serialized.bitmap; + } + } + if (hasBitmap) { + for (const value of map.values()) { + if (value.bitmap) { + transfer.push(value.bitmap); + } + } + } + return map.size > 0 ? { + map, + hash: hash.hexdigest(), + transfer + } : SerializableEmpty; + } + get editorStats() { + let stats = null; + const typeToEditor = new Map(); + for (const value of this.#storage.values()) { + if (!(value instanceof AnnotationEditor)) { + continue; + } + const editorStats = value.telemetryFinalData; + if (!editorStats) { + continue; + } + const { + type + } = editorStats; + if (!typeToEditor.has(type)) { + typeToEditor.set(type, Object.getPrototypeOf(value).constructor); + } + stats ||= Object.create(null); + const map = stats[type] ||= new Map(); + for (const [key, val] of Object.entries(editorStats)) { + if (key === "type") { + continue; + } + let counters = map.get(key); + if (!counters) { + counters = new Map(); + map.set(key, counters); + } + const count = counters.get(val) ?? 0; + counters.set(val, count + 1); + } + } + for (const [type, editor] of typeToEditor) { + stats[type] = editor.computeTelemetryFinalData(stats[type]); + } + return stats; + } + resetModifiedIds() { + this.#modifiedIds = null; + } + get modifiedIds() { + if (this.#modifiedIds) { + return this.#modifiedIds; + } + const ids = []; + for (const value of this.#storage.values()) { + if (!(value instanceof AnnotationEditor) || !value.annotationElementId || !value.serialize()) { + continue; + } + ids.push(value.annotationElementId); + } + return this.#modifiedIds = { + ids: new Set(ids), + hash: ids.join(",") + }; + } +} +class PrintAnnotationStorage extends AnnotationStorage { + #serializable; + constructor(parent) { + super(); + const { + map, + hash, + transfer + } = parent.serializable; + const clone = structuredClone(map, transfer ? { + transfer + } : null); + this.#serializable = { + map: clone, + hash, + transfer + }; + } + get print() { + unreachable("Should not call PrintAnnotationStorage.print"); + } + get serializable() { + return this.#serializable; + } + get modifiedIds() { + return shadow(this, "modifiedIds", { + ids: new Set(), + hash: "" + }); + } +} + +;// ./src/display/font_loader.js + +class FontLoader { + #systemFonts = new Set(); + constructor({ + ownerDocument = globalThis.document, + styleElement = null + }) { + this._document = ownerDocument; + this.nativeFontFaces = new Set(); + this.styleElement = null; + this.loadingRequests = []; + this.loadTestFontId = 0; + } + addNativeFontFace(nativeFontFace) { + this.nativeFontFaces.add(nativeFontFace); + this._document.fonts.add(nativeFontFace); + } + removeNativeFontFace(nativeFontFace) { + this.nativeFontFaces.delete(nativeFontFace); + this._document.fonts.delete(nativeFontFace); + } + insertRule(rule) { + if (!this.styleElement) { + this.styleElement = this._document.createElement("style"); + this._document.documentElement.getElementsByTagName("head")[0].append(this.styleElement); + } + const styleSheet = this.styleElement.sheet; + styleSheet.insertRule(rule, styleSheet.cssRules.length); + } + clear() { + for (const nativeFontFace of this.nativeFontFaces) { + this._document.fonts.delete(nativeFontFace); + } + this.nativeFontFaces.clear(); + this.#systemFonts.clear(); + if (this.styleElement) { + this.styleElement.remove(); + this.styleElement = null; + } + } + async loadSystemFont({ + systemFontInfo: info, + _inspectFont + }) { + if (!info || this.#systemFonts.has(info.loadedName)) { + return; + } + assert(!this.disableFontFace, "loadSystemFont shouldn't be called when `disableFontFace` is set."); + if (this.isFontLoadingAPISupported) { + const { + loadedName, + src, + style + } = info; + const fontFace = new FontFace(loadedName, src, style); + this.addNativeFontFace(fontFace); + try { + await fontFace.load(); + this.#systemFonts.add(loadedName); + _inspectFont?.(info); + } catch { + warn(`Cannot load system font: ${info.baseFontName}, installing it could help to improve PDF rendering.`); + this.removeNativeFontFace(fontFace); + } + return; + } + unreachable("Not implemented: loadSystemFont without the Font Loading API."); + } + async bind(font) { + if (font.attached || font.missingFile && !font.systemFontInfo) { + return; + } + font.attached = true; + if (font.systemFontInfo) { + await this.loadSystemFont(font); + return; + } + if (this.isFontLoadingAPISupported) { + const nativeFontFace = font.createNativeFontFace(); + if (nativeFontFace) { + this.addNativeFontFace(nativeFontFace); + try { + await nativeFontFace.loaded; + } catch (ex) { + warn(`Failed to load font '${nativeFontFace.family}': '${ex}'.`); + font.disableFontFace = true; + throw ex; + } + } + return; + } + const rule = font.createFontFaceRule(); + if (rule) { + this.insertRule(rule); + if (this.isSyncFontLoadingSupported) { + return; + } + await new Promise(resolve => { + const request = this._queueLoadingCallback(resolve); + this._prepareFontLoadEvent(font, request); + }); + } + } + get isFontLoadingAPISupported() { + const hasFonts = !!this._document?.fonts; + return shadow(this, "isFontLoadingAPISupported", hasFonts); + } + get isSyncFontLoadingSupported() { + let supported = false; + if (isNodeJS) { + supported = true; + } else if (typeof navigator !== "undefined" && typeof navigator?.userAgent === "string" && /Mozilla\/5.0.*?rv:\d+.*? Gecko/.test(navigator.userAgent)) { + supported = true; + } + return shadow(this, "isSyncFontLoadingSupported", supported); + } + _queueLoadingCallback(callback) { + function completeRequest() { + assert(!request.done, "completeRequest() cannot be called twice."); + request.done = true; + while (loadingRequests.length > 0 && loadingRequests[0].done) { + const otherRequest = loadingRequests.shift(); + setTimeout(otherRequest.callback, 0); + } + } + const { + loadingRequests + } = this; + const request = { + done: false, + complete: completeRequest, + callback + }; + loadingRequests.push(request); + return request; + } + get _loadTestFont() { + const testFont = atob("T1RUTwALAIAAAwAwQ0ZGIDHtZg4AAAOYAAAAgUZGVE1lkzZwAAAEHAAAABxHREVGABQA" + "FQAABDgAAAAeT1MvMlYNYwkAAAEgAAAAYGNtYXABDQLUAAACNAAAAUJoZWFk/xVFDQAA" + "ALwAAAA2aGhlYQdkA+oAAAD0AAAAJGhtdHgD6AAAAAAEWAAAAAZtYXhwAAJQAAAAARgA" + "AAAGbmFtZVjmdH4AAAGAAAAAsXBvc3T/hgAzAAADeAAAACAAAQAAAAEAALZRFsRfDzz1" + "AAsD6AAAAADOBOTLAAAAAM4KHDwAAAAAA+gDIQAAAAgAAgAAAAAAAAABAAADIQAAAFoD" + "6AAAAAAD6AABAAAAAAAAAAAAAAAAAAAAAQAAUAAAAgAAAAQD6AH0AAUAAAKKArwAAACM" + "AooCvAAAAeAAMQECAAACAAYJAAAAAAAAAAAAAQAAAAAAAAAAAAAAAFBmRWQAwAAuAC4D" + "IP84AFoDIQAAAAAAAQAAAAAAAAAAACAAIAABAAAADgCuAAEAAAAAAAAAAQAAAAEAAAAA" + "AAEAAQAAAAEAAAAAAAIAAQAAAAEAAAAAAAMAAQAAAAEAAAAAAAQAAQAAAAEAAAAAAAUA" + "AQAAAAEAAAAAAAYAAQAAAAMAAQQJAAAAAgABAAMAAQQJAAEAAgABAAMAAQQJAAIAAgAB" + "AAMAAQQJAAMAAgABAAMAAQQJAAQAAgABAAMAAQQJAAUAAgABAAMAAQQJAAYAAgABWABY" + "AAAAAAAAAwAAAAMAAAAcAAEAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAAAC7//wAA" + "AC7////TAAEAAAAAAAABBgAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAD/gwAyAAAAAQAAAAAAAAAAAAAAAAAA" + "AAABAAQEAAEBAQJYAAEBASH4DwD4GwHEAvgcA/gXBIwMAYuL+nz5tQXkD5j3CBLnEQAC" + "AQEBIVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYAAABAQAADwACAQEEE/t3" + "Dov6fAH6fAT+fPp8+nwHDosMCvm1Cvm1DAz6fBQAAAAAAAABAAAAAMmJbzEAAAAAzgTj" + "FQAAAADOBOQpAAEAAAAAAAAADAAUAAQAAAABAAAAAgABAAAAAAAAAAAD6AAAAAAAAA=="); + return shadow(this, "_loadTestFont", testFont); + } + _prepareFontLoadEvent(font, request) { + function int32(data, offset) { + return data.charCodeAt(offset) << 24 | data.charCodeAt(offset + 1) << 16 | data.charCodeAt(offset + 2) << 8 | data.charCodeAt(offset + 3) & 0xff; + } + function spliceString(s, offset, remove, insert) { + const chunk1 = s.substring(0, offset); + const chunk2 = s.substring(offset + remove); + return chunk1 + insert + chunk2; + } + let i, ii; + const canvas = this._document.createElement("canvas"); + canvas.width = 1; + canvas.height = 1; + const ctx = canvas.getContext("2d"); + let called = 0; + function isFontReady(name, callback) { + if (++called > 30) { + warn("Load test font never loaded."); + callback(); + return; + } + ctx.font = "30px " + name; + ctx.fillText(".", 0, 20); + const imageData = ctx.getImageData(0, 0, 1, 1); + if (imageData.data[3] > 0) { + callback(); + return; + } + setTimeout(isFontReady.bind(null, name, callback)); + } + const loadTestFontId = `lt${Date.now()}${this.loadTestFontId++}`; + let data = this._loadTestFont; + const COMMENT_OFFSET = 976; + data = spliceString(data, COMMENT_OFFSET, loadTestFontId.length, loadTestFontId); + const CFF_CHECKSUM_OFFSET = 16; + const XXXX_VALUE = 0x58585858; + let checksum = int32(data, CFF_CHECKSUM_OFFSET); + for (i = 0, ii = loadTestFontId.length - 3; i < ii; i += 4) { + checksum = checksum - XXXX_VALUE + int32(loadTestFontId, i) | 0; + } + if (i < loadTestFontId.length) { + checksum = checksum - XXXX_VALUE + int32(loadTestFontId + "XXX", i) | 0; + } + data = spliceString(data, CFF_CHECKSUM_OFFSET, 4, string32(checksum)); + const url = `url(data:font/opentype;base64,${btoa(data)});`; + const rule = `@font-face {font-family:"${loadTestFontId}";src:${url}}`; + this.insertRule(rule); + const div = this._document.createElement("div"); + div.style.visibility = "hidden"; + div.style.width = div.style.height = "10px"; + div.style.position = "absolute"; + div.style.top = div.style.left = "0px"; + for (const name of [font.loadedName, loadTestFontId]) { + const span = this._document.createElement("span"); + span.textContent = "Hi"; + span.style.fontFamily = name; + div.append(span); + } + this._document.body.append(div); + isFontReady(loadTestFontId, () => { + div.remove(); + request.complete(); + }); + } +} +class FontFaceObject { + constructor(translatedData, { + disableFontFace = false, + fontExtraProperties = false, + inspectFont = null + }) { + this.compiledGlyphs = Object.create(null); + for (const i in translatedData) { + this[i] = translatedData[i]; + } + this.disableFontFace = disableFontFace === true; + this.fontExtraProperties = fontExtraProperties === true; + this._inspectFont = inspectFont; + } + createNativeFontFace() { + if (!this.data || this.disableFontFace) { + return null; + } + let nativeFontFace; + if (!this.cssFontInfo) { + nativeFontFace = new FontFace(this.loadedName, this.data, {}); + } else { + const css = { + weight: this.cssFontInfo.fontWeight + }; + if (this.cssFontInfo.italicAngle) { + css.style = `oblique ${this.cssFontInfo.italicAngle}deg`; + } + nativeFontFace = new FontFace(this.cssFontInfo.fontFamily, this.data, css); + } + this._inspectFont?.(this); + return nativeFontFace; + } + createFontFaceRule() { + if (!this.data || this.disableFontFace) { + return null; + } + const url = `url(data:${this.mimetype};base64,${toBase64Util(this.data)});`; + let rule; + if (!this.cssFontInfo) { + rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`; + } else { + let css = `font-weight: ${this.cssFontInfo.fontWeight};`; + if (this.cssFontInfo.italicAngle) { + css += `font-style: oblique ${this.cssFontInfo.italicAngle}deg;`; + } + rule = `@font-face {font-family:"${this.cssFontInfo.fontFamily}";${css}src:${url}}`; + } + this._inspectFont?.(this, url); + return rule; + } + getPathGenerator(objs, character) { + if (this.compiledGlyphs[character] !== undefined) { + return this.compiledGlyphs[character]; + } + const objId = this.loadedName + "_path_" + character; + let cmds; + try { + cmds = objs.get(objId); + } catch (ex) { + warn(`getPathGenerator - ignoring character: "${ex}".`); + } + const path = new Path2D(cmds || ""); + if (!this.fontExtraProperties) { + objs.delete(objId); + } + return this.compiledGlyphs[character] = path; + } +} + +;// ./src/shared/message_handler.js + +const CallbackKind = { + DATA: 1, + ERROR: 2 +}; +const StreamKind = { + CANCEL: 1, + CANCEL_COMPLETE: 2, + CLOSE: 3, + ENQUEUE: 4, + ERROR: 5, + PULL: 6, + PULL_COMPLETE: 7, + START_COMPLETE: 8 +}; +function onFn() {} +function wrapReason(ex) { + if (ex instanceof AbortException || ex instanceof InvalidPDFException || ex instanceof MissingPDFException || ex instanceof PasswordException || ex instanceof UnexpectedResponseException || ex instanceof UnknownErrorException) { + return ex; + } + if (!(ex instanceof Error || typeof ex === "object" && ex !== null)) { + unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.'); + } + switch (ex.name) { + case "AbortException": + return new AbortException(ex.message); + case "InvalidPDFException": + return new InvalidPDFException(ex.message); + case "MissingPDFException": + return new MissingPDFException(ex.message); + case "PasswordException": + return new PasswordException(ex.message, ex.code); + case "UnexpectedResponseException": + return new UnexpectedResponseException(ex.message, ex.status); + case "UnknownErrorException": + return new UnknownErrorException(ex.message, ex.details); + } + return new UnknownErrorException(ex.message, ex.toString()); +} +class MessageHandler { + #messageAC = new AbortController(); + constructor(sourceName, targetName, comObj) { + this.sourceName = sourceName; + this.targetName = targetName; + this.comObj = comObj; + this.callbackId = 1; + this.streamId = 1; + this.streamSinks = Object.create(null); + this.streamControllers = Object.create(null); + this.callbackCapabilities = Object.create(null); + this.actionHandler = Object.create(null); + comObj.addEventListener("message", this.#onMessage.bind(this), { + signal: this.#messageAC.signal + }); + } + #onMessage({ + data + }) { + if (data.targetName !== this.sourceName) { + return; + } + if (data.stream) { + this.#processStreamMessage(data); + return; + } + if (data.callback) { + const callbackId = data.callbackId; + const capability = this.callbackCapabilities[callbackId]; + if (!capability) { + throw new Error(`Cannot resolve callback ${callbackId}`); + } + delete this.callbackCapabilities[callbackId]; + if (data.callback === CallbackKind.DATA) { + capability.resolve(data.data); + } else if (data.callback === CallbackKind.ERROR) { + capability.reject(wrapReason(data.reason)); + } else { + throw new Error("Unexpected callback case"); + } + return; + } + const action = this.actionHandler[data.action]; + if (!action) { + throw new Error(`Unknown action from worker: ${data.action}`); + } + if (data.callbackId) { + const sourceName = this.sourceName, + targetName = data.sourceName, + comObj = this.comObj; + Promise.try(action, data.data).then(function (result) { + comObj.postMessage({ + sourceName, + targetName, + callback: CallbackKind.DATA, + callbackId: data.callbackId, + data: result + }); + }, function (reason) { + comObj.postMessage({ + sourceName, + targetName, + callback: CallbackKind.ERROR, + callbackId: data.callbackId, + reason: wrapReason(reason) + }); + }); + return; + } + if (data.streamId) { + this.#createStreamSink(data); + return; + } + action(data.data); + } + on(actionName, handler) { + const ah = this.actionHandler; + if (ah[actionName]) { + throw new Error(`There is already an actionName called "${actionName}"`); + } + ah[actionName] = handler; + } + send(actionName, data, transfers) { + this.comObj.postMessage({ + sourceName: this.sourceName, + targetName: this.targetName, + action: actionName, + data + }, transfers); + } + sendWithPromise(actionName, data, transfers) { + const callbackId = this.callbackId++; + const capability = Promise.withResolvers(); + this.callbackCapabilities[callbackId] = capability; + try { + this.comObj.postMessage({ + sourceName: this.sourceName, + targetName: this.targetName, + action: actionName, + callbackId, + data + }, transfers); + } catch (ex) { + capability.reject(ex); + } + return capability.promise; + } + sendWithStream(actionName, data, queueingStrategy, transfers) { + const streamId = this.streamId++, + sourceName = this.sourceName, + targetName = this.targetName, + comObj = this.comObj; + return new ReadableStream({ + start: controller => { + const startCapability = Promise.withResolvers(); + this.streamControllers[streamId] = { + controller, + startCall: startCapability, + pullCall: null, + cancelCall: null, + isClosed: false + }; + comObj.postMessage({ + sourceName, + targetName, + action: actionName, + streamId, + data, + desiredSize: controller.desiredSize + }, transfers); + return startCapability.promise; + }, + pull: controller => { + const pullCapability = Promise.withResolvers(); + this.streamControllers[streamId].pullCall = pullCapability; + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.PULL, + streamId, + desiredSize: controller.desiredSize + }); + return pullCapability.promise; + }, + cancel: reason => { + assert(reason instanceof Error, "cancel must have a valid reason"); + const cancelCapability = Promise.withResolvers(); + this.streamControllers[streamId].cancelCall = cancelCapability; + this.streamControllers[streamId].isClosed = true; + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.CANCEL, + streamId, + reason: wrapReason(reason) + }); + return cancelCapability.promise; + } + }, queueingStrategy); + } + #createStreamSink(data) { + const streamId = data.streamId, + sourceName = this.sourceName, + targetName = data.sourceName, + comObj = this.comObj; + const self = this, + action = this.actionHandler[data.action]; + const streamSink = { + enqueue(chunk, size = 1, transfers) { + if (this.isCancelled) { + return; + } + const lastDesiredSize = this.desiredSize; + this.desiredSize -= size; + if (lastDesiredSize > 0 && this.desiredSize <= 0) { + this.sinkCapability = Promise.withResolvers(); + this.ready = this.sinkCapability.promise; + } + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.ENQUEUE, + streamId, + chunk + }, transfers); + }, + close() { + if (this.isCancelled) { + return; + } + this.isCancelled = true; + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.CLOSE, + streamId + }); + delete self.streamSinks[streamId]; + }, + error(reason) { + assert(reason instanceof Error, "error must have a valid reason"); + if (this.isCancelled) { + return; + } + this.isCancelled = true; + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.ERROR, + streamId, + reason: wrapReason(reason) + }); + }, + sinkCapability: Promise.withResolvers(), + onPull: null, + onCancel: null, + isCancelled: false, + desiredSize: data.desiredSize, + ready: null + }; + streamSink.sinkCapability.resolve(); + streamSink.ready = streamSink.sinkCapability.promise; + this.streamSinks[streamId] = streamSink; + Promise.try(action, data.data, streamSink).then(function () { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.START_COMPLETE, + streamId, + success: true + }); + }, function (reason) { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.START_COMPLETE, + streamId, + reason: wrapReason(reason) + }); + }); + } + #processStreamMessage(data) { + const streamId = data.streamId, + sourceName = this.sourceName, + targetName = data.sourceName, + comObj = this.comObj; + const streamController = this.streamControllers[streamId], + streamSink = this.streamSinks[streamId]; + switch (data.stream) { + case StreamKind.START_COMPLETE: + if (data.success) { + streamController.startCall.resolve(); + } else { + streamController.startCall.reject(wrapReason(data.reason)); + } + break; + case StreamKind.PULL_COMPLETE: + if (data.success) { + streamController.pullCall.resolve(); + } else { + streamController.pullCall.reject(wrapReason(data.reason)); + } + break; + case StreamKind.PULL: + if (!streamSink) { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.PULL_COMPLETE, + streamId, + success: true + }); + break; + } + if (streamSink.desiredSize <= 0 && data.desiredSize > 0) { + streamSink.sinkCapability.resolve(); + } + streamSink.desiredSize = data.desiredSize; + Promise.try(streamSink.onPull || onFn).then(function () { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.PULL_COMPLETE, + streamId, + success: true + }); + }, function (reason) { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.PULL_COMPLETE, + streamId, + reason: wrapReason(reason) + }); + }); + break; + case StreamKind.ENQUEUE: + assert(streamController, "enqueue should have stream controller"); + if (streamController.isClosed) { + break; + } + streamController.controller.enqueue(data.chunk); + break; + case StreamKind.CLOSE: + assert(streamController, "close should have stream controller"); + if (streamController.isClosed) { + break; + } + streamController.isClosed = true; + streamController.controller.close(); + this.#deleteStreamController(streamController, streamId); + break; + case StreamKind.ERROR: + assert(streamController, "error should have stream controller"); + streamController.controller.error(wrapReason(data.reason)); + this.#deleteStreamController(streamController, streamId); + break; + case StreamKind.CANCEL_COMPLETE: + if (data.success) { + streamController.cancelCall.resolve(); + } else { + streamController.cancelCall.reject(wrapReason(data.reason)); + } + this.#deleteStreamController(streamController, streamId); + break; + case StreamKind.CANCEL: + if (!streamSink) { + break; + } + const dataReason = wrapReason(data.reason); + Promise.try(streamSink.onCancel || onFn, dataReason).then(function () { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.CANCEL_COMPLETE, + streamId, + success: true + }); + }, function (reason) { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.CANCEL_COMPLETE, + streamId, + reason: wrapReason(reason) + }); + }); + streamSink.sinkCapability.reject(dataReason); + streamSink.isCancelled = true; + delete this.streamSinks[streamId]; + break; + default: + throw new Error("Unexpected stream case"); + } + } + async #deleteStreamController(streamController, streamId) { + await Promise.allSettled([streamController.startCall?.promise, streamController.pullCall?.promise, streamController.cancelCall?.promise]); + delete this.streamControllers[streamId]; + } + destroy() { + this.#messageAC?.abort(); + this.#messageAC = null; + } +} + +;// ./src/display/canvas_factory.js + +class BaseCanvasFactory { + #enableHWA = false; + constructor({ + enableHWA = false + }) { + this.#enableHWA = enableHWA; + } + create(width, height) { + if (width <= 0 || height <= 0) { + throw new Error("Invalid canvas size"); + } + const canvas = this._createCanvas(width, height); + return { + canvas, + context: canvas.getContext("2d", { + willReadFrequently: !this.#enableHWA + }) + }; + } + reset(canvasAndContext, width, height) { + if (!canvasAndContext.canvas) { + throw new Error("Canvas is not specified"); + } + if (width <= 0 || height <= 0) { + throw new Error("Invalid canvas size"); + } + canvasAndContext.canvas.width = width; + canvasAndContext.canvas.height = height; + } + destroy(canvasAndContext) { + if (!canvasAndContext.canvas) { + throw new Error("Canvas is not specified"); + } + canvasAndContext.canvas.width = 0; + canvasAndContext.canvas.height = 0; + canvasAndContext.canvas = null; + canvasAndContext.context = null; + } + _createCanvas(width, height) { + unreachable("Abstract method `_createCanvas` called."); + } +} +class DOMCanvasFactory extends BaseCanvasFactory { + constructor({ + ownerDocument = globalThis.document, + enableHWA = false + }) { + super({ + enableHWA + }); + this._document = ownerDocument; + } + _createCanvas(width, height) { + const canvas = this._document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + return canvas; + } +} + +;// ./src/display/cmap_reader_factory.js + + +class BaseCMapReaderFactory { + constructor({ + baseUrl = null, + isCompressed = true + }) { + this.baseUrl = baseUrl; + this.isCompressed = isCompressed; + } + async fetch({ + name + }) { + if (!this.baseUrl) { + throw new Error("Ensure that the `cMapUrl` and `cMapPacked` API parameters are provided."); + } + if (!name) { + throw new Error("CMap name must be specified."); + } + const url = this.baseUrl + name + (this.isCompressed ? ".bcmap" : ""); + return this._fetch(url).then(cMapData => ({ + cMapData, + isCompressed: this.isCompressed + })).catch(reason => { + throw new Error(`Unable to load ${this.isCompressed ? "binary " : ""}CMap at: ${url}`); + }); + } + async _fetch(url) { + unreachable("Abstract method `_fetch` called."); + } +} +class DOMCMapReaderFactory extends BaseCMapReaderFactory { + async _fetch(url) { + const data = await fetchData(url, this.isCompressed ? "arraybuffer" : "text"); + return data instanceof ArrayBuffer ? new Uint8Array(data) : stringToBytes(data); + } +} + +;// ./src/display/filter_factory.js + + +class BaseFilterFactory { + addFilter(maps) { + return "none"; + } + addHCMFilter(fgColor, bgColor) { + return "none"; + } + addAlphaFilter(map) { + return "none"; + } + addLuminosityFilter(map) { + return "none"; + } + addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) { + return "none"; + } + destroy(keepHCM = false) {} +} +class DOMFilterFactory extends BaseFilterFactory { + #baseUrl; + #_cache; + #_defs; + #docId; + #document; + #_hcmCache; + #id = 0; + constructor({ + docId, + ownerDocument = globalThis.document + }) { + super(); + this.#docId = docId; + this.#document = ownerDocument; + } + get #cache() { + return this.#_cache ||= new Map(); + } + get #hcmCache() { + return this.#_hcmCache ||= new Map(); + } + get #defs() { + if (!this.#_defs) { + const div = this.#document.createElement("div"); + const { + style + } = div; + style.visibility = "hidden"; + style.contain = "strict"; + style.width = style.height = 0; + style.position = "absolute"; + style.top = style.left = 0; + style.zIndex = -1; + const svg = this.#document.createElementNS(SVG_NS, "svg"); + svg.setAttribute("width", 0); + svg.setAttribute("height", 0); + this.#_defs = this.#document.createElementNS(SVG_NS, "defs"); + div.append(svg); + svg.append(this.#_defs); + this.#document.body.append(div); + } + return this.#_defs; + } + #createTables(maps) { + if (maps.length === 1) { + const mapR = maps[0]; + const buffer = new Array(256); + for (let i = 0; i < 256; i++) { + buffer[i] = mapR[i] / 255; + } + const table = buffer.join(","); + return [table, table, table]; + } + const [mapR, mapG, mapB] = maps; + const bufferR = new Array(256); + const bufferG = new Array(256); + const bufferB = new Array(256); + for (let i = 0; i < 256; i++) { + bufferR[i] = mapR[i] / 255; + bufferG[i] = mapG[i] / 255; + bufferB[i] = mapB[i] / 255; + } + return [bufferR.join(","), bufferG.join(","), bufferB.join(",")]; + } + #createUrl(id) { + if (this.#baseUrl === undefined) { + this.#baseUrl = ""; + const url = this.#document.URL; + if (url !== this.#document.baseURI) { + if (isDataScheme(url)) { + warn('#createUrl: ignore "data:"-URL for performance reasons.'); + } else { + this.#baseUrl = url.split("#", 1)[0]; + } + } + } + return `url(${this.#baseUrl}#${id})`; + } + addFilter(maps) { + if (!maps) { + return "none"; + } + let value = this.#cache.get(maps); + if (value) { + return value; + } + const [tableR, tableG, tableB] = this.#createTables(maps); + const key = maps.length === 1 ? tableR : `${tableR}${tableG}${tableB}`; + value = this.#cache.get(key); + if (value) { + this.#cache.set(maps, value); + return value; + } + const id = `g_${this.#docId}_transfer_map_${this.#id++}`; + const url = this.#createUrl(id); + this.#cache.set(maps, url); + this.#cache.set(key, url); + const filter = this.#createFilter(id); + this.#addTransferMapConversion(tableR, tableG, tableB, filter); + return url; + } + addHCMFilter(fgColor, bgColor) { + const key = `${fgColor}-${bgColor}`; + const filterName = "base"; + let info = this.#hcmCache.get(filterName); + if (info?.key === key) { + return info.url; + } + if (info) { + info.filter?.remove(); + info.key = key; + info.url = "none"; + info.filter = null; + } else { + info = { + key, + url: "none", + filter: null + }; + this.#hcmCache.set(filterName, info); + } + if (!fgColor || !bgColor) { + return info.url; + } + const fgRGB = this.#getRGB(fgColor); + fgColor = Util.makeHexColor(...fgRGB); + const bgRGB = this.#getRGB(bgColor); + bgColor = Util.makeHexColor(...bgRGB); + this.#defs.style.color = ""; + if (fgColor === "#000000" && bgColor === "#ffffff" || fgColor === bgColor) { + return info.url; + } + const map = new Array(256); + for (let i = 0; i <= 255; i++) { + const x = i / 255; + map[i] = x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4; + } + const table = map.join(","); + const id = `g_${this.#docId}_hcm_filter`; + const filter = info.filter = this.#createFilter(id); + this.#addTransferMapConversion(table, table, table, filter); + this.#addGrayConversion(filter); + const getSteps = (c, n) => { + const start = fgRGB[c] / 255; + const end = bgRGB[c] / 255; + const arr = new Array(n + 1); + for (let i = 0; i <= n; i++) { + arr[i] = start + i / n * (end - start); + } + return arr.join(","); + }; + this.#addTransferMapConversion(getSteps(0, 5), getSteps(1, 5), getSteps(2, 5), filter); + info.url = this.#createUrl(id); + return info.url; + } + addAlphaFilter(map) { + let value = this.#cache.get(map); + if (value) { + return value; + } + const [tableA] = this.#createTables([map]); + const key = `alpha_${tableA}`; + value = this.#cache.get(key); + if (value) { + this.#cache.set(map, value); + return value; + } + const id = `g_${this.#docId}_alpha_map_${this.#id++}`; + const url = this.#createUrl(id); + this.#cache.set(map, url); + this.#cache.set(key, url); + const filter = this.#createFilter(id); + this.#addTransferMapAlphaConversion(tableA, filter); + return url; + } + addLuminosityFilter(map) { + let value = this.#cache.get(map || "luminosity"); + if (value) { + return value; + } + let tableA, key; + if (map) { + [tableA] = this.#createTables([map]); + key = `luminosity_${tableA}`; + } else { + key = "luminosity"; + } + value = this.#cache.get(key); + if (value) { + this.#cache.set(map, value); + return value; + } + const id = `g_${this.#docId}_luminosity_map_${this.#id++}`; + const url = this.#createUrl(id); + this.#cache.set(map, url); + this.#cache.set(key, url); + const filter = this.#createFilter(id); + this.#addLuminosityConversion(filter); + if (map) { + this.#addTransferMapAlphaConversion(tableA, filter); + } + return url; + } + addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) { + const key = `${fgColor}-${bgColor}-${newFgColor}-${newBgColor}`; + let info = this.#hcmCache.get(filterName); + if (info?.key === key) { + return info.url; + } + if (info) { + info.filter?.remove(); + info.key = key; + info.url = "none"; + info.filter = null; + } else { + info = { + key, + url: "none", + filter: null + }; + this.#hcmCache.set(filterName, info); + } + if (!fgColor || !bgColor) { + return info.url; + } + const [fgRGB, bgRGB] = [fgColor, bgColor].map(this.#getRGB.bind(this)); + let fgGray = Math.round(0.2126 * fgRGB[0] + 0.7152 * fgRGB[1] + 0.0722 * fgRGB[2]); + let bgGray = Math.round(0.2126 * bgRGB[0] + 0.7152 * bgRGB[1] + 0.0722 * bgRGB[2]); + let [newFgRGB, newBgRGB] = [newFgColor, newBgColor].map(this.#getRGB.bind(this)); + if (bgGray < fgGray) { + [fgGray, bgGray, newFgRGB, newBgRGB] = [bgGray, fgGray, newBgRGB, newFgRGB]; + } + this.#defs.style.color = ""; + const getSteps = (fg, bg, n) => { + const arr = new Array(256); + const step = (bgGray - fgGray) / n; + const newStart = fg / 255; + const newStep = (bg - fg) / (255 * n); + let prev = 0; + for (let i = 0; i <= n; i++) { + const k = Math.round(fgGray + i * step); + const value = newStart + i * newStep; + for (let j = prev; j <= k; j++) { + arr[j] = value; + } + prev = k + 1; + } + for (let i = prev; i < 256; i++) { + arr[i] = arr[prev - 1]; + } + return arr.join(","); + }; + const id = `g_${this.#docId}_hcm_${filterName}_filter`; + const filter = info.filter = this.#createFilter(id); + this.#addGrayConversion(filter); + this.#addTransferMapConversion(getSteps(newFgRGB[0], newBgRGB[0], 5), getSteps(newFgRGB[1], newBgRGB[1], 5), getSteps(newFgRGB[2], newBgRGB[2], 5), filter); + info.url = this.#createUrl(id); + return info.url; + } + destroy(keepHCM = false) { + if (keepHCM && this.#_hcmCache?.size) { + return; + } + this.#_defs?.parentNode.parentNode.remove(); + this.#_defs = null; + this.#_cache?.clear(); + this.#_cache = null; + this.#_hcmCache?.clear(); + this.#_hcmCache = null; + this.#id = 0; + } + #addLuminosityConversion(filter) { + const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix"); + feColorMatrix.setAttribute("type", "matrix"); + feColorMatrix.setAttribute("values", "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.59 0.11 0 0"); + filter.append(feColorMatrix); + } + #addGrayConversion(filter) { + const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix"); + feColorMatrix.setAttribute("type", "matrix"); + feColorMatrix.setAttribute("values", "0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"); + filter.append(feColorMatrix); + } + #createFilter(id) { + const filter = this.#document.createElementNS(SVG_NS, "filter"); + filter.setAttribute("color-interpolation-filters", "sRGB"); + filter.setAttribute("id", id); + this.#defs.append(filter); + return filter; + } + #appendFeFunc(feComponentTransfer, func, table) { + const feFunc = this.#document.createElementNS(SVG_NS, func); + feFunc.setAttribute("type", "discrete"); + feFunc.setAttribute("tableValues", table); + feComponentTransfer.append(feFunc); + } + #addTransferMapConversion(rTable, gTable, bTable, filter) { + const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer"); + filter.append(feComponentTransfer); + this.#appendFeFunc(feComponentTransfer, "feFuncR", rTable); + this.#appendFeFunc(feComponentTransfer, "feFuncG", gTable); + this.#appendFeFunc(feComponentTransfer, "feFuncB", bTable); + } + #addTransferMapAlphaConversion(aTable, filter) { + const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer"); + filter.append(feComponentTransfer); + this.#appendFeFunc(feComponentTransfer, "feFuncA", aTable); + } + #getRGB(color) { + this.#defs.style.color = color; + return getRGB(getComputedStyle(this.#defs).getPropertyValue("color")); + } +} + +;// ./src/display/standard_fontdata_factory.js + + +class BaseStandardFontDataFactory { + constructor({ + baseUrl = null + }) { + this.baseUrl = baseUrl; + } + async fetch({ + filename + }) { + if (!this.baseUrl) { + throw new Error("Ensure that the `standardFontDataUrl` API parameter is provided."); + } + if (!filename) { + throw new Error("Font filename must be specified."); + } + const url = `${this.baseUrl}${filename}`; + return this._fetch(url).catch(reason => { + throw new Error(`Unable to load font data at: ${url}`); + }); + } + async _fetch(url) { + unreachable("Abstract method `_fetch` called."); + } +} +class DOMStandardFontDataFactory extends BaseStandardFontDataFactory { + async _fetch(url) { + const data = await fetchData(url, "arraybuffer"); + return new Uint8Array(data); + } +} + +;// ./src/display/node_utils.js + + + + + +if (isNodeJS) { + warn("Please use the `legacy` build in Node.js environments."); +} +async function node_utils_fetchData(url) { + const fs = process.getBuiltinModule("fs"); + const data = await fs.promises.readFile(url); + return new Uint8Array(data); +} +class NodeFilterFactory extends BaseFilterFactory {} +class NodeCanvasFactory extends BaseCanvasFactory { + _createCanvas(width, height) { + const require = process.getBuiltinModule("module").createRequire(import.meta.url); + const canvas = require("@napi-rs/canvas"); + return canvas.createCanvas(width, height); + } +} +class NodeCMapReaderFactory extends BaseCMapReaderFactory { + async _fetch(url) { + return node_utils_fetchData(url); + } +} +class NodeStandardFontDataFactory extends BaseStandardFontDataFactory { + async _fetch(url) { + return node_utils_fetchData(url); + } +} + +;// ./src/display/pattern_helper.js + + +const PathType = { + FILL: "Fill", + STROKE: "Stroke", + SHADING: "Shading" +}; +function applyBoundingBox(ctx, bbox) { + if (!bbox) { + return; + } + const width = bbox[2] - bbox[0]; + const height = bbox[3] - bbox[1]; + const region = new Path2D(); + region.rect(bbox[0], bbox[1], width, height); + ctx.clip(region); +} +class BaseShadingPattern { + getPattern() { + unreachable("Abstract method `getPattern` called."); + } +} +class RadialAxialShadingPattern extends BaseShadingPattern { + constructor(IR) { + super(); + this._type = IR[1]; + this._bbox = IR[2]; + this._colorStops = IR[3]; + this._p0 = IR[4]; + this._p1 = IR[5]; + this._r0 = IR[6]; + this._r1 = IR[7]; + this.matrix = null; + } + _createGradient(ctx) { + let grad; + if (this._type === "axial") { + grad = ctx.createLinearGradient(this._p0[0], this._p0[1], this._p1[0], this._p1[1]); + } else if (this._type === "radial") { + grad = ctx.createRadialGradient(this._p0[0], this._p0[1], this._r0, this._p1[0], this._p1[1], this._r1); + } + for (const colorStop of this._colorStops) { + grad.addColorStop(colorStop[0], colorStop[1]); + } + return grad; + } + getPattern(ctx, owner, inverse, pathType) { + let pattern; + if (pathType === PathType.STROKE || pathType === PathType.FILL) { + const ownerBBox = owner.current.getClippedPathBoundingBox(pathType, getCurrentTransform(ctx)) || [0, 0, 0, 0]; + const width = Math.ceil(ownerBBox[2] - ownerBBox[0]) || 1; + const height = Math.ceil(ownerBBox[3] - ownerBBox[1]) || 1; + const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", width, height); + const tmpCtx = tmpCanvas.context; + tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height); + tmpCtx.beginPath(); + tmpCtx.rect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height); + tmpCtx.translate(-ownerBBox[0], -ownerBBox[1]); + inverse = Util.transform(inverse, [1, 0, 0, 1, ownerBBox[0], ownerBBox[1]]); + tmpCtx.transform(...owner.baseTransform); + if (this.matrix) { + tmpCtx.transform(...this.matrix); + } + applyBoundingBox(tmpCtx, this._bbox); + tmpCtx.fillStyle = this._createGradient(tmpCtx); + tmpCtx.fill(); + pattern = ctx.createPattern(tmpCanvas.canvas, "no-repeat"); + const domMatrix = new DOMMatrix(inverse); + pattern.setTransform(domMatrix); + } else { + applyBoundingBox(ctx, this._bbox); + pattern = this._createGradient(ctx); + } + return pattern; + } +} +function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) { + const coords = context.coords, + colors = context.colors; + const bytes = data.data, + rowSize = data.width * 4; + let tmp; + if (coords[p1 + 1] > coords[p2 + 1]) { + tmp = p1; + p1 = p2; + p2 = tmp; + tmp = c1; + c1 = c2; + c2 = tmp; + } + if (coords[p2 + 1] > coords[p3 + 1]) { + tmp = p2; + p2 = p3; + p3 = tmp; + tmp = c2; + c2 = c3; + c3 = tmp; + } + if (coords[p1 + 1] > coords[p2 + 1]) { + tmp = p1; + p1 = p2; + p2 = tmp; + tmp = c1; + c1 = c2; + c2 = tmp; + } + const x1 = (coords[p1] + context.offsetX) * context.scaleX; + const y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY; + const x2 = (coords[p2] + context.offsetX) * context.scaleX; + const y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY; + const x3 = (coords[p3] + context.offsetX) * context.scaleX; + const y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY; + if (y1 >= y3) { + return; + } + const c1r = colors[c1], + c1g = colors[c1 + 1], + c1b = colors[c1 + 2]; + const c2r = colors[c2], + c2g = colors[c2 + 1], + c2b = colors[c2 + 2]; + const c3r = colors[c3], + c3g = colors[c3 + 1], + c3b = colors[c3 + 2]; + const minY = Math.round(y1), + maxY = Math.round(y3); + let xa, car, cag, cab; + let xb, cbr, cbg, cbb; + for (let y = minY; y <= maxY; y++) { + if (y < y2) { + const k = y < y1 ? 0 : (y1 - y) / (y1 - y2); + xa = x1 - (x1 - x2) * k; + car = c1r - (c1r - c2r) * k; + cag = c1g - (c1g - c2g) * k; + cab = c1b - (c1b - c2b) * k; + } else { + let k; + if (y > y3) { + k = 1; + } else if (y2 === y3) { + k = 0; + } else { + k = (y2 - y) / (y2 - y3); + } + xa = x2 - (x2 - x3) * k; + car = c2r - (c2r - c3r) * k; + cag = c2g - (c2g - c3g) * k; + cab = c2b - (c2b - c3b) * k; + } + let k; + if (y < y1) { + k = 0; + } else if (y > y3) { + k = 1; + } else { + k = (y1 - y) / (y1 - y3); + } + xb = x1 - (x1 - x3) * k; + cbr = c1r - (c1r - c3r) * k; + cbg = c1g - (c1g - c3g) * k; + cbb = c1b - (c1b - c3b) * k; + const x1_ = Math.round(Math.min(xa, xb)); + const x2_ = Math.round(Math.max(xa, xb)); + let j = rowSize * y + x1_ * 4; + for (let x = x1_; x <= x2_; x++) { + k = (xa - x) / (xa - xb); + if (k < 0) { + k = 0; + } else if (k > 1) { + k = 1; + } + bytes[j++] = car - (car - cbr) * k | 0; + bytes[j++] = cag - (cag - cbg) * k | 0; + bytes[j++] = cab - (cab - cbb) * k | 0; + bytes[j++] = 255; + } + } +} +function drawFigure(data, figure, context) { + const ps = figure.coords; + const cs = figure.colors; + let i, ii; + switch (figure.type) { + case "lattice": + const verticesPerRow = figure.verticesPerRow; + const rows = Math.floor(ps.length / verticesPerRow) - 1; + const cols = verticesPerRow - 1; + for (i = 0; i < rows; i++) { + let q = i * verticesPerRow; + for (let j = 0; j < cols; j++, q++) { + drawTriangle(data, context, ps[q], ps[q + 1], ps[q + verticesPerRow], cs[q], cs[q + 1], cs[q + verticesPerRow]); + drawTriangle(data, context, ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow], cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]); + } + } + break; + case "triangles": + for (i = 0, ii = ps.length; i < ii; i += 3) { + drawTriangle(data, context, ps[i], ps[i + 1], ps[i + 2], cs[i], cs[i + 1], cs[i + 2]); + } + break; + default: + throw new Error("illegal figure"); + } +} +class MeshShadingPattern extends BaseShadingPattern { + constructor(IR) { + super(); + this._coords = IR[2]; + this._colors = IR[3]; + this._figures = IR[4]; + this._bounds = IR[5]; + this._bbox = IR[7]; + this._background = IR[8]; + this.matrix = null; + } + _createMeshCanvas(combinedScale, backgroundColor, cachedCanvases) { + const EXPECTED_SCALE = 1.1; + const MAX_PATTERN_SIZE = 3000; + const BORDER_SIZE = 2; + const offsetX = Math.floor(this._bounds[0]); + const offsetY = Math.floor(this._bounds[1]); + const boundsWidth = Math.ceil(this._bounds[2]) - offsetX; + const boundsHeight = Math.ceil(this._bounds[3]) - offsetY; + const width = Math.min(Math.ceil(Math.abs(boundsWidth * combinedScale[0] * EXPECTED_SCALE)), MAX_PATTERN_SIZE); + const height = Math.min(Math.ceil(Math.abs(boundsHeight * combinedScale[1] * EXPECTED_SCALE)), MAX_PATTERN_SIZE); + const scaleX = boundsWidth / width; + const scaleY = boundsHeight / height; + const context = { + coords: this._coords, + colors: this._colors, + offsetX: -offsetX, + offsetY: -offsetY, + scaleX: 1 / scaleX, + scaleY: 1 / scaleY + }; + const paddedWidth = width + BORDER_SIZE * 2; + const paddedHeight = height + BORDER_SIZE * 2; + const tmpCanvas = cachedCanvases.getCanvas("mesh", paddedWidth, paddedHeight); + const tmpCtx = tmpCanvas.context; + const data = tmpCtx.createImageData(width, height); + if (backgroundColor) { + const bytes = data.data; + for (let i = 0, ii = bytes.length; i < ii; i += 4) { + bytes[i] = backgroundColor[0]; + bytes[i + 1] = backgroundColor[1]; + bytes[i + 2] = backgroundColor[2]; + bytes[i + 3] = 255; + } + } + for (const figure of this._figures) { + drawFigure(data, figure, context); + } + tmpCtx.putImageData(data, BORDER_SIZE, BORDER_SIZE); + const canvas = tmpCanvas.canvas; + return { + canvas, + offsetX: offsetX - BORDER_SIZE * scaleX, + offsetY: offsetY - BORDER_SIZE * scaleY, + scaleX, + scaleY + }; + } + getPattern(ctx, owner, inverse, pathType) { + applyBoundingBox(ctx, this._bbox); + let scale; + if (pathType === PathType.SHADING) { + scale = Util.singularValueDecompose2dScale(getCurrentTransform(ctx)); + } else { + scale = Util.singularValueDecompose2dScale(owner.baseTransform); + if (this.matrix) { + const matrixScale = Util.singularValueDecompose2dScale(this.matrix); + scale = [scale[0] * matrixScale[0], scale[1] * matrixScale[1]]; + } + } + const temporaryPatternCanvas = this._createMeshCanvas(scale, pathType === PathType.SHADING ? null : this._background, owner.cachedCanvases); + if (pathType !== PathType.SHADING) { + ctx.setTransform(...owner.baseTransform); + if (this.matrix) { + ctx.transform(...this.matrix); + } + } + ctx.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY); + ctx.scale(temporaryPatternCanvas.scaleX, temporaryPatternCanvas.scaleY); + return ctx.createPattern(temporaryPatternCanvas.canvas, "no-repeat"); + } +} +class DummyShadingPattern extends BaseShadingPattern { + getPattern() { + return "hotpink"; + } +} +function getShadingPattern(IR) { + switch (IR[0]) { + case "RadialAxial": + return new RadialAxialShadingPattern(IR); + case "Mesh": + return new MeshShadingPattern(IR); + case "Dummy": + return new DummyShadingPattern(); + } + throw new Error(`Unknown IR type: ${IR[0]}`); +} +const PaintType = { + COLORED: 1, + UNCOLORED: 2 +}; +class TilingPattern { + static MAX_PATTERN_SIZE = 3000; + constructor(IR, color, ctx, canvasGraphicsFactory, baseTransform) { + this.operatorList = IR[2]; + this.matrix = IR[3]; + this.bbox = IR[4]; + this.xstep = IR[5]; + this.ystep = IR[6]; + this.paintType = IR[7]; + this.tilingType = IR[8]; + this.color = color; + this.ctx = ctx; + this.canvasGraphicsFactory = canvasGraphicsFactory; + this.baseTransform = baseTransform; + } + createPatternCanvas(owner) { + const { + bbox, + operatorList, + paintType, + tilingType, + color, + canvasGraphicsFactory + } = this; + let { + xstep, + ystep + } = this; + xstep = Math.abs(xstep); + ystep = Math.abs(ystep); + info("TilingType: " + tilingType); + const x0 = bbox[0], + y0 = bbox[1], + x1 = bbox[2], + y1 = bbox[3]; + const width = x1 - x0; + const height = y1 - y0; + const matrixScale = Util.singularValueDecompose2dScale(this.matrix); + const curMatrixScale = Util.singularValueDecompose2dScale(this.baseTransform); + const combinedScaleX = matrixScale[0] * curMatrixScale[0]; + const combinedScaleY = matrixScale[1] * curMatrixScale[1]; + let canvasWidth = width, + canvasHeight = height, + redrawHorizontally = false, + redrawVertically = false; + const xScaledStep = Math.ceil(xstep * combinedScaleX); + const yScaledStep = Math.ceil(ystep * combinedScaleY); + const xScaledWidth = Math.ceil(width * combinedScaleX); + const yScaledHeight = Math.ceil(height * combinedScaleY); + if (xScaledStep >= xScaledWidth) { + canvasWidth = xstep; + } else { + redrawHorizontally = true; + } + if (yScaledStep >= yScaledHeight) { + canvasHeight = ystep; + } else { + redrawVertically = true; + } + const dimx = this.getSizeAndScale(canvasWidth, this.ctx.canvas.width, combinedScaleX); + const dimy = this.getSizeAndScale(canvasHeight, this.ctx.canvas.height, combinedScaleY); + const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", dimx.size, dimy.size); + const tmpCtx = tmpCanvas.context; + const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx); + graphics.groupLevel = owner.groupLevel; + this.setFillAndStrokeStyleToContext(graphics, paintType, color); + tmpCtx.translate(-dimx.scale * x0, -dimy.scale * y0); + graphics.transform(dimx.scale, 0, 0, dimy.scale, 0, 0); + tmpCtx.save(); + this.clipBbox(graphics, x0, y0, x1, y1); + graphics.baseTransform = getCurrentTransform(graphics.ctx); + graphics.executeOperatorList(operatorList); + graphics.endDrawing(); + tmpCtx.restore(); + if (redrawHorizontally || redrawVertically) { + const image = tmpCanvas.canvas; + if (redrawHorizontally) { + canvasWidth = xstep; + } + if (redrawVertically) { + canvasHeight = ystep; + } + const dimx2 = this.getSizeAndScale(canvasWidth, this.ctx.canvas.width, combinedScaleX); + const dimy2 = this.getSizeAndScale(canvasHeight, this.ctx.canvas.height, combinedScaleY); + const xSize = dimx2.size; + const ySize = dimy2.size; + const tmpCanvas2 = owner.cachedCanvases.getCanvas("pattern-workaround", xSize, ySize); + const tmpCtx2 = tmpCanvas2.context; + const ii = redrawHorizontally ? Math.floor(width / xstep) : 0; + const jj = redrawVertically ? Math.floor(height / ystep) : 0; + for (let i = 0; i <= ii; i++) { + for (let j = 0; j <= jj; j++) { + tmpCtx2.drawImage(image, xSize * i, ySize * j, xSize, ySize, 0, 0, xSize, ySize); + } + } + return { + canvas: tmpCanvas2.canvas, + scaleX: dimx2.scale, + scaleY: dimy2.scale, + offsetX: x0, + offsetY: y0 + }; + } + return { + canvas: tmpCanvas.canvas, + scaleX: dimx.scale, + scaleY: dimy.scale, + offsetX: x0, + offsetY: y0 + }; + } + getSizeAndScale(step, realOutputSize, scale) { + const maxSize = Math.max(TilingPattern.MAX_PATTERN_SIZE, realOutputSize); + let size = Math.ceil(step * scale); + if (size >= maxSize) { + size = maxSize; + } else { + scale = size / step; + } + return { + scale, + size + }; + } + clipBbox(graphics, x0, y0, x1, y1) { + const bboxWidth = x1 - x0; + const bboxHeight = y1 - y0; + graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight); + graphics.current.updateRectMinMax(getCurrentTransform(graphics.ctx), [x0, y0, x1, y1]); + graphics.clip(); + graphics.endPath(); + } + setFillAndStrokeStyleToContext(graphics, paintType, color) { + const context = graphics.ctx, + current = graphics.current; + switch (paintType) { + case PaintType.COLORED: + const ctx = this.ctx; + context.fillStyle = ctx.fillStyle; + context.strokeStyle = ctx.strokeStyle; + current.fillColor = ctx.fillStyle; + current.strokeColor = ctx.strokeStyle; + break; + case PaintType.UNCOLORED: + const cssColor = Util.makeHexColor(color[0], color[1], color[2]); + context.fillStyle = cssColor; + context.strokeStyle = cssColor; + current.fillColor = cssColor; + current.strokeColor = cssColor; + break; + default: + throw new FormatError(`Unsupported paint type: ${paintType}`); + } + } + getPattern(ctx, owner, inverse, pathType) { + let matrix = inverse; + if (pathType !== PathType.SHADING) { + matrix = Util.transform(matrix, owner.baseTransform); + if (this.matrix) { + matrix = Util.transform(matrix, this.matrix); + } + } + const temporaryPatternCanvas = this.createPatternCanvas(owner); + let domMatrix = new DOMMatrix(matrix); + domMatrix = domMatrix.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY); + domMatrix = domMatrix.scale(1 / temporaryPatternCanvas.scaleX, 1 / temporaryPatternCanvas.scaleY); + const pattern = ctx.createPattern(temporaryPatternCanvas.canvas, "repeat"); + pattern.setTransform(domMatrix); + return pattern; + } +} + +;// ./src/shared/image_utils.js + +function convertToRGBA(params) { + switch (params.kind) { + case ImageKind.GRAYSCALE_1BPP: + return convertBlackAndWhiteToRGBA(params); + case ImageKind.RGB_24BPP: + return convertRGBToRGBA(params); + } + return null; +} +function convertBlackAndWhiteToRGBA({ + src, + srcPos = 0, + dest, + width, + height, + nonBlackColor = 0xffffffff, + inverseDecode = false +}) { + const black = util_FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff; + const [zeroMapping, oneMapping] = inverseDecode ? [nonBlackColor, black] : [black, nonBlackColor]; + const widthInSource = width >> 3; + const widthRemainder = width & 7; + const srcLength = src.length; + dest = new Uint32Array(dest.buffer); + let destPos = 0; + for (let i = 0; i < height; i++) { + for (const max = srcPos + widthInSource; srcPos < max; srcPos++) { + const elem = srcPos < srcLength ? src[srcPos] : 255; + dest[destPos++] = elem & 0b10000000 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b1000000 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b100000 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b10000 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b1000 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b100 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b10 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b1 ? oneMapping : zeroMapping; + } + if (widthRemainder === 0) { + continue; + } + const elem = srcPos < srcLength ? src[srcPos++] : 255; + for (let j = 0; j < widthRemainder; j++) { + dest[destPos++] = elem & 1 << 7 - j ? oneMapping : zeroMapping; + } + } + return { + srcPos, + destPos + }; +} +function convertRGBToRGBA({ + src, + srcPos = 0, + dest, + destPos = 0, + width, + height +}) { + let i = 0; + const len = width * height * 3; + const len32 = len >> 2; + const src32 = new Uint32Array(src.buffer, srcPos, len32); + if (FeatureTest.isLittleEndian) { + for (; i < len32 - 2; i += 3, destPos += 4) { + const s1 = src32[i]; + const s2 = src32[i + 1]; + const s3 = src32[i + 2]; + dest[destPos] = s1 | 0xff000000; + dest[destPos + 1] = s1 >>> 24 | s2 << 8 | 0xff000000; + dest[destPos + 2] = s2 >>> 16 | s3 << 16 | 0xff000000; + dest[destPos + 3] = s3 >>> 8 | 0xff000000; + } + for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) { + dest[destPos++] = src[j] | src[j + 1] << 8 | src[j + 2] << 16 | 0xff000000; + } + } else { + for (; i < len32 - 2; i += 3, destPos += 4) { + const s1 = src32[i]; + const s2 = src32[i + 1]; + const s3 = src32[i + 2]; + dest[destPos] = s1 | 0xff; + dest[destPos + 1] = s1 << 24 | s2 >>> 8 | 0xff; + dest[destPos + 2] = s2 << 16 | s3 >>> 16 | 0xff; + dest[destPos + 3] = s3 << 8 | 0xff; + } + for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) { + dest[destPos++] = src[j] << 24 | src[j + 1] << 16 | src[j + 2] << 8 | 0xff; + } + } + return { + srcPos: srcPos + len, + destPos + }; +} +function grayToRGBA(src, dest) { + if (FeatureTest.isLittleEndian) { + for (let i = 0, ii = src.length; i < ii; i++) { + dest[i] = src[i] * 0x10101 | 0xff000000; + } + } else { + for (let i = 0, ii = src.length; i < ii; i++) { + dest[i] = src[i] * 0x1010100 | 0x000000ff; + } + } +} + +;// ./src/display/canvas.js + + + + +const MIN_FONT_SIZE = 16; +const MAX_FONT_SIZE = 100; +const EXECUTION_TIME = 15; +const EXECUTION_STEPS = 10; +const MAX_SIZE_TO_COMPILE = 1000; +const FULL_CHUNK_HEIGHT = 16; +function mirrorContextOperations(ctx, destCtx) { + if (ctx._removeMirroring) { + throw new Error("Context is already forwarding operations."); + } + ctx.__originalSave = ctx.save; + ctx.__originalRestore = ctx.restore; + ctx.__originalRotate = ctx.rotate; + ctx.__originalScale = ctx.scale; + ctx.__originalTranslate = ctx.translate; + ctx.__originalTransform = ctx.transform; + ctx.__originalSetTransform = ctx.setTransform; + ctx.__originalResetTransform = ctx.resetTransform; + ctx.__originalClip = ctx.clip; + ctx.__originalMoveTo = ctx.moveTo; + ctx.__originalLineTo = ctx.lineTo; + ctx.__originalBezierCurveTo = ctx.bezierCurveTo; + ctx.__originalRect = ctx.rect; + ctx.__originalClosePath = ctx.closePath; + ctx.__originalBeginPath = ctx.beginPath; + ctx._removeMirroring = () => { + ctx.save = ctx.__originalSave; + ctx.restore = ctx.__originalRestore; + ctx.rotate = ctx.__originalRotate; + ctx.scale = ctx.__originalScale; + ctx.translate = ctx.__originalTranslate; + ctx.transform = ctx.__originalTransform; + ctx.setTransform = ctx.__originalSetTransform; + ctx.resetTransform = ctx.__originalResetTransform; + ctx.clip = ctx.__originalClip; + ctx.moveTo = ctx.__originalMoveTo; + ctx.lineTo = ctx.__originalLineTo; + ctx.bezierCurveTo = ctx.__originalBezierCurveTo; + ctx.rect = ctx.__originalRect; + ctx.closePath = ctx.__originalClosePath; + ctx.beginPath = ctx.__originalBeginPath; + delete ctx._removeMirroring; + }; + ctx.save = function ctxSave() { + destCtx.save(); + this.__originalSave(); + }; + ctx.restore = function ctxRestore() { + destCtx.restore(); + this.__originalRestore(); + }; + ctx.translate = function ctxTranslate(x, y) { + destCtx.translate(x, y); + this.__originalTranslate(x, y); + }; + ctx.scale = function ctxScale(x, y) { + destCtx.scale(x, y); + this.__originalScale(x, y); + }; + ctx.transform = function ctxTransform(a, b, c, d, e, f) { + destCtx.transform(a, b, c, d, e, f); + this.__originalTransform(a, b, c, d, e, f); + }; + ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) { + destCtx.setTransform(a, b, c, d, e, f); + this.__originalSetTransform(a, b, c, d, e, f); + }; + ctx.resetTransform = function ctxResetTransform() { + destCtx.resetTransform(); + this.__originalResetTransform(); + }; + ctx.rotate = function ctxRotate(angle) { + destCtx.rotate(angle); + this.__originalRotate(angle); + }; + ctx.clip = function ctxRotate(rule) { + destCtx.clip(rule); + this.__originalClip(rule); + }; + ctx.moveTo = function (x, y) { + destCtx.moveTo(x, y); + this.__originalMoveTo(x, y); + }; + ctx.lineTo = function (x, y) { + destCtx.lineTo(x, y); + this.__originalLineTo(x, y); + }; + ctx.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) { + destCtx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); + this.__originalBezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); + }; + ctx.rect = function (x, y, width, height) { + destCtx.rect(x, y, width, height); + this.__originalRect(x, y, width, height); + }; + ctx.closePath = function () { + destCtx.closePath(); + this.__originalClosePath(); + }; + ctx.beginPath = function () { + destCtx.beginPath(); + this.__originalBeginPath(); + }; +} +class CachedCanvases { + constructor(canvasFactory) { + this.canvasFactory = canvasFactory; + this.cache = Object.create(null); + } + getCanvas(id, width, height) { + let canvasEntry; + if (this.cache[id] !== undefined) { + canvasEntry = this.cache[id]; + this.canvasFactory.reset(canvasEntry, width, height); + } else { + canvasEntry = this.canvasFactory.create(width, height); + this.cache[id] = canvasEntry; + } + return canvasEntry; + } + delete(id) { + delete this.cache[id]; + } + clear() { + for (const id in this.cache) { + const canvasEntry = this.cache[id]; + this.canvasFactory.destroy(canvasEntry); + delete this.cache[id]; + } + } +} +function drawImageAtIntegerCoords(ctx, srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH) { + const [a, b, c, d, tx, ty] = getCurrentTransform(ctx); + if (b === 0 && c === 0) { + const tlX = destX * a + tx; + const rTlX = Math.round(tlX); + const tlY = destY * d + ty; + const rTlY = Math.round(tlY); + const brX = (destX + destW) * a + tx; + const rWidth = Math.abs(Math.round(brX) - rTlX) || 1; + const brY = (destY + destH) * d + ty; + const rHeight = Math.abs(Math.round(brY) - rTlY) || 1; + ctx.setTransform(Math.sign(a), 0, 0, Math.sign(d), rTlX, rTlY); + ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rWidth, rHeight); + ctx.setTransform(a, b, c, d, tx, ty); + return [rWidth, rHeight]; + } + if (a === 0 && d === 0) { + const tlX = destY * c + tx; + const rTlX = Math.round(tlX); + const tlY = destX * b + ty; + const rTlY = Math.round(tlY); + const brX = (destY + destH) * c + tx; + const rWidth = Math.abs(Math.round(brX) - rTlX) || 1; + const brY = (destX + destW) * b + ty; + const rHeight = Math.abs(Math.round(brY) - rTlY) || 1; + ctx.setTransform(0, Math.sign(b), Math.sign(c), 0, rTlX, rTlY); + ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rHeight, rWidth); + ctx.setTransform(a, b, c, d, tx, ty); + return [rHeight, rWidth]; + } + ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH); + const scaleX = Math.hypot(a, b); + const scaleY = Math.hypot(c, d); + return [scaleX * destW, scaleY * destH]; +} +function compileType3Glyph(imgData) { + const { + width, + height + } = imgData; + if (width > MAX_SIZE_TO_COMPILE || height > MAX_SIZE_TO_COMPILE) { + return null; + } + const POINT_TO_PROCESS_LIMIT = 1000; + const POINT_TYPES = new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]); + const width1 = width + 1; + let points = new Uint8Array(width1 * (height + 1)); + let i, j, j0; + const lineSize = width + 7 & ~7; + let data = new Uint8Array(lineSize * height), + pos = 0; + for (const elem of imgData.data) { + let mask = 128; + while (mask > 0) { + data[pos++] = elem & mask ? 0 : 255; + mask >>= 1; + } + } + let count = 0; + pos = 0; + if (data[pos] !== 0) { + points[0] = 1; + ++count; + } + for (j = 1; j < width; j++) { + if (data[pos] !== data[pos + 1]) { + points[j] = data[pos] ? 2 : 1; + ++count; + } + pos++; + } + if (data[pos] !== 0) { + points[j] = 2; + ++count; + } + for (i = 1; i < height; i++) { + pos = i * lineSize; + j0 = i * width1; + if (data[pos - lineSize] !== data[pos]) { + points[j0] = data[pos] ? 1 : 8; + ++count; + } + let sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0); + for (j = 1; j < width; j++) { + sum = (sum >> 2) + (data[pos + 1] ? 4 : 0) + (data[pos - lineSize + 1] ? 8 : 0); + if (POINT_TYPES[sum]) { + points[j0 + j] = POINT_TYPES[sum]; + ++count; + } + pos++; + } + if (data[pos - lineSize] !== data[pos]) { + points[j0 + j] = data[pos] ? 2 : 4; + ++count; + } + if (count > POINT_TO_PROCESS_LIMIT) { + return null; + } + } + pos = lineSize * (height - 1); + j0 = i * width1; + if (data[pos] !== 0) { + points[j0] = 8; + ++count; + } + for (j = 1; j < width; j++) { + if (data[pos] !== data[pos + 1]) { + points[j0 + j] = data[pos] ? 4 : 8; + ++count; + } + pos++; + } + if (data[pos] !== 0) { + points[j0 + j] = 4; + ++count; + } + if (count > POINT_TO_PROCESS_LIMIT) { + return null; + } + const steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]); + const path = new Path2D(); + for (i = 0; count && i <= height; i++) { + let p = i * width1; + const end = p + width; + while (p < end && !points[p]) { + p++; + } + if (p === end) { + continue; + } + path.moveTo(p % width1, i); + const p0 = p; + let type = points[p]; + do { + const step = steps[type]; + do { + p += step; + } while (!points[p]); + const pp = points[p]; + if (pp !== 5 && pp !== 10) { + type = pp; + points[p] = 0; + } else { + type = pp & 0x33 * type >> 4; + points[p] &= type >> 2 | type << 2; + } + path.lineTo(p % width1, p / width1 | 0); + if (!points[p]) { + --count; + } + } while (p0 !== p); + --i; + } + data = null; + points = null; + const drawOutline = function (c) { + c.save(); + c.scale(1 / width, -1 / height); + c.translate(0, -height); + c.fill(path); + c.beginPath(); + c.restore(); + }; + return drawOutline; +} +class CanvasExtraState { + constructor(width, height) { + this.alphaIsShape = false; + this.fontSize = 0; + this.fontSizeScale = 1; + this.textMatrix = IDENTITY_MATRIX; + this.textMatrixScale = 1; + this.fontMatrix = FONT_IDENTITY_MATRIX; + this.leading = 0; + this.x = 0; + this.y = 0; + this.lineX = 0; + this.lineY = 0; + this.charSpacing = 0; + this.wordSpacing = 0; + this.textHScale = 1; + this.textRenderingMode = TextRenderingMode.FILL; + this.textRise = 0; + this.fillColor = "#000000"; + this.strokeColor = "#000000"; + this.patternFill = false; + this.patternStroke = false; + this.fillAlpha = 1; + this.strokeAlpha = 1; + this.lineWidth = 1; + this.activeSMask = null; + this.transferMaps = "none"; + this.startNewPathAndClipBox([0, 0, width, height]); + } + clone() { + const clone = Object.create(this); + clone.clipBox = this.clipBox.slice(); + return clone; + } + setCurrentPoint(x, y) { + this.x = x; + this.y = y; + } + updatePathMinMax(transform, x, y) { + [x, y] = Util.applyTransform([x, y], transform); + this.minX = Math.min(this.minX, x); + this.minY = Math.min(this.minY, y); + this.maxX = Math.max(this.maxX, x); + this.maxY = Math.max(this.maxY, y); + } + updateRectMinMax(transform, rect) { + const p1 = Util.applyTransform(rect, transform); + const p2 = Util.applyTransform(rect.slice(2), transform); + const p3 = Util.applyTransform([rect[0], rect[3]], transform); + const p4 = Util.applyTransform([rect[2], rect[1]], transform); + this.minX = Math.min(this.minX, p1[0], p2[0], p3[0], p4[0]); + this.minY = Math.min(this.minY, p1[1], p2[1], p3[1], p4[1]); + this.maxX = Math.max(this.maxX, p1[0], p2[0], p3[0], p4[0]); + this.maxY = Math.max(this.maxY, p1[1], p2[1], p3[1], p4[1]); + } + updateScalingPathMinMax(transform, minMax) { + Util.scaleMinMax(transform, minMax); + this.minX = Math.min(this.minX, minMax[0]); + this.minY = Math.min(this.minY, minMax[1]); + this.maxX = Math.max(this.maxX, minMax[2]); + this.maxY = Math.max(this.maxY, minMax[3]); + } + updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3, minMax) { + const box = Util.bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax); + if (minMax) { + return; + } + this.updateRectMinMax(transform, box); + } + getPathBoundingBox(pathType = PathType.FILL, transform = null) { + const box = [this.minX, this.minY, this.maxX, this.maxY]; + if (pathType === PathType.STROKE) { + if (!transform) { + unreachable("Stroke bounding box must include transform."); + } + const scale = Util.singularValueDecompose2dScale(transform); + const xStrokePad = scale[0] * this.lineWidth / 2; + const yStrokePad = scale[1] * this.lineWidth / 2; + box[0] -= xStrokePad; + box[1] -= yStrokePad; + box[2] += xStrokePad; + box[3] += yStrokePad; + } + return box; + } + updateClipFromPath() { + const intersect = Util.intersect(this.clipBox, this.getPathBoundingBox()); + this.startNewPathAndClipBox(intersect || [0, 0, 0, 0]); + } + isEmptyClip() { + return this.minX === Infinity; + } + startNewPathAndClipBox(box) { + this.clipBox = box; + this.minX = Infinity; + this.minY = Infinity; + this.maxX = 0; + this.maxY = 0; + } + getClippedPathBoundingBox(pathType = PathType.FILL, transform = null) { + return Util.intersect(this.clipBox, this.getPathBoundingBox(pathType, transform)); + } +} +function putBinaryImageData(ctx, imgData) { + if (imgData instanceof ImageData) { + ctx.putImageData(imgData, 0, 0); + return; + } + const height = imgData.height, + width = imgData.width; + const partialChunkHeight = height % FULL_CHUNK_HEIGHT; + const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; + const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; + const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT); + let srcPos = 0, + destPos; + const src = imgData.data; + const dest = chunkImgData.data; + let i, j, thisChunkHeight, elemsInThisChunk; + if (imgData.kind === util_ImageKind.GRAYSCALE_1BPP) { + const srcLength = src.byteLength; + const dest32 = new Uint32Array(dest.buffer, 0, dest.byteLength >> 2); + const dest32DataLength = dest32.length; + const fullSrcDiff = width + 7 >> 3; + const white = 0xffffffff; + const black = util_FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff; + for (i = 0; i < totalChunks; i++) { + thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight; + destPos = 0; + for (j = 0; j < thisChunkHeight; j++) { + const srcDiff = srcLength - srcPos; + let k = 0; + const kEnd = srcDiff > fullSrcDiff ? width : srcDiff * 8 - 7; + const kEndUnrolled = kEnd & ~7; + let mask = 0; + let srcByte = 0; + for (; k < kEndUnrolled; k += 8) { + srcByte = src[srcPos++]; + dest32[destPos++] = srcByte & 128 ? white : black; + dest32[destPos++] = srcByte & 64 ? white : black; + dest32[destPos++] = srcByte & 32 ? white : black; + dest32[destPos++] = srcByte & 16 ? white : black; + dest32[destPos++] = srcByte & 8 ? white : black; + dest32[destPos++] = srcByte & 4 ? white : black; + dest32[destPos++] = srcByte & 2 ? white : black; + dest32[destPos++] = srcByte & 1 ? white : black; + } + for (; k < kEnd; k++) { + if (mask === 0) { + srcByte = src[srcPos++]; + mask = 128; + } + dest32[destPos++] = srcByte & mask ? white : black; + mask >>= 1; + } + } + while (destPos < dest32DataLength) { + dest32[destPos++] = 0; + } + ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); + } + } else if (imgData.kind === util_ImageKind.RGBA_32BPP) { + j = 0; + elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4; + for (i = 0; i < fullChunks; i++) { + dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); + srcPos += elemsInThisChunk; + ctx.putImageData(chunkImgData, 0, j); + j += FULL_CHUNK_HEIGHT; + } + if (i < totalChunks) { + elemsInThisChunk = width * partialChunkHeight * 4; + dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); + ctx.putImageData(chunkImgData, 0, j); + } + } else if (imgData.kind === util_ImageKind.RGB_24BPP) { + thisChunkHeight = FULL_CHUNK_HEIGHT; + elemsInThisChunk = width * thisChunkHeight; + for (i = 0; i < totalChunks; i++) { + if (i >= fullChunks) { + thisChunkHeight = partialChunkHeight; + elemsInThisChunk = width * thisChunkHeight; + } + destPos = 0; + for (j = elemsInThisChunk; j--;) { + dest[destPos++] = src[srcPos++]; + dest[destPos++] = src[srcPos++]; + dest[destPos++] = src[srcPos++]; + dest[destPos++] = 255; + } + ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); + } + } else { + throw new Error(`bad image kind: ${imgData.kind}`); + } +} +function putBinaryImageMask(ctx, imgData) { + if (imgData.bitmap) { + ctx.drawImage(imgData.bitmap, 0, 0); + return; + } + const height = imgData.height, + width = imgData.width; + const partialChunkHeight = height % FULL_CHUNK_HEIGHT; + const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; + const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; + const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT); + let srcPos = 0; + const src = imgData.data; + const dest = chunkImgData.data; + for (let i = 0; i < totalChunks; i++) { + const thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight; + ({ + srcPos + } = convertBlackAndWhiteToRGBA({ + src, + srcPos, + dest, + width, + height: thisChunkHeight, + nonBlackColor: 0 + })); + ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); + } +} +function copyCtxState(sourceCtx, destCtx) { + const properties = ["strokeStyle", "fillStyle", "fillRule", "globalAlpha", "lineWidth", "lineCap", "lineJoin", "miterLimit", "globalCompositeOperation", "font", "filter"]; + for (const property of properties) { + if (sourceCtx[property] !== undefined) { + destCtx[property] = sourceCtx[property]; + } + } + if (sourceCtx.setLineDash !== undefined) { + destCtx.setLineDash(sourceCtx.getLineDash()); + destCtx.lineDashOffset = sourceCtx.lineDashOffset; + } +} +function resetCtxToDefault(ctx) { + ctx.strokeStyle = ctx.fillStyle = "#000000"; + ctx.fillRule = "nonzero"; + ctx.globalAlpha = 1; + ctx.lineWidth = 1; + ctx.lineCap = "butt"; + ctx.lineJoin = "miter"; + ctx.miterLimit = 10; + ctx.globalCompositeOperation = "source-over"; + ctx.font = "10px sans-serif"; + if (ctx.setLineDash !== undefined) { + ctx.setLineDash([]); + ctx.lineDashOffset = 0; + } + if (!isNodeJS) { + const { + filter + } = ctx; + if (filter !== "none" && filter !== "") { + ctx.filter = "none"; + } + } +} +function getImageSmoothingEnabled(transform, interpolate) { + if (interpolate) { + return true; + } + const scale = Util.singularValueDecompose2dScale(transform); + scale[0] = Math.fround(scale[0]); + scale[1] = Math.fround(scale[1]); + const actualScale = Math.fround((globalThis.devicePixelRatio || 1) * PixelsPerInch.PDF_TO_CSS_UNITS); + return scale[0] <= actualScale && scale[1] <= actualScale; +} +const LINE_CAP_STYLES = ["butt", "round", "square"]; +const LINE_JOIN_STYLES = ["miter", "round", "bevel"]; +const NORMAL_CLIP = {}; +const EO_CLIP = {}; +class CanvasGraphics { + constructor(canvasCtx, commonObjs, objs, canvasFactory, filterFactory, { + optionalContentConfig, + markedContentStack = null + }, annotationCanvasMap, pageColors) { + this.ctx = canvasCtx; + this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height); + this.stateStack = []; + this.pendingClip = null; + this.pendingEOFill = false; + this.res = null; + this.xobjs = null; + this.commonObjs = commonObjs; + this.objs = objs; + this.canvasFactory = canvasFactory; + this.filterFactory = filterFactory; + this.groupStack = []; + this.processingType3 = null; + this.baseTransform = null; + this.baseTransformStack = []; + this.groupLevel = 0; + this.smaskStack = []; + this.smaskCounter = 0; + this.tempSMask = null; + this.suspendedCtx = null; + this.contentVisible = true; + this.markedContentStack = markedContentStack || []; + this.optionalContentConfig = optionalContentConfig; + this.cachedCanvases = new CachedCanvases(this.canvasFactory); + this.cachedPatterns = new Map(); + this.annotationCanvasMap = annotationCanvasMap; + this.viewportScale = 1; + this.outputScaleX = 1; + this.outputScaleY = 1; + this.pageColors = pageColors; + this._cachedScaleForStroking = [-1, 0]; + this._cachedGetSinglePixelWidth = null; + this._cachedBitmapsMap = new Map(); + } + getObject(data, fallback = null) { + if (typeof data === "string") { + return data.startsWith("g_") ? this.commonObjs.get(data) : this.objs.get(data); + } + return fallback; + } + beginDrawing({ + transform, + viewport, + transparency = false, + background = null + }) { + const width = this.ctx.canvas.width; + const height = this.ctx.canvas.height; + const savedFillStyle = this.ctx.fillStyle; + this.ctx.fillStyle = background || "#ffffff"; + this.ctx.fillRect(0, 0, width, height); + this.ctx.fillStyle = savedFillStyle; + if (transparency) { + const transparentCanvas = this.cachedCanvases.getCanvas("transparent", width, height); + this.compositeCtx = this.ctx; + this.transparentCanvas = transparentCanvas.canvas; + this.ctx = transparentCanvas.context; + this.ctx.save(); + this.ctx.transform(...getCurrentTransform(this.compositeCtx)); + } + this.ctx.save(); + resetCtxToDefault(this.ctx); + if (transform) { + this.ctx.transform(...transform); + this.outputScaleX = transform[0]; + this.outputScaleY = transform[0]; + } + this.ctx.transform(...viewport.transform); + this.viewportScale = viewport.scale; + this.baseTransform = getCurrentTransform(this.ctx); + } + executeOperatorList(operatorList, executionStartIdx, continueCallback, stepper) { + const argsArray = operatorList.argsArray; + const fnArray = operatorList.fnArray; + let i = executionStartIdx || 0; + const argsArrayLen = argsArray.length; + if (argsArrayLen === i) { + return i; + } + const chunkOperations = argsArrayLen - i > EXECUTION_STEPS && typeof continueCallback === "function"; + const endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0; + let steps = 0; + const commonObjs = this.commonObjs; + const objs = this.objs; + let fnId; + while (true) { + if (stepper !== undefined && i === stepper.nextBreakPoint) { + stepper.breakIt(i, continueCallback); + return i; + } + fnId = fnArray[i]; + if (fnId !== OPS.dependency) { + this[fnId].apply(this, argsArray[i]); + } else { + for (const depObjId of argsArray[i]) { + const objsPool = depObjId.startsWith("g_") ? commonObjs : objs; + if (!objsPool.has(depObjId)) { + objsPool.get(depObjId, continueCallback); + return i; + } + } + } + i++; + if (i === argsArrayLen) { + return i; + } + if (chunkOperations && ++steps > EXECUTION_STEPS) { + if (Date.now() > endTime) { + continueCallback(); + return i; + } + steps = 0; + } + } + } + #restoreInitialState() { + while (this.stateStack.length || this.inSMaskMode) { + this.restore(); + } + this.current.activeSMask = null; + this.ctx.restore(); + if (this.transparentCanvas) { + this.ctx = this.compositeCtx; + this.ctx.save(); + this.ctx.setTransform(1, 0, 0, 1, 0, 0); + this.ctx.drawImage(this.transparentCanvas, 0, 0); + this.ctx.restore(); + this.transparentCanvas = null; + } + } + endDrawing() { + this.#restoreInitialState(); + this.cachedCanvases.clear(); + this.cachedPatterns.clear(); + for (const cache of this._cachedBitmapsMap.values()) { + for (const canvas of cache.values()) { + if (typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement) { + canvas.width = canvas.height = 0; + } + } + cache.clear(); + } + this._cachedBitmapsMap.clear(); + this.#drawFilter(); + } + #drawFilter() { + if (this.pageColors) { + const hcmFilterId = this.filterFactory.addHCMFilter(this.pageColors.foreground, this.pageColors.background); + if (hcmFilterId !== "none") { + const savedFilter = this.ctx.filter; + this.ctx.filter = hcmFilterId; + this.ctx.drawImage(this.ctx.canvas, 0, 0); + this.ctx.filter = savedFilter; + } + } + } + _scaleImage(img, inverseTransform) { + const width = img.width ?? img.displayWidth; + const height = img.height ?? img.displayHeight; + let widthScale = Math.max(Math.hypot(inverseTransform[0], inverseTransform[1]), 1); + let heightScale = Math.max(Math.hypot(inverseTransform[2], inverseTransform[3]), 1); + let paintWidth = width, + paintHeight = height; + let tmpCanvasId = "prescale1"; + let tmpCanvas, tmpCtx; + while (widthScale > 2 && paintWidth > 1 || heightScale > 2 && paintHeight > 1) { + let newWidth = paintWidth, + newHeight = paintHeight; + if (widthScale > 2 && paintWidth > 1) { + newWidth = paintWidth >= 16384 ? Math.floor(paintWidth / 2) - 1 || 1 : Math.ceil(paintWidth / 2); + widthScale /= paintWidth / newWidth; + } + if (heightScale > 2 && paintHeight > 1) { + newHeight = paintHeight >= 16384 ? Math.floor(paintHeight / 2) - 1 || 1 : Math.ceil(paintHeight) / 2; + heightScale /= paintHeight / newHeight; + } + tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight); + tmpCtx = tmpCanvas.context; + tmpCtx.clearRect(0, 0, newWidth, newHeight); + tmpCtx.drawImage(img, 0, 0, paintWidth, paintHeight, 0, 0, newWidth, newHeight); + img = tmpCanvas.canvas; + paintWidth = newWidth; + paintHeight = newHeight; + tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1"; + } + return { + img, + paintWidth, + paintHeight + }; + } + _createMaskCanvas(img) { + const ctx = this.ctx; + const { + width, + height + } = img; + const fillColor = this.current.fillColor; + const isPatternFill = this.current.patternFill; + const currentTransform = getCurrentTransform(ctx); + let cache, cacheKey, scaled, maskCanvas; + if ((img.bitmap || img.data) && img.count > 1) { + const mainKey = img.bitmap || img.data.buffer; + cacheKey = JSON.stringify(isPatternFill ? currentTransform : [currentTransform.slice(0, 4), fillColor]); + cache = this._cachedBitmapsMap.get(mainKey); + if (!cache) { + cache = new Map(); + this._cachedBitmapsMap.set(mainKey, cache); + } + const cachedImage = cache.get(cacheKey); + if (cachedImage && !isPatternFill) { + const offsetX = Math.round(Math.min(currentTransform[0], currentTransform[2]) + currentTransform[4]); + const offsetY = Math.round(Math.min(currentTransform[1], currentTransform[3]) + currentTransform[5]); + return { + canvas: cachedImage, + offsetX, + offsetY + }; + } + scaled = cachedImage; + } + if (!scaled) { + maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height); + putBinaryImageMask(maskCanvas.context, img); + } + let maskToCanvas = Util.transform(currentTransform, [1 / width, 0, 0, -1 / height, 0, 0]); + maskToCanvas = Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]); + const [minX, minY, maxX, maxY] = Util.getAxialAlignedBoundingBox([0, 0, width, height], maskToCanvas); + const drawnWidth = Math.round(maxX - minX) || 1; + const drawnHeight = Math.round(maxY - minY) || 1; + const fillCanvas = this.cachedCanvases.getCanvas("fillCanvas", drawnWidth, drawnHeight); + const fillCtx = fillCanvas.context; + const offsetX = minX; + const offsetY = minY; + fillCtx.translate(-offsetX, -offsetY); + fillCtx.transform(...maskToCanvas); + if (!scaled) { + scaled = this._scaleImage(maskCanvas.canvas, getCurrentTransformInverse(fillCtx)); + scaled = scaled.img; + if (cache && isPatternFill) { + cache.set(cacheKey, scaled); + } + } + fillCtx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(fillCtx), img.interpolate); + drawImageAtIntegerCoords(fillCtx, scaled, 0, 0, scaled.width, scaled.height, 0, 0, width, height); + fillCtx.globalCompositeOperation = "source-in"; + const inverse = Util.transform(getCurrentTransformInverse(fillCtx), [1, 0, 0, 1, -offsetX, -offsetY]); + fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, PathType.FILL) : fillColor; + fillCtx.fillRect(0, 0, width, height); + if (cache && !isPatternFill) { + this.cachedCanvases.delete("fillCanvas"); + cache.set(cacheKey, fillCanvas.canvas); + } + return { + canvas: fillCanvas.canvas, + offsetX: Math.round(offsetX), + offsetY: Math.round(offsetY) + }; + } + setLineWidth(width) { + if (width !== this.current.lineWidth) { + this._cachedScaleForStroking[0] = -1; + } + this.current.lineWidth = width; + this.ctx.lineWidth = width; + } + setLineCap(style) { + this.ctx.lineCap = LINE_CAP_STYLES[style]; + } + setLineJoin(style) { + this.ctx.lineJoin = LINE_JOIN_STYLES[style]; + } + setMiterLimit(limit) { + this.ctx.miterLimit = limit; + } + setDash(dashArray, dashPhase) { + const ctx = this.ctx; + if (ctx.setLineDash !== undefined) { + ctx.setLineDash(dashArray); + ctx.lineDashOffset = dashPhase; + } + } + setRenderingIntent(intent) {} + setFlatness(flatness) {} + setGState(states) { + for (const [key, value] of states) { + switch (key) { + case "LW": + this.setLineWidth(value); + break; + case "LC": + this.setLineCap(value); + break; + case "LJ": + this.setLineJoin(value); + break; + case "ML": + this.setMiterLimit(value); + break; + case "D": + this.setDash(value[0], value[1]); + break; + case "RI": + this.setRenderingIntent(value); + break; + case "FL": + this.setFlatness(value); + break; + case "Font": + this.setFont(value[0], value[1]); + break; + case "CA": + this.current.strokeAlpha = value; + break; + case "ca": + this.current.fillAlpha = value; + this.ctx.globalAlpha = value; + break; + case "BM": + this.ctx.globalCompositeOperation = value; + break; + case "SMask": + this.current.activeSMask = value ? this.tempSMask : null; + this.tempSMask = null; + this.checkSMaskState(); + break; + case "TR": + this.ctx.filter = this.current.transferMaps = this.filterFactory.addFilter(value); + break; + } + } + } + get inSMaskMode() { + return !!this.suspendedCtx; + } + checkSMaskState() { + const inSMaskMode = this.inSMaskMode; + if (this.current.activeSMask && !inSMaskMode) { + this.beginSMaskMode(); + } else if (!this.current.activeSMask && inSMaskMode) { + this.endSMaskMode(); + } + } + beginSMaskMode() { + if (this.inSMaskMode) { + throw new Error("beginSMaskMode called while already in smask mode"); + } + const drawnWidth = this.ctx.canvas.width; + const drawnHeight = this.ctx.canvas.height; + const cacheId = "smaskGroupAt" + this.groupLevel; + const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight); + this.suspendedCtx = this.ctx; + this.ctx = scratchCanvas.context; + const ctx = this.ctx; + ctx.setTransform(...getCurrentTransform(this.suspendedCtx)); + copyCtxState(this.suspendedCtx, ctx); + mirrorContextOperations(ctx, this.suspendedCtx); + this.setGState([["BM", "source-over"], ["ca", 1], ["CA", 1]]); + } + endSMaskMode() { + if (!this.inSMaskMode) { + throw new Error("endSMaskMode called while not in smask mode"); + } + this.ctx._removeMirroring(); + copyCtxState(this.ctx, this.suspendedCtx); + this.ctx = this.suspendedCtx; + this.suspendedCtx = null; + } + compose(dirtyBox) { + if (!this.current.activeSMask) { + return; + } + if (!dirtyBox) { + dirtyBox = [0, 0, this.ctx.canvas.width, this.ctx.canvas.height]; + } else { + dirtyBox[0] = Math.floor(dirtyBox[0]); + dirtyBox[1] = Math.floor(dirtyBox[1]); + dirtyBox[2] = Math.ceil(dirtyBox[2]); + dirtyBox[3] = Math.ceil(dirtyBox[3]); + } + const smask = this.current.activeSMask; + const suspendedCtx = this.suspendedCtx; + this.composeSMask(suspendedCtx, smask, this.ctx, dirtyBox); + this.ctx.save(); + this.ctx.setTransform(1, 0, 0, 1, 0, 0); + this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); + this.ctx.restore(); + } + composeSMask(ctx, smask, layerCtx, layerBox) { + const layerOffsetX = layerBox[0]; + const layerOffsetY = layerBox[1]; + const layerWidth = layerBox[2] - layerOffsetX; + const layerHeight = layerBox[3] - layerOffsetY; + if (layerWidth === 0 || layerHeight === 0) { + return; + } + this.genericComposeSMask(smask.context, layerCtx, layerWidth, layerHeight, smask.subtype, smask.backdrop, smask.transferMap, layerOffsetX, layerOffsetY, smask.offsetX, smask.offsetY); + ctx.save(); + ctx.globalAlpha = 1; + ctx.globalCompositeOperation = "source-over"; + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.drawImage(layerCtx.canvas, 0, 0); + ctx.restore(); + } + genericComposeSMask(maskCtx, layerCtx, width, height, subtype, backdrop, transferMap, layerOffsetX, layerOffsetY, maskOffsetX, maskOffsetY) { + let maskCanvas = maskCtx.canvas; + let maskX = layerOffsetX - maskOffsetX; + let maskY = layerOffsetY - maskOffsetY; + if (backdrop) { + const backdropRGB = Util.makeHexColor(...backdrop); + if (maskX < 0 || maskY < 0 || maskX + width > maskCanvas.width || maskY + height > maskCanvas.height) { + const canvas = this.cachedCanvases.getCanvas("maskExtension", width, height); + const ctx = canvas.context; + ctx.drawImage(maskCanvas, -maskX, -maskY); + ctx.globalCompositeOperation = "destination-atop"; + ctx.fillStyle = backdropRGB; + ctx.fillRect(0, 0, width, height); + ctx.globalCompositeOperation = "source-over"; + maskCanvas = canvas.canvas; + maskX = maskY = 0; + } else { + maskCtx.save(); + maskCtx.globalAlpha = 1; + maskCtx.setTransform(1, 0, 0, 1, 0, 0); + const clip = new Path2D(); + clip.rect(maskX, maskY, width, height); + maskCtx.clip(clip); + maskCtx.globalCompositeOperation = "destination-atop"; + maskCtx.fillStyle = backdropRGB; + maskCtx.fillRect(maskX, maskY, width, height); + maskCtx.restore(); + } + } + layerCtx.save(); + layerCtx.globalAlpha = 1; + layerCtx.setTransform(1, 0, 0, 1, 0, 0); + if (subtype === "Alpha" && transferMap) { + layerCtx.filter = this.filterFactory.addAlphaFilter(transferMap); + } else if (subtype === "Luminosity") { + layerCtx.filter = this.filterFactory.addLuminosityFilter(transferMap); + } + const clip = new Path2D(); + clip.rect(layerOffsetX, layerOffsetY, width, height); + layerCtx.clip(clip); + layerCtx.globalCompositeOperation = "destination-in"; + layerCtx.drawImage(maskCanvas, maskX, maskY, width, height, layerOffsetX, layerOffsetY, width, height); + layerCtx.restore(); + } + save() { + if (this.inSMaskMode) { + copyCtxState(this.ctx, this.suspendedCtx); + this.suspendedCtx.save(); + } else { + this.ctx.save(); + } + const old = this.current; + this.stateStack.push(old); + this.current = old.clone(); + } + restore() { + if (this.stateStack.length === 0 && this.inSMaskMode) { + this.endSMaskMode(); + } + if (this.stateStack.length !== 0) { + this.current = this.stateStack.pop(); + if (this.inSMaskMode) { + this.suspendedCtx.restore(); + copyCtxState(this.suspendedCtx, this.ctx); + } else { + this.ctx.restore(); + } + this.checkSMaskState(); + this.pendingClip = null; + this._cachedScaleForStroking[0] = -1; + this._cachedGetSinglePixelWidth = null; + } + } + transform(a, b, c, d, e, f) { + this.ctx.transform(a, b, c, d, e, f); + this._cachedScaleForStroking[0] = -1; + this._cachedGetSinglePixelWidth = null; + } + constructPath(ops, args, minMax) { + const ctx = this.ctx; + const current = this.current; + let x = current.x, + y = current.y; + let startX, startY; + const currentTransform = getCurrentTransform(ctx); + const isScalingMatrix = currentTransform[0] === 0 && currentTransform[3] === 0 || currentTransform[1] === 0 && currentTransform[2] === 0; + const minMaxForBezier = isScalingMatrix ? minMax.slice(0) : null; + for (let i = 0, j = 0, ii = ops.length; i < ii; i++) { + switch (ops[i] | 0) { + case OPS.rectangle: + x = args[j++]; + y = args[j++]; + const width = args[j++]; + const height = args[j++]; + const xw = x + width; + const yh = y + height; + ctx.moveTo(x, y); + if (width === 0 || height === 0) { + ctx.lineTo(xw, yh); + } else { + ctx.lineTo(xw, y); + ctx.lineTo(xw, yh); + ctx.lineTo(x, yh); + } + if (!isScalingMatrix) { + current.updateRectMinMax(currentTransform, [x, y, xw, yh]); + } + ctx.closePath(); + break; + case OPS.moveTo: + x = args[j++]; + y = args[j++]; + ctx.moveTo(x, y); + if (!isScalingMatrix) { + current.updatePathMinMax(currentTransform, x, y); + } + break; + case OPS.lineTo: + x = args[j++]; + y = args[j++]; + ctx.lineTo(x, y); + if (!isScalingMatrix) { + current.updatePathMinMax(currentTransform, x, y); + } + break; + case OPS.curveTo: + startX = x; + startY = y; + x = args[j + 4]; + y = args[j + 5]; + ctx.bezierCurveTo(args[j], args[j + 1], args[j + 2], args[j + 3], x, y); + current.updateCurvePathMinMax(currentTransform, startX, startY, args[j], args[j + 1], args[j + 2], args[j + 3], x, y, minMaxForBezier); + j += 6; + break; + case OPS.curveTo2: + startX = x; + startY = y; + ctx.bezierCurveTo(x, y, args[j], args[j + 1], args[j + 2], args[j + 3]); + current.updateCurvePathMinMax(currentTransform, startX, startY, x, y, args[j], args[j + 1], args[j + 2], args[j + 3], minMaxForBezier); + x = args[j + 2]; + y = args[j + 3]; + j += 4; + break; + case OPS.curveTo3: + startX = x; + startY = y; + x = args[j + 2]; + y = args[j + 3]; + ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y); + current.updateCurvePathMinMax(currentTransform, startX, startY, args[j], args[j + 1], x, y, x, y, minMaxForBezier); + j += 4; + break; + case OPS.closePath: + ctx.closePath(); + break; + } + } + if (isScalingMatrix) { + current.updateScalingPathMinMax(currentTransform, minMaxForBezier); + } + current.setCurrentPoint(x, y); + } + closePath() { + this.ctx.closePath(); + } + stroke(consumePath = true) { + const ctx = this.ctx; + const strokeColor = this.current.strokeColor; + ctx.globalAlpha = this.current.strokeAlpha; + if (this.contentVisible) { + if (typeof strokeColor === "object" && strokeColor?.getPattern) { + ctx.save(); + ctx.strokeStyle = strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE); + this.rescaleAndStroke(false); + ctx.restore(); + } else { + this.rescaleAndStroke(true); + } + } + if (consumePath) { + this.consumePath(this.current.getClippedPathBoundingBox()); + } + ctx.globalAlpha = this.current.fillAlpha; + } + closeStroke() { + this.closePath(); + this.stroke(); + } + fill(consumePath = true) { + const ctx = this.ctx; + const fillColor = this.current.fillColor; + const isPatternFill = this.current.patternFill; + let needRestore = false; + if (isPatternFill) { + ctx.save(); + ctx.fillStyle = fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL); + needRestore = true; + } + const intersect = this.current.getClippedPathBoundingBox(); + if (this.contentVisible && intersect !== null) { + if (this.pendingEOFill) { + ctx.fill("evenodd"); + this.pendingEOFill = false; + } else { + ctx.fill(); + } + } + if (needRestore) { + ctx.restore(); + } + if (consumePath) { + this.consumePath(intersect); + } + } + eoFill() { + this.pendingEOFill = true; + this.fill(); + } + fillStroke() { + this.fill(false); + this.stroke(false); + this.consumePath(); + } + eoFillStroke() { + this.pendingEOFill = true; + this.fillStroke(); + } + closeFillStroke() { + this.closePath(); + this.fillStroke(); + } + closeEOFillStroke() { + this.pendingEOFill = true; + this.closePath(); + this.fillStroke(); + } + endPath() { + this.consumePath(); + } + clip() { + this.pendingClip = NORMAL_CLIP; + } + eoClip() { + this.pendingClip = EO_CLIP; + } + beginText() { + this.current.textMatrix = IDENTITY_MATRIX; + this.current.textMatrixScale = 1; + this.current.x = this.current.lineX = 0; + this.current.y = this.current.lineY = 0; + } + endText() { + const paths = this.pendingTextPaths; + const ctx = this.ctx; + if (paths === undefined) { + ctx.beginPath(); + return; + } + const newPath = new Path2D(); + const invTransf = ctx.getTransform().invertSelf(); + for (const { + transform, + x, + y, + fontSize, + path + } of paths) { + newPath.addPath(path, new DOMMatrix(transform).preMultiplySelf(invTransf).translate(x, y).scale(fontSize, -fontSize)); + } + ctx.clip(newPath); + ctx.beginPath(); + delete this.pendingTextPaths; + } + setCharSpacing(spacing) { + this.current.charSpacing = spacing; + } + setWordSpacing(spacing) { + this.current.wordSpacing = spacing; + } + setHScale(scale) { + this.current.textHScale = scale / 100; + } + setLeading(leading) { + this.current.leading = -leading; + } + setFont(fontRefName, size) { + const fontObj = this.commonObjs.get(fontRefName); + const current = this.current; + if (!fontObj) { + throw new Error(`Can't find font for ${fontRefName}`); + } + current.fontMatrix = fontObj.fontMatrix || FONT_IDENTITY_MATRIX; + if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) { + warn("Invalid font matrix for font " + fontRefName); + } + if (size < 0) { + size = -size; + current.fontDirection = -1; + } else { + current.fontDirection = 1; + } + this.current.font = fontObj; + this.current.fontSize = size; + if (fontObj.isType3Font) { + return; + } + const name = fontObj.loadedName || "sans-serif"; + const typeface = fontObj.systemFontInfo?.css || `"${name}", ${fontObj.fallbackName}`; + let bold = "normal"; + if (fontObj.black) { + bold = "900"; + } else if (fontObj.bold) { + bold = "bold"; + } + const italic = fontObj.italic ? "italic" : "normal"; + let browserFontSize = size; + if (size < MIN_FONT_SIZE) { + browserFontSize = MIN_FONT_SIZE; + } else if (size > MAX_FONT_SIZE) { + browserFontSize = MAX_FONT_SIZE; + } + this.current.fontSizeScale = size / browserFontSize; + this.ctx.font = `${italic} ${bold} ${browserFontSize}px ${typeface}`; + } + setTextRenderingMode(mode) { + this.current.textRenderingMode = mode; + } + setTextRise(rise) { + this.current.textRise = rise; + } + moveText(x, y) { + this.current.x = this.current.lineX += x; + this.current.y = this.current.lineY += y; + } + setLeadingMoveText(x, y) { + this.setLeading(-y); + this.moveText(x, y); + } + setTextMatrix(a, b, c, d, e, f) { + this.current.textMatrix = [a, b, c, d, e, f]; + this.current.textMatrixScale = Math.hypot(a, b); + this.current.x = this.current.lineX = 0; + this.current.y = this.current.lineY = 0; + } + nextLine() { + this.moveText(0, this.current.leading); + } + #getScaledPath(path, currentTransform, transform) { + const newPath = new Path2D(); + newPath.addPath(path, new DOMMatrix(transform).invertSelf().multiplySelf(currentTransform)); + return newPath; + } + paintChar(character, x, y, patternFillTransform, patternStrokeTransform) { + const ctx = this.ctx; + const current = this.current; + const font = current.font; + const textRenderingMode = current.textRenderingMode; + const fontSize = current.fontSize / current.fontSizeScale; + const fillStrokeMode = textRenderingMode & TextRenderingMode.FILL_STROKE_MASK; + const isAddToPathSet = !!(textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG); + const patternFill = current.patternFill && !font.missingFile; + const patternStroke = current.patternStroke && !font.missingFile; + let path; + if (font.disableFontFace || isAddToPathSet || patternFill || patternStroke) { + path = font.getPathGenerator(this.commonObjs, character); + } + if (font.disableFontFace || patternFill || patternStroke) { + ctx.save(); + ctx.translate(x, y); + ctx.scale(fontSize, -fontSize); + if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) { + if (patternFillTransform) { + const currentTransform = ctx.getTransform(); + ctx.setTransform(...patternFillTransform); + ctx.fill(this.#getScaledPath(path, currentTransform, patternFillTransform)); + } else { + ctx.fill(path); + } + } + if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) { + if (patternStrokeTransform) { + const currentTransform = ctx.getTransform(); + ctx.setTransform(...patternStrokeTransform); + ctx.stroke(this.#getScaledPath(path, currentTransform, patternStrokeTransform)); + } else { + ctx.lineWidth /= fontSize; + ctx.stroke(path); + } + } + ctx.restore(); + } else { + if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) { + ctx.fillText(character, x, y); + } + if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) { + ctx.strokeText(character, x, y); + } + } + if (isAddToPathSet) { + const paths = this.pendingTextPaths ||= []; + paths.push({ + transform: getCurrentTransform(ctx), + x, + y, + fontSize, + path + }); + } + } + get isFontSubpixelAAEnabled() { + const { + context: ctx + } = this.cachedCanvases.getCanvas("isFontSubpixelAAEnabled", 10, 10); + ctx.scale(1.5, 1); + ctx.fillText("I", 0, 10); + const data = ctx.getImageData(0, 0, 10, 10).data; + let enabled = false; + for (let i = 3; i < data.length; i += 4) { + if (data[i] > 0 && data[i] < 255) { + enabled = true; + break; + } + } + return shadow(this, "isFontSubpixelAAEnabled", enabled); + } + showText(glyphs) { + const current = this.current; + const font = current.font; + if (font.isType3Font) { + return this.showType3Text(glyphs); + } + const fontSize = current.fontSize; + if (fontSize === 0) { + return undefined; + } + const ctx = this.ctx; + const fontSizeScale = current.fontSizeScale; + const charSpacing = current.charSpacing; + const wordSpacing = current.wordSpacing; + const fontDirection = current.fontDirection; + const textHScale = current.textHScale * fontDirection; + const glyphsLength = glyphs.length; + const vertical = font.vertical; + const spacingDir = vertical ? 1 : -1; + const defaultVMetrics = font.defaultVMetrics; + const widthAdvanceScale = fontSize * current.fontMatrix[0]; + const simpleFillText = current.textRenderingMode === TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill; + ctx.save(); + ctx.transform(...current.textMatrix); + ctx.translate(current.x, current.y + current.textRise); + if (fontDirection > 0) { + ctx.scale(textHScale, -1); + } else { + ctx.scale(textHScale, 1); + } + let patternFillTransform, patternStrokeTransform; + if (current.patternFill) { + ctx.save(); + const pattern = current.fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL); + patternFillTransform = getCurrentTransform(ctx); + ctx.restore(); + ctx.fillStyle = pattern; + } + if (current.patternStroke) { + ctx.save(); + const pattern = current.strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE); + patternStrokeTransform = getCurrentTransform(ctx); + ctx.restore(); + ctx.strokeStyle = pattern; + } + let lineWidth = current.lineWidth; + const scale = current.textMatrixScale; + if (scale === 0 || lineWidth === 0) { + const fillStrokeMode = current.textRenderingMode & TextRenderingMode.FILL_STROKE_MASK; + if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) { + lineWidth = this.getSinglePixelWidth(); + } + } else { + lineWidth /= scale; + } + if (fontSizeScale !== 1.0) { + ctx.scale(fontSizeScale, fontSizeScale); + lineWidth /= fontSizeScale; + } + ctx.lineWidth = lineWidth; + if (font.isInvalidPDFjsFont) { + const chars = []; + let width = 0; + for (const glyph of glyphs) { + chars.push(glyph.unicode); + width += glyph.width; + } + ctx.fillText(chars.join(""), 0, 0); + current.x += width * widthAdvanceScale * textHScale; + ctx.restore(); + this.compose(); + return undefined; + } + let x = 0, + i; + for (i = 0; i < glyphsLength; ++i) { + const glyph = glyphs[i]; + if (typeof glyph === "number") { + x += spacingDir * glyph * fontSize / 1000; + continue; + } + let restoreNeeded = false; + const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing; + const character = glyph.fontChar; + const accent = glyph.accent; + let scaledX, scaledY; + let width = glyph.width; + if (vertical) { + const vmetric = glyph.vmetric || defaultVMetrics; + const vx = -(glyph.vmetric ? vmetric[1] : width * 0.5) * widthAdvanceScale; + const vy = vmetric[2] * widthAdvanceScale; + width = vmetric ? -vmetric[0] : width; + scaledX = vx / fontSizeScale; + scaledY = (x + vy) / fontSizeScale; + } else { + scaledX = x / fontSizeScale; + scaledY = 0; + } + if (font.remeasure && width > 0) { + const measuredWidth = ctx.measureText(character).width * 1000 / fontSize * fontSizeScale; + if (width < measuredWidth && this.isFontSubpixelAAEnabled) { + const characterScaleX = width / measuredWidth; + restoreNeeded = true; + ctx.save(); + ctx.scale(characterScaleX, 1); + scaledX /= characterScaleX; + } else if (width !== measuredWidth) { + scaledX += (width - measuredWidth) / 2000 * fontSize / fontSizeScale; + } + } + if (this.contentVisible && (glyph.isInFont || font.missingFile)) { + if (simpleFillText && !accent) { + ctx.fillText(character, scaledX, scaledY); + } else { + this.paintChar(character, scaledX, scaledY, patternFillTransform, patternStrokeTransform); + if (accent) { + const scaledAccentX = scaledX + fontSize * accent.offset.x / fontSizeScale; + const scaledAccentY = scaledY - fontSize * accent.offset.y / fontSizeScale; + this.paintChar(accent.fontChar, scaledAccentX, scaledAccentY, patternFillTransform, patternStrokeTransform); + } + } + } + const charWidth = vertical ? width * widthAdvanceScale - spacing * fontDirection : width * widthAdvanceScale + spacing * fontDirection; + x += charWidth; + if (restoreNeeded) { + ctx.restore(); + } + } + if (vertical) { + current.y -= x; + } else { + current.x += x * textHScale; + } + ctx.restore(); + this.compose(); + return undefined; + } + showType3Text(glyphs) { + const ctx = this.ctx; + const current = this.current; + const font = current.font; + const fontSize = current.fontSize; + const fontDirection = current.fontDirection; + const spacingDir = font.vertical ? 1 : -1; + const charSpacing = current.charSpacing; + const wordSpacing = current.wordSpacing; + const textHScale = current.textHScale * fontDirection; + const fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX; + const glyphsLength = glyphs.length; + const isTextInvisible = current.textRenderingMode === TextRenderingMode.INVISIBLE; + let i, glyph, width, spacingLength; + if (isTextInvisible || fontSize === 0) { + return; + } + this._cachedScaleForStroking[0] = -1; + this._cachedGetSinglePixelWidth = null; + ctx.save(); + ctx.transform(...current.textMatrix); + ctx.translate(current.x, current.y); + ctx.scale(textHScale, fontDirection); + for (i = 0; i < glyphsLength; ++i) { + glyph = glyphs[i]; + if (typeof glyph === "number") { + spacingLength = spacingDir * glyph * fontSize / 1000; + this.ctx.translate(spacingLength, 0); + current.x += spacingLength * textHScale; + continue; + } + const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing; + const operatorList = font.charProcOperatorList[glyph.operatorListId]; + if (!operatorList) { + warn(`Type3 character "${glyph.operatorListId}" is not available.`); + continue; + } + if (this.contentVisible) { + this.processingType3 = glyph; + this.save(); + ctx.scale(fontSize, fontSize); + ctx.transform(...fontMatrix); + this.executeOperatorList(operatorList); + this.restore(); + } + const transformed = Util.applyTransform([glyph.width, 0], fontMatrix); + width = transformed[0] * fontSize + spacing; + ctx.translate(width, 0); + current.x += width * textHScale; + } + ctx.restore(); + this.processingType3 = null; + } + setCharWidth(xWidth, yWidth) {} + setCharWidthAndBounds(xWidth, yWidth, llx, lly, urx, ury) { + this.ctx.rect(llx, lly, urx - llx, ury - lly); + this.ctx.clip(); + this.endPath(); + } + getColorN_Pattern(IR) { + let pattern; + if (IR[0] === "TilingPattern") { + const color = IR[1]; + const baseTransform = this.baseTransform || getCurrentTransform(this.ctx); + const canvasGraphicsFactory = { + createCanvasGraphics: ctx => new CanvasGraphics(ctx, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, { + optionalContentConfig: this.optionalContentConfig, + markedContentStack: this.markedContentStack + }) + }; + pattern = new TilingPattern(IR, color, this.ctx, canvasGraphicsFactory, baseTransform); + } else { + pattern = this._getPattern(IR[1], IR[2]); + } + return pattern; + } + setStrokeColorN() { + this.current.strokeColor = this.getColorN_Pattern(arguments); + this.current.patternStroke = true; + } + setFillColorN() { + this.current.fillColor = this.getColorN_Pattern(arguments); + this.current.patternFill = true; + } + setStrokeRGBColor(r, g, b) { + this.ctx.strokeStyle = this.current.strokeColor = Util.makeHexColor(r, g, b); + this.current.patternStroke = false; + } + setStrokeTransparent() { + this.ctx.strokeStyle = this.current.strokeColor = "transparent"; + this.current.patternStroke = false; + } + setFillRGBColor(r, g, b) { + this.ctx.fillStyle = this.current.fillColor = Util.makeHexColor(r, g, b); + this.current.patternFill = false; + } + setFillTransparent() { + this.ctx.fillStyle = this.current.fillColor = "transparent"; + this.current.patternFill = false; + } + _getPattern(objId, matrix = null) { + let pattern; + if (this.cachedPatterns.has(objId)) { + pattern = this.cachedPatterns.get(objId); + } else { + pattern = getShadingPattern(this.getObject(objId)); + this.cachedPatterns.set(objId, pattern); + } + if (matrix) { + pattern.matrix = matrix; + } + return pattern; + } + shadingFill(objId) { + if (!this.contentVisible) { + return; + } + const ctx = this.ctx; + this.save(); + const pattern = this._getPattern(objId); + ctx.fillStyle = pattern.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.SHADING); + const inv = getCurrentTransformInverse(ctx); + if (inv) { + const { + width, + height + } = ctx.canvas; + const [x0, y0, x1, y1] = Util.getAxialAlignedBoundingBox([0, 0, width, height], inv); + this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0); + } else { + this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10); + } + this.compose(this.current.getClippedPathBoundingBox()); + this.restore(); + } + beginInlineImage() { + unreachable("Should not call beginInlineImage"); + } + beginImageData() { + unreachable("Should not call beginImageData"); + } + paintFormXObjectBegin(matrix, bbox) { + if (!this.contentVisible) { + return; + } + this.save(); + this.baseTransformStack.push(this.baseTransform); + if (matrix) { + this.transform(...matrix); + } + this.baseTransform = getCurrentTransform(this.ctx); + if (bbox) { + const width = bbox[2] - bbox[0]; + const height = bbox[3] - bbox[1]; + this.ctx.rect(bbox[0], bbox[1], width, height); + this.current.updateRectMinMax(getCurrentTransform(this.ctx), bbox); + this.clip(); + this.endPath(); + } + } + paintFormXObjectEnd() { + if (!this.contentVisible) { + return; + } + this.restore(); + this.baseTransform = this.baseTransformStack.pop(); + } + beginGroup(group) { + if (!this.contentVisible) { + return; + } + this.save(); + if (this.inSMaskMode) { + this.endSMaskMode(); + this.current.activeSMask = null; + } + const currentCtx = this.ctx; + if (!group.isolated) { + info("TODO: Support non-isolated groups."); + } + if (group.knockout) { + warn("Knockout groups not supported."); + } + const currentTransform = getCurrentTransform(currentCtx); + if (group.matrix) { + currentCtx.transform(...group.matrix); + } + if (!group.bbox) { + throw new Error("Bounding box is required."); + } + let bounds = Util.getAxialAlignedBoundingBox(group.bbox, getCurrentTransform(currentCtx)); + const canvasBounds = [0, 0, currentCtx.canvas.width, currentCtx.canvas.height]; + bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0]; + const offsetX = Math.floor(bounds[0]); + const offsetY = Math.floor(bounds[1]); + const drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1); + const drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1); + this.current.startNewPathAndClipBox([0, 0, drawnWidth, drawnHeight]); + let cacheId = "groupAt" + this.groupLevel; + if (group.smask) { + cacheId += "_smask_" + this.smaskCounter++ % 2; + } + const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight); + const groupCtx = scratchCanvas.context; + groupCtx.translate(-offsetX, -offsetY); + groupCtx.transform(...currentTransform); + if (group.smask) { + this.smaskStack.push({ + canvas: scratchCanvas.canvas, + context: groupCtx, + offsetX, + offsetY, + subtype: group.smask.subtype, + backdrop: group.smask.backdrop, + transferMap: group.smask.transferMap || null, + startTransformInverse: null + }); + } else { + currentCtx.setTransform(1, 0, 0, 1, 0, 0); + currentCtx.translate(offsetX, offsetY); + currentCtx.save(); + } + copyCtxState(currentCtx, groupCtx); + this.ctx = groupCtx; + this.setGState([["BM", "source-over"], ["ca", 1], ["CA", 1]]); + this.groupStack.push(currentCtx); + this.groupLevel++; + } + endGroup(group) { + if (!this.contentVisible) { + return; + } + this.groupLevel--; + const groupCtx = this.ctx; + const ctx = this.groupStack.pop(); + this.ctx = ctx; + this.ctx.imageSmoothingEnabled = false; + if (group.smask) { + this.tempSMask = this.smaskStack.pop(); + this.restore(); + } else { + this.ctx.restore(); + const currentMtx = getCurrentTransform(this.ctx); + this.restore(); + this.ctx.save(); + this.ctx.setTransform(...currentMtx); + const dirtyBox = Util.getAxialAlignedBoundingBox([0, 0, groupCtx.canvas.width, groupCtx.canvas.height], currentMtx); + this.ctx.drawImage(groupCtx.canvas, 0, 0); + this.ctx.restore(); + this.compose(dirtyBox); + } + } + beginAnnotation(id, rect, transform, matrix, hasOwnCanvas) { + this.#restoreInitialState(); + resetCtxToDefault(this.ctx); + this.ctx.save(); + this.save(); + if (this.baseTransform) { + this.ctx.setTransform(...this.baseTransform); + } + if (rect) { + const width = rect[2] - rect[0]; + const height = rect[3] - rect[1]; + if (hasOwnCanvas && this.annotationCanvasMap) { + transform = transform.slice(); + transform[4] -= rect[0]; + transform[5] -= rect[1]; + rect = rect.slice(); + rect[0] = rect[1] = 0; + rect[2] = width; + rect[3] = height; + const [scaleX, scaleY] = Util.singularValueDecompose2dScale(getCurrentTransform(this.ctx)); + const { + viewportScale + } = this; + const canvasWidth = Math.ceil(width * this.outputScaleX * viewportScale); + const canvasHeight = Math.ceil(height * this.outputScaleY * viewportScale); + this.annotationCanvas = this.canvasFactory.create(canvasWidth, canvasHeight); + const { + canvas, + context + } = this.annotationCanvas; + this.annotationCanvasMap.set(id, canvas); + this.annotationCanvas.savedCtx = this.ctx; + this.ctx = context; + this.ctx.save(); + this.ctx.setTransform(scaleX, 0, 0, -scaleY, 0, height * scaleY); + resetCtxToDefault(this.ctx); + } else { + resetCtxToDefault(this.ctx); + this.endPath(); + this.ctx.rect(rect[0], rect[1], width, height); + this.ctx.clip(); + this.ctx.beginPath(); + } + } + this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height); + this.transform(...transform); + this.transform(...matrix); + } + endAnnotation() { + if (this.annotationCanvas) { + this.ctx.restore(); + this.#drawFilter(); + this.ctx = this.annotationCanvas.savedCtx; + delete this.annotationCanvas.savedCtx; + delete this.annotationCanvas; + } + } + paintImageMaskXObject(img) { + if (!this.contentVisible) { + return; + } + const count = img.count; + img = this.getObject(img.data, img); + img.count = count; + const ctx = this.ctx; + const glyph = this.processingType3; + if (glyph) { + if (glyph.compiled === undefined) { + glyph.compiled = compileType3Glyph(img); + } + if (glyph.compiled) { + glyph.compiled(ctx); + return; + } + } + const mask = this._createMaskCanvas(img); + const maskCanvas = mask.canvas; + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY); + ctx.restore(); + this.compose(); + } + paintImageMaskXObjectRepeat(img, scaleX, skewX = 0, skewY = 0, scaleY, positions) { + if (!this.contentVisible) { + return; + } + img = this.getObject(img.data, img); + const ctx = this.ctx; + ctx.save(); + const currentTransform = getCurrentTransform(ctx); + ctx.transform(scaleX, skewX, skewY, scaleY, 0, 0); + const mask = this._createMaskCanvas(img); + ctx.setTransform(1, 0, 0, 1, mask.offsetX - currentTransform[4], mask.offsetY - currentTransform[5]); + for (let i = 0, ii = positions.length; i < ii; i += 2) { + const trans = Util.transform(currentTransform, [scaleX, skewX, skewY, scaleY, positions[i], positions[i + 1]]); + const [x, y] = Util.applyTransform([0, 0], trans); + ctx.drawImage(mask.canvas, x, y); + } + ctx.restore(); + this.compose(); + } + paintImageMaskXObjectGroup(images) { + if (!this.contentVisible) { + return; + } + const ctx = this.ctx; + const fillColor = this.current.fillColor; + const isPatternFill = this.current.patternFill; + for (const image of images) { + const { + data, + width, + height, + transform + } = image; + const maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height); + const maskCtx = maskCanvas.context; + maskCtx.save(); + const img = this.getObject(data, image); + putBinaryImageMask(maskCtx, img); + maskCtx.globalCompositeOperation = "source-in"; + maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this, getCurrentTransformInverse(ctx), PathType.FILL) : fillColor; + maskCtx.fillRect(0, 0, width, height); + maskCtx.restore(); + ctx.save(); + ctx.transform(...transform); + ctx.scale(1, -1); + drawImageAtIntegerCoords(ctx, maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1); + ctx.restore(); + } + this.compose(); + } + paintImageXObject(objId) { + if (!this.contentVisible) { + return; + } + const imgData = this.getObject(objId); + if (!imgData) { + warn("Dependent image isn't ready yet"); + return; + } + this.paintInlineImageXObject(imgData); + } + paintImageXObjectRepeat(objId, scaleX, scaleY, positions) { + if (!this.contentVisible) { + return; + } + const imgData = this.getObject(objId); + if (!imgData) { + warn("Dependent image isn't ready yet"); + return; + } + const width = imgData.width; + const height = imgData.height; + const map = []; + for (let i = 0, ii = positions.length; i < ii; i += 2) { + map.push({ + transform: [scaleX, 0, 0, scaleY, positions[i], positions[i + 1]], + x: 0, + y: 0, + w: width, + h: height + }); + } + this.paintInlineImageXObjectGroup(imgData, map); + } + applyTransferMapsToCanvas(ctx) { + if (this.current.transferMaps !== "none") { + ctx.filter = this.current.transferMaps; + ctx.drawImage(ctx.canvas, 0, 0); + ctx.filter = "none"; + } + return ctx.canvas; + } + applyTransferMapsToBitmap(imgData) { + if (this.current.transferMaps === "none") { + return imgData.bitmap; + } + const { + bitmap, + width, + height + } = imgData; + const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height); + const tmpCtx = tmpCanvas.context; + tmpCtx.filter = this.current.transferMaps; + tmpCtx.drawImage(bitmap, 0, 0); + tmpCtx.filter = "none"; + return tmpCanvas.canvas; + } + paintInlineImageXObject(imgData) { + if (!this.contentVisible) { + return; + } + const width = imgData.width; + const height = imgData.height; + const ctx = this.ctx; + this.save(); + if (!isNodeJS) { + const { + filter + } = ctx; + if (filter !== "none" && filter !== "") { + ctx.filter = "none"; + } + } + ctx.scale(1 / width, -1 / height); + let imgToPaint; + if (imgData.bitmap) { + imgToPaint = this.applyTransferMapsToBitmap(imgData); + } else if (typeof HTMLElement === "function" && imgData instanceof HTMLElement || !imgData.data) { + imgToPaint = imgData; + } else { + const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height); + const tmpCtx = tmpCanvas.context; + putBinaryImageData(tmpCtx, imgData); + imgToPaint = this.applyTransferMapsToCanvas(tmpCtx); + } + const scaled = this._scaleImage(imgToPaint, getCurrentTransformInverse(ctx)); + ctx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(ctx), imgData.interpolate); + drawImageAtIntegerCoords(ctx, scaled.img, 0, 0, scaled.paintWidth, scaled.paintHeight, 0, -height, width, height); + this.compose(); + this.restore(); + } + paintInlineImageXObjectGroup(imgData, map) { + if (!this.contentVisible) { + return; + } + const ctx = this.ctx; + let imgToPaint; + if (imgData.bitmap) { + imgToPaint = imgData.bitmap; + } else { + const w = imgData.width; + const h = imgData.height; + const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", w, h); + const tmpCtx = tmpCanvas.context; + putBinaryImageData(tmpCtx, imgData); + imgToPaint = this.applyTransferMapsToCanvas(tmpCtx); + } + for (const entry of map) { + ctx.save(); + ctx.transform(...entry.transform); + ctx.scale(1, -1); + drawImageAtIntegerCoords(ctx, imgToPaint, entry.x, entry.y, entry.w, entry.h, 0, -1, 1, 1); + ctx.restore(); + } + this.compose(); + } + paintSolidColorImageMask() { + if (!this.contentVisible) { + return; + } + this.ctx.fillRect(0, 0, 1, 1); + this.compose(); + } + markPoint(tag) {} + markPointProps(tag, properties) {} + beginMarkedContent(tag) { + this.markedContentStack.push({ + visible: true + }); + } + beginMarkedContentProps(tag, properties) { + if (tag === "OC") { + this.markedContentStack.push({ + visible: this.optionalContentConfig.isVisible(properties) + }); + } else { + this.markedContentStack.push({ + visible: true + }); + } + this.contentVisible = this.isContentVisible(); + } + endMarkedContent() { + this.markedContentStack.pop(); + this.contentVisible = this.isContentVisible(); + } + beginCompat() {} + endCompat() {} + consumePath(clipBox) { + const isEmpty = this.current.isEmptyClip(); + if (this.pendingClip) { + this.current.updateClipFromPath(); + } + if (!this.pendingClip) { + this.compose(clipBox); + } + const ctx = this.ctx; + if (this.pendingClip) { + if (!isEmpty) { + if (this.pendingClip === EO_CLIP) { + ctx.clip("evenodd"); + } else { + ctx.clip(); + } + } + this.pendingClip = null; + } + this.current.startNewPathAndClipBox(this.current.clipBox); + ctx.beginPath(); + } + getSinglePixelWidth() { + if (!this._cachedGetSinglePixelWidth) { + const m = getCurrentTransform(this.ctx); + if (m[1] === 0 && m[2] === 0) { + this._cachedGetSinglePixelWidth = 1 / Math.min(Math.abs(m[0]), Math.abs(m[3])); + } else { + const absDet = Math.abs(m[0] * m[3] - m[2] * m[1]); + const normX = Math.hypot(m[0], m[2]); + const normY = Math.hypot(m[1], m[3]); + this._cachedGetSinglePixelWidth = Math.max(normX, normY) / absDet; + } + } + return this._cachedGetSinglePixelWidth; + } + getScaleForStroking() { + if (this._cachedScaleForStroking[0] === -1) { + const { + lineWidth + } = this.current; + const { + a, + b, + c, + d + } = this.ctx.getTransform(); + let scaleX, scaleY; + if (b === 0 && c === 0) { + const normX = Math.abs(a); + const normY = Math.abs(d); + if (normX === normY) { + if (lineWidth === 0) { + scaleX = scaleY = 1 / normX; + } else { + const scaledLineWidth = normX * lineWidth; + scaleX = scaleY = scaledLineWidth < 1 ? 1 / scaledLineWidth : 1; + } + } else if (lineWidth === 0) { + scaleX = 1 / normX; + scaleY = 1 / normY; + } else { + const scaledXLineWidth = normX * lineWidth; + const scaledYLineWidth = normY * lineWidth; + scaleX = scaledXLineWidth < 1 ? 1 / scaledXLineWidth : 1; + scaleY = scaledYLineWidth < 1 ? 1 / scaledYLineWidth : 1; + } + } else { + const absDet = Math.abs(a * d - b * c); + const normX = Math.hypot(a, b); + const normY = Math.hypot(c, d); + if (lineWidth === 0) { + scaleX = normY / absDet; + scaleY = normX / absDet; + } else { + const baseArea = lineWidth * absDet; + scaleX = normY > baseArea ? normY / baseArea : 1; + scaleY = normX > baseArea ? normX / baseArea : 1; + } + } + this._cachedScaleForStroking[0] = scaleX; + this._cachedScaleForStroking[1] = scaleY; + } + return this._cachedScaleForStroking; + } + rescaleAndStroke(saveRestore) { + const { + ctx + } = this; + const { + lineWidth + } = this.current; + const [scaleX, scaleY] = this.getScaleForStroking(); + ctx.lineWidth = lineWidth || 1; + if (scaleX === 1 && scaleY === 1) { + ctx.stroke(); + return; + } + const dashes = ctx.getLineDash(); + if (saveRestore) { + ctx.save(); + } + ctx.scale(scaleX, scaleY); + if (dashes.length > 0) { + const scale = Math.max(scaleX, scaleY); + ctx.setLineDash(dashes.map(x => x / scale)); + ctx.lineDashOffset /= scale; + } + ctx.stroke(); + if (saveRestore) { + ctx.restore(); + } + } + isContentVisible() { + for (let i = this.markedContentStack.length - 1; i >= 0; i--) { + if (!this.markedContentStack[i].visible) { + return false; + } + } + return true; + } +} +for (const op in OPS) { + if (CanvasGraphics.prototype[op] !== undefined) { + CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op]; + } +} + +;// ./src/display/worker_options.js +class GlobalWorkerOptions { + static #port = null; + static #src = ""; + static get workerPort() { + return this.#port; + } + static set workerPort(val) { + if (!(typeof Worker !== "undefined" && val instanceof Worker) && val !== null) { + throw new Error("Invalid `workerPort` type."); + } + this.#port = val; + } + static get workerSrc() { + return this.#src; + } + static set workerSrc(val) { + if (typeof val !== "string") { + throw new Error("Invalid `workerSrc` type."); + } + this.#src = val; + } +} + +;// ./src/display/metadata.js + +class Metadata { + #metadataMap; + #data; + constructor({ + parsedData, + rawData + }) { + this.#metadataMap = parsedData; + this.#data = rawData; + } + getRaw() { + return this.#data; + } + get(name) { + return this.#metadataMap.get(name) ?? null; + } + getAll() { + return objectFromMap(this.#metadataMap); + } + has(name) { + return this.#metadataMap.has(name); + } +} + +;// ./src/display/optional_content_config.js + + +const INTERNAL = Symbol("INTERNAL"); +class OptionalContentGroup { + #isDisplay = false; + #isPrint = false; + #userSet = false; + #visible = true; + constructor(renderingIntent, { + name, + intent, + usage, + rbGroups + }) { + this.#isDisplay = !!(renderingIntent & RenderingIntentFlag.DISPLAY); + this.#isPrint = !!(renderingIntent & RenderingIntentFlag.PRINT); + this.name = name; + this.intent = intent; + this.usage = usage; + this.rbGroups = rbGroups; + } + get visible() { + if (this.#userSet) { + return this.#visible; + } + if (!this.#visible) { + return false; + } + const { + print, + view + } = this.usage; + if (this.#isDisplay) { + return view?.viewState !== "OFF"; + } else if (this.#isPrint) { + return print?.printState !== "OFF"; + } + return true; + } + _setVisible(internal, visible, userSet = false) { + if (internal !== INTERNAL) { + unreachable("Internal method `_setVisible` called."); + } + this.#userSet = userSet; + this.#visible = visible; + } +} +class OptionalContentConfig { + #cachedGetHash = null; + #groups = new Map(); + #initialHash = null; + #order = null; + constructor(data, renderingIntent = RenderingIntentFlag.DISPLAY) { + this.renderingIntent = renderingIntent; + this.name = null; + this.creator = null; + if (data === null) { + return; + } + this.name = data.name; + this.creator = data.creator; + this.#order = data.order; + for (const group of data.groups) { + this.#groups.set(group.id, new OptionalContentGroup(renderingIntent, group)); + } + if (data.baseState === "OFF") { + for (const group of this.#groups.values()) { + group._setVisible(INTERNAL, false); + } + } + for (const on of data.on) { + this.#groups.get(on)._setVisible(INTERNAL, true); + } + for (const off of data.off) { + this.#groups.get(off)._setVisible(INTERNAL, false); + } + this.#initialHash = this.getHash(); + } + #evaluateVisibilityExpression(array) { + const length = array.length; + if (length < 2) { + return true; + } + const operator = array[0]; + for (let i = 1; i < length; i++) { + const element = array[i]; + let state; + if (Array.isArray(element)) { + state = this.#evaluateVisibilityExpression(element); + } else if (this.#groups.has(element)) { + state = this.#groups.get(element).visible; + } else { + warn(`Optional content group not found: ${element}`); + return true; + } + switch (operator) { + case "And": + if (!state) { + return false; + } + break; + case "Or": + if (state) { + return true; + } + break; + case "Not": + return !state; + default: + return true; + } + } + return operator === "And"; + } + isVisible(group) { + if (this.#groups.size === 0) { + return true; + } + if (!group) { + info("Optional content group not defined."); + return true; + } + if (group.type === "OCG") { + if (!this.#groups.has(group.id)) { + warn(`Optional content group not found: ${group.id}`); + return true; + } + return this.#groups.get(group.id).visible; + } else if (group.type === "OCMD") { + if (group.expression) { + return this.#evaluateVisibilityExpression(group.expression); + } + if (!group.policy || group.policy === "AnyOn") { + for (const id of group.ids) { + if (!this.#groups.has(id)) { + warn(`Optional content group not found: ${id}`); + return true; + } + if (this.#groups.get(id).visible) { + return true; + } + } + return false; + } else if (group.policy === "AllOn") { + for (const id of group.ids) { + if (!this.#groups.has(id)) { + warn(`Optional content group not found: ${id}`); + return true; + } + if (!this.#groups.get(id).visible) { + return false; + } + } + return true; + } else if (group.policy === "AnyOff") { + for (const id of group.ids) { + if (!this.#groups.has(id)) { + warn(`Optional content group not found: ${id}`); + return true; + } + if (!this.#groups.get(id).visible) { + return true; + } + } + return false; + } else if (group.policy === "AllOff") { + for (const id of group.ids) { + if (!this.#groups.has(id)) { + warn(`Optional content group not found: ${id}`); + return true; + } + if (this.#groups.get(id).visible) { + return false; + } + } + return true; + } + warn(`Unknown optional content policy ${group.policy}.`); + return true; + } + warn(`Unknown group type ${group.type}.`); + return true; + } + setVisibility(id, visible = true, preserveRB = true) { + const group = this.#groups.get(id); + if (!group) { + warn(`Optional content group not found: ${id}`); + return; + } + if (preserveRB && visible && group.rbGroups.length) { + for (const rbGroup of group.rbGroups) { + for (const otherId of rbGroup) { + if (otherId !== id) { + this.#groups.get(otherId)?._setVisible(INTERNAL, false, true); + } + } + } + } + group._setVisible(INTERNAL, !!visible, true); + this.#cachedGetHash = null; + } + setOCGState({ + state, + preserveRB + }) { + let operator; + for (const elem of state) { + switch (elem) { + case "ON": + case "OFF": + case "Toggle": + operator = elem; + continue; + } + const group = this.#groups.get(elem); + if (!group) { + continue; + } + switch (operator) { + case "ON": + this.setVisibility(elem, true, preserveRB); + break; + case "OFF": + this.setVisibility(elem, false, preserveRB); + break; + case "Toggle": + this.setVisibility(elem, !group.visible, preserveRB); + break; + } + } + this.#cachedGetHash = null; + } + get hasInitialVisibility() { + return this.#initialHash === null || this.getHash() === this.#initialHash; + } + getOrder() { + if (!this.#groups.size) { + return null; + } + if (this.#order) { + return this.#order.slice(); + } + return [...this.#groups.keys()]; + } + getGroups() { + return this.#groups.size > 0 ? objectFromMap(this.#groups) : null; + } + getGroup(id) { + return this.#groups.get(id) || null; + } + getHash() { + if (this.#cachedGetHash !== null) { + return this.#cachedGetHash; + } + const hash = new MurmurHash3_64(); + for (const [id, group] of this.#groups) { + hash.update(`${id}:${group.visible}`); + } + return this.#cachedGetHash = hash.hexdigest(); + } +} + +;// ./src/display/transport_stream.js + + +class PDFDataTransportStream { + constructor(pdfDataRangeTransport, { + disableRange = false, + disableStream = false + }) { + assert(pdfDataRangeTransport, 'PDFDataTransportStream - missing required "pdfDataRangeTransport" argument.'); + const { + length, + initialData, + progressiveDone, + contentDispositionFilename + } = pdfDataRangeTransport; + this._queuedChunks = []; + this._progressiveDone = progressiveDone; + this._contentDispositionFilename = contentDispositionFilename; + if (initialData?.length > 0) { + const buffer = initialData instanceof Uint8Array && initialData.byteLength === initialData.buffer.byteLength ? initialData.buffer : new Uint8Array(initialData).buffer; + this._queuedChunks.push(buffer); + } + this._pdfDataRangeTransport = pdfDataRangeTransport; + this._isStreamingSupported = !disableStream; + this._isRangeSupported = !disableRange; + this._contentLength = length; + this._fullRequestReader = null; + this._rangeReaders = []; + pdfDataRangeTransport.addRangeListener((begin, chunk) => { + this._onReceiveData({ + begin, + chunk + }); + }); + pdfDataRangeTransport.addProgressListener((loaded, total) => { + this._onProgress({ + loaded, + total + }); + }); + pdfDataRangeTransport.addProgressiveReadListener(chunk => { + this._onReceiveData({ + chunk + }); + }); + pdfDataRangeTransport.addProgressiveDoneListener(() => { + this._onProgressiveDone(); + }); + pdfDataRangeTransport.transportReady(); + } + _onReceiveData({ + begin, + chunk + }) { + const buffer = chunk instanceof Uint8Array && chunk.byteLength === chunk.buffer.byteLength ? chunk.buffer : new Uint8Array(chunk).buffer; + if (begin === undefined) { + if (this._fullRequestReader) { + this._fullRequestReader._enqueue(buffer); + } else { + this._queuedChunks.push(buffer); + } + } else { + const found = this._rangeReaders.some(function (rangeReader) { + if (rangeReader._begin !== begin) { + return false; + } + rangeReader._enqueue(buffer); + return true; + }); + assert(found, "_onReceiveData - no `PDFDataTransportStreamRangeReader` instance found."); + } + } + get _progressiveDataLength() { + return this._fullRequestReader?._loaded ?? 0; + } + _onProgress(evt) { + if (evt.total === undefined) { + this._rangeReaders[0]?.onProgress?.({ + loaded: evt.loaded + }); + } else { + this._fullRequestReader?.onProgress?.({ + loaded: evt.loaded, + total: evt.total + }); + } + } + _onProgressiveDone() { + this._fullRequestReader?.progressiveDone(); + this._progressiveDone = true; + } + _removeRangeReader(reader) { + const i = this._rangeReaders.indexOf(reader); + if (i >= 0) { + this._rangeReaders.splice(i, 1); + } + } + getFullReader() { + assert(!this._fullRequestReader, "PDFDataTransportStream.getFullReader can only be called once."); + const queuedChunks = this._queuedChunks; + this._queuedChunks = null; + return new PDFDataTransportStreamReader(this, queuedChunks, this._progressiveDone, this._contentDispositionFilename); + } + getRangeReader(begin, end) { + if (end <= this._progressiveDataLength) { + return null; + } + const reader = new PDFDataTransportStreamRangeReader(this, begin, end); + this._pdfDataRangeTransport.requestDataRange(begin, end); + this._rangeReaders.push(reader); + return reader; + } + cancelAllRequests(reason) { + this._fullRequestReader?.cancel(reason); + for (const reader of this._rangeReaders.slice(0)) { + reader.cancel(reason); + } + this._pdfDataRangeTransport.abort(); + } +} +class PDFDataTransportStreamReader { + constructor(stream, queuedChunks, progressiveDone = false, contentDispositionFilename = null) { + this._stream = stream; + this._done = progressiveDone || false; + this._filename = isPdfFile(contentDispositionFilename) ? contentDispositionFilename : null; + this._queuedChunks = queuedChunks || []; + this._loaded = 0; + for (const chunk of this._queuedChunks) { + this._loaded += chunk.byteLength; + } + this._requests = []; + this._headersReady = Promise.resolve(); + stream._fullRequestReader = this; + this.onProgress = null; + } + _enqueue(chunk) { + if (this._done) { + return; + } + if (this._requests.length > 0) { + const requestCapability = this._requests.shift(); + requestCapability.resolve({ + value: chunk, + done: false + }); + } else { + this._queuedChunks.push(chunk); + } + this._loaded += chunk.byteLength; + } + get headersReady() { + return this._headersReady; + } + get filename() { + return this._filename; + } + get isRangeSupported() { + return this._stream._isRangeSupported; + } + get isStreamingSupported() { + return this._stream._isStreamingSupported; + } + get contentLength() { + return this._stream._contentLength; + } + async read() { + if (this._queuedChunks.length > 0) { + const chunk = this._queuedChunks.shift(); + return { + value: chunk, + done: false + }; + } + if (this._done) { + return { + value: undefined, + done: true + }; + } + const requestCapability = Promise.withResolvers(); + this._requests.push(requestCapability); + return requestCapability.promise; + } + cancel(reason) { + this._done = true; + for (const requestCapability of this._requests) { + requestCapability.resolve({ + value: undefined, + done: true + }); + } + this._requests.length = 0; + } + progressiveDone() { + if (this._done) { + return; + } + this._done = true; + } +} +class PDFDataTransportStreamRangeReader { + constructor(stream, begin, end) { + this._stream = stream; + this._begin = begin; + this._end = end; + this._queuedChunk = null; + this._requests = []; + this._done = false; + this.onProgress = null; + } + _enqueue(chunk) { + if (this._done) { + return; + } + if (this._requests.length === 0) { + this._queuedChunk = chunk; + } else { + const requestsCapability = this._requests.shift(); + requestsCapability.resolve({ + value: chunk, + done: false + }); + for (const requestCapability of this._requests) { + requestCapability.resolve({ + value: undefined, + done: true + }); + } + this._requests.length = 0; + } + this._done = true; + this._stream._removeRangeReader(this); + } + get isStreamingSupported() { + return false; + } + async read() { + if (this._queuedChunk) { + const chunk = this._queuedChunk; + this._queuedChunk = null; + return { + value: chunk, + done: false + }; + } + if (this._done) { + return { + value: undefined, + done: true + }; + } + const requestCapability = Promise.withResolvers(); + this._requests.push(requestCapability); + return requestCapability.promise; + } + cancel(reason) { + this._done = true; + for (const requestCapability of this._requests) { + requestCapability.resolve({ + value: undefined, + done: true + }); + } + this._requests.length = 0; + this._stream._removeRangeReader(this); + } +} + +;// ./src/display/content_disposition.js + +function getFilenameFromContentDispositionHeader(contentDisposition) { + let needsEncodingFixup = true; + let tmp = toParamRegExp("filename\\*", "i").exec(contentDisposition); + if (tmp) { + tmp = tmp[1]; + let filename = rfc2616unquote(tmp); + filename = unescape(filename); + filename = rfc5987decode(filename); + filename = rfc2047decode(filename); + return fixupEncoding(filename); + } + tmp = rfc2231getparam(contentDisposition); + if (tmp) { + const filename = rfc2047decode(tmp); + return fixupEncoding(filename); + } + tmp = toParamRegExp("filename", "i").exec(contentDisposition); + if (tmp) { + tmp = tmp[1]; + let filename = rfc2616unquote(tmp); + filename = rfc2047decode(filename); + return fixupEncoding(filename); + } + function toParamRegExp(attributePattern, flags) { + return new RegExp("(?:^|;)\\s*" + attributePattern + "\\s*=\\s*" + "(" + '[^";\\s][^;\\s]*' + "|" + '"(?:[^"\\\\]|\\\\"?)+"?' + ")", flags); + } + function textdecode(encoding, value) { + if (encoding) { + if (!/^[\x00-\xFF]+$/.test(value)) { + return value; + } + try { + const decoder = new TextDecoder(encoding, { + fatal: true + }); + const buffer = stringToBytes(value); + value = decoder.decode(buffer); + needsEncodingFixup = false; + } catch {} + } + return value; + } + function fixupEncoding(value) { + if (needsEncodingFixup && /[\x80-\xff]/.test(value)) { + value = textdecode("utf-8", value); + if (needsEncodingFixup) { + value = textdecode("iso-8859-1", value); + } + } + return value; + } + function rfc2231getparam(contentDispositionStr) { + const matches = []; + let match; + const iter = toParamRegExp("filename\\*((?!0\\d)\\d+)(\\*?)", "ig"); + while ((match = iter.exec(contentDispositionStr)) !== null) { + let [, n, quot, part] = match; + n = parseInt(n, 10); + if (n in matches) { + if (n === 0) { + break; + } + continue; + } + matches[n] = [quot, part]; + } + const parts = []; + for (let n = 0; n < matches.length; ++n) { + if (!(n in matches)) { + break; + } + let [quot, part] = matches[n]; + part = rfc2616unquote(part); + if (quot) { + part = unescape(part); + if (n === 0) { + part = rfc5987decode(part); + } + } + parts.push(part); + } + return parts.join(""); + } + function rfc2616unquote(value) { + if (value.startsWith('"')) { + const parts = value.slice(1).split('\\"'); + for (let i = 0; i < parts.length; ++i) { + const quotindex = parts[i].indexOf('"'); + if (quotindex !== -1) { + parts[i] = parts[i].slice(0, quotindex); + parts.length = i + 1; + } + parts[i] = parts[i].replaceAll(/\\(.)/g, "$1"); + } + value = parts.join('"'); + } + return value; + } + function rfc5987decode(extvalue) { + const encodingend = extvalue.indexOf("'"); + if (encodingend === -1) { + return extvalue; + } + const encoding = extvalue.slice(0, encodingend); + const langvalue = extvalue.slice(encodingend + 1); + const value = langvalue.replace(/^[^']*'/, ""); + return textdecode(encoding, value); + } + function rfc2047decode(value) { + if (!value.startsWith("=?") || /[\x00-\x19\x80-\xff]/.test(value)) { + return value; + } + return value.replaceAll(/=\?([\w-]*)\?([QqBb])\?((?:[^?]|\?(?!=))*)\?=/g, function (matches, charset, encoding, text) { + if (encoding === "q" || encoding === "Q") { + text = text.replaceAll("_", " "); + text = text.replaceAll(/=([0-9a-fA-F]{2})/g, function (match, hex) { + return String.fromCharCode(parseInt(hex, 16)); + }); + return textdecode(charset, text); + } + try { + text = atob(text); + } catch {} + return textdecode(charset, text); + }); + } + return ""; +} + +;// ./src/display/network_utils.js + + + +function createHeaders(isHttp, httpHeaders) { + const headers = new Headers(); + if (!isHttp || !httpHeaders || typeof httpHeaders !== "object") { + return headers; + } + for (const key in httpHeaders) { + const val = httpHeaders[key]; + if (val !== undefined) { + headers.append(key, val); + } + } + return headers; +} +function getResponseOrigin(url) { + try { + return new URL(url).origin; + } catch {} + return null; +} +function validateRangeRequestCapabilities({ + responseHeaders, + isHttp, + rangeChunkSize, + disableRange +}) { + const returnValues = { + allowRangeRequests: false, + suggestedLength: undefined + }; + const length = parseInt(responseHeaders.get("Content-Length"), 10); + if (!Number.isInteger(length)) { + return returnValues; + } + returnValues.suggestedLength = length; + if (length <= 2 * rangeChunkSize) { + return returnValues; + } + if (disableRange || !isHttp) { + return returnValues; + } + if (responseHeaders.get("Accept-Ranges") !== "bytes") { + return returnValues; + } + const contentEncoding = responseHeaders.get("Content-Encoding") || "identity"; + if (contentEncoding !== "identity") { + return returnValues; + } + returnValues.allowRangeRequests = true; + return returnValues; +} +function extractFilenameFromHeader(responseHeaders) { + const contentDisposition = responseHeaders.get("Content-Disposition"); + if (contentDisposition) { + let filename = getFilenameFromContentDispositionHeader(contentDisposition); + if (filename.includes("%")) { + try { + filename = decodeURIComponent(filename); + } catch {} + } + if (isPdfFile(filename)) { + return filename; + } + } + return null; +} +function createResponseStatusError(status, url) { + if (status === 404 || status === 0 && url.startsWith("file:")) { + return new MissingPDFException('Missing PDF "' + url + '".'); + } + return new UnexpectedResponseException(`Unexpected server response (${status}) while retrieving PDF "${url}".`, status); +} +function validateResponseStatus(status) { + return status === 200 || status === 206; +} + +;// ./src/display/fetch_stream.js + + +function createFetchOptions(headers, withCredentials, abortController) { + return { + method: "GET", + headers, + signal: abortController.signal, + mode: "cors", + credentials: withCredentials ? "include" : "same-origin", + redirect: "follow" + }; +} +function getArrayBuffer(val) { + if (val instanceof Uint8Array) { + return val.buffer; + } + if (val instanceof ArrayBuffer) { + return val; + } + warn(`getArrayBuffer - unexpected data format: ${val}`); + return new Uint8Array(val).buffer; +} +class PDFFetchStream { + _responseOrigin = null; + constructor(source) { + this.source = source; + this.isHttp = /^https?:/i.test(source.url); + this.headers = createHeaders(this.isHttp, source.httpHeaders); + this._fullRequestReader = null; + this._rangeRequestReaders = []; + } + get _progressiveDataLength() { + return this._fullRequestReader?._loaded ?? 0; + } + getFullReader() { + assert(!this._fullRequestReader, "PDFFetchStream.getFullReader can only be called once."); + this._fullRequestReader = new PDFFetchStreamReader(this); + return this._fullRequestReader; + } + getRangeReader(begin, end) { + if (end <= this._progressiveDataLength) { + return null; + } + const reader = new PDFFetchStreamRangeReader(this, begin, end); + this._rangeRequestReaders.push(reader); + return reader; + } + cancelAllRequests(reason) { + this._fullRequestReader?.cancel(reason); + for (const reader of this._rangeRequestReaders.slice(0)) { + reader.cancel(reason); + } + } +} +class PDFFetchStreamReader { + constructor(stream) { + this._stream = stream; + this._reader = null; + this._loaded = 0; + this._filename = null; + const source = stream.source; + this._withCredentials = source.withCredentials || false; + this._contentLength = source.length; + this._headersCapability = Promise.withResolvers(); + this._disableRange = source.disableRange || false; + this._rangeChunkSize = source.rangeChunkSize; + if (!this._rangeChunkSize && !this._disableRange) { + this._disableRange = true; + } + this._abortController = new AbortController(); + this._isStreamingSupported = !source.disableStream; + this._isRangeSupported = !source.disableRange; + const headers = new Headers(stream.headers); + const url = source.url; + fetch(url, createFetchOptions(headers, this._withCredentials, this._abortController)).then(response => { + stream._responseOrigin = getResponseOrigin(response.url); + if (!validateResponseStatus(response.status)) { + throw createResponseStatusError(response.status, url); + } + this._reader = response.body.getReader(); + this._headersCapability.resolve(); + const responseHeaders = response.headers; + const { + allowRangeRequests, + suggestedLength + } = validateRangeRequestCapabilities({ + responseHeaders, + isHttp: stream.isHttp, + rangeChunkSize: this._rangeChunkSize, + disableRange: this._disableRange + }); + this._isRangeSupported = allowRangeRequests; + this._contentLength = suggestedLength || this._contentLength; + this._filename = extractFilenameFromHeader(responseHeaders); + if (!this._isStreamingSupported && this._isRangeSupported) { + this.cancel(new AbortException("Streaming is disabled.")); + } + }).catch(this._headersCapability.reject); + this.onProgress = null; + } + get headersReady() { + return this._headersCapability.promise; + } + get filename() { + return this._filename; + } + get contentLength() { + return this._contentLength; + } + get isRangeSupported() { + return this._isRangeSupported; + } + get isStreamingSupported() { + return this._isStreamingSupported; + } + async read() { + await this._headersCapability.promise; + const { + value, + done + } = await this._reader.read(); + if (done) { + return { + value, + done + }; + } + this._loaded += value.byteLength; + this.onProgress?.({ + loaded: this._loaded, + total: this._contentLength + }); + return { + value: getArrayBuffer(value), + done: false + }; + } + cancel(reason) { + this._reader?.cancel(reason); + this._abortController.abort(); + } +} +class PDFFetchStreamRangeReader { + constructor(stream, begin, end) { + this._stream = stream; + this._reader = null; + this._loaded = 0; + const source = stream.source; + this._withCredentials = source.withCredentials || false; + this._readCapability = Promise.withResolvers(); + this._isStreamingSupported = !source.disableStream; + this._abortController = new AbortController(); + const headers = new Headers(stream.headers); + headers.append("Range", `bytes=${begin}-${end - 1}`); + const url = source.url; + fetch(url, createFetchOptions(headers, this._withCredentials, this._abortController)).then(response => { + const responseOrigin = getResponseOrigin(response.url); + if (responseOrigin !== stream._responseOrigin) { + throw new Error(`Expected range response-origin "${responseOrigin}" to match "${stream._responseOrigin}".`); + } + if (!validateResponseStatus(response.status)) { + throw createResponseStatusError(response.status, url); + } + this._readCapability.resolve(); + this._reader = response.body.getReader(); + }).catch(this._readCapability.reject); + this.onProgress = null; + } + get isStreamingSupported() { + return this._isStreamingSupported; + } + async read() { + await this._readCapability.promise; + const { + value, + done + } = await this._reader.read(); + if (done) { + return { + value, + done + }; + } + this._loaded += value.byteLength; + this.onProgress?.({ + loaded: this._loaded + }); + return { + value: getArrayBuffer(value), + done: false + }; + } + cancel(reason) { + this._reader?.cancel(reason); + this._abortController.abort(); + } +} + +;// ./src/display/network.js + + +const OK_RESPONSE = 200; +const PARTIAL_CONTENT_RESPONSE = 206; +function network_getArrayBuffer(xhr) { + const data = xhr.response; + if (typeof data !== "string") { + return data; + } + return stringToBytes(data).buffer; +} +class NetworkManager { + _responseOrigin = null; + constructor({ + url, + httpHeaders, + withCredentials + }) { + this.url = url; + this.isHttp = /^https?:/i.test(url); + this.headers = createHeaders(this.isHttp, httpHeaders); + this.withCredentials = withCredentials || false; + this.currXhrId = 0; + this.pendingRequests = Object.create(null); + } + request(args) { + const xhr = new XMLHttpRequest(); + const xhrId = this.currXhrId++; + const pendingRequest = this.pendingRequests[xhrId] = { + xhr + }; + xhr.open("GET", this.url); + xhr.withCredentials = this.withCredentials; + for (const [key, val] of this.headers) { + xhr.setRequestHeader(key, val); + } + if (this.isHttp && "begin" in args && "end" in args) { + xhr.setRequestHeader("Range", `bytes=${args.begin}-${args.end - 1}`); + pendingRequest.expectedStatus = PARTIAL_CONTENT_RESPONSE; + } else { + pendingRequest.expectedStatus = OK_RESPONSE; + } + xhr.responseType = "arraybuffer"; + assert(args.onError, "Expected `onError` callback to be provided."); + xhr.onerror = () => { + args.onError(xhr.status); + }; + xhr.onreadystatechange = this.onStateChange.bind(this, xhrId); + xhr.onprogress = this.onProgress.bind(this, xhrId); + pendingRequest.onHeadersReceived = args.onHeadersReceived; + pendingRequest.onDone = args.onDone; + pendingRequest.onError = args.onError; + pendingRequest.onProgress = args.onProgress; + xhr.send(null); + return xhrId; + } + onProgress(xhrId, evt) { + const pendingRequest = this.pendingRequests[xhrId]; + if (!pendingRequest) { + return; + } + pendingRequest.onProgress?.(evt); + } + onStateChange(xhrId, evt) { + const pendingRequest = this.pendingRequests[xhrId]; + if (!pendingRequest) { + return; + } + const xhr = pendingRequest.xhr; + if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) { + pendingRequest.onHeadersReceived(); + delete pendingRequest.onHeadersReceived; + } + if (xhr.readyState !== 4) { + return; + } + if (!(xhrId in this.pendingRequests)) { + return; + } + delete this.pendingRequests[xhrId]; + if (xhr.status === 0 && this.isHttp) { + pendingRequest.onError(xhr.status); + return; + } + const xhrStatus = xhr.status || OK_RESPONSE; + const ok_response_on_range_request = xhrStatus === OK_RESPONSE && pendingRequest.expectedStatus === PARTIAL_CONTENT_RESPONSE; + if (!ok_response_on_range_request && xhrStatus !== pendingRequest.expectedStatus) { + pendingRequest.onError(xhr.status); + return; + } + const chunk = network_getArrayBuffer(xhr); + if (xhrStatus === PARTIAL_CONTENT_RESPONSE) { + const rangeHeader = xhr.getResponseHeader("Content-Range"); + const matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader); + if (matches) { + pendingRequest.onDone({ + begin: parseInt(matches[1], 10), + chunk + }); + } else { + warn(`Missing or invalid "Content-Range" header.`); + pendingRequest.onError(0); + } + } else if (chunk) { + pendingRequest.onDone({ + begin: 0, + chunk + }); + } else { + pendingRequest.onError(xhr.status); + } + } + getRequestXhr(xhrId) { + return this.pendingRequests[xhrId].xhr; + } + isPendingRequest(xhrId) { + return xhrId in this.pendingRequests; + } + abortRequest(xhrId) { + const xhr = this.pendingRequests[xhrId].xhr; + delete this.pendingRequests[xhrId]; + xhr.abort(); + } +} +class PDFNetworkStream { + constructor(source) { + this._source = source; + this._manager = new NetworkManager(source); + this._rangeChunkSize = source.rangeChunkSize; + this._fullRequestReader = null; + this._rangeRequestReaders = []; + } + _onRangeRequestReaderClosed(reader) { + const i = this._rangeRequestReaders.indexOf(reader); + if (i >= 0) { + this._rangeRequestReaders.splice(i, 1); + } + } + getFullReader() { + assert(!this._fullRequestReader, "PDFNetworkStream.getFullReader can only be called once."); + this._fullRequestReader = new PDFNetworkStreamFullRequestReader(this._manager, this._source); + return this._fullRequestReader; + } + getRangeReader(begin, end) { + const reader = new PDFNetworkStreamRangeRequestReader(this._manager, begin, end); + reader.onClosed = this._onRangeRequestReaderClosed.bind(this); + this._rangeRequestReaders.push(reader); + return reader; + } + cancelAllRequests(reason) { + this._fullRequestReader?.cancel(reason); + for (const reader of this._rangeRequestReaders.slice(0)) { + reader.cancel(reason); + } + } +} +class PDFNetworkStreamFullRequestReader { + constructor(manager, source) { + this._manager = manager; + this._url = source.url; + this._fullRequestId = manager.request({ + onHeadersReceived: this._onHeadersReceived.bind(this), + onDone: this._onDone.bind(this), + onError: this._onError.bind(this), + onProgress: this._onProgress.bind(this) + }); + this._headersCapability = Promise.withResolvers(); + this._disableRange = source.disableRange || false; + this._contentLength = source.length; + this._rangeChunkSize = source.rangeChunkSize; + if (!this._rangeChunkSize && !this._disableRange) { + this._disableRange = true; + } + this._isStreamingSupported = false; + this._isRangeSupported = false; + this._cachedChunks = []; + this._requests = []; + this._done = false; + this._storedError = undefined; + this._filename = null; + this.onProgress = null; + } + _onHeadersReceived() { + const fullRequestXhrId = this._fullRequestId; + const fullRequestXhr = this._manager.getRequestXhr(fullRequestXhrId); + this._manager._responseOrigin = getResponseOrigin(fullRequestXhr.responseURL); + const rawResponseHeaders = fullRequestXhr.getAllResponseHeaders(); + const responseHeaders = new Headers(rawResponseHeaders ? rawResponseHeaders.trimStart().replace(/[^\S ]+$/, "").split(/[\r\n]+/).map(x => { + const [key, ...val] = x.split(": "); + return [key, val.join(": ")]; + }) : []); + const { + allowRangeRequests, + suggestedLength + } = validateRangeRequestCapabilities({ + responseHeaders, + isHttp: this._manager.isHttp, + rangeChunkSize: this._rangeChunkSize, + disableRange: this._disableRange + }); + if (allowRangeRequests) { + this._isRangeSupported = true; + } + this._contentLength = suggestedLength || this._contentLength; + this._filename = extractFilenameFromHeader(responseHeaders); + if (this._isRangeSupported) { + this._manager.abortRequest(fullRequestXhrId); + } + this._headersCapability.resolve(); + } + _onDone(data) { + if (data) { + if (this._requests.length > 0) { + const requestCapability = this._requests.shift(); + requestCapability.resolve({ + value: data.chunk, + done: false + }); + } else { + this._cachedChunks.push(data.chunk); + } + } + this._done = true; + if (this._cachedChunks.length > 0) { + return; + } + for (const requestCapability of this._requests) { + requestCapability.resolve({ + value: undefined, + done: true + }); + } + this._requests.length = 0; + } + _onError(status) { + this._storedError = createResponseStatusError(status, this._url); + this._headersCapability.reject(this._storedError); + for (const requestCapability of this._requests) { + requestCapability.reject(this._storedError); + } + this._requests.length = 0; + this._cachedChunks.length = 0; + } + _onProgress(evt) { + this.onProgress?.({ + loaded: evt.loaded, + total: evt.lengthComputable ? evt.total : this._contentLength + }); + } + get filename() { + return this._filename; + } + get isRangeSupported() { + return this._isRangeSupported; + } + get isStreamingSupported() { + return this._isStreamingSupported; + } + get contentLength() { + return this._contentLength; + } + get headersReady() { + return this._headersCapability.promise; + } + async read() { + await this._headersCapability.promise; + if (this._storedError) { + throw this._storedError; + } + if (this._cachedChunks.length > 0) { + const chunk = this._cachedChunks.shift(); + return { + value: chunk, + done: false + }; + } + if (this._done) { + return { + value: undefined, + done: true + }; + } + const requestCapability = Promise.withResolvers(); + this._requests.push(requestCapability); + return requestCapability.promise; + } + cancel(reason) { + this._done = true; + this._headersCapability.reject(reason); + for (const requestCapability of this._requests) { + requestCapability.resolve({ + value: undefined, + done: true + }); + } + this._requests.length = 0; + if (this._manager.isPendingRequest(this._fullRequestId)) { + this._manager.abortRequest(this._fullRequestId); + } + this._fullRequestReader = null; + } +} +class PDFNetworkStreamRangeRequestReader { + constructor(manager, begin, end) { + this._manager = manager; + this._url = manager.url; + this._requestId = manager.request({ + begin, + end, + onHeadersReceived: this._onHeadersReceived.bind(this), + onDone: this._onDone.bind(this), + onError: this._onError.bind(this), + onProgress: this._onProgress.bind(this) + }); + this._requests = []; + this._queuedChunk = null; + this._done = false; + this._storedError = undefined; + this.onProgress = null; + this.onClosed = null; + } + _onHeadersReceived() { + const responseOrigin = getResponseOrigin(this._manager.getRequestXhr(this._requestId)?.responseURL); + if (responseOrigin !== this._manager._responseOrigin) { + this._storedError = new Error(`Expected range response-origin "${responseOrigin}" to match "${this._manager._responseOrigin}".`); + this._onError(0); + } + } + _close() { + this.onClosed?.(this); + } + _onDone(data) { + const chunk = data.chunk; + if (this._requests.length > 0) { + const requestCapability = this._requests.shift(); + requestCapability.resolve({ + value: chunk, + done: false + }); + } else { + this._queuedChunk = chunk; + } + this._done = true; + for (const requestCapability of this._requests) { + requestCapability.resolve({ + value: undefined, + done: true + }); + } + this._requests.length = 0; + this._close(); + } + _onError(status) { + this._storedError ??= createResponseStatusError(status, this._url); + for (const requestCapability of this._requests) { + requestCapability.reject(this._storedError); + } + this._requests.length = 0; + this._queuedChunk = null; + } + _onProgress(evt) { + if (!this.isStreamingSupported) { + this.onProgress?.({ + loaded: evt.loaded + }); + } + } + get isStreamingSupported() { + return false; + } + async read() { + if (this._storedError) { + throw this._storedError; + } + if (this._queuedChunk !== null) { + const chunk = this._queuedChunk; + this._queuedChunk = null; + return { + value: chunk, + done: false + }; + } + if (this._done) { + return { + value: undefined, + done: true + }; + } + const requestCapability = Promise.withResolvers(); + this._requests.push(requestCapability); + return requestCapability.promise; + } + cancel(reason) { + this._done = true; + for (const requestCapability of this._requests) { + requestCapability.resolve({ + value: undefined, + done: true + }); + } + this._requests.length = 0; + if (this._manager.isPendingRequest(this._requestId)) { + this._manager.abortRequest(this._requestId); + } + this._close(); + } +} + +;// ./src/display/node_stream.js + +const urlRegex = /^[a-z][a-z0-9\-+.]+:/i; +function parseUrlOrPath(sourceUrl) { + if (urlRegex.test(sourceUrl)) { + return new URL(sourceUrl); + } + const url = process.getBuiltinModule("url"); + return new URL(url.pathToFileURL(sourceUrl)); +} +class PDFNodeStream { + constructor(source) { + this.source = source; + this.url = parseUrlOrPath(source.url); + assert(this.url.protocol === "file:", "PDFNodeStream only supports file:// URLs."); + this._fullRequestReader = null; + this._rangeRequestReaders = []; + } + get _progressiveDataLength() { + return this._fullRequestReader?._loaded ?? 0; + } + getFullReader() { + assert(!this._fullRequestReader, "PDFNodeStream.getFullReader can only be called once."); + this._fullRequestReader = new PDFNodeStreamFsFullReader(this); + return this._fullRequestReader; + } + getRangeReader(start, end) { + if (end <= this._progressiveDataLength) { + return null; + } + const rangeReader = new PDFNodeStreamFsRangeReader(this, start, end); + this._rangeRequestReaders.push(rangeReader); + return rangeReader; + } + cancelAllRequests(reason) { + this._fullRequestReader?.cancel(reason); + for (const reader of this._rangeRequestReaders.slice(0)) { + reader.cancel(reason); + } + } +} +class PDFNodeStreamFsFullReader { + constructor(stream) { + this._url = stream.url; + this._done = false; + this._storedError = null; + this.onProgress = null; + const source = stream.source; + this._contentLength = source.length; + this._loaded = 0; + this._filename = null; + this._disableRange = source.disableRange || false; + this._rangeChunkSize = source.rangeChunkSize; + if (!this._rangeChunkSize && !this._disableRange) { + this._disableRange = true; + } + this._isStreamingSupported = !source.disableStream; + this._isRangeSupported = !source.disableRange; + this._readableStream = null; + this._readCapability = Promise.withResolvers(); + this._headersCapability = Promise.withResolvers(); + const fs = process.getBuiltinModule("fs"); + fs.promises.lstat(this._url).then(stat => { + this._contentLength = stat.size; + this._setReadableStream(fs.createReadStream(this._url)); + this._headersCapability.resolve(); + }, error => { + if (error.code === "ENOENT") { + error = new MissingPDFException(`Missing PDF "${this._url}".`); + } + this._storedError = error; + this._headersCapability.reject(error); + }); + } + get headersReady() { + return this._headersCapability.promise; + } + get filename() { + return this._filename; + } + get contentLength() { + return this._contentLength; + } + get isRangeSupported() { + return this._isRangeSupported; + } + get isStreamingSupported() { + return this._isStreamingSupported; + } + async read() { + await this._readCapability.promise; + if (this._done) { + return { + value: undefined, + done: true + }; + } + if (this._storedError) { + throw this._storedError; + } + const chunk = this._readableStream.read(); + if (chunk === null) { + this._readCapability = Promise.withResolvers(); + return this.read(); + } + this._loaded += chunk.length; + this.onProgress?.({ + loaded: this._loaded, + total: this._contentLength + }); + const buffer = new Uint8Array(chunk).buffer; + return { + value: buffer, + done: false + }; + } + cancel(reason) { + if (!this._readableStream) { + this._error(reason); + return; + } + this._readableStream.destroy(reason); + } + _error(reason) { + this._storedError = reason; + this._readCapability.resolve(); + } + _setReadableStream(readableStream) { + this._readableStream = readableStream; + readableStream.on("readable", () => { + this._readCapability.resolve(); + }); + readableStream.on("end", () => { + readableStream.destroy(); + this._done = true; + this._readCapability.resolve(); + }); + readableStream.on("error", reason => { + this._error(reason); + }); + if (!this._isStreamingSupported && this._isRangeSupported) { + this._error(new AbortException("streaming is disabled")); + } + if (this._storedError) { + this._readableStream.destroy(this._storedError); + } + } +} +class PDFNodeStreamFsRangeReader { + constructor(stream, start, end) { + this._url = stream.url; + this._done = false; + this._storedError = null; + this.onProgress = null; + this._loaded = 0; + this._readableStream = null; + this._readCapability = Promise.withResolvers(); + const source = stream.source; + this._isStreamingSupported = !source.disableStream; + const fs = process.getBuiltinModule("fs"); + this._setReadableStream(fs.createReadStream(this._url, { + start, + end: end - 1 + })); + } + get isStreamingSupported() { + return this._isStreamingSupported; + } + async read() { + await this._readCapability.promise; + if (this._done) { + return { + value: undefined, + done: true + }; + } + if (this._storedError) { + throw this._storedError; + } + const chunk = this._readableStream.read(); + if (chunk === null) { + this._readCapability = Promise.withResolvers(); + return this.read(); + } + this._loaded += chunk.length; + this.onProgress?.({ + loaded: this._loaded + }); + const buffer = new Uint8Array(chunk).buffer; + return { + value: buffer, + done: false + }; + } + cancel(reason) { + if (!this._readableStream) { + this._error(reason); + return; + } + this._readableStream.destroy(reason); + } + _error(reason) { + this._storedError = reason; + this._readCapability.resolve(); + } + _setReadableStream(readableStream) { + this._readableStream = readableStream; + readableStream.on("readable", () => { + this._readCapability.resolve(); + }); + readableStream.on("end", () => { + readableStream.destroy(); + this._done = true; + this._readCapability.resolve(); + }); + readableStream.on("error", reason => { + this._error(reason); + }); + if (this._storedError) { + this._readableStream.destroy(this._storedError); + } + } +} + +;// ./src/display/text_layer.js + + +const MAX_TEXT_DIVS_TO_RENDER = 100000; +const DEFAULT_FONT_SIZE = 30; +const DEFAULT_FONT_ASCENT = 0.8; +class TextLayer { + #capability = Promise.withResolvers(); + #container = null; + #disableProcessItems = false; + #fontInspectorEnabled = !!globalThis.FontInspector?.enabled; + #lang = null; + #layoutTextParams = null; + #pageHeight = 0; + #pageWidth = 0; + #reader = null; + #rootContainer = null; + #rotation = 0; + #scale = 0; + #styleCache = Object.create(null); + #textContentItemsStr = []; + #textContentSource = null; + #textDivs = []; + #textDivProperties = new WeakMap(); + #transform = null; + static #ascentCache = new Map(); + static #canvasContexts = new Map(); + static #canvasCtxFonts = new WeakMap(); + static #minFontSize = null; + static #pendingTextLayers = new Set(); + constructor({ + textContentSource, + container, + viewport + }) { + if (textContentSource instanceof ReadableStream) { + this.#textContentSource = textContentSource; + } else if (typeof textContentSource === "object") { + this.#textContentSource = new ReadableStream({ + start(controller) { + controller.enqueue(textContentSource); + controller.close(); + } + }); + } else { + throw new Error('No "textContentSource" parameter specified.'); + } + this.#container = this.#rootContainer = container; + this.#scale = viewport.scale * (globalThis.devicePixelRatio || 1); + this.#rotation = viewport.rotation; + this.#layoutTextParams = { + div: null, + properties: null, + ctx: null + }; + const { + pageWidth, + pageHeight, + pageX, + pageY + } = viewport.rawDims; + this.#transform = [1, 0, 0, -1, -pageX, pageY + pageHeight]; + this.#pageWidth = pageWidth; + this.#pageHeight = pageHeight; + TextLayer.#ensureMinFontSizeComputed(); + setLayerDimensions(container, viewport); + this.#capability.promise.finally(() => { + TextLayer.#pendingTextLayers.delete(this); + this.#layoutTextParams = null; + this.#styleCache = null; + }).catch(() => {}); + } + static get fontFamilyMap() { + const { + isWindows, + isFirefox + } = util_FeatureTest.platform; + return shadow(this, "fontFamilyMap", new Map([["sans-serif", `${isWindows && isFirefox ? "Calibri, " : ""}sans-serif`], ["monospace", `${isWindows && isFirefox ? "Lucida Console, " : ""}monospace`]])); + } + render() { + const pump = () => { + this.#reader.read().then(({ + value, + done + }) => { + if (done) { + this.#capability.resolve(); + return; + } + this.#lang ??= value.lang; + Object.assign(this.#styleCache, value.styles); + this.#processItems(value.items); + pump(); + }, this.#capability.reject); + }; + this.#reader = this.#textContentSource.getReader(); + TextLayer.#pendingTextLayers.add(this); + pump(); + return this.#capability.promise; + } + update({ + viewport, + onBefore = null + }) { + const scale = viewport.scale * (globalThis.devicePixelRatio || 1); + const rotation = viewport.rotation; + if (rotation !== this.#rotation) { + onBefore?.(); + this.#rotation = rotation; + setLayerDimensions(this.#rootContainer, { + rotation + }); + } + if (scale !== this.#scale) { + onBefore?.(); + this.#scale = scale; + const params = { + div: null, + properties: null, + ctx: TextLayer.#getCtx(this.#lang) + }; + for (const div of this.#textDivs) { + params.properties = this.#textDivProperties.get(div); + params.div = div; + this.#layout(params); + } + } + } + cancel() { + const abortEx = new AbortException("TextLayer task cancelled."); + this.#reader?.cancel(abortEx).catch(() => {}); + this.#reader = null; + this.#capability.reject(abortEx); + } + get textDivs() { + return this.#textDivs; + } + get textContentItemsStr() { + return this.#textContentItemsStr; + } + #processItems(items) { + if (this.#disableProcessItems) { + return; + } + this.#layoutTextParams.ctx ??= TextLayer.#getCtx(this.#lang); + const textDivs = this.#textDivs, + textContentItemsStr = this.#textContentItemsStr; + for (const item of items) { + if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER) { + warn("Ignoring additional textDivs for performance reasons."); + this.#disableProcessItems = true; + return; + } + if (item.str === undefined) { + if (item.type === "beginMarkedContentProps" || item.type === "beginMarkedContent") { + const parent = this.#container; + this.#container = document.createElement("span"); + this.#container.classList.add("markedContent"); + if (item.id !== null) { + this.#container.setAttribute("id", `${item.id}`); + } + parent.append(this.#container); + } else if (item.type === "endMarkedContent") { + this.#container = this.#container.parentNode; + } + continue; + } + textContentItemsStr.push(item.str); + this.#appendText(item); + } + } + #appendText(geom) { + const textDiv = document.createElement("span"); + const textDivProperties = { + angle: 0, + canvasWidth: 0, + hasText: geom.str !== "", + hasEOL: geom.hasEOL, + fontSize: 0 + }; + this.#textDivs.push(textDiv); + const tx = Util.transform(this.#transform, geom.transform); + let angle = Math.atan2(tx[1], tx[0]); + const style = this.#styleCache[geom.fontName]; + if (style.vertical) { + angle += Math.PI / 2; + } + let fontFamily = this.#fontInspectorEnabled && style.fontSubstitution || style.fontFamily; + fontFamily = TextLayer.fontFamilyMap.get(fontFamily) || fontFamily; + const fontHeight = Math.hypot(tx[2], tx[3]); + const fontAscent = fontHeight * TextLayer.#getAscent(fontFamily, this.#lang); + let left, top; + if (angle === 0) { + left = tx[4]; + top = tx[5] - fontAscent; + } else { + left = tx[4] + fontAscent * Math.sin(angle); + top = tx[5] - fontAscent * Math.cos(angle); + } + const scaleFactorStr = "calc(var(--scale-factor)*"; + const divStyle = textDiv.style; + if (this.#container === this.#rootContainer) { + divStyle.left = `${(100 * left / this.#pageWidth).toFixed(2)}%`; + divStyle.top = `${(100 * top / this.#pageHeight).toFixed(2)}%`; + } else { + divStyle.left = `${scaleFactorStr}${left.toFixed(2)}px)`; + divStyle.top = `${scaleFactorStr}${top.toFixed(2)}px)`; + } + divStyle.fontSize = `${scaleFactorStr}${(TextLayer.#minFontSize * fontHeight).toFixed(2)}px)`; + divStyle.fontFamily = fontFamily; + textDivProperties.fontSize = fontHeight; + textDiv.setAttribute("role", "presentation"); + textDiv.textContent = geom.str; + textDiv.dir = geom.dir; + if (this.#fontInspectorEnabled) { + textDiv.dataset.fontName = style.fontSubstitutionLoadedName || geom.fontName; + } + if (angle !== 0) { + textDivProperties.angle = angle * (180 / Math.PI); + } + let shouldScaleText = false; + if (geom.str.length > 1) { + shouldScaleText = true; + } else if (geom.str !== " " && geom.transform[0] !== geom.transform[3]) { + const absScaleX = Math.abs(geom.transform[0]), + absScaleY = Math.abs(geom.transform[3]); + if (absScaleX !== absScaleY && Math.max(absScaleX, absScaleY) / Math.min(absScaleX, absScaleY) > 1.5) { + shouldScaleText = true; + } + } + if (shouldScaleText) { + textDivProperties.canvasWidth = style.vertical ? geom.height : geom.width; + } + this.#textDivProperties.set(textDiv, textDivProperties); + this.#layoutTextParams.div = textDiv; + this.#layoutTextParams.properties = textDivProperties; + this.#layout(this.#layoutTextParams); + if (textDivProperties.hasText) { + this.#container.append(textDiv); + } + if (textDivProperties.hasEOL) { + const br = document.createElement("br"); + br.setAttribute("role", "presentation"); + this.#container.append(br); + } + } + #layout(params) { + const { + div, + properties, + ctx + } = params; + const { + style + } = div; + let transform = ""; + if (TextLayer.#minFontSize > 1) { + transform = `scale(${1 / TextLayer.#minFontSize})`; + } + if (properties.canvasWidth !== 0 && properties.hasText) { + const { + fontFamily + } = style; + const { + canvasWidth, + fontSize + } = properties; + TextLayer.#ensureCtxFont(ctx, fontSize * this.#scale, fontFamily); + const { + width + } = ctx.measureText(div.textContent); + if (width > 0) { + transform = `scaleX(${canvasWidth * this.#scale / width}) ${transform}`; + } + } + if (properties.angle !== 0) { + transform = `rotate(${properties.angle}deg) ${transform}`; + } + if (transform.length > 0) { + style.transform = transform; + } + } + static cleanup() { + if (this.#pendingTextLayers.size > 0) { + return; + } + this.#ascentCache.clear(); + for (const { + canvas + } of this.#canvasContexts.values()) { + canvas.remove(); + } + this.#canvasContexts.clear(); + } + static #getCtx(lang = null) { + let ctx = this.#canvasContexts.get(lang ||= ""); + if (!ctx) { + const canvas = document.createElement("canvas"); + canvas.className = "hiddenCanvasElement"; + canvas.lang = lang; + document.body.append(canvas); + ctx = canvas.getContext("2d", { + alpha: false, + willReadFrequently: true + }); + this.#canvasContexts.set(lang, ctx); + this.#canvasCtxFonts.set(ctx, { + size: 0, + family: "" + }); + } + return ctx; + } + static #ensureCtxFont(ctx, size, family) { + const cached = this.#canvasCtxFonts.get(ctx); + if (size === cached.size && family === cached.family) { + return; + } + ctx.font = `${size}px ${family}`; + cached.size = size; + cached.family = family; + } + static #ensureMinFontSizeComputed() { + if (this.#minFontSize !== null) { + return; + } + const div = document.createElement("div"); + div.style.opacity = 0; + div.style.lineHeight = 1; + div.style.fontSize = "1px"; + div.style.position = "absolute"; + div.textContent = "X"; + document.body.append(div); + this.#minFontSize = div.getBoundingClientRect().height; + div.remove(); + } + static #getAscent(fontFamily, lang) { + const cachedAscent = this.#ascentCache.get(fontFamily); + if (cachedAscent) { + return cachedAscent; + } + const ctx = this.#getCtx(lang); + ctx.canvas.width = ctx.canvas.height = DEFAULT_FONT_SIZE; + this.#ensureCtxFont(ctx, DEFAULT_FONT_SIZE, fontFamily); + const metrics = ctx.measureText(""); + let ascent = metrics.fontBoundingBoxAscent; + let descent = Math.abs(metrics.fontBoundingBoxDescent); + if (ascent) { + const ratio = ascent / (ascent + descent); + this.#ascentCache.set(fontFamily, ratio); + ctx.canvas.width = ctx.canvas.height = 0; + return ratio; + } + ctx.strokeStyle = "red"; + ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE); + ctx.strokeText("g", 0, 0); + let pixels = ctx.getImageData(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE).data; + descent = 0; + for (let i = pixels.length - 1 - 3; i >= 0; i -= 4) { + if (pixels[i] > 0) { + descent = Math.ceil(i / 4 / DEFAULT_FONT_SIZE); + break; + } + } + ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE); + ctx.strokeText("A", 0, DEFAULT_FONT_SIZE); + pixels = ctx.getImageData(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE).data; + ascent = 0; + for (let i = 0, ii = pixels.length; i < ii; i += 4) { + if (pixels[i] > 0) { + ascent = DEFAULT_FONT_SIZE - Math.floor(i / 4 / DEFAULT_FONT_SIZE); + break; + } + } + ctx.canvas.width = ctx.canvas.height = 0; + const ratio = ascent ? ascent / (ascent + descent) : DEFAULT_FONT_ASCENT; + this.#ascentCache.set(fontFamily, ratio); + return ratio; + } +} + +;// ./src/display/xfa_text.js +class XfaText { + static textContent(xfa) { + const items = []; + const output = { + items, + styles: Object.create(null) + }; + function walk(node) { + if (!node) { + return; + } + let str = null; + const name = node.name; + if (name === "#text") { + str = node.value; + } else if (!XfaText.shouldBuildText(name)) { + return; + } else if (node?.attributes?.textContent) { + str = node.attributes.textContent; + } else if (node.value) { + str = node.value; + } + if (str !== null) { + items.push({ + str + }); + } + if (!node.children) { + return; + } + for (const child of node.children) { + walk(child); + } + } + walk(xfa); + return output; + } + static shouldBuildText(name) { + return !(name === "textarea" || name === "input" || name === "option" || name === "select"); + } +} + +;// ./src/display/api.js + + + + + + + + + + + + + + + + + + + + +const DEFAULT_RANGE_CHUNK_SIZE = 65536; +const RENDERING_CANCELLED_TIMEOUT = 100; +const DELAYED_CLEANUP_TIMEOUT = 5000; +const DefaultCanvasFactory = isNodeJS ? NodeCanvasFactory : DOMCanvasFactory; +const DefaultCMapReaderFactory = isNodeJS ? NodeCMapReaderFactory : DOMCMapReaderFactory; +const DefaultFilterFactory = isNodeJS ? NodeFilterFactory : DOMFilterFactory; +const DefaultStandardFontDataFactory = isNodeJS ? NodeStandardFontDataFactory : DOMStandardFontDataFactory; +function getDocument(src = {}) { + if (typeof src === "string" || src instanceof URL) { + src = { + url: src + }; + } else if (src instanceof ArrayBuffer || ArrayBuffer.isView(src)) { + src = { + data: src + }; + } + const task = new PDFDocumentLoadingTask(); + const { + docId + } = task; + const url = src.url ? getUrlProp(src.url) : null; + const data = src.data ? getDataProp(src.data) : null; + const httpHeaders = src.httpHeaders || null; + const withCredentials = src.withCredentials === true; + const password = src.password ?? null; + const rangeTransport = src.range instanceof PDFDataRangeTransport ? src.range : null; + const rangeChunkSize = Number.isInteger(src.rangeChunkSize) && src.rangeChunkSize > 0 ? src.rangeChunkSize : DEFAULT_RANGE_CHUNK_SIZE; + let worker = src.worker instanceof PDFWorker ? src.worker : null; + const verbosity = src.verbosity; + const docBaseUrl = typeof src.docBaseUrl === "string" && !isDataScheme(src.docBaseUrl) ? src.docBaseUrl : null; + const cMapUrl = typeof src.cMapUrl === "string" ? src.cMapUrl : null; + const cMapPacked = src.cMapPacked !== false; + const CMapReaderFactory = src.CMapReaderFactory || DefaultCMapReaderFactory; + const standardFontDataUrl = typeof src.standardFontDataUrl === "string" ? src.standardFontDataUrl : null; + const StandardFontDataFactory = src.StandardFontDataFactory || DefaultStandardFontDataFactory; + const ignoreErrors = src.stopAtErrors !== true; + const maxImageSize = Number.isInteger(src.maxImageSize) && src.maxImageSize > -1 ? src.maxImageSize : -1; + const isEvalSupported = src.isEvalSupported !== false; + const isOffscreenCanvasSupported = typeof src.isOffscreenCanvasSupported === "boolean" ? src.isOffscreenCanvasSupported : !isNodeJS; + const isImageDecoderSupported = typeof src.isImageDecoderSupported === "boolean" ? src.isImageDecoderSupported : !isNodeJS && (util_FeatureTest.platform.isFirefox || !globalThis.chrome); + const canvasMaxAreaInBytes = Number.isInteger(src.canvasMaxAreaInBytes) ? src.canvasMaxAreaInBytes : -1; + const disableFontFace = typeof src.disableFontFace === "boolean" ? src.disableFontFace : isNodeJS; + const fontExtraProperties = src.fontExtraProperties === true; + const enableXfa = src.enableXfa === true; + const ownerDocument = src.ownerDocument || globalThis.document; + const disableRange = src.disableRange === true; + const disableStream = src.disableStream === true; + const disableAutoFetch = src.disableAutoFetch === true; + const pdfBug = src.pdfBug === true; + const CanvasFactory = src.CanvasFactory || DefaultCanvasFactory; + const FilterFactory = src.FilterFactory || DefaultFilterFactory; + const enableHWA = src.enableHWA === true; + const length = rangeTransport ? rangeTransport.length : src.length ?? NaN; + const useSystemFonts = typeof src.useSystemFonts === "boolean" ? src.useSystemFonts : !isNodeJS && !disableFontFace; + const useWorkerFetch = typeof src.useWorkerFetch === "boolean" ? src.useWorkerFetch : CMapReaderFactory === DOMCMapReaderFactory && StandardFontDataFactory === DOMStandardFontDataFactory && cMapUrl && standardFontDataUrl && isValidFetchUrl(cMapUrl, document.baseURI) && isValidFetchUrl(standardFontDataUrl, document.baseURI); + const styleElement = null; + setVerbosityLevel(verbosity); + const transportFactory = { + canvasFactory: new CanvasFactory({ + ownerDocument, + enableHWA + }), + filterFactory: new FilterFactory({ + docId, + ownerDocument + }), + cMapReaderFactory: useWorkerFetch ? null : new CMapReaderFactory({ + baseUrl: cMapUrl, + isCompressed: cMapPacked + }), + standardFontDataFactory: useWorkerFetch ? null : new StandardFontDataFactory({ + baseUrl: standardFontDataUrl + }) + }; + if (!worker) { + const workerParams = { + verbosity, + port: GlobalWorkerOptions.workerPort + }; + worker = workerParams.port ? PDFWorker.fromPort(workerParams) : new PDFWorker(workerParams); + task._worker = worker; + } + const docParams = { + docId, + apiVersion: "4.10.38", + data, + password, + disableAutoFetch, + rangeChunkSize, + length, + docBaseUrl, + enableXfa, + evaluatorOptions: { + maxImageSize, + disableFontFace, + ignoreErrors, + isEvalSupported, + isOffscreenCanvasSupported, + isImageDecoderSupported, + canvasMaxAreaInBytes, + fontExtraProperties, + useSystemFonts, + cMapUrl: useWorkerFetch ? cMapUrl : null, + standardFontDataUrl: useWorkerFetch ? standardFontDataUrl : null + } + }; + const transportParams = { + disableFontFace, + fontExtraProperties, + ownerDocument, + pdfBug, + styleElement, + loadingParams: { + disableAutoFetch, + enableXfa + } + }; + worker.promise.then(function () { + if (task.destroyed) { + throw new Error("Loading aborted"); + } + if (worker.destroyed) { + throw new Error("Worker was destroyed"); + } + const workerIdPromise = worker.messageHandler.sendWithPromise("GetDocRequest", docParams, data ? [data.buffer] : null); + let networkStream; + if (rangeTransport) { + networkStream = new PDFDataTransportStream(rangeTransport, { + disableRange, + disableStream + }); + } else if (!data) { + if (!url) { + throw new Error("getDocument - no `url` parameter provided."); + } + let NetworkStream; + if (isNodeJS) { + if (isValidFetchUrl(url)) { + if (typeof fetch === "undefined" || typeof Response === "undefined" || !("body" in Response.prototype)) { + throw new Error("getDocument - the Fetch API was disabled in Node.js, see `--no-experimental-fetch`."); + } + NetworkStream = PDFFetchStream; + } else { + NetworkStream = PDFNodeStream; + } + } else { + NetworkStream = isValidFetchUrl(url) ? PDFFetchStream : PDFNetworkStream; + } + networkStream = new NetworkStream({ + url, + length, + httpHeaders, + withCredentials, + rangeChunkSize, + disableRange, + disableStream + }); + } + return workerIdPromise.then(workerId => { + if (task.destroyed) { + throw new Error("Loading aborted"); + } + if (worker.destroyed) { + throw new Error("Worker was destroyed"); + } + const messageHandler = new MessageHandler(docId, workerId, worker.port); + const transport = new WorkerTransport(messageHandler, task, networkStream, transportParams, transportFactory); + task._transport = transport; + messageHandler.send("Ready", null); + }); + }).catch(task._capability.reject); + return task; +} +function getUrlProp(val) { + if (val instanceof URL) { + return val.href; + } + try { + return new URL(val, window.location).href; + } catch { + if (isNodeJS && typeof val === "string") { + return val; + } + } + throw new Error("Invalid PDF url data: " + "either string or URL-object is expected in the url property."); +} +function getDataProp(val) { + if (isNodeJS && typeof Buffer !== "undefined" && val instanceof Buffer) { + throw new Error("Please provide binary data as `Uint8Array`, rather than `Buffer`."); + } + if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) { + return val; + } + if (typeof val === "string") { + return stringToBytes(val); + } + if (val instanceof ArrayBuffer || ArrayBuffer.isView(val) || typeof val === "object" && !isNaN(val?.length)) { + return new Uint8Array(val); + } + throw new Error("Invalid PDF binary data: either TypedArray, " + "string, or array-like object is expected in the data property."); +} +function isRefProxy(ref) { + return typeof ref === "object" && Number.isInteger(ref?.num) && ref.num >= 0 && Number.isInteger(ref?.gen) && ref.gen >= 0; +} +class PDFDocumentLoadingTask { + static #docId = 0; + constructor() { + this._capability = Promise.withResolvers(); + this._transport = null; + this._worker = null; + this.docId = `d${PDFDocumentLoadingTask.#docId++}`; + this.destroyed = false; + this.onPassword = null; + this.onProgress = null; + } + get promise() { + return this._capability.promise; + } + async destroy() { + this.destroyed = true; + try { + if (this._worker?.port) { + this._worker._pendingDestroy = true; + } + await this._transport?.destroy(); + } catch (ex) { + if (this._worker?.port) { + delete this._worker._pendingDestroy; + } + throw ex; + } + this._transport = null; + this._worker?.destroy(); + this._worker = null; + } +} +class PDFDataRangeTransport { + constructor(length, initialData, progressiveDone = false, contentDispositionFilename = null) { + this.length = length; + this.initialData = initialData; + this.progressiveDone = progressiveDone; + this.contentDispositionFilename = contentDispositionFilename; + this._rangeListeners = []; + this._progressListeners = []; + this._progressiveReadListeners = []; + this._progressiveDoneListeners = []; + this._readyCapability = Promise.withResolvers(); + } + addRangeListener(listener) { + this._rangeListeners.push(listener); + } + addProgressListener(listener) { + this._progressListeners.push(listener); + } + addProgressiveReadListener(listener) { + this._progressiveReadListeners.push(listener); + } + addProgressiveDoneListener(listener) { + this._progressiveDoneListeners.push(listener); + } + onDataRange(begin, chunk) { + for (const listener of this._rangeListeners) { + listener(begin, chunk); + } + } + onDataProgress(loaded, total) { + this._readyCapability.promise.then(() => { + for (const listener of this._progressListeners) { + listener(loaded, total); + } + }); + } + onDataProgressiveRead(chunk) { + this._readyCapability.promise.then(() => { + for (const listener of this._progressiveReadListeners) { + listener(chunk); + } + }); + } + onDataProgressiveDone() { + this._readyCapability.promise.then(() => { + for (const listener of this._progressiveDoneListeners) { + listener(); + } + }); + } + transportReady() { + this._readyCapability.resolve(); + } + requestDataRange(begin, end) { + unreachable("Abstract method PDFDataRangeTransport.requestDataRange"); + } + abort() {} +} +class PDFDocumentProxy { + constructor(pdfInfo, transport) { + this._pdfInfo = pdfInfo; + this._transport = transport; + } + get annotationStorage() { + return this._transport.annotationStorage; + } + get canvasFactory() { + return this._transport.canvasFactory; + } + get filterFactory() { + return this._transport.filterFactory; + } + get numPages() { + return this._pdfInfo.numPages; + } + get fingerprints() { + return this._pdfInfo.fingerprints; + } + get isPureXfa() { + return shadow(this, "isPureXfa", !!this._transport._htmlForXfa); + } + get allXfaHtml() { + return this._transport._htmlForXfa; + } + getPage(pageNumber) { + return this._transport.getPage(pageNumber); + } + getPageIndex(ref) { + return this._transport.getPageIndex(ref); + } + getDestinations() { + return this._transport.getDestinations(); + } + getDestination(id) { + return this._transport.getDestination(id); + } + getPageLabels() { + return this._transport.getPageLabels(); + } + getPageLayout() { + return this._transport.getPageLayout(); + } + getPageMode() { + return this._transport.getPageMode(); + } + getViewerPreferences() { + return this._transport.getViewerPreferences(); + } + getOpenAction() { + return this._transport.getOpenAction(); + } + getAttachments() { + return this._transport.getAttachments(); + } + getJSActions() { + return this._transport.getDocJSActions(); + } + getOutline() { + return this._transport.getOutline(); + } + getOptionalContentConfig({ + intent = "display" + } = {}) { + const { + renderingIntent + } = this._transport.getRenderingIntent(intent); + return this._transport.getOptionalContentConfig(renderingIntent); + } + getPermissions() { + return this._transport.getPermissions(); + } + getMetadata() { + return this._transport.getMetadata(); + } + getMarkInfo() { + return this._transport.getMarkInfo(); + } + getData() { + return this._transport.getData(); + } + saveDocument() { + return this._transport.saveDocument(); + } + getDownloadInfo() { + return this._transport.downloadInfoCapability.promise; + } + cleanup(keepLoadedFonts = false) { + return this._transport.startCleanup(keepLoadedFonts || this.isPureXfa); + } + destroy() { + return this.loadingTask.destroy(); + } + cachedPageNumber(ref) { + return this._transport.cachedPageNumber(ref); + } + get loadingParams() { + return this._transport.loadingParams; + } + get loadingTask() { + return this._transport.loadingTask; + } + getFieldObjects() { + return this._transport.getFieldObjects(); + } + hasJSActions() { + return this._transport.hasJSActions(); + } + getCalculationOrderIds() { + return this._transport.getCalculationOrderIds(); + } +} +class PDFPageProxy { + #delayedCleanupTimeout = null; + #pendingCleanup = false; + constructor(pageIndex, pageInfo, transport, pdfBug = false) { + this._pageIndex = pageIndex; + this._pageInfo = pageInfo; + this._transport = transport; + this._stats = pdfBug ? new StatTimer() : null; + this._pdfBug = pdfBug; + this.commonObjs = transport.commonObjs; + this.objs = new PDFObjects(); + this._maybeCleanupAfterRender = false; + this._intentStates = new Map(); + this.destroyed = false; + } + get pageNumber() { + return this._pageIndex + 1; + } + get rotate() { + return this._pageInfo.rotate; + } + get ref() { + return this._pageInfo.ref; + } + get userUnit() { + return this._pageInfo.userUnit; + } + get view() { + return this._pageInfo.view; + } + getViewport({ + scale, + rotation = this.rotate, + offsetX = 0, + offsetY = 0, + dontFlip = false + } = {}) { + return new PageViewport({ + viewBox: this.view, + userUnit: this.userUnit, + scale, + rotation, + offsetX, + offsetY, + dontFlip + }); + } + getAnnotations({ + intent = "display" + } = {}) { + const { + renderingIntent + } = this._transport.getRenderingIntent(intent); + return this._transport.getAnnotations(this._pageIndex, renderingIntent); + } + getJSActions() { + return this._transport.getPageJSActions(this._pageIndex); + } + get filterFactory() { + return this._transport.filterFactory; + } + get isPureXfa() { + return shadow(this, "isPureXfa", !!this._transport._htmlForXfa); + } + async getXfa() { + return this._transport._htmlForXfa?.children[this._pageIndex] || null; + } + render({ + canvasContext, + viewport, + intent = "display", + annotationMode = AnnotationMode.ENABLE, + transform = null, + background = null, + optionalContentConfigPromise = null, + annotationCanvasMap = null, + pageColors = null, + printAnnotationStorage = null, + isEditing = false + }) { + this._stats?.time("Overall"); + const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage, isEditing); + const { + renderingIntent, + cacheKey + } = intentArgs; + this.#pendingCleanup = false; + this.#abortDelayedCleanup(); + optionalContentConfigPromise ||= this._transport.getOptionalContentConfig(renderingIntent); + let intentState = this._intentStates.get(cacheKey); + if (!intentState) { + intentState = Object.create(null); + this._intentStates.set(cacheKey, intentState); + } + if (intentState.streamReaderCancelTimeout) { + clearTimeout(intentState.streamReaderCancelTimeout); + intentState.streamReaderCancelTimeout = null; + } + const intentPrint = !!(renderingIntent & RenderingIntentFlag.PRINT); + if (!intentState.displayReadyCapability) { + intentState.displayReadyCapability = Promise.withResolvers(); + intentState.operatorList = { + fnArray: [], + argsArray: [], + lastChunk: false, + separateAnnots: null + }; + this._stats?.time("Page Request"); + this._pumpOperatorList(intentArgs); + } + const complete = error => { + intentState.renderTasks.delete(internalRenderTask); + if (this._maybeCleanupAfterRender || intentPrint) { + this.#pendingCleanup = true; + } + this.#tryCleanup(!intentPrint); + if (error) { + internalRenderTask.capability.reject(error); + this._abortOperatorList({ + intentState, + reason: error instanceof Error ? error : new Error(error) + }); + } else { + internalRenderTask.capability.resolve(); + } + if (this._stats) { + this._stats.timeEnd("Rendering"); + this._stats.timeEnd("Overall"); + if (globalThis.Stats?.enabled) { + globalThis.Stats.add(this.pageNumber, this._stats); + } + } + }; + const internalRenderTask = new InternalRenderTask({ + callback: complete, + params: { + canvasContext, + viewport, + transform, + background + }, + objs: this.objs, + commonObjs: this.commonObjs, + annotationCanvasMap, + operatorList: intentState.operatorList, + pageIndex: this._pageIndex, + canvasFactory: this._transport.canvasFactory, + filterFactory: this._transport.filterFactory, + useRequestAnimationFrame: !intentPrint, + pdfBug: this._pdfBug, + pageColors + }); + (intentState.renderTasks ||= new Set()).add(internalRenderTask); + const renderTask = internalRenderTask.task; + Promise.all([intentState.displayReadyCapability.promise, optionalContentConfigPromise]).then(([transparency, optionalContentConfig]) => { + if (this.destroyed) { + complete(); + return; + } + this._stats?.time("Rendering"); + if (!(optionalContentConfig.renderingIntent & renderingIntent)) { + throw new Error("Must use the same `intent`-argument when calling the `PDFPageProxy.render` " + "and `PDFDocumentProxy.getOptionalContentConfig` methods."); + } + internalRenderTask.initializeGraphics({ + transparency, + optionalContentConfig + }); + internalRenderTask.operatorListChanged(); + }).catch(complete); + return renderTask; + } + getOperatorList({ + intent = "display", + annotationMode = AnnotationMode.ENABLE, + printAnnotationStorage = null, + isEditing = false + } = {}) { + function operatorListChanged() { + if (intentState.operatorList.lastChunk) { + intentState.opListReadCapability.resolve(intentState.operatorList); + intentState.renderTasks.delete(opListTask); + } + } + const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage, isEditing, true); + let intentState = this._intentStates.get(intentArgs.cacheKey); + if (!intentState) { + intentState = Object.create(null); + this._intentStates.set(intentArgs.cacheKey, intentState); + } + let opListTask; + if (!intentState.opListReadCapability) { + opListTask = Object.create(null); + opListTask.operatorListChanged = operatorListChanged; + intentState.opListReadCapability = Promise.withResolvers(); + (intentState.renderTasks ||= new Set()).add(opListTask); + intentState.operatorList = { + fnArray: [], + argsArray: [], + lastChunk: false, + separateAnnots: null + }; + this._stats?.time("Page Request"); + this._pumpOperatorList(intentArgs); + } + return intentState.opListReadCapability.promise; + } + streamTextContent({ + includeMarkedContent = false, + disableNormalization = false + } = {}) { + const TEXT_CONTENT_CHUNK_SIZE = 100; + return this._transport.messageHandler.sendWithStream("GetTextContent", { + pageIndex: this._pageIndex, + includeMarkedContent: includeMarkedContent === true, + disableNormalization: disableNormalization === true + }, { + highWaterMark: TEXT_CONTENT_CHUNK_SIZE, + size(textContent) { + return textContent.items.length; + } + }); + } + getTextContent(params = {}) { + if (this._transport._htmlForXfa) { + return this.getXfa().then(xfa => XfaText.textContent(xfa)); + } + const readableStream = this.streamTextContent(params); + return new Promise(function (resolve, reject) { + function pump() { + reader.read().then(function ({ + value, + done + }) { + if (done) { + resolve(textContent); + return; + } + textContent.lang ??= value.lang; + Object.assign(textContent.styles, value.styles); + textContent.items.push(...value.items); + pump(); + }, reject); + } + const reader = readableStream.getReader(); + const textContent = { + items: [], + styles: Object.create(null), + lang: null + }; + pump(); + }); + } + getStructTree() { + return this._transport.getStructTree(this._pageIndex); + } + _destroy() { + this.destroyed = true; + const waitOn = []; + for (const intentState of this._intentStates.values()) { + this._abortOperatorList({ + intentState, + reason: new Error("Page was destroyed."), + force: true + }); + if (intentState.opListReadCapability) { + continue; + } + for (const internalRenderTask of intentState.renderTasks) { + waitOn.push(internalRenderTask.completed); + internalRenderTask.cancel(); + } + } + this.objs.clear(); + this.#pendingCleanup = false; + this.#abortDelayedCleanup(); + return Promise.all(waitOn); + } + cleanup(resetStats = false) { + this.#pendingCleanup = true; + const success = this.#tryCleanup(false); + if (resetStats && success) { + this._stats &&= new StatTimer(); + } + return success; + } + #tryCleanup(delayed = false) { + this.#abortDelayedCleanup(); + if (!this.#pendingCleanup || this.destroyed) { + return false; + } + if (delayed) { + this.#delayedCleanupTimeout = setTimeout(() => { + this.#delayedCleanupTimeout = null; + this.#tryCleanup(false); + }, DELAYED_CLEANUP_TIMEOUT); + return false; + } + for (const { + renderTasks, + operatorList + } of this._intentStates.values()) { + if (renderTasks.size > 0 || !operatorList.lastChunk) { + return false; + } + } + this._intentStates.clear(); + this.objs.clear(); + this.#pendingCleanup = false; + return true; + } + #abortDelayedCleanup() { + if (this.#delayedCleanupTimeout) { + clearTimeout(this.#delayedCleanupTimeout); + this.#delayedCleanupTimeout = null; + } + } + _startRenderPage(transparency, cacheKey) { + const intentState = this._intentStates.get(cacheKey); + if (!intentState) { + return; + } + this._stats?.timeEnd("Page Request"); + intentState.displayReadyCapability?.resolve(transparency); + } + _renderPageChunk(operatorListChunk, intentState) { + for (let i = 0, ii = operatorListChunk.length; i < ii; i++) { + intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]); + intentState.operatorList.argsArray.push(operatorListChunk.argsArray[i]); + } + intentState.operatorList.lastChunk = operatorListChunk.lastChunk; + intentState.operatorList.separateAnnots = operatorListChunk.separateAnnots; + for (const internalRenderTask of intentState.renderTasks) { + internalRenderTask.operatorListChanged(); + } + if (operatorListChunk.lastChunk) { + this.#tryCleanup(true); + } + } + _pumpOperatorList({ + renderingIntent, + cacheKey, + annotationStorageSerializable, + modifiedIds + }) { + const { + map, + transfer + } = annotationStorageSerializable; + const readableStream = this._transport.messageHandler.sendWithStream("GetOperatorList", { + pageIndex: this._pageIndex, + intent: renderingIntent, + cacheKey, + annotationStorage: map, + modifiedIds + }, transfer); + const reader = readableStream.getReader(); + const intentState = this._intentStates.get(cacheKey); + intentState.streamReader = reader; + const pump = () => { + reader.read().then(({ + value, + done + }) => { + if (done) { + intentState.streamReader = null; + return; + } + if (this._transport.destroyed) { + return; + } + this._renderPageChunk(value, intentState); + pump(); + }, reason => { + intentState.streamReader = null; + if (this._transport.destroyed) { + return; + } + if (intentState.operatorList) { + intentState.operatorList.lastChunk = true; + for (const internalRenderTask of intentState.renderTasks) { + internalRenderTask.operatorListChanged(); + } + this.#tryCleanup(true); + } + if (intentState.displayReadyCapability) { + intentState.displayReadyCapability.reject(reason); + } else if (intentState.opListReadCapability) { + intentState.opListReadCapability.reject(reason); + } else { + throw reason; + } + }); + }; + pump(); + } + _abortOperatorList({ + intentState, + reason, + force = false + }) { + if (!intentState.streamReader) { + return; + } + if (intentState.streamReaderCancelTimeout) { + clearTimeout(intentState.streamReaderCancelTimeout); + intentState.streamReaderCancelTimeout = null; + } + if (!force) { + if (intentState.renderTasks.size > 0) { + return; + } + if (reason instanceof RenderingCancelledException) { + let delay = RENDERING_CANCELLED_TIMEOUT; + if (reason.extraDelay > 0 && reason.extraDelay < 1000) { + delay += reason.extraDelay; + } + intentState.streamReaderCancelTimeout = setTimeout(() => { + intentState.streamReaderCancelTimeout = null; + this._abortOperatorList({ + intentState, + reason, + force: true + }); + }, delay); + return; + } + } + intentState.streamReader.cancel(new AbortException(reason.message)).catch(() => {}); + intentState.streamReader = null; + if (this._transport.destroyed) { + return; + } + for (const [curCacheKey, curIntentState] of this._intentStates) { + if (curIntentState === intentState) { + this._intentStates.delete(curCacheKey); + break; + } + } + this.cleanup(); + } + get stats() { + return this._stats; + } +} +class LoopbackPort { + #listeners = new Map(); + #deferred = Promise.resolve(); + postMessage(obj, transfer) { + const event = { + data: structuredClone(obj, transfer ? { + transfer + } : null) + }; + this.#deferred.then(() => { + for (const [listener] of this.#listeners) { + listener.call(this, event); + } + }); + } + addEventListener(name, listener, options = null) { + let rmAbort = null; + if (options?.signal instanceof AbortSignal) { + const { + signal + } = options; + if (signal.aborted) { + warn("LoopbackPort - cannot use an `aborted` signal."); + return; + } + const onAbort = () => this.removeEventListener(name, listener); + rmAbort = () => signal.removeEventListener("abort", onAbort); + signal.addEventListener("abort", onAbort); + } + this.#listeners.set(listener, rmAbort); + } + removeEventListener(name, listener) { + const rmAbort = this.#listeners.get(listener); + rmAbort?.(); + this.#listeners.delete(listener); + } + terminate() { + for (const [, rmAbort] of this.#listeners) { + rmAbort?.(); + } + this.#listeners.clear(); + } +} +class PDFWorker { + static #fakeWorkerId = 0; + static #isWorkerDisabled = false; + static #workerPorts; + static { + if (isNodeJS) { + this.#isWorkerDisabled = true; + GlobalWorkerOptions.workerSrc ||= "./pdf.worker.mjs"; + } + this._isSameOrigin = (baseUrl, otherUrl) => { + let base; + try { + base = new URL(baseUrl); + if (!base.origin || base.origin === "null") { + return false; + } + } catch { + return false; + } + const other = new URL(otherUrl, base); + return base.origin === other.origin; + }; + this._createCDNWrapper = url => { + const wrapper = `await import("${url}");`; + return URL.createObjectURL(new Blob([wrapper], { + type: "text/javascript" + })); + }; + } + constructor({ + name = null, + port = null, + verbosity = getVerbosityLevel() + } = {}) { + this.name = name; + this.destroyed = false; + this.verbosity = verbosity; + this._readyCapability = Promise.withResolvers(); + this._port = null; + this._webWorker = null; + this._messageHandler = null; + if (port) { + if (PDFWorker.#workerPorts?.has(port)) { + throw new Error("Cannot use more than one PDFWorker per port."); + } + (PDFWorker.#workerPorts ||= new WeakMap()).set(port, this); + this._initializeFromPort(port); + return; + } + this._initialize(); + } + get promise() { + return this._readyCapability.promise; + } + #resolve() { + this._readyCapability.resolve(); + this._messageHandler.send("configure", { + verbosity: this.verbosity + }); + } + get port() { + return this._port; + } + get messageHandler() { + return this._messageHandler; + } + _initializeFromPort(port) { + this._port = port; + this._messageHandler = new MessageHandler("main", "worker", port); + this._messageHandler.on("ready", function () {}); + this.#resolve(); + } + _initialize() { + if (PDFWorker.#isWorkerDisabled || PDFWorker.#mainThreadWorkerMessageHandler) { + this._setupFakeWorker(); + return; + } + let { + workerSrc + } = PDFWorker; + try { + if (!PDFWorker._isSameOrigin(window.location.href, workerSrc)) { + workerSrc = PDFWorker._createCDNWrapper(new URL(workerSrc, window.location).href); + } + const worker = new Worker(workerSrc, { + type: "module" + }); + const messageHandler = new MessageHandler("main", "worker", worker); + const terminateEarly = () => { + ac.abort(); + messageHandler.destroy(); + worker.terminate(); + if (this.destroyed) { + this._readyCapability.reject(new Error("Worker was destroyed")); + } else { + this._setupFakeWorker(); + } + }; + const ac = new AbortController(); + worker.addEventListener("error", () => { + if (!this._webWorker) { + terminateEarly(); + } + }, { + signal: ac.signal + }); + messageHandler.on("test", data => { + ac.abort(); + if (this.destroyed || !data) { + terminateEarly(); + return; + } + this._messageHandler = messageHandler; + this._port = worker; + this._webWorker = worker; + this.#resolve(); + }); + messageHandler.on("ready", data => { + ac.abort(); + if (this.destroyed) { + terminateEarly(); + return; + } + try { + sendTest(); + } catch { + this._setupFakeWorker(); + } + }); + const sendTest = () => { + const testObj = new Uint8Array(); + messageHandler.send("test", testObj, [testObj.buffer]); + }; + sendTest(); + return; + } catch { + info("The worker has been disabled."); + } + this._setupFakeWorker(); + } + _setupFakeWorker() { + if (!PDFWorker.#isWorkerDisabled) { + warn("Setting up fake worker."); + PDFWorker.#isWorkerDisabled = true; + } + PDFWorker._setupFakeWorkerGlobal.then(WorkerMessageHandler => { + if (this.destroyed) { + this._readyCapability.reject(new Error("Worker was destroyed")); + return; + } + const port = new LoopbackPort(); + this._port = port; + const id = `fake${PDFWorker.#fakeWorkerId++}`; + const workerHandler = new MessageHandler(id + "_worker", id, port); + WorkerMessageHandler.setup(workerHandler, port); + this._messageHandler = new MessageHandler(id, id + "_worker", port); + this.#resolve(); + }).catch(reason => { + this._readyCapability.reject(new Error(`Setting up fake worker failed: "${reason.message}".`)); + }); + } + destroy() { + this.destroyed = true; + this._webWorker?.terminate(); + this._webWorker = null; + PDFWorker.#workerPorts?.delete(this._port); + this._port = null; + this._messageHandler?.destroy(); + this._messageHandler = null; + } + static fromPort(params) { + if (!params?.port) { + throw new Error("PDFWorker.fromPort - invalid method signature."); + } + const cachedPort = this.#workerPorts?.get(params.port); + if (cachedPort) { + if (cachedPort._pendingDestroy) { + throw new Error("PDFWorker.fromPort - the worker is being destroyed.\n" + "Please remember to await `PDFDocumentLoadingTask.destroy()`-calls."); + } + return cachedPort; + } + return new PDFWorker(params); + } + static get workerSrc() { + if (GlobalWorkerOptions.workerSrc) { + return GlobalWorkerOptions.workerSrc; + } + throw new Error('No "GlobalWorkerOptions.workerSrc" specified.'); + } + static get #mainThreadWorkerMessageHandler() { + try { + return globalThis.pdfjsWorker?.WorkerMessageHandler || null; + } catch { + return null; + } + } + static get _setupFakeWorkerGlobal() { + const loader = async () => { + if (this.#mainThreadWorkerMessageHandler) { + return this.#mainThreadWorkerMessageHandler; + } + const worker = await import(/*webpackIgnore: true*/this.workerSrc); + return worker.WorkerMessageHandler; + }; + return shadow(this, "_setupFakeWorkerGlobal", loader()); + } +} +class WorkerTransport { + #methodPromises = new Map(); + #pageCache = new Map(); + #pagePromises = new Map(); + #pageRefCache = new Map(); + #passwordCapability = null; + constructor(messageHandler, loadingTask, networkStream, params, factory) { + this.messageHandler = messageHandler; + this.loadingTask = loadingTask; + this.commonObjs = new PDFObjects(); + this.fontLoader = new FontLoader({ + ownerDocument: params.ownerDocument, + styleElement: params.styleElement + }); + this.loadingParams = params.loadingParams; + this._params = params; + this.canvasFactory = factory.canvasFactory; + this.filterFactory = factory.filterFactory; + this.cMapReaderFactory = factory.cMapReaderFactory; + this.standardFontDataFactory = factory.standardFontDataFactory; + this.destroyed = false; + this.destroyCapability = null; + this._networkStream = networkStream; + this._fullReader = null; + this._lastProgress = null; + this.downloadInfoCapability = Promise.withResolvers(); + this.setupMessageHandler(); + } + #cacheSimpleMethod(name, data = null) { + const cachedPromise = this.#methodPromises.get(name); + if (cachedPromise) { + return cachedPromise; + } + const promise = this.messageHandler.sendWithPromise(name, data); + this.#methodPromises.set(name, promise); + return promise; + } + get annotationStorage() { + return shadow(this, "annotationStorage", new AnnotationStorage()); + } + getRenderingIntent(intent, annotationMode = AnnotationMode.ENABLE, printAnnotationStorage = null, isEditing = false, isOpList = false) { + let renderingIntent = RenderingIntentFlag.DISPLAY; + let annotationStorageSerializable = SerializableEmpty; + switch (intent) { + case "any": + renderingIntent = RenderingIntentFlag.ANY; + break; + case "display": + break; + case "print": + renderingIntent = RenderingIntentFlag.PRINT; + break; + default: + warn(`getRenderingIntent - invalid intent: ${intent}`); + } + const annotationStorage = renderingIntent & RenderingIntentFlag.PRINT && printAnnotationStorage instanceof PrintAnnotationStorage ? printAnnotationStorage : this.annotationStorage; + switch (annotationMode) { + case AnnotationMode.DISABLE: + renderingIntent += RenderingIntentFlag.ANNOTATIONS_DISABLE; + break; + case AnnotationMode.ENABLE: + break; + case AnnotationMode.ENABLE_FORMS: + renderingIntent += RenderingIntentFlag.ANNOTATIONS_FORMS; + break; + case AnnotationMode.ENABLE_STORAGE: + renderingIntent += RenderingIntentFlag.ANNOTATIONS_STORAGE; + annotationStorageSerializable = annotationStorage.serializable; + break; + default: + warn(`getRenderingIntent - invalid annotationMode: ${annotationMode}`); + } + if (isEditing) { + renderingIntent += RenderingIntentFlag.IS_EDITING; + } + if (isOpList) { + renderingIntent += RenderingIntentFlag.OPLIST; + } + const { + ids: modifiedIds, + hash: modifiedIdsHash + } = annotationStorage.modifiedIds; + const cacheKeyBuf = [renderingIntent, annotationStorageSerializable.hash, modifiedIdsHash]; + return { + renderingIntent, + cacheKey: cacheKeyBuf.join("_"), + annotationStorageSerializable, + modifiedIds + }; + } + destroy() { + if (this.destroyCapability) { + return this.destroyCapability.promise; + } + this.destroyed = true; + this.destroyCapability = Promise.withResolvers(); + this.#passwordCapability?.reject(new Error("Worker was destroyed during onPassword callback")); + const waitOn = []; + for (const page of this.#pageCache.values()) { + waitOn.push(page._destroy()); + } + this.#pageCache.clear(); + this.#pagePromises.clear(); + this.#pageRefCache.clear(); + if (this.hasOwnProperty("annotationStorage")) { + this.annotationStorage.resetModified(); + } + const terminated = this.messageHandler.sendWithPromise("Terminate", null); + waitOn.push(terminated); + Promise.all(waitOn).then(() => { + this.commonObjs.clear(); + this.fontLoader.clear(); + this.#methodPromises.clear(); + this.filterFactory.destroy(); + TextLayer.cleanup(); + this._networkStream?.cancelAllRequests(new AbortException("Worker was terminated.")); + this.messageHandler?.destroy(); + this.messageHandler = null; + this.destroyCapability.resolve(); + }, this.destroyCapability.reject); + return this.destroyCapability.promise; + } + setupMessageHandler() { + const { + messageHandler, + loadingTask + } = this; + messageHandler.on("GetReader", (data, sink) => { + assert(this._networkStream, "GetReader - no `IPDFStream` instance available."); + this._fullReader = this._networkStream.getFullReader(); + this._fullReader.onProgress = evt => { + this._lastProgress = { + loaded: evt.loaded, + total: evt.total + }; + }; + sink.onPull = () => { + this._fullReader.read().then(function ({ + value, + done + }) { + if (done) { + sink.close(); + return; + } + assert(value instanceof ArrayBuffer, "GetReader - expected an ArrayBuffer."); + sink.enqueue(new Uint8Array(value), 1, [value]); + }).catch(reason => { + sink.error(reason); + }); + }; + sink.onCancel = reason => { + this._fullReader.cancel(reason); + sink.ready.catch(readyReason => { + if (this.destroyed) { + return; + } + throw readyReason; + }); + }; + }); + messageHandler.on("ReaderHeadersReady", async data => { + await this._fullReader.headersReady; + const { + isStreamingSupported, + isRangeSupported, + contentLength + } = this._fullReader; + if (!isStreamingSupported || !isRangeSupported) { + if (this._lastProgress) { + loadingTask.onProgress?.(this._lastProgress); + } + this._fullReader.onProgress = evt => { + loadingTask.onProgress?.({ + loaded: evt.loaded, + total: evt.total + }); + }; + } + return { + isStreamingSupported, + isRangeSupported, + contentLength + }; + }); + messageHandler.on("GetRangeReader", (data, sink) => { + assert(this._networkStream, "GetRangeReader - no `IPDFStream` instance available."); + const rangeReader = this._networkStream.getRangeReader(data.begin, data.end); + if (!rangeReader) { + sink.close(); + return; + } + sink.onPull = () => { + rangeReader.read().then(function ({ + value, + done + }) { + if (done) { + sink.close(); + return; + } + assert(value instanceof ArrayBuffer, "GetRangeReader - expected an ArrayBuffer."); + sink.enqueue(new Uint8Array(value), 1, [value]); + }).catch(reason => { + sink.error(reason); + }); + }; + sink.onCancel = reason => { + rangeReader.cancel(reason); + sink.ready.catch(readyReason => { + if (this.destroyed) { + return; + } + throw readyReason; + }); + }; + }); + messageHandler.on("GetDoc", ({ + pdfInfo + }) => { + this._numPages = pdfInfo.numPages; + this._htmlForXfa = pdfInfo.htmlForXfa; + delete pdfInfo.htmlForXfa; + loadingTask._capability.resolve(new PDFDocumentProxy(pdfInfo, this)); + }); + messageHandler.on("DocException", ex => { + loadingTask._capability.reject(wrapReason(ex)); + }); + messageHandler.on("PasswordRequest", ex => { + this.#passwordCapability = Promise.withResolvers(); + try { + if (!loadingTask.onPassword) { + throw wrapReason(ex); + } + const updatePassword = password => { + if (password instanceof Error) { + this.#passwordCapability.reject(password); + } else { + this.#passwordCapability.resolve({ + password + }); + } + }; + loadingTask.onPassword(updatePassword, ex.code); + } catch (err) { + this.#passwordCapability.reject(err); + } + return this.#passwordCapability.promise; + }); + messageHandler.on("DataLoaded", data => { + loadingTask.onProgress?.({ + loaded: data.length, + total: data.length + }); + this.downloadInfoCapability.resolve(data); + }); + messageHandler.on("StartRenderPage", data => { + if (this.destroyed) { + return; + } + const page = this.#pageCache.get(data.pageIndex); + page._startRenderPage(data.transparency, data.cacheKey); + }); + messageHandler.on("commonobj", ([id, type, exportedData]) => { + if (this.destroyed) { + return null; + } + if (this.commonObjs.has(id)) { + return null; + } + switch (type) { + case "Font": + const { + disableFontFace, + fontExtraProperties, + pdfBug + } = this._params; + if ("error" in exportedData) { + const exportedError = exportedData.error; + warn(`Error during font loading: ${exportedError}`); + this.commonObjs.resolve(id, exportedError); + break; + } + const inspectFont = pdfBug && globalThis.FontInspector?.enabled ? (font, url) => globalThis.FontInspector.fontAdded(font, url) : null; + const font = new FontFaceObject(exportedData, { + disableFontFace, + fontExtraProperties, + inspectFont + }); + this.fontLoader.bind(font).catch(() => messageHandler.sendWithPromise("FontFallback", { + id + })).finally(() => { + if (!fontExtraProperties && font.data) { + font.data = null; + } + this.commonObjs.resolve(id, font); + }); + break; + case "CopyLocalImage": + const { + imageRef + } = exportedData; + assert(imageRef, "The imageRef must be defined."); + for (const pageProxy of this.#pageCache.values()) { + for (const [, data] of pageProxy.objs) { + if (data?.ref !== imageRef) { + continue; + } + if (!data.dataLen) { + return null; + } + this.commonObjs.resolve(id, structuredClone(data)); + return data.dataLen; + } + } + break; + case "FontPath": + case "Image": + case "Pattern": + this.commonObjs.resolve(id, exportedData); + break; + default: + throw new Error(`Got unknown common object type ${type}`); + } + return null; + }); + messageHandler.on("obj", ([id, pageIndex, type, imageData]) => { + if (this.destroyed) { + return; + } + const pageProxy = this.#pageCache.get(pageIndex); + if (pageProxy.objs.has(id)) { + return; + } + if (pageProxy._intentStates.size === 0) { + imageData?.bitmap?.close(); + return; + } + switch (type) { + case "Image": + pageProxy.objs.resolve(id, imageData); + if (imageData?.dataLen > MAX_IMAGE_SIZE_TO_CACHE) { + pageProxy._maybeCleanupAfterRender = true; + } + break; + case "Pattern": + pageProxy.objs.resolve(id, imageData); + break; + default: + throw new Error(`Got unknown object type ${type}`); + } + }); + messageHandler.on("DocProgress", data => { + if (this.destroyed) { + return; + } + loadingTask.onProgress?.({ + loaded: data.loaded, + total: data.total + }); + }); + messageHandler.on("FetchBuiltInCMap", async data => { + if (this.destroyed) { + throw new Error("Worker was destroyed."); + } + if (!this.cMapReaderFactory) { + throw new Error("CMapReaderFactory not initialized, see the `useWorkerFetch` parameter."); + } + return this.cMapReaderFactory.fetch(data); + }); + messageHandler.on("FetchStandardFontData", async data => { + if (this.destroyed) { + throw new Error("Worker was destroyed."); + } + if (!this.standardFontDataFactory) { + throw new Error("StandardFontDataFactory not initialized, see the `useWorkerFetch` parameter."); + } + return this.standardFontDataFactory.fetch(data); + }); + } + getData() { + return this.messageHandler.sendWithPromise("GetData", null); + } + saveDocument() { + if (this.annotationStorage.size <= 0) { + warn("saveDocument called while `annotationStorage` is empty, " + "please use the getData-method instead."); + } + const { + map, + transfer + } = this.annotationStorage.serializable; + return this.messageHandler.sendWithPromise("SaveDocument", { + isPureXfa: !!this._htmlForXfa, + numPages: this._numPages, + annotationStorage: map, + filename: this._fullReader?.filename ?? null + }, transfer).finally(() => { + this.annotationStorage.resetModified(); + }); + } + getPage(pageNumber) { + if (!Number.isInteger(pageNumber) || pageNumber <= 0 || pageNumber > this._numPages) { + return Promise.reject(new Error("Invalid page request.")); + } + const pageIndex = pageNumber - 1, + cachedPromise = this.#pagePromises.get(pageIndex); + if (cachedPromise) { + return cachedPromise; + } + const promise = this.messageHandler.sendWithPromise("GetPage", { + pageIndex + }).then(pageInfo => { + if (this.destroyed) { + throw new Error("Transport destroyed"); + } + if (pageInfo.refStr) { + this.#pageRefCache.set(pageInfo.refStr, pageNumber); + } + const page = new PDFPageProxy(pageIndex, pageInfo, this, this._params.pdfBug); + this.#pageCache.set(pageIndex, page); + return page; + }); + this.#pagePromises.set(pageIndex, promise); + return promise; + } + getPageIndex(ref) { + if (!isRefProxy(ref)) { + return Promise.reject(new Error("Invalid pageIndex request.")); + } + return this.messageHandler.sendWithPromise("GetPageIndex", { + num: ref.num, + gen: ref.gen + }); + } + getAnnotations(pageIndex, intent) { + return this.messageHandler.sendWithPromise("GetAnnotations", { + pageIndex, + intent + }); + } + getFieldObjects() { + return this.#cacheSimpleMethod("GetFieldObjects"); + } + hasJSActions() { + return this.#cacheSimpleMethod("HasJSActions"); + } + getCalculationOrderIds() { + return this.messageHandler.sendWithPromise("GetCalculationOrderIds", null); + } + getDestinations() { + return this.messageHandler.sendWithPromise("GetDestinations", null); + } + getDestination(id) { + if (typeof id !== "string") { + return Promise.reject(new Error("Invalid destination request.")); + } + return this.messageHandler.sendWithPromise("GetDestination", { + id + }); + } + getPageLabels() { + return this.messageHandler.sendWithPromise("GetPageLabels", null); + } + getPageLayout() { + return this.messageHandler.sendWithPromise("GetPageLayout", null); + } + getPageMode() { + return this.messageHandler.sendWithPromise("GetPageMode", null); + } + getViewerPreferences() { + return this.messageHandler.sendWithPromise("GetViewerPreferences", null); + } + getOpenAction() { + return this.messageHandler.sendWithPromise("GetOpenAction", null); + } + getAttachments() { + return this.messageHandler.sendWithPromise("GetAttachments", null); + } + getDocJSActions() { + return this.#cacheSimpleMethod("GetDocJSActions"); + } + getPageJSActions(pageIndex) { + return this.messageHandler.sendWithPromise("GetPageJSActions", { + pageIndex + }); + } + getStructTree(pageIndex) { + return this.messageHandler.sendWithPromise("GetStructTree", { + pageIndex + }); + } + getOutline() { + return this.messageHandler.sendWithPromise("GetOutline", null); + } + getOptionalContentConfig(renderingIntent) { + return this.#cacheSimpleMethod("GetOptionalContentConfig").then(data => new OptionalContentConfig(data, renderingIntent)); + } + getPermissions() { + return this.messageHandler.sendWithPromise("GetPermissions", null); + } + getMetadata() { + const name = "GetMetadata", + cachedPromise = this.#methodPromises.get(name); + if (cachedPromise) { + return cachedPromise; + } + const promise = this.messageHandler.sendWithPromise(name, null).then(results => ({ + info: results[0], + metadata: results[1] ? new Metadata(results[1]) : null, + contentDispositionFilename: this._fullReader?.filename ?? null, + contentLength: this._fullReader?.contentLength ?? null + })); + this.#methodPromises.set(name, promise); + return promise; + } + getMarkInfo() { + return this.messageHandler.sendWithPromise("GetMarkInfo", null); + } + async startCleanup(keepLoadedFonts = false) { + if (this.destroyed) { + return; + } + await this.messageHandler.sendWithPromise("Cleanup", null); + for (const page of this.#pageCache.values()) { + const cleanupSuccessful = page.cleanup(); + if (!cleanupSuccessful) { + throw new Error(`startCleanup: Page ${page.pageNumber} is currently rendering.`); + } + } + this.commonObjs.clear(); + if (!keepLoadedFonts) { + this.fontLoader.clear(); + } + this.#methodPromises.clear(); + this.filterFactory.destroy(true); + TextLayer.cleanup(); + } + cachedPageNumber(ref) { + if (!isRefProxy(ref)) { + return null; + } + const refStr = ref.gen === 0 ? `${ref.num}R` : `${ref.num}R${ref.gen}`; + return this.#pageRefCache.get(refStr) ?? null; + } +} +const INITIAL_DATA = Symbol("INITIAL_DATA"); +class PDFObjects { + #objs = Object.create(null); + #ensureObj(objId) { + return this.#objs[objId] ||= { + ...Promise.withResolvers(), + data: INITIAL_DATA + }; + } + get(objId, callback = null) { + if (callback) { + const obj = this.#ensureObj(objId); + obj.promise.then(() => callback(obj.data)); + return null; + } + const obj = this.#objs[objId]; + if (!obj || obj.data === INITIAL_DATA) { + throw new Error(`Requesting object that isn't resolved yet ${objId}.`); + } + return obj.data; + } + has(objId) { + const obj = this.#objs[objId]; + return !!obj && obj.data !== INITIAL_DATA; + } + delete(objId) { + const obj = this.#objs[objId]; + if (!obj || obj.data === INITIAL_DATA) { + return false; + } + delete this.#objs[objId]; + return true; + } + resolve(objId, data = null) { + const obj = this.#ensureObj(objId); + obj.data = data; + obj.resolve(); + } + clear() { + for (const objId in this.#objs) { + const { + data + } = this.#objs[objId]; + data?.bitmap?.close(); + } + this.#objs = Object.create(null); + } + *[Symbol.iterator]() { + for (const objId in this.#objs) { + const { + data + } = this.#objs[objId]; + if (data === INITIAL_DATA) { + continue; + } + yield [objId, data]; + } + } +} +class RenderTask { + #internalRenderTask = null; + constructor(internalRenderTask) { + this.#internalRenderTask = internalRenderTask; + this.onContinue = null; + } + get promise() { + return this.#internalRenderTask.capability.promise; + } + cancel(extraDelay = 0) { + this.#internalRenderTask.cancel(null, extraDelay); + } + get separateAnnots() { + const { + separateAnnots + } = this.#internalRenderTask.operatorList; + if (!separateAnnots) { + return false; + } + const { + annotationCanvasMap + } = this.#internalRenderTask; + return separateAnnots.form || separateAnnots.canvas && annotationCanvasMap?.size > 0; + } +} +class InternalRenderTask { + #rAF = null; + static #canvasInUse = new WeakSet(); + constructor({ + callback, + params, + objs, + commonObjs, + annotationCanvasMap, + operatorList, + pageIndex, + canvasFactory, + filterFactory, + useRequestAnimationFrame = false, + pdfBug = false, + pageColors = null + }) { + this.callback = callback; + this.params = params; + this.objs = objs; + this.commonObjs = commonObjs; + this.annotationCanvasMap = annotationCanvasMap; + this.operatorListIdx = null; + this.operatorList = operatorList; + this._pageIndex = pageIndex; + this.canvasFactory = canvasFactory; + this.filterFactory = filterFactory; + this._pdfBug = pdfBug; + this.pageColors = pageColors; + this.running = false; + this.graphicsReadyCallback = null; + this.graphicsReady = false; + this._useRequestAnimationFrame = useRequestAnimationFrame === true && typeof window !== "undefined"; + this.cancelled = false; + this.capability = Promise.withResolvers(); + this.task = new RenderTask(this); + this._cancelBound = this.cancel.bind(this); + this._continueBound = this._continue.bind(this); + this._scheduleNextBound = this._scheduleNext.bind(this); + this._nextBound = this._next.bind(this); + this._canvas = params.canvasContext.canvas; + } + get completed() { + return this.capability.promise.catch(function () {}); + } + initializeGraphics({ + transparency = false, + optionalContentConfig + }) { + if (this.cancelled) { + return; + } + if (this._canvas) { + if (InternalRenderTask.#canvasInUse.has(this._canvas)) { + throw new Error("Cannot use the same canvas during multiple render() operations. " + "Use different canvas or ensure previous operations were " + "cancelled or completed."); + } + InternalRenderTask.#canvasInUse.add(this._canvas); + } + if (this._pdfBug && globalThis.StepperManager?.enabled) { + this.stepper = globalThis.StepperManager.create(this._pageIndex); + this.stepper.init(this.operatorList); + this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint(); + } + const { + canvasContext, + viewport, + transform, + background + } = this.params; + this.gfx = new CanvasGraphics(canvasContext, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, { + optionalContentConfig + }, this.annotationCanvasMap, this.pageColors); + this.gfx.beginDrawing({ + transform, + viewport, + transparency, + background + }); + this.operatorListIdx = 0; + this.graphicsReady = true; + this.graphicsReadyCallback?.(); + } + cancel(error = null, extraDelay = 0) { + this.running = false; + this.cancelled = true; + this.gfx?.endDrawing(); + if (this.#rAF) { + window.cancelAnimationFrame(this.#rAF); + this.#rAF = null; + } + InternalRenderTask.#canvasInUse.delete(this._canvas); + this.callback(error || new RenderingCancelledException(`Rendering cancelled, page ${this._pageIndex + 1}`, extraDelay)); + } + operatorListChanged() { + if (!this.graphicsReady) { + this.graphicsReadyCallback ||= this._continueBound; + return; + } + this.stepper?.updateOperatorList(this.operatorList); + if (this.running) { + return; + } + this._continue(); + } + _continue() { + this.running = true; + if (this.cancelled) { + return; + } + if (this.task.onContinue) { + this.task.onContinue(this._scheduleNextBound); + } else { + this._scheduleNext(); + } + } + _scheduleNext() { + if (this._useRequestAnimationFrame) { + this.#rAF = window.requestAnimationFrame(() => { + this.#rAF = null; + this._nextBound().catch(this._cancelBound); + }); + } else { + Promise.resolve().then(this._nextBound).catch(this._cancelBound); + } + } + async _next() { + if (this.cancelled) { + return; + } + this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, this.operatorListIdx, this._continueBound, this.stepper); + if (this.operatorListIdx === this.operatorList.argsArray.length) { + this.running = false; + if (this.operatorList.lastChunk) { + this.gfx.endDrawing(); + InternalRenderTask.#canvasInUse.delete(this._canvas); + this.callback(); + } + } + } +} +const version = "4.10.38"; +const build = "f9bea397f"; + +;// ./src/shared/scripting_utils.js +function makeColorComp(n) { + return Math.floor(Math.max(0, Math.min(1, n)) * 255).toString(16).padStart(2, "0"); +} +function scaleAndClamp(x) { + return Math.max(0, Math.min(255, 255 * x)); +} +class ColorConverters { + static CMYK_G([c, y, m, k]) { + return ["G", 1 - Math.min(1, 0.3 * c + 0.59 * m + 0.11 * y + k)]; + } + static G_CMYK([g]) { + return ["CMYK", 0, 0, 0, 1 - g]; + } + static G_RGB([g]) { + return ["RGB", g, g, g]; + } + static G_rgb([g]) { + g = scaleAndClamp(g); + return [g, g, g]; + } + static G_HTML([g]) { + const G = makeColorComp(g); + return `#${G}${G}${G}`; + } + static RGB_G([r, g, b]) { + return ["G", 0.3 * r + 0.59 * g + 0.11 * b]; + } + static RGB_rgb(color) { + return color.map(scaleAndClamp); + } + static RGB_HTML(color) { + return `#${color.map(makeColorComp).join("")}`; + } + static T_HTML() { + return "#00000000"; + } + static T_rgb() { + return [null]; + } + static CMYK_RGB([c, y, m, k]) { + return ["RGB", 1 - Math.min(1, c + k), 1 - Math.min(1, m + k), 1 - Math.min(1, y + k)]; + } + static CMYK_rgb([c, y, m, k]) { + return [scaleAndClamp(1 - Math.min(1, c + k)), scaleAndClamp(1 - Math.min(1, m + k)), scaleAndClamp(1 - Math.min(1, y + k))]; + } + static CMYK_HTML(components) { + const rgb = this.CMYK_RGB(components).slice(1); + return this.RGB_HTML(rgb); + } + static RGB_CMYK([r, g, b]) { + const c = 1 - r; + const m = 1 - g; + const y = 1 - b; + const k = Math.min(c, m, y); + return ["CMYK", c, m, y, k]; + } +} + +;// ./src/display/svg_factory.js + + +class BaseSVGFactory { + create(width, height, skipDimensions = false) { + if (width <= 0 || height <= 0) { + throw new Error("Invalid SVG dimensions"); + } + const svg = this._createSVG("svg:svg"); + svg.setAttribute("version", "1.1"); + if (!skipDimensions) { + svg.setAttribute("width", `${width}px`); + svg.setAttribute("height", `${height}px`); + } + svg.setAttribute("preserveAspectRatio", "none"); + svg.setAttribute("viewBox", `0 0 ${width} ${height}`); + return svg; + } + createElement(type) { + if (typeof type !== "string") { + throw new Error("Invalid SVG element type"); + } + return this._createSVG(type); + } + _createSVG(type) { + unreachable("Abstract method `_createSVG` called."); + } +} +class DOMSVGFactory extends BaseSVGFactory { + _createSVG(type) { + return document.createElementNS(SVG_NS, type); + } +} + +;// ./src/display/xfa_layer.js + +class XfaLayer { + static setupStorage(html, id, element, storage, intent) { + const storedData = storage.getValue(id, { + value: null + }); + switch (element.name) { + case "textarea": + if (storedData.value !== null) { + html.textContent = storedData.value; + } + if (intent === "print") { + break; + } + html.addEventListener("input", event => { + storage.setValue(id, { + value: event.target.value + }); + }); + break; + case "input": + if (element.attributes.type === "radio" || element.attributes.type === "checkbox") { + if (storedData.value === element.attributes.xfaOn) { + html.setAttribute("checked", true); + } else if (storedData.value === element.attributes.xfaOff) { + html.removeAttribute("checked"); + } + if (intent === "print") { + break; + } + html.addEventListener("change", event => { + storage.setValue(id, { + value: event.target.checked ? event.target.getAttribute("xfaOn") : event.target.getAttribute("xfaOff") + }); + }); + } else { + if (storedData.value !== null) { + html.setAttribute("value", storedData.value); + } + if (intent === "print") { + break; + } + html.addEventListener("input", event => { + storage.setValue(id, { + value: event.target.value + }); + }); + } + break; + case "select": + if (storedData.value !== null) { + html.setAttribute("value", storedData.value); + for (const option of element.children) { + if (option.attributes.value === storedData.value) { + option.attributes.selected = true; + } else if (option.attributes.hasOwnProperty("selected")) { + delete option.attributes.selected; + } + } + } + html.addEventListener("input", event => { + const options = event.target.options; + const value = options.selectedIndex === -1 ? "" : options[options.selectedIndex].value; + storage.setValue(id, { + value + }); + }); + break; + } + } + static setAttributes({ + html, + element, + storage = null, + intent, + linkService + }) { + const { + attributes + } = element; + const isHTMLAnchorElement = html instanceof HTMLAnchorElement; + if (attributes.type === "radio") { + attributes.name = `${attributes.name}-${intent}`; + } + for (const [key, value] of Object.entries(attributes)) { + if (value === null || value === undefined) { + continue; + } + switch (key) { + case "class": + if (value.length) { + html.setAttribute(key, value.join(" ")); + } + break; + case "dataId": + break; + case "id": + html.setAttribute("data-element-id", value); + break; + case "style": + Object.assign(html.style, value); + break; + case "textContent": + html.textContent = value; + break; + default: + if (!isHTMLAnchorElement || key !== "href" && key !== "newWindow") { + html.setAttribute(key, value); + } + } + } + if (isHTMLAnchorElement) { + linkService.addLinkAttributes(html, attributes.href, attributes.newWindow); + } + if (storage && attributes.dataId) { + this.setupStorage(html, attributes.dataId, element, storage); + } + } + static render(parameters) { + const storage = parameters.annotationStorage; + const linkService = parameters.linkService; + const root = parameters.xfaHtml; + const intent = parameters.intent || "display"; + const rootHtml = document.createElement(root.name); + if (root.attributes) { + this.setAttributes({ + html: rootHtml, + element: root, + intent, + linkService + }); + } + const isNotForRichText = intent !== "richText"; + const rootDiv = parameters.div; + rootDiv.append(rootHtml); + if (parameters.viewport) { + const transform = `matrix(${parameters.viewport.transform.join(",")})`; + rootDiv.style.transform = transform; + } + if (isNotForRichText) { + rootDiv.setAttribute("class", "xfaLayer xfaFont"); + } + const textDivs = []; + if (root.children.length === 0) { + if (root.value) { + const node = document.createTextNode(root.value); + rootHtml.append(node); + if (isNotForRichText && XfaText.shouldBuildText(root.name)) { + textDivs.push(node); + } + } + return { + textDivs + }; + } + const stack = [[root, -1, rootHtml]]; + while (stack.length > 0) { + const [parent, i, html] = stack.at(-1); + if (i + 1 === parent.children.length) { + stack.pop(); + continue; + } + const child = parent.children[++stack.at(-1)[1]]; + if (child === null) { + continue; + } + const { + name + } = child; + if (name === "#text") { + const node = document.createTextNode(child.value); + textDivs.push(node); + html.append(node); + continue; + } + const childHtml = child?.attributes?.xmlns ? document.createElementNS(child.attributes.xmlns, name) : document.createElement(name); + html.append(childHtml); + if (child.attributes) { + this.setAttributes({ + html: childHtml, + element: child, + storage, + intent, + linkService + }); + } + if (child.children?.length > 0) { + stack.push([child, -1, childHtml]); + } else if (child.value) { + const node = document.createTextNode(child.value); + if (isNotForRichText && XfaText.shouldBuildText(name)) { + textDivs.push(node); + } + childHtml.append(node); + } + } + for (const el of rootDiv.querySelectorAll(".xfaNonInteractive input, .xfaNonInteractive textarea")) { + el.setAttribute("readOnly", true); + } + return { + textDivs + }; + } + static update(parameters) { + const transform = `matrix(${parameters.viewport.transform.join(",")})`; + parameters.div.style.transform = transform; + parameters.div.hidden = false; + } +} + +;// ./src/display/annotation_layer.js + + + + + + +const DEFAULT_TAB_INDEX = 1000; +const annotation_layer_DEFAULT_FONT_SIZE = 9; +const GetElementsByNameSet = new WeakSet(); +function getRectDims(rect) { + return { + width: rect[2] - rect[0], + height: rect[3] - rect[1] + }; +} +class AnnotationElementFactory { + static create(parameters) { + const subtype = parameters.data.annotationType; + switch (subtype) { + case AnnotationType.LINK: + return new LinkAnnotationElement(parameters); + case AnnotationType.TEXT: + return new TextAnnotationElement(parameters); + case AnnotationType.WIDGET: + const fieldType = parameters.data.fieldType; + switch (fieldType) { + case "Tx": + return new TextWidgetAnnotationElement(parameters); + case "Btn": + if (parameters.data.radioButton) { + return new RadioButtonWidgetAnnotationElement(parameters); + } else if (parameters.data.checkBox) { + return new CheckboxWidgetAnnotationElement(parameters); + } + return new PushButtonWidgetAnnotationElement(parameters); + case "Ch": + return new ChoiceWidgetAnnotationElement(parameters); + case "Sig": + return new SignatureWidgetAnnotationElement(parameters); + } + return new WidgetAnnotationElement(parameters); + case AnnotationType.POPUP: + return new PopupAnnotationElement(parameters); + case AnnotationType.FREETEXT: + return new FreeTextAnnotationElement(parameters); + case AnnotationType.LINE: + return new LineAnnotationElement(parameters); + case AnnotationType.SQUARE: + return new SquareAnnotationElement(parameters); + case AnnotationType.CIRCLE: + return new CircleAnnotationElement(parameters); + case AnnotationType.POLYLINE: + return new PolylineAnnotationElement(parameters); + case AnnotationType.CARET: + return new CaretAnnotationElement(parameters); + case AnnotationType.INK: + return new InkAnnotationElement(parameters); + case AnnotationType.POLYGON: + return new PolygonAnnotationElement(parameters); + case AnnotationType.HIGHLIGHT: + return new HighlightAnnotationElement(parameters); + case AnnotationType.UNDERLINE: + return new UnderlineAnnotationElement(parameters); + case AnnotationType.SQUIGGLY: + return new SquigglyAnnotationElement(parameters); + case AnnotationType.STRIKEOUT: + return new StrikeOutAnnotationElement(parameters); + case AnnotationType.STAMP: + return new StampAnnotationElement(parameters); + case AnnotationType.FILEATTACHMENT: + return new FileAttachmentAnnotationElement(parameters); + default: + return new AnnotationElement(parameters); + } + } +} +class AnnotationElement { + #updates = null; + #hasBorder = false; + #popupElement = null; + constructor(parameters, { + isRenderable = false, + ignoreBorder = false, + createQuadrilaterals = false + } = {}) { + this.isRenderable = isRenderable; + this.data = parameters.data; + this.layer = parameters.layer; + this.linkService = parameters.linkService; + this.downloadManager = parameters.downloadManager; + this.imageResourcesPath = parameters.imageResourcesPath; + this.renderForms = parameters.renderForms; + this.svgFactory = parameters.svgFactory; + this.annotationStorage = parameters.annotationStorage; + this.enableScripting = parameters.enableScripting; + this.hasJSActions = parameters.hasJSActions; + this._fieldObjects = parameters.fieldObjects; + this.parent = parameters.parent; + if (isRenderable) { + this.container = this._createContainer(ignoreBorder); + } + if (createQuadrilaterals) { + this._createQuadrilaterals(); + } + } + static _hasPopupData({ + titleObj, + contentsObj, + richText + }) { + return !!(titleObj?.str || contentsObj?.str || richText?.str); + } + get _isEditable() { + return this.data.isEditable; + } + get hasPopupData() { + return AnnotationElement._hasPopupData(this.data); + } + updateEdited(params) { + if (!this.container) { + return; + } + this.#updates ||= { + rect: this.data.rect.slice(0) + }; + const { + rect + } = params; + if (rect) { + this.#setRectEdited(rect); + } + this.#popupElement?.popup.updateEdited(params); + } + resetEdited() { + if (!this.#updates) { + return; + } + this.#setRectEdited(this.#updates.rect); + this.#popupElement?.popup.resetEdited(); + this.#updates = null; + } + #setRectEdited(rect) { + const { + container: { + style + }, + data: { + rect: currentRect, + rotation + }, + parent: { + viewport: { + rawDims: { + pageWidth, + pageHeight, + pageX, + pageY + } + } + } + } = this; + currentRect?.splice(0, 4, ...rect); + const { + width, + height + } = getRectDims(rect); + style.left = `${100 * (rect[0] - pageX) / pageWidth}%`; + style.top = `${100 * (pageHeight - rect[3] + pageY) / pageHeight}%`; + if (rotation === 0) { + style.width = `${100 * width / pageWidth}%`; + style.height = `${100 * height / pageHeight}%`; + } else { + this.setRotation(rotation); + } + } + _createContainer(ignoreBorder) { + const { + data, + parent: { + page, + viewport + } + } = this; + const container = document.createElement("section"); + container.setAttribute("data-annotation-id", data.id); + if (!(this instanceof WidgetAnnotationElement)) { + container.tabIndex = DEFAULT_TAB_INDEX; + } + const { + style + } = container; + style.zIndex = this.parent.zIndex++; + if (data.alternativeText) { + container.title = data.alternativeText; + } + if (data.noRotate) { + container.classList.add("norotate"); + } + if (!data.rect || this instanceof PopupAnnotationElement) { + const { + rotation + } = data; + if (!data.hasOwnCanvas && rotation !== 0) { + this.setRotation(rotation, container); + } + return container; + } + const { + width, + height + } = getRectDims(data.rect); + if (!ignoreBorder && data.borderStyle.width > 0) { + style.borderWidth = `${data.borderStyle.width}px`; + const horizontalRadius = data.borderStyle.horizontalCornerRadius; + const verticalRadius = data.borderStyle.verticalCornerRadius; + if (horizontalRadius > 0 || verticalRadius > 0) { + const radius = `calc(${horizontalRadius}px * var(--scale-factor)) / calc(${verticalRadius}px * var(--scale-factor))`; + style.borderRadius = radius; + } else if (this instanceof RadioButtonWidgetAnnotationElement) { + const radius = `calc(${width}px * var(--scale-factor)) / calc(${height}px * var(--scale-factor))`; + style.borderRadius = radius; + } + switch (data.borderStyle.style) { + case AnnotationBorderStyleType.SOLID: + style.borderStyle = "solid"; + break; + case AnnotationBorderStyleType.DASHED: + style.borderStyle = "dashed"; + break; + case AnnotationBorderStyleType.BEVELED: + warn("Unimplemented border style: beveled"); + break; + case AnnotationBorderStyleType.INSET: + warn("Unimplemented border style: inset"); + break; + case AnnotationBorderStyleType.UNDERLINE: + style.borderBottomStyle = "solid"; + break; + default: + break; + } + const borderColor = data.borderColor || null; + if (borderColor) { + this.#hasBorder = true; + style.borderColor = Util.makeHexColor(borderColor[0] | 0, borderColor[1] | 0, borderColor[2] | 0); + } else { + style.borderWidth = 0; + } + } + const rect = Util.normalizeRect([data.rect[0], page.view[3] - data.rect[1] + page.view[1], data.rect[2], page.view[3] - data.rect[3] + page.view[1]]); + const { + pageWidth, + pageHeight, + pageX, + pageY + } = viewport.rawDims; + style.left = `${100 * (rect[0] - pageX) / pageWidth}%`; + style.top = `${100 * (rect[1] - pageY) / pageHeight}%`; + const { + rotation + } = data; + if (data.hasOwnCanvas || rotation === 0) { + style.width = `${100 * width / pageWidth}%`; + style.height = `${100 * height / pageHeight}%`; + } else { + this.setRotation(rotation, container); + } + return container; + } + setRotation(angle, container = this.container) { + if (!this.data.rect) { + return; + } + const { + pageWidth, + pageHeight + } = this.parent.viewport.rawDims; + const { + width, + height + } = getRectDims(this.data.rect); + let elementWidth, elementHeight; + if (angle % 180 === 0) { + elementWidth = 100 * width / pageWidth; + elementHeight = 100 * height / pageHeight; + } else { + elementWidth = 100 * height / pageWidth; + elementHeight = 100 * width / pageHeight; + } + container.style.width = `${elementWidth}%`; + container.style.height = `${elementHeight}%`; + container.setAttribute("data-main-rotation", (360 - angle) % 360); + } + get _commonActions() { + const setColor = (jsName, styleName, event) => { + const color = event.detail[jsName]; + const colorType = color[0]; + const colorArray = color.slice(1); + event.target.style[styleName] = ColorConverters[`${colorType}_HTML`](colorArray); + this.annotationStorage.setValue(this.data.id, { + [styleName]: ColorConverters[`${colorType}_rgb`](colorArray) + }); + }; + return shadow(this, "_commonActions", { + display: event => { + const { + display + } = event.detail; + const hidden = display % 2 === 1; + this.container.style.visibility = hidden ? "hidden" : "visible"; + this.annotationStorage.setValue(this.data.id, { + noView: hidden, + noPrint: display === 1 || display === 2 + }); + }, + print: event => { + this.annotationStorage.setValue(this.data.id, { + noPrint: !event.detail.print + }); + }, + hidden: event => { + const { + hidden + } = event.detail; + this.container.style.visibility = hidden ? "hidden" : "visible"; + this.annotationStorage.setValue(this.data.id, { + noPrint: hidden, + noView: hidden + }); + }, + focus: event => { + setTimeout(() => event.target.focus({ + preventScroll: false + }), 0); + }, + userName: event => { + event.target.title = event.detail.userName; + }, + readonly: event => { + event.target.disabled = event.detail.readonly; + }, + required: event => { + this._setRequired(event.target, event.detail.required); + }, + bgColor: event => { + setColor("bgColor", "backgroundColor", event); + }, + fillColor: event => { + setColor("fillColor", "backgroundColor", event); + }, + fgColor: event => { + setColor("fgColor", "color", event); + }, + textColor: event => { + setColor("textColor", "color", event); + }, + borderColor: event => { + setColor("borderColor", "borderColor", event); + }, + strokeColor: event => { + setColor("strokeColor", "borderColor", event); + }, + rotation: event => { + const angle = event.detail.rotation; + this.setRotation(angle); + this.annotationStorage.setValue(this.data.id, { + rotation: angle + }); + } + }); + } + _dispatchEventFromSandbox(actions, jsEvent) { + const commonActions = this._commonActions; + for (const name of Object.keys(jsEvent.detail)) { + const action = actions[name] || commonActions[name]; + action?.(jsEvent); + } + } + _setDefaultPropertiesFromJS(element) { + if (!this.enableScripting) { + return; + } + const storedData = this.annotationStorage.getRawValue(this.data.id); + if (!storedData) { + return; + } + const commonActions = this._commonActions; + for (const [actionName, detail] of Object.entries(storedData)) { + const action = commonActions[actionName]; + if (action) { + const eventProxy = { + detail: { + [actionName]: detail + }, + target: element + }; + action(eventProxy); + delete storedData[actionName]; + } + } + } + _createQuadrilaterals() { + if (!this.container) { + return; + } + const { + quadPoints + } = this.data; + if (!quadPoints) { + return; + } + const [rectBlX, rectBlY, rectTrX, rectTrY] = this.data.rect.map(x => Math.fround(x)); + if (quadPoints.length === 8) { + const [trX, trY, blX, blY] = quadPoints.subarray(2, 6); + if (rectTrX === trX && rectTrY === trY && rectBlX === blX && rectBlY === blY) { + return; + } + } + const { + style + } = this.container; + let svgBuffer; + if (this.#hasBorder) { + const { + borderColor, + borderWidth + } = style; + style.borderWidth = 0; + svgBuffer = ["url('data:image/svg+xml;utf8,", ``, ``]; + this.container.classList.add("hasBorder"); + } + const width = rectTrX - rectBlX; + const height = rectTrY - rectBlY; + const { + svgFactory + } = this; + const svg = svgFactory.createElement("svg"); + svg.classList.add("quadrilateralsContainer"); + svg.setAttribute("width", 0); + svg.setAttribute("height", 0); + const defs = svgFactory.createElement("defs"); + svg.append(defs); + const clipPath = svgFactory.createElement("clipPath"); + const id = `clippath_${this.data.id}`; + clipPath.setAttribute("id", id); + clipPath.setAttribute("clipPathUnits", "objectBoundingBox"); + defs.append(clipPath); + for (let i = 2, ii = quadPoints.length; i < ii; i += 8) { + const trX = quadPoints[i]; + const trY = quadPoints[i + 1]; + const blX = quadPoints[i + 2]; + const blY = quadPoints[i + 3]; + const rect = svgFactory.createElement("rect"); + const x = (blX - rectBlX) / width; + const y = (rectTrY - trY) / height; + const rectWidth = (trX - blX) / width; + const rectHeight = (trY - blY) / height; + rect.setAttribute("x", x); + rect.setAttribute("y", y); + rect.setAttribute("width", rectWidth); + rect.setAttribute("height", rectHeight); + clipPath.append(rect); + svgBuffer?.push(``); + } + if (this.#hasBorder) { + svgBuffer.push(`')`); + style.backgroundImage = svgBuffer.join(""); + } + this.container.append(svg); + this.container.style.clipPath = `url(#${id})`; + } + _createPopup() { + const { + data + } = this; + const popup = this.#popupElement = new PopupAnnotationElement({ + data: { + color: data.color, + titleObj: data.titleObj, + modificationDate: data.modificationDate, + contentsObj: data.contentsObj, + richText: data.richText, + parentRect: data.rect, + borderStyle: 0, + id: `popup_${data.id}`, + rotation: data.rotation + }, + parent: this.parent, + elements: [this] + }); + this.parent.div.append(popup.render()); + } + render() { + unreachable("Abstract method `AnnotationElement.render` called"); + } + _getElementsByName(name, skipId = null) { + const fields = []; + if (this._fieldObjects) { + const fieldObj = this._fieldObjects[name]; + if (fieldObj) { + for (const { + page, + id, + exportValues + } of fieldObj) { + if (page === -1) { + continue; + } + if (id === skipId) { + continue; + } + const exportValue = typeof exportValues === "string" ? exportValues : null; + const domElement = document.querySelector(`[data-element-id="${id}"]`); + if (domElement && !GetElementsByNameSet.has(domElement)) { + warn(`_getElementsByName - element not allowed: ${id}`); + continue; + } + fields.push({ + id, + exportValue, + domElement + }); + } + } + return fields; + } + for (const domElement of document.getElementsByName(name)) { + const { + exportValue + } = domElement; + const id = domElement.getAttribute("data-element-id"); + if (id === skipId) { + continue; + } + if (!GetElementsByNameSet.has(domElement)) { + continue; + } + fields.push({ + id, + exportValue, + domElement + }); + } + return fields; + } + show() { + if (this.container) { + this.container.hidden = false; + } + this.popup?.maybeShow(); + } + hide() { + if (this.container) { + this.container.hidden = true; + } + this.popup?.forceHide(); + } + getElementsToTriggerPopup() { + return this.container; + } + addHighlightArea() { + const triggers = this.getElementsToTriggerPopup(); + if (Array.isArray(triggers)) { + for (const element of triggers) { + element.classList.add("highlightArea"); + } + } else { + triggers.classList.add("highlightArea"); + } + } + _editOnDoubleClick() { + if (!this._isEditable) { + return; + } + const { + annotationEditorType: mode, + data: { + id: editId + } + } = this; + this.container.addEventListener("dblclick", () => { + this.linkService.eventBus?.dispatch("switchannotationeditormode", { + source: this, + mode, + editId + }); + }); + } +} +class LinkAnnotationElement extends AnnotationElement { + constructor(parameters, options = null) { + super(parameters, { + isRenderable: true, + ignoreBorder: !!options?.ignoreBorder, + createQuadrilaterals: true + }); + this.isTooltipOnly = parameters.data.isTooltipOnly; + } + render() { + const { + data, + linkService + } = this; + const link = document.createElement("a"); + link.setAttribute("data-element-id", data.id); + let isBound = false; + if (data.url) { + linkService.addLinkAttributes(link, data.url, data.newWindow); + isBound = true; + } else if (data.action) { + this._bindNamedAction(link, data.action); + isBound = true; + } else if (data.attachment) { + this.#bindAttachment(link, data.attachment, data.attachmentDest); + isBound = true; + } else if (data.setOCGState) { + this.#bindSetOCGState(link, data.setOCGState); + isBound = true; + } else if (data.dest) { + this._bindLink(link, data.dest); + isBound = true; + } else { + if (data.actions && (data.actions.Action || data.actions["Mouse Up"] || data.actions["Mouse Down"]) && this.enableScripting && this.hasJSActions) { + this._bindJSAction(link, data); + isBound = true; + } + if (data.resetForm) { + this._bindResetFormAction(link, data.resetForm); + isBound = true; + } else if (this.isTooltipOnly && !isBound) { + this._bindLink(link, ""); + isBound = true; + } + } + this.container.classList.add("linkAnnotation"); + if (isBound) { + this.container.append(link); + } + return this.container; + } + #setInternalLink() { + this.container.setAttribute("data-internal-link", ""); + } + _bindLink(link, destination) { + link.href = this.linkService.getDestinationHash(destination); + link.onclick = () => { + if (destination) { + this.linkService.goToDestination(destination); + } + return false; + }; + if (destination || destination === "") { + this.#setInternalLink(); + } + } + _bindNamedAction(link, action) { + link.href = this.linkService.getAnchorUrl(""); + link.onclick = () => { + this.linkService.executeNamedAction(action); + return false; + }; + this.#setInternalLink(); + } + #bindAttachment(link, attachment, dest = null) { + link.href = this.linkService.getAnchorUrl(""); + if (attachment.description) { + link.title = attachment.description; + } + link.onclick = () => { + this.downloadManager?.openOrDownloadData(attachment.content, attachment.filename, dest); + return false; + }; + this.#setInternalLink(); + } + #bindSetOCGState(link, action) { + link.href = this.linkService.getAnchorUrl(""); + link.onclick = () => { + this.linkService.executeSetOCGState(action); + return false; + }; + this.#setInternalLink(); + } + _bindJSAction(link, data) { + link.href = this.linkService.getAnchorUrl(""); + const map = new Map([["Action", "onclick"], ["Mouse Up", "onmouseup"], ["Mouse Down", "onmousedown"]]); + for (const name of Object.keys(data.actions)) { + const jsName = map.get(name); + if (!jsName) { + continue; + } + link[jsName] = () => { + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id: data.id, + name + } + }); + return false; + }; + } + if (!link.onclick) { + link.onclick = () => false; + } + this.#setInternalLink(); + } + _bindResetFormAction(link, resetForm) { + const otherClickAction = link.onclick; + if (!otherClickAction) { + link.href = this.linkService.getAnchorUrl(""); + } + this.#setInternalLink(); + if (!this._fieldObjects) { + warn(`_bindResetFormAction - "resetForm" action not supported, ` + "ensure that the `fieldObjects` parameter is provided."); + if (!otherClickAction) { + link.onclick = () => false; + } + return; + } + link.onclick = () => { + otherClickAction?.(); + const { + fields: resetFormFields, + refs: resetFormRefs, + include + } = resetForm; + const allFields = []; + if (resetFormFields.length !== 0 || resetFormRefs.length !== 0) { + const fieldIds = new Set(resetFormRefs); + for (const fieldName of resetFormFields) { + const fields = this._fieldObjects[fieldName] || []; + for (const { + id + } of fields) { + fieldIds.add(id); + } + } + for (const fields of Object.values(this._fieldObjects)) { + for (const field of fields) { + if (fieldIds.has(field.id) === include) { + allFields.push(field); + } + } + } + } else { + for (const fields of Object.values(this._fieldObjects)) { + allFields.push(...fields); + } + } + const storage = this.annotationStorage; + const allIds = []; + for (const field of allFields) { + const { + id + } = field; + allIds.push(id); + switch (field.type) { + case "text": + { + const value = field.defaultValue || ""; + storage.setValue(id, { + value + }); + break; + } + case "checkbox": + case "radiobutton": + { + const value = field.defaultValue === field.exportValues; + storage.setValue(id, { + value + }); + break; + } + case "combobox": + case "listbox": + { + const value = field.defaultValue || ""; + storage.setValue(id, { + value + }); + break; + } + default: + continue; + } + const domElement = document.querySelector(`[data-element-id="${id}"]`); + if (!domElement) { + continue; + } else if (!GetElementsByNameSet.has(domElement)) { + warn(`_bindResetFormAction - element not allowed: ${id}`); + continue; + } + domElement.dispatchEvent(new Event("resetform")); + } + if (this.enableScripting) { + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id: "app", + ids: allIds, + name: "ResetForm" + } + }); + } + return false; + }; + } +} +class TextAnnotationElement extends AnnotationElement { + constructor(parameters) { + super(parameters, { + isRenderable: true + }); + } + render() { + this.container.classList.add("textAnnotation"); + const image = document.createElement("img"); + image.src = this.imageResourcesPath + "annotation-" + this.data.name.toLowerCase() + ".svg"; + image.setAttribute("data-l10n-id", "pdfjs-text-annotation-type"); + image.setAttribute("data-l10n-args", JSON.stringify({ + type: this.data.name + })); + if (!this.data.popupRef && this.hasPopupData) { + this._createPopup(); + } + this.container.append(image); + return this.container; + } +} +class WidgetAnnotationElement extends AnnotationElement { + render() { + return this.container; + } + showElementAndHideCanvas(element) { + if (this.data.hasOwnCanvas) { + if (element.previousSibling?.nodeName === "CANVAS") { + element.previousSibling.hidden = true; + } + element.hidden = false; + } + } + _getKeyModifier(event) { + return util_FeatureTest.platform.isMac ? event.metaKey : event.ctrlKey; + } + _setEventListener(element, elementData, baseName, eventName, valueGetter) { + if (baseName.includes("mouse")) { + element.addEventListener(baseName, event => { + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id: this.data.id, + name: eventName, + value: valueGetter(event), + shift: event.shiftKey, + modifier: this._getKeyModifier(event) + } + }); + }); + } else { + element.addEventListener(baseName, event => { + if (baseName === "blur") { + if (!elementData.focused || !event.relatedTarget) { + return; + } + elementData.focused = false; + } else if (baseName === "focus") { + if (elementData.focused) { + return; + } + elementData.focused = true; + } + if (!valueGetter) { + return; + } + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id: this.data.id, + name: eventName, + value: valueGetter(event) + } + }); + }); + } + } + _setEventListeners(element, elementData, names, getter) { + for (const [baseName, eventName] of names) { + if (eventName === "Action" || this.data.actions?.[eventName]) { + if (eventName === "Focus" || eventName === "Blur") { + elementData ||= { + focused: false + }; + } + this._setEventListener(element, elementData, baseName, eventName, getter); + if (eventName === "Focus" && !this.data.actions?.Blur) { + this._setEventListener(element, elementData, "blur", "Blur", null); + } else if (eventName === "Blur" && !this.data.actions?.Focus) { + this._setEventListener(element, elementData, "focus", "Focus", null); + } + } + } + } + _setBackgroundColor(element) { + const color = this.data.backgroundColor || null; + element.style.backgroundColor = color === null ? "transparent" : Util.makeHexColor(color[0], color[1], color[2]); + } + _setTextStyle(element) { + const TEXT_ALIGNMENT = ["left", "center", "right"]; + const { + fontColor + } = this.data.defaultAppearanceData; + const fontSize = this.data.defaultAppearanceData.fontSize || annotation_layer_DEFAULT_FONT_SIZE; + const style = element.style; + let computedFontSize; + const BORDER_SIZE = 2; + const roundToOneDecimal = x => Math.round(10 * x) / 10; + if (this.data.multiLine) { + const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE); + const numberOfLines = Math.round(height / (LINE_FACTOR * fontSize)) || 1; + const lineHeight = height / numberOfLines; + computedFontSize = Math.min(fontSize, roundToOneDecimal(lineHeight / LINE_FACTOR)); + } else { + const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE); + computedFontSize = Math.min(fontSize, roundToOneDecimal(height / LINE_FACTOR)); + } + style.fontSize = `calc(${computedFontSize}px * var(--scale-factor))`; + style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]); + if (this.data.textAlignment !== null) { + style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment]; + } + } + _setRequired(element, isRequired) { + if (isRequired) { + element.setAttribute("required", true); + } else { + element.removeAttribute("required"); + } + element.setAttribute("aria-required", isRequired); + } +} +class TextWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + const isRenderable = parameters.renderForms || parameters.data.hasOwnCanvas || !parameters.data.hasAppearance && !!parameters.data.fieldValue; + super(parameters, { + isRenderable + }); + } + setPropertyOnSiblings(base, key, value, keyInStorage) { + const storage = this.annotationStorage; + for (const element of this._getElementsByName(base.name, base.id)) { + if (element.domElement) { + element.domElement[key] = value; + } + storage.setValue(element.id, { + [keyInStorage]: value + }); + } + } + render() { + const storage = this.annotationStorage; + const id = this.data.id; + this.container.classList.add("textWidgetAnnotation"); + let element = null; + if (this.renderForms) { + const storedData = storage.getValue(id, { + value: this.data.fieldValue + }); + let textContent = storedData.value || ""; + const maxLen = storage.getValue(id, { + charLimit: this.data.maxLen + }).charLimit; + if (maxLen && textContent.length > maxLen) { + textContent = textContent.slice(0, maxLen); + } + let fieldFormattedValues = storedData.formattedValue || this.data.textContent?.join("\n") || null; + if (fieldFormattedValues && this.data.comb) { + fieldFormattedValues = fieldFormattedValues.replaceAll(/\s+/g, ""); + } + const elementData = { + userValue: textContent, + formattedValue: fieldFormattedValues, + lastCommittedValue: null, + commitKey: 1, + focused: false + }; + if (this.data.multiLine) { + element = document.createElement("textarea"); + element.textContent = fieldFormattedValues ?? textContent; + if (this.data.doNotScroll) { + element.style.overflowY = "hidden"; + } + } else { + element = document.createElement("input"); + element.type = "text"; + element.setAttribute("value", fieldFormattedValues ?? textContent); + if (this.data.doNotScroll) { + element.style.overflowX = "hidden"; + } + } + if (this.data.hasOwnCanvas) { + element.hidden = true; + } + GetElementsByNameSet.add(element); + element.setAttribute("data-element-id", id); + element.disabled = this.data.readOnly; + element.name = this.data.fieldName; + element.tabIndex = DEFAULT_TAB_INDEX; + this._setRequired(element, this.data.required); + if (maxLen) { + element.maxLength = maxLen; + } + element.addEventListener("input", event => { + storage.setValue(id, { + value: event.target.value + }); + this.setPropertyOnSiblings(element, "value", event.target.value, "value"); + elementData.formattedValue = null; + }); + element.addEventListener("resetform", event => { + const defaultValue = this.data.defaultFieldValue ?? ""; + element.value = elementData.userValue = defaultValue; + elementData.formattedValue = null; + }); + let blurListener = event => { + const { + formattedValue + } = elementData; + if (formattedValue !== null && formattedValue !== undefined) { + event.target.value = formattedValue; + } + event.target.scrollLeft = 0; + }; + if (this.enableScripting && this.hasJSActions) { + element.addEventListener("focus", event => { + if (elementData.focused) { + return; + } + const { + target + } = event; + if (elementData.userValue) { + target.value = elementData.userValue; + } + elementData.lastCommittedValue = target.value; + elementData.commitKey = 1; + if (!this.data.actions?.Focus) { + elementData.focused = true; + } + }); + element.addEventListener("updatefromsandbox", jsEvent => { + this.showElementAndHideCanvas(jsEvent.target); + const actions = { + value(event) { + elementData.userValue = event.detail.value ?? ""; + storage.setValue(id, { + value: elementData.userValue.toString() + }); + event.target.value = elementData.userValue; + }, + formattedValue(event) { + const { + formattedValue + } = event.detail; + elementData.formattedValue = formattedValue; + if (formattedValue !== null && formattedValue !== undefined && event.target !== document.activeElement) { + event.target.value = formattedValue; + } + storage.setValue(id, { + formattedValue + }); + }, + selRange(event) { + event.target.setSelectionRange(...event.detail.selRange); + }, + charLimit: event => { + const { + charLimit + } = event.detail; + const { + target + } = event; + if (charLimit === 0) { + target.removeAttribute("maxLength"); + return; + } + target.setAttribute("maxLength", charLimit); + let value = elementData.userValue; + if (!value || value.length <= charLimit) { + return; + } + value = value.slice(0, charLimit); + target.value = elementData.userValue = value; + storage.setValue(id, { + value + }); + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id, + name: "Keystroke", + value, + willCommit: true, + commitKey: 1, + selStart: target.selectionStart, + selEnd: target.selectionEnd + } + }); + } + }; + this._dispatchEventFromSandbox(actions, jsEvent); + }); + element.addEventListener("keydown", event => { + elementData.commitKey = 1; + let commitKey = -1; + if (event.key === "Escape") { + commitKey = 0; + } else if (event.key === "Enter" && !this.data.multiLine) { + commitKey = 2; + } else if (event.key === "Tab") { + elementData.commitKey = 3; + } + if (commitKey === -1) { + return; + } + const { + value + } = event.target; + if (elementData.lastCommittedValue === value) { + return; + } + elementData.lastCommittedValue = value; + elementData.userValue = value; + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id, + name: "Keystroke", + value, + willCommit: true, + commitKey, + selStart: event.target.selectionStart, + selEnd: event.target.selectionEnd + } + }); + }); + const _blurListener = blurListener; + blurListener = null; + element.addEventListener("blur", event => { + if (!elementData.focused || !event.relatedTarget) { + return; + } + if (!this.data.actions?.Blur) { + elementData.focused = false; + } + const { + value + } = event.target; + elementData.userValue = value; + if (elementData.lastCommittedValue !== value) { + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id, + name: "Keystroke", + value, + willCommit: true, + commitKey: elementData.commitKey, + selStart: event.target.selectionStart, + selEnd: event.target.selectionEnd + } + }); + } + _blurListener(event); + }); + if (this.data.actions?.Keystroke) { + element.addEventListener("beforeinput", event => { + elementData.lastCommittedValue = null; + const { + data, + target + } = event; + const { + value, + selectionStart, + selectionEnd + } = target; + let selStart = selectionStart, + selEnd = selectionEnd; + switch (event.inputType) { + case "deleteWordBackward": + { + const match = value.substring(0, selectionStart).match(/\w*[^\w]*$/); + if (match) { + selStart -= match[0].length; + } + break; + } + case "deleteWordForward": + { + const match = value.substring(selectionStart).match(/^[^\w]*\w*/); + if (match) { + selEnd += match[0].length; + } + break; + } + case "deleteContentBackward": + if (selectionStart === selectionEnd) { + selStart -= 1; + } + break; + case "deleteContentForward": + if (selectionStart === selectionEnd) { + selEnd += 1; + } + break; + } + event.preventDefault(); + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id, + name: "Keystroke", + value, + change: data || "", + willCommit: false, + selStart, + selEnd + } + }); + }); + } + this._setEventListeners(element, elementData, [["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.value); + } + if (blurListener) { + element.addEventListener("blur", blurListener); + } + if (this.data.comb) { + const fieldWidth = this.data.rect[2] - this.data.rect[0]; + const combWidth = fieldWidth / maxLen; + element.classList.add("comb"); + element.style.letterSpacing = `calc(${combWidth}px * var(--scale-factor) - 1ch)`; + } + } else { + element = document.createElement("div"); + element.textContent = this.data.fieldValue; + element.style.verticalAlign = "middle"; + element.style.display = "table-cell"; + if (this.data.hasOwnCanvas) { + element.hidden = true; + } + } + this._setTextStyle(element); + this._setBackgroundColor(element); + this._setDefaultPropertiesFromJS(element); + this.container.append(element); + return this.container; + } +} +class SignatureWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + super(parameters, { + isRenderable: !!parameters.data.hasOwnCanvas + }); + } +} +class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + super(parameters, { + isRenderable: parameters.renderForms + }); + } + render() { + const storage = this.annotationStorage; + const data = this.data; + const id = data.id; + let value = storage.getValue(id, { + value: data.exportValue === data.fieldValue + }).value; + if (typeof value === "string") { + value = value !== "Off"; + storage.setValue(id, { + value + }); + } + this.container.classList.add("buttonWidgetAnnotation", "checkBox"); + const element = document.createElement("input"); + GetElementsByNameSet.add(element); + element.setAttribute("data-element-id", id); + element.disabled = data.readOnly; + this._setRequired(element, this.data.required); + element.type = "checkbox"; + element.name = data.fieldName; + if (value) { + element.setAttribute("checked", true); + } + element.setAttribute("exportValue", data.exportValue); + element.tabIndex = DEFAULT_TAB_INDEX; + element.addEventListener("change", event => { + const { + name, + checked + } = event.target; + for (const checkbox of this._getElementsByName(name, id)) { + const curChecked = checked && checkbox.exportValue === data.exportValue; + if (checkbox.domElement) { + checkbox.domElement.checked = curChecked; + } + storage.setValue(checkbox.id, { + value: curChecked + }); + } + storage.setValue(id, { + value: checked + }); + }); + element.addEventListener("resetform", event => { + const defaultValue = data.defaultFieldValue || "Off"; + event.target.checked = defaultValue === data.exportValue; + }); + if (this.enableScripting && this.hasJSActions) { + element.addEventListener("updatefromsandbox", jsEvent => { + const actions = { + value(event) { + event.target.checked = event.detail.value !== "Off"; + storage.setValue(id, { + value: event.target.checked + }); + } + }; + this._dispatchEventFromSandbox(actions, jsEvent); + }); + this._setEventListeners(element, null, [["change", "Validate"], ["change", "Action"], ["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.checked); + } + this._setBackgroundColor(element); + this._setDefaultPropertiesFromJS(element); + this.container.append(element); + return this.container; + } +} +class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + super(parameters, { + isRenderable: parameters.renderForms + }); + } + render() { + this.container.classList.add("buttonWidgetAnnotation", "radioButton"); + const storage = this.annotationStorage; + const data = this.data; + const id = data.id; + let value = storage.getValue(id, { + value: data.fieldValue === data.buttonValue + }).value; + if (typeof value === "string") { + value = value !== data.buttonValue; + storage.setValue(id, { + value + }); + } + if (value) { + for (const radio of this._getElementsByName(data.fieldName, id)) { + storage.setValue(radio.id, { + value: false + }); + } + } + const element = document.createElement("input"); + GetElementsByNameSet.add(element); + element.setAttribute("data-element-id", id); + element.disabled = data.readOnly; + this._setRequired(element, this.data.required); + element.type = "radio"; + element.name = data.fieldName; + if (value) { + element.setAttribute("checked", true); + } + element.tabIndex = DEFAULT_TAB_INDEX; + element.addEventListener("change", event => { + const { + name, + checked + } = event.target; + for (const radio of this._getElementsByName(name, id)) { + storage.setValue(radio.id, { + value: false + }); + } + storage.setValue(id, { + value: checked + }); + }); + element.addEventListener("resetform", event => { + const defaultValue = data.defaultFieldValue; + event.target.checked = defaultValue !== null && defaultValue !== undefined && defaultValue === data.buttonValue; + }); + if (this.enableScripting && this.hasJSActions) { + const pdfButtonValue = data.buttonValue; + element.addEventListener("updatefromsandbox", jsEvent => { + const actions = { + value: event => { + const checked = pdfButtonValue === event.detail.value; + for (const radio of this._getElementsByName(event.target.name)) { + const curChecked = checked && radio.id === id; + if (radio.domElement) { + radio.domElement.checked = curChecked; + } + storage.setValue(radio.id, { + value: curChecked + }); + } + } + }; + this._dispatchEventFromSandbox(actions, jsEvent); + }); + this._setEventListeners(element, null, [["change", "Validate"], ["change", "Action"], ["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.checked); + } + this._setBackgroundColor(element); + this._setDefaultPropertiesFromJS(element); + this.container.append(element); + return this.container; + } +} +class PushButtonWidgetAnnotationElement extends LinkAnnotationElement { + constructor(parameters) { + super(parameters, { + ignoreBorder: parameters.data.hasAppearance + }); + } + render() { + const container = super.render(); + container.classList.add("buttonWidgetAnnotation", "pushButton"); + const linkElement = container.lastChild; + if (this.enableScripting && this.hasJSActions && linkElement) { + this._setDefaultPropertiesFromJS(linkElement); + linkElement.addEventListener("updatefromsandbox", jsEvent => { + this._dispatchEventFromSandbox({}, jsEvent); + }); + } + return container; + } +} +class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + super(parameters, { + isRenderable: parameters.renderForms + }); + } + render() { + this.container.classList.add("choiceWidgetAnnotation"); + const storage = this.annotationStorage; + const id = this.data.id; + const storedData = storage.getValue(id, { + value: this.data.fieldValue + }); + const selectElement = document.createElement("select"); + GetElementsByNameSet.add(selectElement); + selectElement.setAttribute("data-element-id", id); + selectElement.disabled = this.data.readOnly; + this._setRequired(selectElement, this.data.required); + selectElement.name = this.data.fieldName; + selectElement.tabIndex = DEFAULT_TAB_INDEX; + let addAnEmptyEntry = this.data.combo && this.data.options.length > 0; + if (!this.data.combo) { + selectElement.size = this.data.options.length; + if (this.data.multiSelect) { + selectElement.multiple = true; + } + } + selectElement.addEventListener("resetform", event => { + const defaultValue = this.data.defaultFieldValue; + for (const option of selectElement.options) { + option.selected = option.value === defaultValue; + } + }); + for (const option of this.data.options) { + const optionElement = document.createElement("option"); + optionElement.textContent = option.displayValue; + optionElement.value = option.exportValue; + if (storedData.value.includes(option.exportValue)) { + optionElement.setAttribute("selected", true); + addAnEmptyEntry = false; + } + selectElement.append(optionElement); + } + let removeEmptyEntry = null; + if (addAnEmptyEntry) { + const noneOptionElement = document.createElement("option"); + noneOptionElement.value = " "; + noneOptionElement.setAttribute("hidden", true); + noneOptionElement.setAttribute("selected", true); + selectElement.prepend(noneOptionElement); + removeEmptyEntry = () => { + noneOptionElement.remove(); + selectElement.removeEventListener("input", removeEmptyEntry); + removeEmptyEntry = null; + }; + selectElement.addEventListener("input", removeEmptyEntry); + } + const getValue = isExport => { + const name = isExport ? "value" : "textContent"; + const { + options, + multiple + } = selectElement; + if (!multiple) { + return options.selectedIndex === -1 ? null : options[options.selectedIndex][name]; + } + return Array.prototype.filter.call(options, option => option.selected).map(option => option[name]); + }; + let selectedValues = getValue(false); + const getItems = event => { + const options = event.target.options; + return Array.prototype.map.call(options, option => ({ + displayValue: option.textContent, + exportValue: option.value + })); + }; + if (this.enableScripting && this.hasJSActions) { + selectElement.addEventListener("updatefromsandbox", jsEvent => { + const actions = { + value(event) { + removeEmptyEntry?.(); + const value = event.detail.value; + const values = new Set(Array.isArray(value) ? value : [value]); + for (const option of selectElement.options) { + option.selected = values.has(option.value); + } + storage.setValue(id, { + value: getValue(true) + }); + selectedValues = getValue(false); + }, + multipleSelection(event) { + selectElement.multiple = true; + }, + remove(event) { + const options = selectElement.options; + const index = event.detail.remove; + options[index].selected = false; + selectElement.remove(index); + if (options.length > 0) { + const i = Array.prototype.findIndex.call(options, option => option.selected); + if (i === -1) { + options[0].selected = true; + } + } + storage.setValue(id, { + value: getValue(true), + items: getItems(event) + }); + selectedValues = getValue(false); + }, + clear(event) { + while (selectElement.length !== 0) { + selectElement.remove(0); + } + storage.setValue(id, { + value: null, + items: [] + }); + selectedValues = getValue(false); + }, + insert(event) { + const { + index, + displayValue, + exportValue + } = event.detail.insert; + const selectChild = selectElement.children[index]; + const optionElement = document.createElement("option"); + optionElement.textContent = displayValue; + optionElement.value = exportValue; + if (selectChild) { + selectChild.before(optionElement); + } else { + selectElement.append(optionElement); + } + storage.setValue(id, { + value: getValue(true), + items: getItems(event) + }); + selectedValues = getValue(false); + }, + items(event) { + const { + items + } = event.detail; + while (selectElement.length !== 0) { + selectElement.remove(0); + } + for (const item of items) { + const { + displayValue, + exportValue + } = item; + const optionElement = document.createElement("option"); + optionElement.textContent = displayValue; + optionElement.value = exportValue; + selectElement.append(optionElement); + } + if (selectElement.options.length > 0) { + selectElement.options[0].selected = true; + } + storage.setValue(id, { + value: getValue(true), + items: getItems(event) + }); + selectedValues = getValue(false); + }, + indices(event) { + const indices = new Set(event.detail.indices); + for (const option of event.target.options) { + option.selected = indices.has(option.index); + } + storage.setValue(id, { + value: getValue(true) + }); + selectedValues = getValue(false); + }, + editable(event) { + event.target.disabled = !event.detail.editable; + } + }; + this._dispatchEventFromSandbox(actions, jsEvent); + }); + selectElement.addEventListener("input", event => { + const exportValue = getValue(true); + const change = getValue(false); + storage.setValue(id, { + value: exportValue + }); + event.preventDefault(); + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id, + name: "Keystroke", + value: selectedValues, + change, + changeEx: exportValue, + willCommit: false, + commitKey: 1, + keyDown: false + } + }); + }); + this._setEventListeners(selectElement, null, [["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"], ["input", "Action"], ["input", "Validate"]], event => event.target.value); + } else { + selectElement.addEventListener("input", function (event) { + storage.setValue(id, { + value: getValue(true) + }); + }); + } + if (this.data.combo) { + this._setTextStyle(selectElement); + } else {} + this._setBackgroundColor(selectElement); + this._setDefaultPropertiesFromJS(selectElement); + this.container.append(selectElement); + return this.container; + } +} +class PopupAnnotationElement extends AnnotationElement { + constructor(parameters) { + const { + data, + elements + } = parameters; + super(parameters, { + isRenderable: AnnotationElement._hasPopupData(data) + }); + this.elements = elements; + this.popup = null; + } + render() { + this.container.classList.add("popupAnnotation"); + const popup = this.popup = new PopupElement({ + container: this.container, + color: this.data.color, + titleObj: this.data.titleObj, + modificationDate: this.data.modificationDate, + contentsObj: this.data.contentsObj, + richText: this.data.richText, + rect: this.data.rect, + parentRect: this.data.parentRect || null, + parent: this.parent, + elements: this.elements, + open: this.data.open + }); + const elementIds = []; + for (const element of this.elements) { + element.popup = popup; + element.container.ariaHasPopup = "dialog"; + elementIds.push(element.data.id); + element.addHighlightArea(); + } + this.container.setAttribute("aria-controls", elementIds.map(id => `${AnnotationPrefix}${id}`).join(",")); + return this.container; + } +} +class PopupElement { + #boundKeyDown = this.#keyDown.bind(this); + #boundHide = this.#hide.bind(this); + #boundShow = this.#show.bind(this); + #boundToggle = this.#toggle.bind(this); + #color = null; + #container = null; + #contentsObj = null; + #dateObj = null; + #elements = null; + #parent = null; + #parentRect = null; + #pinned = false; + #popup = null; + #position = null; + #rect = null; + #richText = null; + #titleObj = null; + #updates = null; + #wasVisible = false; + constructor({ + container, + color, + elements, + titleObj, + modificationDate, + contentsObj, + richText, + parent, + rect, + parentRect, + open + }) { + this.#container = container; + this.#titleObj = titleObj; + this.#contentsObj = contentsObj; + this.#richText = richText; + this.#parent = parent; + this.#color = color; + this.#rect = rect; + this.#parentRect = parentRect; + this.#elements = elements; + this.#dateObj = PDFDateString.toDateObject(modificationDate); + this.trigger = elements.flatMap(e => e.getElementsToTriggerPopup()); + for (const element of this.trigger) { + element.addEventListener("click", this.#boundToggle); + element.addEventListener("mouseenter", this.#boundShow); + element.addEventListener("mouseleave", this.#boundHide); + element.classList.add("popupTriggerArea"); + } + for (const element of elements) { + element.container?.addEventListener("keydown", this.#boundKeyDown); + } + this.#container.hidden = true; + if (open) { + this.#toggle(); + } + } + render() { + if (this.#popup) { + return; + } + const popup = this.#popup = document.createElement("div"); + popup.className = "popup"; + if (this.#color) { + const baseColor = popup.style.outlineColor = Util.makeHexColor(...this.#color); + if (CSS.supports("background-color", "color-mix(in srgb, red 30%, white)")) { + popup.style.backgroundColor = `color-mix(in srgb, ${baseColor} 30%, white)`; + } else { + const BACKGROUND_ENLIGHT = 0.7; + popup.style.backgroundColor = Util.makeHexColor(...this.#color.map(c => Math.floor(BACKGROUND_ENLIGHT * (255 - c) + c))); + } + } + const header = document.createElement("span"); + header.className = "header"; + const title = document.createElement("h1"); + header.append(title); + ({ + dir: title.dir, + str: title.textContent + } = this.#titleObj); + popup.append(header); + if (this.#dateObj) { + const modificationDate = document.createElement("span"); + modificationDate.classList.add("popupDate"); + modificationDate.setAttribute("data-l10n-id", "pdfjs-annotation-date-time-string"); + modificationDate.setAttribute("data-l10n-args", JSON.stringify({ + dateObj: this.#dateObj.valueOf() + })); + header.append(modificationDate); + } + const html = this.#html; + if (html) { + XfaLayer.render({ + xfaHtml: html, + intent: "richText", + div: popup + }); + popup.lastChild.classList.add("richText", "popupContent"); + } else { + const contents = this._formatContents(this.#contentsObj); + popup.append(contents); + } + this.#container.append(popup); + } + get #html() { + const richText = this.#richText; + const contentsObj = this.#contentsObj; + if (richText?.str && (!contentsObj?.str || contentsObj.str === richText.str)) { + return this.#richText.html || null; + } + return null; + } + get #fontSize() { + return this.#html?.attributes?.style?.fontSize || 0; + } + get #fontColor() { + return this.#html?.attributes?.style?.color || null; + } + #makePopupContent(text) { + const popupLines = []; + const popupContent = { + str: text, + html: { + name: "div", + attributes: { + dir: "auto" + }, + children: [{ + name: "p", + children: popupLines + }] + } + }; + const lineAttributes = { + style: { + color: this.#fontColor, + fontSize: this.#fontSize ? `calc(${this.#fontSize}px * var(--scale-factor))` : "" + } + }; + for (const line of text.split("\n")) { + popupLines.push({ + name: "span", + value: line, + attributes: lineAttributes + }); + } + return popupContent; + } + _formatContents({ + str, + dir + }) { + const p = document.createElement("p"); + p.classList.add("popupContent"); + p.dir = dir; + const lines = str.split(/(?:\r\n?|\n)/); + for (let i = 0, ii = lines.length; i < ii; ++i) { + const line = lines[i]; + p.append(document.createTextNode(line)); + if (i < ii - 1) { + p.append(document.createElement("br")); + } + } + return p; + } + #keyDown(event) { + if (event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) { + return; + } + if (event.key === "Enter" || event.key === "Escape" && this.#pinned) { + this.#toggle(); + } + } + updateEdited({ + rect, + popupContent + }) { + this.#updates ||= { + contentsObj: this.#contentsObj, + richText: this.#richText + }; + if (rect) { + this.#position = null; + } + if (popupContent) { + this.#richText = this.#makePopupContent(popupContent); + this.#contentsObj = null; + } + this.#popup?.remove(); + this.#popup = null; + } + resetEdited() { + if (!this.#updates) { + return; + } + ({ + contentsObj: this.#contentsObj, + richText: this.#richText + } = this.#updates); + this.#updates = null; + this.#popup?.remove(); + this.#popup = null; + this.#position = null; + } + #setPosition() { + if (this.#position !== null) { + return; + } + const { + page: { + view + }, + viewport: { + rawDims: { + pageWidth, + pageHeight, + pageX, + pageY + } + } + } = this.#parent; + let useParentRect = !!this.#parentRect; + let rect = useParentRect ? this.#parentRect : this.#rect; + for (const element of this.#elements) { + if (!rect || Util.intersect(element.data.rect, rect) !== null) { + rect = element.data.rect; + useParentRect = true; + break; + } + } + const normalizedRect = Util.normalizeRect([rect[0], view[3] - rect[1] + view[1], rect[2], view[3] - rect[3] + view[1]]); + const HORIZONTAL_SPACE_AFTER_ANNOTATION = 5; + const parentWidth = useParentRect ? rect[2] - rect[0] + HORIZONTAL_SPACE_AFTER_ANNOTATION : 0; + const popupLeft = normalizedRect[0] + parentWidth; + const popupTop = normalizedRect[1]; + this.#position = [100 * (popupLeft - pageX) / pageWidth, 100 * (popupTop - pageY) / pageHeight]; + const { + style + } = this.#container; + style.left = `${this.#position[0]}%`; + style.top = `${this.#position[1]}%`; + } + #toggle() { + this.#pinned = !this.#pinned; + if (this.#pinned) { + this.#show(); + this.#container.addEventListener("click", this.#boundToggle); + this.#container.addEventListener("keydown", this.#boundKeyDown); + } else { + this.#hide(); + this.#container.removeEventListener("click", this.#boundToggle); + this.#container.removeEventListener("keydown", this.#boundKeyDown); + } + } + #show() { + if (!this.#popup) { + this.render(); + } + if (!this.isVisible) { + this.#setPosition(); + this.#container.hidden = false; + this.#container.style.zIndex = parseInt(this.#container.style.zIndex) + 1000; + } else if (this.#pinned) { + this.#container.classList.add("focused"); + } + } + #hide() { + this.#container.classList.remove("focused"); + if (this.#pinned || !this.isVisible) { + return; + } + this.#container.hidden = true; + this.#container.style.zIndex = parseInt(this.#container.style.zIndex) - 1000; + } + forceHide() { + this.#wasVisible = this.isVisible; + if (!this.#wasVisible) { + return; + } + this.#container.hidden = true; + } + maybeShow() { + if (!this.#wasVisible) { + return; + } + if (!this.#popup) { + this.#show(); + } + this.#wasVisible = false; + this.#container.hidden = false; + } + get isVisible() { + return this.#container.hidden === false; + } +} +class FreeTextAnnotationElement extends AnnotationElement { + constructor(parameters) { + super(parameters, { + isRenderable: true, + ignoreBorder: true + }); + this.textContent = parameters.data.textContent; + this.textPosition = parameters.data.textPosition; + this.annotationEditorType = AnnotationEditorType.FREETEXT; + } + render() { + this.container.classList.add("freeTextAnnotation"); + if (this.textContent) { + const content = document.createElement("div"); + content.classList.add("annotationTextContent"); + content.setAttribute("role", "comment"); + for (const line of this.textContent) { + const lineSpan = document.createElement("span"); + lineSpan.textContent = line; + content.append(lineSpan); + } + this.container.append(content); + } + if (!this.data.popupRef && this.hasPopupData) { + this._createPopup(); + } + this._editOnDoubleClick(); + return this.container; + } +} +class LineAnnotationElement extends AnnotationElement { + #line = null; + constructor(parameters) { + super(parameters, { + isRenderable: true, + ignoreBorder: true + }); + } + render() { + this.container.classList.add("lineAnnotation"); + const data = this.data; + const { + width, + height + } = getRectDims(data.rect); + const svg = this.svgFactory.create(width, height, true); + const line = this.#line = this.svgFactory.createElement("svg:line"); + line.setAttribute("x1", data.rect[2] - data.lineCoordinates[0]); + line.setAttribute("y1", data.rect[3] - data.lineCoordinates[1]); + line.setAttribute("x2", data.rect[2] - data.lineCoordinates[2]); + line.setAttribute("y2", data.rect[3] - data.lineCoordinates[3]); + line.setAttribute("stroke-width", data.borderStyle.width || 1); + line.setAttribute("stroke", "transparent"); + line.setAttribute("fill", "transparent"); + svg.append(line); + this.container.append(svg); + if (!data.popupRef && this.hasPopupData) { + this._createPopup(); + } + return this.container; + } + getElementsToTriggerPopup() { + return this.#line; + } + addHighlightArea() { + this.container.classList.add("highlightArea"); + } +} +class SquareAnnotationElement extends AnnotationElement { + #square = null; + constructor(parameters) { + super(parameters, { + isRenderable: true, + ignoreBorder: true + }); + } + render() { + this.container.classList.add("squareAnnotation"); + const data = this.data; + const { + width, + height + } = getRectDims(data.rect); + const svg = this.svgFactory.create(width, height, true); + const borderWidth = data.borderStyle.width; + const square = this.#square = this.svgFactory.createElement("svg:rect"); + square.setAttribute("x", borderWidth / 2); + square.setAttribute("y", borderWidth / 2); + square.setAttribute("width", width - borderWidth); + square.setAttribute("height", height - borderWidth); + square.setAttribute("stroke-width", borderWidth || 1); + square.setAttribute("stroke", "transparent"); + square.setAttribute("fill", "transparent"); + svg.append(square); + this.container.append(svg); + if (!data.popupRef && this.hasPopupData) { + this._createPopup(); + } + return this.container; + } + getElementsToTriggerPopup() { + return this.#square; + } + addHighlightArea() { + this.container.classList.add("highlightArea"); + } +} +class CircleAnnotationElement extends AnnotationElement { + #circle = null; + constructor(parameters) { + super(parameters, { + isRenderable: true, + ignoreBorder: true + }); + } + render() { + this.container.classList.add("circleAnnotation"); + const data = this.data; + const { + width, + height + } = getRectDims(data.rect); + const svg = this.svgFactory.create(width, height, true); + const borderWidth = data.borderStyle.width; + const circle = this.#circle = this.svgFactory.createElement("svg:ellipse"); + circle.setAttribute("cx", width / 2); + circle.setAttribute("cy", height / 2); + circle.setAttribute("rx", width / 2 - borderWidth / 2); + circle.setAttribute("ry", height / 2 - borderWidth / 2); + circle.setAttribute("stroke-width", borderWidth || 1); + circle.setAttribute("stroke", "transparent"); + circle.setAttribute("fill", "transparent"); + svg.append(circle); + this.container.append(svg); + if (!data.popupRef && this.hasPopupData) { + this._createPopup(); + } + return this.container; + } + getElementsToTriggerPopup() { + return this.#circle; + } + addHighlightArea() { + this.container.classList.add("highlightArea"); + } +} +class PolylineAnnotationElement extends AnnotationElement { + #polyline = null; + constructor(parameters) { + super(parameters, { + isRenderable: true, + ignoreBorder: true + }); + this.containerClassName = "polylineAnnotation"; + this.svgElementName = "svg:polyline"; + } + render() { + this.container.classList.add(this.containerClassName); + const { + data: { + rect, + vertices, + borderStyle, + popupRef + } + } = this; + if (!vertices) { + return this.container; + } + const { + width, + height + } = getRectDims(rect); + const svg = this.svgFactory.create(width, height, true); + let points = []; + for (let i = 0, ii = vertices.length; i < ii; i += 2) { + const x = vertices[i] - rect[0]; + const y = rect[3] - vertices[i + 1]; + points.push(`${x},${y}`); + } + points = points.join(" "); + const polyline = this.#polyline = this.svgFactory.createElement(this.svgElementName); + polyline.setAttribute("points", points); + polyline.setAttribute("stroke-width", borderStyle.width || 1); + polyline.setAttribute("stroke", "transparent"); + polyline.setAttribute("fill", "transparent"); + svg.append(polyline); + this.container.append(svg); + if (!popupRef && this.hasPopupData) { + this._createPopup(); + } + return this.container; + } + getElementsToTriggerPopup() { + return this.#polyline; + } + addHighlightArea() { + this.container.classList.add("highlightArea"); + } +} +class PolygonAnnotationElement extends PolylineAnnotationElement { + constructor(parameters) { + super(parameters); + this.containerClassName = "polygonAnnotation"; + this.svgElementName = "svg:polygon"; + } +} +class CaretAnnotationElement extends AnnotationElement { + constructor(parameters) { + super(parameters, { + isRenderable: true, + ignoreBorder: true + }); + } + render() { + this.container.classList.add("caretAnnotation"); + if (!this.data.popupRef && this.hasPopupData) { + this._createPopup(); + } + return this.container; + } +} +class InkAnnotationElement extends AnnotationElement { + #polylinesGroupElement = null; + #polylines = []; + constructor(parameters) { + super(parameters, { + isRenderable: true, + ignoreBorder: true + }); + this.containerClassName = "inkAnnotation"; + this.svgElementName = "svg:polyline"; + this.annotationEditorType = this.data.it === "InkHighlight" ? AnnotationEditorType.HIGHLIGHT : AnnotationEditorType.INK; + } + #getTransform(rotation, rect) { + switch (rotation) { + case 90: + return { + transform: `rotate(90) translate(${-rect[0]},${rect[1]}) scale(1,-1)`, + width: rect[3] - rect[1], + height: rect[2] - rect[0] + }; + case 180: + return { + transform: `rotate(180) translate(${-rect[2]},${rect[1]}) scale(1,-1)`, + width: rect[2] - rect[0], + height: rect[3] - rect[1] + }; + case 270: + return { + transform: `rotate(270) translate(${-rect[2]},${rect[3]}) scale(1,-1)`, + width: rect[3] - rect[1], + height: rect[2] - rect[0] + }; + default: + return { + transform: `translate(${-rect[0]},${rect[3]}) scale(1,-1)`, + width: rect[2] - rect[0], + height: rect[3] - rect[1] + }; + } + } + render() { + this.container.classList.add(this.containerClassName); + const { + data: { + rect, + rotation, + inkLists, + borderStyle, + popupRef + } + } = this; + const { + transform, + width, + height + } = this.#getTransform(rotation, rect); + const svg = this.svgFactory.create(width, height, true); + const g = this.#polylinesGroupElement = this.svgFactory.createElement("svg:g"); + svg.append(g); + g.setAttribute("stroke-width", borderStyle.width || 1); + g.setAttribute("stroke-linecap", "round"); + g.setAttribute("stroke-linejoin", "round"); + g.setAttribute("stroke-miterlimit", 10); + g.setAttribute("stroke", "transparent"); + g.setAttribute("fill", "transparent"); + g.setAttribute("transform", transform); + for (let i = 0, ii = inkLists.length; i < ii; i++) { + const polyline = this.svgFactory.createElement(this.svgElementName); + this.#polylines.push(polyline); + polyline.setAttribute("points", inkLists[i].join(",")); + g.append(polyline); + } + if (!popupRef && this.hasPopupData) { + this._createPopup(); + } + this.container.append(svg); + this._editOnDoubleClick(); + return this.container; + } + updateEdited(params) { + super.updateEdited(params); + const { + thickness, + points, + rect + } = params; + const g = this.#polylinesGroupElement; + if (thickness >= 0) { + g.setAttribute("stroke-width", thickness || 1); + } + if (points) { + for (let i = 0, ii = this.#polylines.length; i < ii; i++) { + this.#polylines[i].setAttribute("points", points[i].join(",")); + } + } + if (rect) { + const { + transform, + width, + height + } = this.#getTransform(this.data.rotation, rect); + const root = g.parentElement; + root.setAttribute("viewBox", `0 0 ${width} ${height}`); + g.setAttribute("transform", transform); + } + } + getElementsToTriggerPopup() { + return this.#polylines; + } + addHighlightArea() { + this.container.classList.add("highlightArea"); + } +} +class HighlightAnnotationElement extends AnnotationElement { + constructor(parameters) { + super(parameters, { + isRenderable: true, + ignoreBorder: true, + createQuadrilaterals: true + }); + this.annotationEditorType = AnnotationEditorType.HIGHLIGHT; + } + render() { + if (!this.data.popupRef && this.hasPopupData) { + this._createPopup(); + } + this.container.classList.add("highlightAnnotation"); + this._editOnDoubleClick(); + return this.container; + } +} +class UnderlineAnnotationElement extends AnnotationElement { + constructor(parameters) { + super(parameters, { + isRenderable: true, + ignoreBorder: true, + createQuadrilaterals: true + }); + } + render() { + if (!this.data.popupRef && this.hasPopupData) { + this._createPopup(); + } + this.container.classList.add("underlineAnnotation"); + return this.container; + } +} +class SquigglyAnnotationElement extends AnnotationElement { + constructor(parameters) { + super(parameters, { + isRenderable: true, + ignoreBorder: true, + createQuadrilaterals: true + }); + } + render() { + if (!this.data.popupRef && this.hasPopupData) { + this._createPopup(); + } + this.container.classList.add("squigglyAnnotation"); + return this.container; + } +} +class StrikeOutAnnotationElement extends AnnotationElement { + constructor(parameters) { + super(parameters, { + isRenderable: true, + ignoreBorder: true, + createQuadrilaterals: true + }); + } + render() { + if (!this.data.popupRef && this.hasPopupData) { + this._createPopup(); + } + this.container.classList.add("strikeoutAnnotation"); + return this.container; + } +} +class StampAnnotationElement extends AnnotationElement { + constructor(parameters) { + super(parameters, { + isRenderable: true, + ignoreBorder: true + }); + this.annotationEditorType = AnnotationEditorType.STAMP; + } + render() { + this.container.classList.add("stampAnnotation"); + this.container.setAttribute("role", "img"); + if (!this.data.popupRef && this.hasPopupData) { + this._createPopup(); + } + this._editOnDoubleClick(); + return this.container; + } +} +class FileAttachmentAnnotationElement extends AnnotationElement { + #trigger = null; + constructor(parameters) { + super(parameters, { + isRenderable: true + }); + const { + file + } = this.data; + this.filename = file.filename; + this.content = file.content; + this.linkService.eventBus?.dispatch("fileattachmentannotation", { + source: this, + ...file + }); + } + render() { + this.container.classList.add("fileAttachmentAnnotation"); + const { + container, + data + } = this; + let trigger; + if (data.hasAppearance || data.fillAlpha === 0) { + trigger = document.createElement("div"); + } else { + trigger = document.createElement("img"); + trigger.src = `${this.imageResourcesPath}annotation-${/paperclip/i.test(data.name) ? "paperclip" : "pushpin"}.svg`; + if (data.fillAlpha && data.fillAlpha < 1) { + trigger.style = `filter: opacity(${Math.round(data.fillAlpha * 100)}%);`; + } + } + trigger.addEventListener("dblclick", this.#download.bind(this)); + this.#trigger = trigger; + const { + isMac + } = util_FeatureTest.platform; + container.addEventListener("keydown", evt => { + if (evt.key === "Enter" && (isMac ? evt.metaKey : evt.ctrlKey)) { + this.#download(); + } + }); + if (!data.popupRef && this.hasPopupData) { + this._createPopup(); + } else { + trigger.classList.add("popupTriggerArea"); + } + container.append(trigger); + return container; + } + getElementsToTriggerPopup() { + return this.#trigger; + } + addHighlightArea() { + this.container.classList.add("highlightArea"); + } + #download() { + this.downloadManager?.openOrDownloadData(this.content, this.filename); + } +} +class AnnotationLayer { + #accessibilityManager = null; + #annotationCanvasMap = null; + #editableAnnotations = new Map(); + #structTreeLayer = null; + constructor({ + div, + accessibilityManager, + annotationCanvasMap, + annotationEditorUIManager, + page, + viewport, + structTreeLayer + }) { + this.div = div; + this.#accessibilityManager = accessibilityManager; + this.#annotationCanvasMap = annotationCanvasMap; + this.#structTreeLayer = structTreeLayer || null; + this.page = page; + this.viewport = viewport; + this.zIndex = 0; + this._annotationEditorUIManager = annotationEditorUIManager; + } + hasEditableAnnotations() { + return this.#editableAnnotations.size > 0; + } + async #appendElement(element, id) { + const contentElement = element.firstChild || element; + const annotationId = contentElement.id = `${AnnotationPrefix}${id}`; + const ariaAttributes = await this.#structTreeLayer?.getAriaAttributes(annotationId); + if (ariaAttributes) { + for (const [key, value] of ariaAttributes) { + contentElement.setAttribute(key, value); + } + } + this.div.append(element); + this.#accessibilityManager?.moveElementInDOM(this.div, element, contentElement, false); + } + async render(params) { + const { + annotations + } = params; + const layer = this.div; + setLayerDimensions(layer, this.viewport); + const popupToElements = new Map(); + const elementParams = { + data: null, + layer, + linkService: params.linkService, + downloadManager: params.downloadManager, + imageResourcesPath: params.imageResourcesPath || "", + renderForms: params.renderForms !== false, + svgFactory: new DOMSVGFactory(), + annotationStorage: params.annotationStorage || new AnnotationStorage(), + enableScripting: params.enableScripting === true, + hasJSActions: params.hasJSActions, + fieldObjects: params.fieldObjects, + parent: this, + elements: null + }; + for (const data of annotations) { + if (data.noHTML) { + continue; + } + const isPopupAnnotation = data.annotationType === AnnotationType.POPUP; + if (!isPopupAnnotation) { + const { + width, + height + } = getRectDims(data.rect); + if (width <= 0 || height <= 0) { + continue; + } + } else { + const elements = popupToElements.get(data.id); + if (!elements) { + continue; + } + elementParams.elements = elements; + } + elementParams.data = data; + const element = AnnotationElementFactory.create(elementParams); + if (!element.isRenderable) { + continue; + } + if (!isPopupAnnotation && data.popupRef) { + const elements = popupToElements.get(data.popupRef); + if (!elements) { + popupToElements.set(data.popupRef, [element]); + } else { + elements.push(element); + } + } + const rendered = element.render(); + if (data.hidden) { + rendered.style.visibility = "hidden"; + } + await this.#appendElement(rendered, data.id); + if (element._isEditable) { + this.#editableAnnotations.set(element.data.id, element); + this._annotationEditorUIManager?.renderAnnotationElement(element); + } + } + this.#setAnnotationCanvasMap(); + } + update({ + viewport + }) { + const layer = this.div; + this.viewport = viewport; + setLayerDimensions(layer, { + rotation: viewport.rotation + }); + this.#setAnnotationCanvasMap(); + layer.hidden = false; + } + #setAnnotationCanvasMap() { + if (!this.#annotationCanvasMap) { + return; + } + const layer = this.div; + for (const [id, canvas] of this.#annotationCanvasMap) { + const element = layer.querySelector(`[data-annotation-id="${id}"]`); + if (!element) { + continue; + } + canvas.className = "annotationContent"; + const { + firstChild + } = element; + if (!firstChild) { + element.append(canvas); + } else if (firstChild.nodeName === "CANVAS") { + firstChild.replaceWith(canvas); + } else if (!firstChild.classList.contains("annotationContent")) { + firstChild.before(canvas); + } else { + firstChild.after(canvas); + } + } + this.#annotationCanvasMap.clear(); + } + getEditableAnnotations() { + return Array.from(this.#editableAnnotations.values()); + } + getEditableAnnotation(id) { + return this.#editableAnnotations.get(id); + } +} + +;// ./src/display/editor/freetext.js + + + + +const EOL_PATTERN = /\r\n?|\n/g; +class FreeTextEditor extends AnnotationEditor { + #color; + #content = ""; + #editorDivId = `${this.id}-editor`; + #editModeAC = null; + #fontSize; + static _freeTextDefaultContent = ""; + static _internalPadding = 0; + static _defaultColor = null; + static _defaultFontSize = 10; + static get _keyboardManager() { + const proto = FreeTextEditor.prototype; + const arrowChecker = self => self.isEmpty(); + const small = AnnotationEditorUIManager.TRANSLATE_SMALL; + const big = AnnotationEditorUIManager.TRANSLATE_BIG; + return shadow(this, "_keyboardManager", new KeyboardManager([[["ctrl+s", "mac+meta+s", "ctrl+p", "mac+meta+p"], proto.commitOrRemove, { + bubbles: true + }], [["ctrl+Enter", "mac+meta+Enter", "Escape", "mac+Escape"], proto.commitOrRemove], [["ArrowLeft", "mac+ArrowLeft"], proto._translateEmpty, { + args: [-small, 0], + checker: arrowChecker + }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], proto._translateEmpty, { + args: [-big, 0], + checker: arrowChecker + }], [["ArrowRight", "mac+ArrowRight"], proto._translateEmpty, { + args: [small, 0], + checker: arrowChecker + }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], proto._translateEmpty, { + args: [big, 0], + checker: arrowChecker + }], [["ArrowUp", "mac+ArrowUp"], proto._translateEmpty, { + args: [0, -small], + checker: arrowChecker + }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], proto._translateEmpty, { + args: [0, -big], + checker: arrowChecker + }], [["ArrowDown", "mac+ArrowDown"], proto._translateEmpty, { + args: [0, small], + checker: arrowChecker + }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], proto._translateEmpty, { + args: [0, big], + checker: arrowChecker + }]])); + } + static _type = "freetext"; + static _editorType = AnnotationEditorType.FREETEXT; + constructor(params) { + super({ + ...params, + name: "freeTextEditor" + }); + this.#color = params.color || FreeTextEditor._defaultColor || AnnotationEditor._defaultLineColor; + this.#fontSize = params.fontSize || FreeTextEditor._defaultFontSize; + } + static initialize(l10n, uiManager) { + AnnotationEditor.initialize(l10n, uiManager); + const style = getComputedStyle(document.documentElement); + this._internalPadding = parseFloat(style.getPropertyValue("--freetext-padding")); + } + static updateDefaultParams(type, value) { + switch (type) { + case AnnotationEditorParamsType.FREETEXT_SIZE: + FreeTextEditor._defaultFontSize = value; + break; + case AnnotationEditorParamsType.FREETEXT_COLOR: + FreeTextEditor._defaultColor = value; + break; + } + } + updateParams(type, value) { + switch (type) { + case AnnotationEditorParamsType.FREETEXT_SIZE: + this.#updateFontSize(value); + break; + case AnnotationEditorParamsType.FREETEXT_COLOR: + this.#updateColor(value); + break; + } + } + static get defaultPropertiesToUpdate() { + return [[AnnotationEditorParamsType.FREETEXT_SIZE, FreeTextEditor._defaultFontSize], [AnnotationEditorParamsType.FREETEXT_COLOR, FreeTextEditor._defaultColor || AnnotationEditor._defaultLineColor]]; + } + get propertiesToUpdate() { + return [[AnnotationEditorParamsType.FREETEXT_SIZE, this.#fontSize], [AnnotationEditorParamsType.FREETEXT_COLOR, this.#color]]; + } + #updateFontSize(fontSize) { + const setFontsize = size => { + this.editorDiv.style.fontSize = `calc(${size}px * var(--scale-factor))`; + this.translate(0, -(size - this.#fontSize) * this.parentScale); + this.#fontSize = size; + this.#setEditorDimensions(); + }; + const savedFontsize = this.#fontSize; + this.addCommands({ + cmd: setFontsize.bind(this, fontSize), + undo: setFontsize.bind(this, savedFontsize), + post: this._uiManager.updateUI.bind(this._uiManager, this), + mustExec: true, + type: AnnotationEditorParamsType.FREETEXT_SIZE, + overwriteIfSameType: true, + keepUndo: true + }); + } + #updateColor(color) { + const setColor = col => { + this.#color = this.editorDiv.style.color = col; + }; + const savedColor = this.#color; + this.addCommands({ + cmd: setColor.bind(this, color), + undo: setColor.bind(this, savedColor), + post: this._uiManager.updateUI.bind(this._uiManager, this), + mustExec: true, + type: AnnotationEditorParamsType.FREETEXT_COLOR, + overwriteIfSameType: true, + keepUndo: true + }); + } + _translateEmpty(x, y) { + this._uiManager.translateSelectedEditors(x, y, true); + } + getInitialTranslation() { + const scale = this.parentScale; + return [-FreeTextEditor._internalPadding * scale, -(FreeTextEditor._internalPadding + this.#fontSize) * scale]; + } + rebuild() { + if (!this.parent) { + return; + } + super.rebuild(); + if (this.div === null) { + return; + } + if (!this.isAttachedToDOM) { + this.parent.add(this); + } + } + enableEditMode() { + if (this.isInEditMode()) { + return; + } + this.parent.setEditingState(false); + this.parent.updateToolbar(AnnotationEditorType.FREETEXT); + super.enableEditMode(); + this.overlayDiv.classList.remove("enabled"); + this.editorDiv.contentEditable = true; + this._isDraggable = false; + this.div.removeAttribute("aria-activedescendant"); + this.#editModeAC = new AbortController(); + const signal = this._uiManager.combinedSignal(this.#editModeAC); + this.editorDiv.addEventListener("keydown", this.editorDivKeydown.bind(this), { + signal + }); + this.editorDiv.addEventListener("focus", this.editorDivFocus.bind(this), { + signal + }); + this.editorDiv.addEventListener("blur", this.editorDivBlur.bind(this), { + signal + }); + this.editorDiv.addEventListener("input", this.editorDivInput.bind(this), { + signal + }); + this.editorDiv.addEventListener("paste", this.editorDivPaste.bind(this), { + signal + }); + } + disableEditMode() { + if (!this.isInEditMode()) { + return; + } + this.parent.setEditingState(true); + super.disableEditMode(); + this.overlayDiv.classList.add("enabled"); + this.editorDiv.contentEditable = false; + this.div.setAttribute("aria-activedescendant", this.#editorDivId); + this._isDraggable = true; + this.#editModeAC?.abort(); + this.#editModeAC = null; + this.div.focus({ + preventScroll: true + }); + this.isEditing = false; + this.parent.div.classList.add("freetextEditing"); + } + focusin(event) { + if (!this._focusEventsAllowed) { + return; + } + super.focusin(event); + if (event.target !== this.editorDiv) { + this.editorDiv.focus(); + } + } + onceAdded(focus) { + if (this.width) { + return; + } + this.enableEditMode(); + if (focus) { + this.editorDiv.focus(); + } + if (this._initialOptions?.isCentered) { + this.center(); + } + this._initialOptions = null; + } + isEmpty() { + return !this.editorDiv || this.editorDiv.innerText.trim() === ""; + } + remove() { + this.isEditing = false; + if (this.parent) { + this.parent.setEditingState(true); + this.parent.div.classList.add("freetextEditing"); + } + super.remove(); + } + #extractText() { + const buffer = []; + this.editorDiv.normalize(); + let prevChild = null; + for (const child of this.editorDiv.childNodes) { + if (prevChild?.nodeType === Node.TEXT_NODE && child.nodeName === "BR") { + continue; + } + buffer.push(FreeTextEditor.#getNodeContent(child)); + prevChild = child; + } + return buffer.join("\n"); + } + #setEditorDimensions() { + const [parentWidth, parentHeight] = this.parentDimensions; + let rect; + if (this.isAttachedToDOM) { + rect = this.div.getBoundingClientRect(); + } else { + const { + currentLayer, + div + } = this; + const savedDisplay = div.style.display; + const savedVisibility = div.classList.contains("hidden"); + div.classList.remove("hidden"); + div.style.display = "hidden"; + currentLayer.div.append(this.div); + rect = div.getBoundingClientRect(); + div.remove(); + div.style.display = savedDisplay; + div.classList.toggle("hidden", savedVisibility); + } + if (this.rotation % 180 === this.parentRotation % 180) { + this.width = rect.width / parentWidth; + this.height = rect.height / parentHeight; + } else { + this.width = rect.height / parentWidth; + this.height = rect.width / parentHeight; + } + this.fixAndSetPosition(); + } + commit() { + if (!this.isInEditMode()) { + return; + } + super.commit(); + this.disableEditMode(); + const savedText = this.#content; + const newText = this.#content = this.#extractText().trimEnd(); + if (savedText === newText) { + return; + } + const setText = text => { + this.#content = text; + if (!text) { + this.remove(); + return; + } + this.#setContent(); + this._uiManager.rebuild(this); + this.#setEditorDimensions(); + }; + this.addCommands({ + cmd: () => { + setText(newText); + }, + undo: () => { + setText(savedText); + }, + mustExec: false + }); + this.#setEditorDimensions(); + } + shouldGetKeyboardEvents() { + return this.isInEditMode(); + } + enterInEditMode() { + this.enableEditMode(); + this.editorDiv.focus(); + } + dblclick(event) { + this.enterInEditMode(); + } + keydown(event) { + if (event.target === this.div && event.key === "Enter") { + this.enterInEditMode(); + event.preventDefault(); + } + } + editorDivKeydown(event) { + FreeTextEditor._keyboardManager.exec(this, event); + } + editorDivFocus(event) { + this.isEditing = true; + } + editorDivBlur(event) { + this.isEditing = false; + } + editorDivInput(event) { + this.parent.div.classList.toggle("freetextEditing", this.isEmpty()); + } + disableEditing() { + this.editorDiv.setAttribute("role", "comment"); + this.editorDiv.removeAttribute("aria-multiline"); + } + enableEditing() { + this.editorDiv.setAttribute("role", "textbox"); + this.editorDiv.setAttribute("aria-multiline", true); + } + render() { + if (this.div) { + return this.div; + } + let baseX, baseY; + if (this.width) { + baseX = this.x; + baseY = this.y; + } + super.render(); + this.editorDiv = document.createElement("div"); + this.editorDiv.className = "internal"; + this.editorDiv.setAttribute("id", this.#editorDivId); + this.editorDiv.setAttribute("data-l10n-id", "pdfjs-free-text2"); + this.editorDiv.setAttribute("data-l10n-attrs", "default-content"); + this.enableEditing(); + this.editorDiv.contentEditable = true; + const { + style + } = this.editorDiv; + style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`; + style.color = this.#color; + this.div.append(this.editorDiv); + this.overlayDiv = document.createElement("div"); + this.overlayDiv.classList.add("overlay", "enabled"); + this.div.append(this.overlayDiv); + bindEvents(this, this.div, ["dblclick", "keydown"]); + if (this.width) { + const [parentWidth, parentHeight] = this.parentDimensions; + if (this.annotationElementId) { + const { + position + } = this._initialData; + let [tx, ty] = this.getInitialTranslation(); + [tx, ty] = this.pageTranslationToScreen(tx, ty); + const [pageWidth, pageHeight] = this.pageDimensions; + const [pageX, pageY] = this.pageTranslation; + let posX, posY; + switch (this.rotation) { + case 0: + posX = baseX + (position[0] - pageX) / pageWidth; + posY = baseY + this.height - (position[1] - pageY) / pageHeight; + break; + case 90: + posX = baseX + (position[0] - pageX) / pageWidth; + posY = baseY - (position[1] - pageY) / pageHeight; + [tx, ty] = [ty, -tx]; + break; + case 180: + posX = baseX - this.width + (position[0] - pageX) / pageWidth; + posY = baseY - (position[1] - pageY) / pageHeight; + [tx, ty] = [-tx, -ty]; + break; + case 270: + posX = baseX + (position[0] - pageX - this.height * pageHeight) / pageWidth; + posY = baseY + (position[1] - pageY - this.width * pageWidth) / pageHeight; + [tx, ty] = [-ty, tx]; + break; + } + this.setAt(posX * parentWidth, posY * parentHeight, tx, ty); + } else { + this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight); + } + this.#setContent(); + this._isDraggable = true; + this.editorDiv.contentEditable = false; + } else { + this._isDraggable = false; + this.editorDiv.contentEditable = true; + } + return this.div; + } + static #getNodeContent(node) { + return (node.nodeType === Node.TEXT_NODE ? node.nodeValue : node.innerText).replaceAll(EOL_PATTERN, ""); + } + editorDivPaste(event) { + const clipboardData = event.clipboardData || window.clipboardData; + const { + types + } = clipboardData; + if (types.length === 1 && types[0] === "text/plain") { + return; + } + event.preventDefault(); + const paste = FreeTextEditor.#deserializeContent(clipboardData.getData("text") || "").replaceAll(EOL_PATTERN, "\n"); + if (!paste) { + return; + } + const selection = window.getSelection(); + if (!selection.rangeCount) { + return; + } + this.editorDiv.normalize(); + selection.deleteFromDocument(); + const range = selection.getRangeAt(0); + if (!paste.includes("\n")) { + range.insertNode(document.createTextNode(paste)); + this.editorDiv.normalize(); + selection.collapseToStart(); + return; + } + const { + startContainer, + startOffset + } = range; + const bufferBefore = []; + const bufferAfter = []; + if (startContainer.nodeType === Node.TEXT_NODE) { + const parent = startContainer.parentElement; + bufferAfter.push(startContainer.nodeValue.slice(startOffset).replaceAll(EOL_PATTERN, "")); + if (parent !== this.editorDiv) { + let buffer = bufferBefore; + for (const child of this.editorDiv.childNodes) { + if (child === parent) { + buffer = bufferAfter; + continue; + } + buffer.push(FreeTextEditor.#getNodeContent(child)); + } + } + bufferBefore.push(startContainer.nodeValue.slice(0, startOffset).replaceAll(EOL_PATTERN, "")); + } else if (startContainer === this.editorDiv) { + let buffer = bufferBefore; + let i = 0; + for (const child of this.editorDiv.childNodes) { + if (i++ === startOffset) { + buffer = bufferAfter; + } + buffer.push(FreeTextEditor.#getNodeContent(child)); + } + } + this.#content = `${bufferBefore.join("\n")}${paste}${bufferAfter.join("\n")}`; + this.#setContent(); + const newRange = new Range(); + let beforeLength = bufferBefore.reduce((acc, line) => acc + line.length, 0); + for (const { + firstChild + } of this.editorDiv.childNodes) { + if (firstChild.nodeType === Node.TEXT_NODE) { + const length = firstChild.nodeValue.length; + if (beforeLength <= length) { + newRange.setStart(firstChild, beforeLength); + newRange.setEnd(firstChild, beforeLength); + break; + } + beforeLength -= length; + } + } + selection.removeAllRanges(); + selection.addRange(newRange); + } + #setContent() { + this.editorDiv.replaceChildren(); + if (!this.#content) { + return; + } + for (const line of this.#content.split("\n")) { + const div = document.createElement("div"); + div.append(line ? document.createTextNode(line) : document.createElement("br")); + this.editorDiv.append(div); + } + } + #serializeContent() { + return this.#content.replaceAll("\xa0", " "); + } + static #deserializeContent(content) { + return content.replaceAll(" ", "\xa0"); + } + get contentDiv() { + return this.editorDiv; + } + static async deserialize(data, parent, uiManager) { + let initialData = null; + if (data instanceof FreeTextAnnotationElement) { + const { + data: { + defaultAppearanceData: { + fontSize, + fontColor + }, + rect, + rotation, + id, + popupRef + }, + textContent, + textPosition, + parent: { + page: { + pageNumber + } + } + } = data; + if (!textContent || textContent.length === 0) { + return null; + } + initialData = data = { + annotationType: AnnotationEditorType.FREETEXT, + color: Array.from(fontColor), + fontSize, + value: textContent.join("\n"), + position: textPosition, + pageIndex: pageNumber - 1, + rect: rect.slice(0), + rotation, + id, + deleted: false, + popupRef + }; + } + const editor = await super.deserialize(data, parent, uiManager); + editor.#fontSize = data.fontSize; + editor.#color = Util.makeHexColor(...data.color); + editor.#content = FreeTextEditor.#deserializeContent(data.value); + editor.annotationElementId = data.id || null; + editor._initialData = initialData; + return editor; + } + serialize(isForCopying = false) { + if (this.isEmpty()) { + return null; + } + if (this.deleted) { + return this.serializeDeleted(); + } + const padding = FreeTextEditor._internalPadding * this.parentScale; + const rect = this.getRect(padding, padding); + const color = AnnotationEditor._colorManager.convert(this.isAttachedToDOM ? getComputedStyle(this.editorDiv).color : this.#color); + const serialized = { + annotationType: AnnotationEditorType.FREETEXT, + color, + fontSize: this.#fontSize, + value: this.#serializeContent(), + pageIndex: this.pageIndex, + rect, + rotation: this.rotation, + structTreeParentId: this._structTreeParentId + }; + if (isForCopying) { + return serialized; + } + if (this.annotationElementId && !this.#hasElementChanged(serialized)) { + return null; + } + serialized.id = this.annotationElementId; + return serialized; + } + #hasElementChanged(serialized) { + const { + value, + fontSize, + color, + pageIndex + } = this._initialData; + return this._hasBeenMoved || serialized.value !== value || serialized.fontSize !== fontSize || serialized.color.some((c, i) => c !== color[i]) || serialized.pageIndex !== pageIndex; + } + renderAnnotationElement(annotation) { + const content = super.renderAnnotationElement(annotation); + if (this.deleted) { + return content; + } + const { + style + } = content; + style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`; + style.color = this.#color; + content.replaceChildren(); + for (const line of this.#content.split("\n")) { + const div = document.createElement("div"); + div.append(line ? document.createTextNode(line) : document.createElement("br")); + content.append(div); + } + const padding = FreeTextEditor._internalPadding * this.parentScale; + annotation.updateEdited({ + rect: this.getRect(padding, padding), + popupContent: this.#content + }); + return content; + } + resetAnnotationElement(annotation) { + super.resetAnnotationElement(annotation); + annotation.resetEdited(); + } +} + +;// ./src/display/editor/drawers/outline.js + +class Outline { + static PRECISION = 1e-4; + toSVGPath() { + unreachable("Abstract method `toSVGPath` must be implemented."); + } + get box() { + unreachable("Abstract getter `box` must be implemented."); + } + serialize(_bbox, _rotation) { + unreachable("Abstract method `serialize` must be implemented."); + } + static _rescale(src, tx, ty, sx, sy, dest) { + dest ||= new Float32Array(src.length); + for (let i = 0, ii = src.length; i < ii; i += 2) { + dest[i] = tx + src[i] * sx; + dest[i + 1] = ty + src[i + 1] * sy; + } + return dest; + } + static _rescaleAndSwap(src, tx, ty, sx, sy, dest) { + dest ||= new Float32Array(src.length); + for (let i = 0, ii = src.length; i < ii; i += 2) { + dest[i] = tx + src[i + 1] * sx; + dest[i + 1] = ty + src[i] * sy; + } + return dest; + } + static _translate(src, tx, ty, dest) { + dest ||= new Float32Array(src.length); + for (let i = 0, ii = src.length; i < ii; i += 2) { + dest[i] = tx + src[i]; + dest[i + 1] = ty + src[i + 1]; + } + return dest; + } + static svgRound(x) { + return Math.round(x * 10000); + } + static _normalizePoint(x, y, parentWidth, parentHeight, rotation) { + switch (rotation) { + case 90: + return [1 - y / parentWidth, x / parentHeight]; + case 180: + return [1 - x / parentWidth, 1 - y / parentHeight]; + case 270: + return [y / parentWidth, 1 - x / parentHeight]; + default: + return [x / parentWidth, y / parentHeight]; + } + } + static _normalizePagePoint(x, y, rotation) { + switch (rotation) { + case 90: + return [1 - y, x]; + case 180: + return [1 - x, 1 - y]; + case 270: + return [y, 1 - x]; + default: + return [x, y]; + } + } + static createBezierPoints(x1, y1, x2, y2, x3, y3) { + return [(x1 + 5 * x2) / 6, (y1 + 5 * y2) / 6, (5 * x2 + x3) / 6, (5 * y2 + y3) / 6, (x2 + x3) / 2, (y2 + y3) / 2]; + } +} + +;// ./src/display/editor/drawers/freedraw.js + + +class FreeDrawOutliner { + #box; + #bottom = []; + #innerMargin; + #isLTR; + #top = []; + #last = new Float32Array(18); + #lastX; + #lastY; + #min; + #min_dist; + #scaleFactor; + #thickness; + #points = []; + static #MIN_DIST = 8; + static #MIN_DIFF = 2; + static #MIN = FreeDrawOutliner.#MIN_DIST + FreeDrawOutliner.#MIN_DIFF; + constructor({ + x, + y + }, box, scaleFactor, thickness, isLTR, innerMargin = 0) { + this.#box = box; + this.#thickness = thickness * scaleFactor; + this.#isLTR = isLTR; + this.#last.set([NaN, NaN, NaN, NaN, x, y], 6); + this.#innerMargin = innerMargin; + this.#min_dist = FreeDrawOutliner.#MIN_DIST * scaleFactor; + this.#min = FreeDrawOutliner.#MIN * scaleFactor; + this.#scaleFactor = scaleFactor; + this.#points.push(x, y); + } + isEmpty() { + return isNaN(this.#last[8]); + } + #getLastCoords() { + const lastTop = this.#last.subarray(4, 6); + const lastBottom = this.#last.subarray(16, 18); + const [x, y, width, height] = this.#box; + return [(this.#lastX + (lastTop[0] - lastBottom[0]) / 2 - x) / width, (this.#lastY + (lastTop[1] - lastBottom[1]) / 2 - y) / height, (this.#lastX + (lastBottom[0] - lastTop[0]) / 2 - x) / width, (this.#lastY + (lastBottom[1] - lastTop[1]) / 2 - y) / height]; + } + add({ + x, + y + }) { + this.#lastX = x; + this.#lastY = y; + const [layerX, layerY, layerWidth, layerHeight] = this.#box; + let [x1, y1, x2, y2] = this.#last.subarray(8, 12); + const diffX = x - x2; + const diffY = y - y2; + const d = Math.hypot(diffX, diffY); + if (d < this.#min) { + return false; + } + const diffD = d - this.#min_dist; + const K = diffD / d; + const shiftX = K * diffX; + const shiftY = K * diffY; + let x0 = x1; + let y0 = y1; + x1 = x2; + y1 = y2; + x2 += shiftX; + y2 += shiftY; + this.#points?.push(x, y); + const nX = -shiftY / diffD; + const nY = shiftX / diffD; + const thX = nX * this.#thickness; + const thY = nY * this.#thickness; + this.#last.set(this.#last.subarray(2, 8), 0); + this.#last.set([x2 + thX, y2 + thY], 4); + this.#last.set(this.#last.subarray(14, 18), 12); + this.#last.set([x2 - thX, y2 - thY], 16); + if (isNaN(this.#last[6])) { + if (this.#top.length === 0) { + this.#last.set([x1 + thX, y1 + thY], 2); + this.#top.push(NaN, NaN, NaN, NaN, (x1 + thX - layerX) / layerWidth, (y1 + thY - layerY) / layerHeight); + this.#last.set([x1 - thX, y1 - thY], 14); + this.#bottom.push(NaN, NaN, NaN, NaN, (x1 - thX - layerX) / layerWidth, (y1 - thY - layerY) / layerHeight); + } + this.#last.set([x0, y0, x1, y1, x2, y2], 6); + return !this.isEmpty(); + } + this.#last.set([x0, y0, x1, y1, x2, y2], 6); + const angle = Math.abs(Math.atan2(y0 - y1, x0 - x1) - Math.atan2(shiftY, shiftX)); + if (angle < Math.PI / 2) { + [x1, y1, x2, y2] = this.#last.subarray(2, 6); + this.#top.push(NaN, NaN, NaN, NaN, ((x1 + x2) / 2 - layerX) / layerWidth, ((y1 + y2) / 2 - layerY) / layerHeight); + [x1, y1, x0, y0] = this.#last.subarray(14, 18); + this.#bottom.push(NaN, NaN, NaN, NaN, ((x0 + x1) / 2 - layerX) / layerWidth, ((y0 + y1) / 2 - layerY) / layerHeight); + return true; + } + [x0, y0, x1, y1, x2, y2] = this.#last.subarray(0, 6); + this.#top.push(((x0 + 5 * x1) / 6 - layerX) / layerWidth, ((y0 + 5 * y1) / 6 - layerY) / layerHeight, ((5 * x1 + x2) / 6 - layerX) / layerWidth, ((5 * y1 + y2) / 6 - layerY) / layerHeight, ((x1 + x2) / 2 - layerX) / layerWidth, ((y1 + y2) / 2 - layerY) / layerHeight); + [x2, y2, x1, y1, x0, y0] = this.#last.subarray(12, 18); + this.#bottom.push(((x0 + 5 * x1) / 6 - layerX) / layerWidth, ((y0 + 5 * y1) / 6 - layerY) / layerHeight, ((5 * x1 + x2) / 6 - layerX) / layerWidth, ((5 * y1 + y2) / 6 - layerY) / layerHeight, ((x1 + x2) / 2 - layerX) / layerWidth, ((y1 + y2) / 2 - layerY) / layerHeight); + return true; + } + toSVGPath() { + if (this.isEmpty()) { + return ""; + } + const top = this.#top; + const bottom = this.#bottom; + if (isNaN(this.#last[6]) && !this.isEmpty()) { + return this.#toSVGPathTwoPoints(); + } + const buffer = []; + buffer.push(`M${top[4]} ${top[5]}`); + for (let i = 6; i < top.length; i += 6) { + if (isNaN(top[i])) { + buffer.push(`L${top[i + 4]} ${top[i + 5]}`); + } else { + buffer.push(`C${top[i]} ${top[i + 1]} ${top[i + 2]} ${top[i + 3]} ${top[i + 4]} ${top[i + 5]}`); + } + } + this.#toSVGPathEnd(buffer); + for (let i = bottom.length - 6; i >= 6; i -= 6) { + if (isNaN(bottom[i])) { + buffer.push(`L${bottom[i + 4]} ${bottom[i + 5]}`); + } else { + buffer.push(`C${bottom[i]} ${bottom[i + 1]} ${bottom[i + 2]} ${bottom[i + 3]} ${bottom[i + 4]} ${bottom[i + 5]}`); + } + } + this.#toSVGPathStart(buffer); + return buffer.join(" "); + } + #toSVGPathTwoPoints() { + const [x, y, width, height] = this.#box; + const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); + return `M${(this.#last[2] - x) / width} ${(this.#last[3] - y) / height} L${(this.#last[4] - x) / width} ${(this.#last[5] - y) / height} L${lastTopX} ${lastTopY} L${lastBottomX} ${lastBottomY} L${(this.#last[16] - x) / width} ${(this.#last[17] - y) / height} L${(this.#last[14] - x) / width} ${(this.#last[15] - y) / height} Z`; + } + #toSVGPathStart(buffer) { + const bottom = this.#bottom; + buffer.push(`L${bottom[4]} ${bottom[5]} Z`); + } + #toSVGPathEnd(buffer) { + const [x, y, width, height] = this.#box; + const lastTop = this.#last.subarray(4, 6); + const lastBottom = this.#last.subarray(16, 18); + const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); + buffer.push(`L${(lastTop[0] - x) / width} ${(lastTop[1] - y) / height} L${lastTopX} ${lastTopY} L${lastBottomX} ${lastBottomY} L${(lastBottom[0] - x) / width} ${(lastBottom[1] - y) / height}`); + } + newFreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR) { + return new FreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR); + } + getOutlines() { + const top = this.#top; + const bottom = this.#bottom; + const last = this.#last; + const [layerX, layerY, layerWidth, layerHeight] = this.#box; + const points = new Float32Array((this.#points?.length ?? 0) + 2); + for (let i = 0, ii = points.length - 2; i < ii; i += 2) { + points[i] = (this.#points[i] - layerX) / layerWidth; + points[i + 1] = (this.#points[i + 1] - layerY) / layerHeight; + } + points[points.length - 2] = (this.#lastX - layerX) / layerWidth; + points[points.length - 1] = (this.#lastY - layerY) / layerHeight; + if (isNaN(last[6]) && !this.isEmpty()) { + return this.#getOutlineTwoPoints(points); + } + const outline = new Float32Array(this.#top.length + 24 + this.#bottom.length); + let N = top.length; + for (let i = 0; i < N; i += 2) { + if (isNaN(top[i])) { + outline[i] = outline[i + 1] = NaN; + continue; + } + outline[i] = top[i]; + outline[i + 1] = top[i + 1]; + } + N = this.#getOutlineEnd(outline, N); + for (let i = bottom.length - 6; i >= 6; i -= 6) { + for (let j = 0; j < 6; j += 2) { + if (isNaN(bottom[i + j])) { + outline[N] = outline[N + 1] = NaN; + N += 2; + continue; + } + outline[N] = bottom[i + j]; + outline[N + 1] = bottom[i + j + 1]; + N += 2; + } + } + this.#getOutlineStart(outline, N); + return this.newFreeDrawOutline(outline, points, this.#box, this.#scaleFactor, this.#innerMargin, this.#isLTR); + } + #getOutlineTwoPoints(points) { + const last = this.#last; + const [layerX, layerY, layerWidth, layerHeight] = this.#box; + const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); + const outline = new Float32Array(36); + outline.set([NaN, NaN, NaN, NaN, (last[2] - layerX) / layerWidth, (last[3] - layerY) / layerHeight, NaN, NaN, NaN, NaN, (last[4] - layerX) / layerWidth, (last[5] - layerY) / layerHeight, NaN, NaN, NaN, NaN, lastTopX, lastTopY, NaN, NaN, NaN, NaN, lastBottomX, lastBottomY, NaN, NaN, NaN, NaN, (last[16] - layerX) / layerWidth, (last[17] - layerY) / layerHeight, NaN, NaN, NaN, NaN, (last[14] - layerX) / layerWidth, (last[15] - layerY) / layerHeight], 0); + return this.newFreeDrawOutline(outline, points, this.#box, this.#scaleFactor, this.#innerMargin, this.#isLTR); + } + #getOutlineStart(outline, pos) { + const bottom = this.#bottom; + outline.set([NaN, NaN, NaN, NaN, bottom[4], bottom[5]], pos); + return pos += 6; + } + #getOutlineEnd(outline, pos) { + const lastTop = this.#last.subarray(4, 6); + const lastBottom = this.#last.subarray(16, 18); + const [layerX, layerY, layerWidth, layerHeight] = this.#box; + const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); + outline.set([NaN, NaN, NaN, NaN, (lastTop[0] - layerX) / layerWidth, (lastTop[1] - layerY) / layerHeight, NaN, NaN, NaN, NaN, lastTopX, lastTopY, NaN, NaN, NaN, NaN, lastBottomX, lastBottomY, NaN, NaN, NaN, NaN, (lastBottom[0] - layerX) / layerWidth, (lastBottom[1] - layerY) / layerHeight], pos); + return pos += 24; + } +} +class FreeDrawOutline extends Outline { + #box; + #bbox = new Float32Array(4); + #innerMargin; + #isLTR; + #points; + #scaleFactor; + #outline; + constructor(outline, points, box, scaleFactor, innerMargin, isLTR) { + super(); + this.#outline = outline; + this.#points = points; + this.#box = box; + this.#scaleFactor = scaleFactor; + this.#innerMargin = innerMargin; + this.#isLTR = isLTR; + this.lastPoint = [NaN, NaN]; + this.#computeMinMax(isLTR); + const [x, y, width, height] = this.#bbox; + for (let i = 0, ii = outline.length; i < ii; i += 2) { + outline[i] = (outline[i] - x) / width; + outline[i + 1] = (outline[i + 1] - y) / height; + } + for (let i = 0, ii = points.length; i < ii; i += 2) { + points[i] = (points[i] - x) / width; + points[i + 1] = (points[i + 1] - y) / height; + } + } + toSVGPath() { + const buffer = [`M${this.#outline[4]} ${this.#outline[5]}`]; + for (let i = 6, ii = this.#outline.length; i < ii; i += 6) { + if (isNaN(this.#outline[i])) { + buffer.push(`L${this.#outline[i + 4]} ${this.#outline[i + 5]}`); + continue; + } + buffer.push(`C${this.#outline[i]} ${this.#outline[i + 1]} ${this.#outline[i + 2]} ${this.#outline[i + 3]} ${this.#outline[i + 4]} ${this.#outline[i + 5]}`); + } + buffer.push("Z"); + return buffer.join(" "); + } + serialize([blX, blY, trX, trY], rotation) { + const width = trX - blX; + const height = trY - blY; + let outline; + let points; + switch (rotation) { + case 0: + outline = Outline._rescale(this.#outline, blX, trY, width, -height); + points = Outline._rescale(this.#points, blX, trY, width, -height); + break; + case 90: + outline = Outline._rescaleAndSwap(this.#outline, blX, blY, width, height); + points = Outline._rescaleAndSwap(this.#points, blX, blY, width, height); + break; + case 180: + outline = Outline._rescale(this.#outline, trX, blY, -width, height); + points = Outline._rescale(this.#points, trX, blY, -width, height); + break; + case 270: + outline = Outline._rescaleAndSwap(this.#outline, trX, trY, -width, -height); + points = Outline._rescaleAndSwap(this.#points, trX, trY, -width, -height); + break; + } + return { + outline: Array.from(outline), + points: [Array.from(points)] + }; + } + #computeMinMax(isLTR) { + const outline = this.#outline; + let lastX = outline[4]; + let lastY = outline[5]; + let minX = lastX; + let minY = lastY; + let maxX = lastX; + let maxY = lastY; + let lastPointX = lastX; + let lastPointY = lastY; + const ltrCallback = isLTR ? Math.max : Math.min; + for (let i = 6, ii = outline.length; i < ii; i += 6) { + if (isNaN(outline[i])) { + minX = Math.min(minX, outline[i + 4]); + minY = Math.min(minY, outline[i + 5]); + maxX = Math.max(maxX, outline[i + 4]); + maxY = Math.max(maxY, outline[i + 5]); + if (lastPointY < outline[i + 5]) { + lastPointX = outline[i + 4]; + lastPointY = outline[i + 5]; + } else if (lastPointY === outline[i + 5]) { + lastPointX = ltrCallback(lastPointX, outline[i + 4]); + } + } else { + const bbox = Util.bezierBoundingBox(lastX, lastY, ...outline.slice(i, i + 6)); + minX = Math.min(minX, bbox[0]); + minY = Math.min(minY, bbox[1]); + maxX = Math.max(maxX, bbox[2]); + maxY = Math.max(maxY, bbox[3]); + if (lastPointY < bbox[3]) { + lastPointX = bbox[2]; + lastPointY = bbox[3]; + } else if (lastPointY === bbox[3]) { + lastPointX = ltrCallback(lastPointX, bbox[2]); + } + } + lastX = outline[i + 4]; + lastY = outline[i + 5]; + } + const bbox = this.#bbox; + bbox[0] = minX - this.#innerMargin; + bbox[1] = minY - this.#innerMargin; + bbox[2] = maxX - minX + 2 * this.#innerMargin; + bbox[3] = maxY - minY + 2 * this.#innerMargin; + this.lastPoint = [lastPointX, lastPointY]; + } + get box() { + return this.#bbox; + } + newOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin = 0) { + return new FreeDrawOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin); + } + getNewOutline(thickness, innerMargin) { + const [x, y, width, height] = this.#bbox; + const [layerX, layerY, layerWidth, layerHeight] = this.#box; + const sx = width * layerWidth; + const sy = height * layerHeight; + const tx = x * layerWidth + layerX; + const ty = y * layerHeight + layerY; + const outliner = this.newOutliner({ + x: this.#points[0] * sx + tx, + y: this.#points[1] * sy + ty + }, this.#box, this.#scaleFactor, thickness, this.#isLTR, innerMargin ?? this.#innerMargin); + for (let i = 2; i < this.#points.length; i += 2) { + outliner.add({ + x: this.#points[i] * sx + tx, + y: this.#points[i + 1] * sy + ty + }); + } + return outliner.getOutlines(); + } +} + +;// ./src/display/editor/drawers/highlight.js + + +class HighlightOutliner { + #box; + #lastPoint; + #verticalEdges = []; + #intervals = []; + constructor(boxes, borderWidth = 0, innerMargin = 0, isLTR = true) { + let minX = Infinity; + let maxX = -Infinity; + let minY = Infinity; + let maxY = -Infinity; + const NUMBER_OF_DIGITS = 4; + const EPSILON = 10 ** -NUMBER_OF_DIGITS; + for (const { + x, + y, + width, + height + } of boxes) { + const x1 = Math.floor((x - borderWidth) / EPSILON) * EPSILON; + const x2 = Math.ceil((x + width + borderWidth) / EPSILON) * EPSILON; + const y1 = Math.floor((y - borderWidth) / EPSILON) * EPSILON; + const y2 = Math.ceil((y + height + borderWidth) / EPSILON) * EPSILON; + const left = [x1, y1, y2, true]; + const right = [x2, y1, y2, false]; + this.#verticalEdges.push(left, right); + minX = Math.min(minX, x1); + maxX = Math.max(maxX, x2); + minY = Math.min(minY, y1); + maxY = Math.max(maxY, y2); + } + const bboxWidth = maxX - minX + 2 * innerMargin; + const bboxHeight = maxY - minY + 2 * innerMargin; + const shiftedMinX = minX - innerMargin; + const shiftedMinY = minY - innerMargin; + const lastEdge = this.#verticalEdges.at(isLTR ? -1 : -2); + const lastPoint = [lastEdge[0], lastEdge[2]]; + for (const edge of this.#verticalEdges) { + const [x, y1, y2] = edge; + edge[0] = (x - shiftedMinX) / bboxWidth; + edge[1] = (y1 - shiftedMinY) / bboxHeight; + edge[2] = (y2 - shiftedMinY) / bboxHeight; + } + this.#box = new Float32Array([shiftedMinX, shiftedMinY, bboxWidth, bboxHeight]); + this.#lastPoint = lastPoint; + } + getOutlines() { + this.#verticalEdges.sort((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2]); + const outlineVerticalEdges = []; + for (const edge of this.#verticalEdges) { + if (edge[3]) { + outlineVerticalEdges.push(...this.#breakEdge(edge)); + this.#insert(edge); + } else { + this.#remove(edge); + outlineVerticalEdges.push(...this.#breakEdge(edge)); + } + } + return this.#getOutlines(outlineVerticalEdges); + } + #getOutlines(outlineVerticalEdges) { + const edges = []; + const allEdges = new Set(); + for (const edge of outlineVerticalEdges) { + const [x, y1, y2] = edge; + edges.push([x, y1, edge], [x, y2, edge]); + } + edges.sort((a, b) => a[1] - b[1] || a[0] - b[0]); + for (let i = 0, ii = edges.length; i < ii; i += 2) { + const edge1 = edges[i][2]; + const edge2 = edges[i + 1][2]; + edge1.push(edge2); + edge2.push(edge1); + allEdges.add(edge1); + allEdges.add(edge2); + } + const outlines = []; + let outline; + while (allEdges.size > 0) { + const edge = allEdges.values().next().value; + let [x, y1, y2, edge1, edge2] = edge; + allEdges.delete(edge); + let lastPointX = x; + let lastPointY = y1; + outline = [x, y2]; + outlines.push(outline); + while (true) { + let e; + if (allEdges.has(edge1)) { + e = edge1; + } else if (allEdges.has(edge2)) { + e = edge2; + } else { + break; + } + allEdges.delete(e); + [x, y1, y2, edge1, edge2] = e; + if (lastPointX !== x) { + outline.push(lastPointX, lastPointY, x, lastPointY === y1 ? y1 : y2); + lastPointX = x; + } + lastPointY = lastPointY === y1 ? y2 : y1; + } + outline.push(lastPointX, lastPointY); + } + return new HighlightOutline(outlines, this.#box, this.#lastPoint); + } + #binarySearch(y) { + const array = this.#intervals; + let start = 0; + let end = array.length - 1; + while (start <= end) { + const middle = start + end >> 1; + const y1 = array[middle][0]; + if (y1 === y) { + return middle; + } + if (y1 < y) { + start = middle + 1; + } else { + end = middle - 1; + } + } + return end + 1; + } + #insert([, y1, y2]) { + const index = this.#binarySearch(y1); + this.#intervals.splice(index, 0, [y1, y2]); + } + #remove([, y1, y2]) { + const index = this.#binarySearch(y1); + for (let i = index; i < this.#intervals.length; i++) { + const [start, end] = this.#intervals[i]; + if (start !== y1) { + break; + } + if (start === y1 && end === y2) { + this.#intervals.splice(i, 1); + return; + } + } + for (let i = index - 1; i >= 0; i--) { + const [start, end] = this.#intervals[i]; + if (start !== y1) { + break; + } + if (start === y1 && end === y2) { + this.#intervals.splice(i, 1); + return; + } + } + } + #breakEdge(edge) { + const [x, y1, y2] = edge; + const results = [[x, y1, y2]]; + const index = this.#binarySearch(y2); + for (let i = 0; i < index; i++) { + const [start, end] = this.#intervals[i]; + for (let j = 0, jj = results.length; j < jj; j++) { + const [, y3, y4] = results[j]; + if (end <= y3 || y4 <= start) { + continue; + } + if (y3 >= start) { + if (y4 > end) { + results[j][1] = end; + } else { + if (jj === 1) { + return []; + } + results.splice(j, 1); + j--; + jj--; + } + continue; + } + results[j][2] = start; + if (y4 > end) { + results.push([x, end, y4]); + } + } + } + return results; + } +} +class HighlightOutline extends Outline { + #box; + #outlines; + constructor(outlines, box, lastPoint) { + super(); + this.#outlines = outlines; + this.#box = box; + this.lastPoint = lastPoint; + } + toSVGPath() { + const buffer = []; + for (const polygon of this.#outlines) { + let [prevX, prevY] = polygon; + buffer.push(`M${prevX} ${prevY}`); + for (let i = 2; i < polygon.length; i += 2) { + const x = polygon[i]; + const y = polygon[i + 1]; + if (x === prevX) { + buffer.push(`V${y}`); + prevY = y; + } else if (y === prevY) { + buffer.push(`H${x}`); + prevX = x; + } + } + buffer.push("Z"); + } + return buffer.join(" "); + } + serialize([blX, blY, trX, trY], _rotation) { + const outlines = []; + const width = trX - blX; + const height = trY - blY; + for (const outline of this.#outlines) { + const points = new Array(outline.length); + for (let i = 0; i < outline.length; i += 2) { + points[i] = blX + outline[i] * width; + points[i + 1] = trY - outline[i + 1] * height; + } + outlines.push(points); + } + return outlines; + } + get box() { + return this.#box; + } + get classNamesForOutlining() { + return ["highlightOutline"]; + } +} +class FreeHighlightOutliner extends FreeDrawOutliner { + newFreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR) { + return new FreeHighlightOutline(outline, points, box, scaleFactor, innerMargin, isLTR); + } +} +class FreeHighlightOutline extends FreeDrawOutline { + newOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin = 0) { + return new FreeHighlightOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin); + } +} + +;// ./src/display/editor/color_picker.js + + + +class ColorPicker { + #button = null; + #buttonSwatch = null; + #defaultColor; + #dropdown = null; + #dropdownWasFromKeyboard = false; + #isMainColorPicker = false; + #editor = null; + #eventBus; + #openDropdownAC = null; + #uiManager = null; + #type; + static #l10nColor = null; + static get _keyboardManager() { + return shadow(this, "_keyboardManager", new KeyboardManager([[["Escape", "mac+Escape"], ColorPicker.prototype._hideDropdownFromKeyboard], [[" ", "mac+ "], ColorPicker.prototype._colorSelectFromKeyboard], [["ArrowDown", "ArrowRight", "mac+ArrowDown", "mac+ArrowRight"], ColorPicker.prototype._moveToNext], [["ArrowUp", "ArrowLeft", "mac+ArrowUp", "mac+ArrowLeft"], ColorPicker.prototype._moveToPrevious], [["Home", "mac+Home"], ColorPicker.prototype._moveToBeginning], [["End", "mac+End"], ColorPicker.prototype._moveToEnd]])); + } + constructor({ + editor = null, + uiManager = null + }) { + if (editor) { + this.#isMainColorPicker = false; + this.#type = AnnotationEditorParamsType.HIGHLIGHT_COLOR; + this.#editor = editor; + } else { + this.#isMainColorPicker = true; + this.#type = AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR; + } + this.#uiManager = editor?._uiManager || uiManager; + this.#eventBus = this.#uiManager._eventBus; + this.#defaultColor = editor?.color || this.#uiManager?.highlightColors.values().next().value || "#FFFF98"; + ColorPicker.#l10nColor ||= Object.freeze({ + blue: "pdfjs-editor-colorpicker-blue", + green: "pdfjs-editor-colorpicker-green", + pink: "pdfjs-editor-colorpicker-pink", + red: "pdfjs-editor-colorpicker-red", + yellow: "pdfjs-editor-colorpicker-yellow" + }); + } + renderButton() { + const button = this.#button = document.createElement("button"); + button.className = "colorPicker"; + button.tabIndex = "0"; + button.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-button"); + button.setAttribute("aria-haspopup", true); + const signal = this.#uiManager._signal; + button.addEventListener("click", this.#openDropdown.bind(this), { + signal + }); + button.addEventListener("keydown", this.#keyDown.bind(this), { + signal + }); + const swatch = this.#buttonSwatch = document.createElement("span"); + swatch.className = "swatch"; + swatch.setAttribute("aria-hidden", true); + swatch.style.backgroundColor = this.#defaultColor; + button.append(swatch); + return button; + } + renderMainDropdown() { + const dropdown = this.#dropdown = this.#getDropdownRoot(); + dropdown.setAttribute("aria-orientation", "horizontal"); + dropdown.setAttribute("aria-labelledby", "highlightColorPickerLabel"); + return dropdown; + } + #getDropdownRoot() { + const div = document.createElement("div"); + const signal = this.#uiManager._signal; + div.addEventListener("contextmenu", noContextMenu, { + signal + }); + div.className = "dropdown"; + div.role = "listbox"; + div.setAttribute("aria-multiselectable", false); + div.setAttribute("aria-orientation", "vertical"); + div.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-dropdown"); + for (const [name, color] of this.#uiManager.highlightColors) { + const button = document.createElement("button"); + button.tabIndex = "0"; + button.role = "option"; + button.setAttribute("data-color", color); + button.title = name; + button.setAttribute("data-l10n-id", ColorPicker.#l10nColor[name]); + const swatch = document.createElement("span"); + button.append(swatch); + swatch.className = "swatch"; + swatch.style.backgroundColor = color; + button.setAttribute("aria-selected", color === this.#defaultColor); + button.addEventListener("click", this.#colorSelect.bind(this, color), { + signal + }); + div.append(button); + } + div.addEventListener("keydown", this.#keyDown.bind(this), { + signal + }); + return div; + } + #colorSelect(color, event) { + event.stopPropagation(); + this.#eventBus.dispatch("switchannotationeditorparams", { + source: this, + type: this.#type, + value: color + }); + } + _colorSelectFromKeyboard(event) { + if (event.target === this.#button) { + this.#openDropdown(event); + return; + } + const color = event.target.getAttribute("data-color"); + if (!color) { + return; + } + this.#colorSelect(color, event); + } + _moveToNext(event) { + if (!this.#isDropdownVisible) { + this.#openDropdown(event); + return; + } + if (event.target === this.#button) { + this.#dropdown.firstChild?.focus(); + return; + } + event.target.nextSibling?.focus(); + } + _moveToPrevious(event) { + if (event.target === this.#dropdown?.firstChild || event.target === this.#button) { + if (this.#isDropdownVisible) { + this._hideDropdownFromKeyboard(); + } + return; + } + if (!this.#isDropdownVisible) { + this.#openDropdown(event); + } + event.target.previousSibling?.focus(); + } + _moveToBeginning(event) { + if (!this.#isDropdownVisible) { + this.#openDropdown(event); + return; + } + this.#dropdown.firstChild?.focus(); + } + _moveToEnd(event) { + if (!this.#isDropdownVisible) { + this.#openDropdown(event); + return; + } + this.#dropdown.lastChild?.focus(); + } + #keyDown(event) { + ColorPicker._keyboardManager.exec(this, event); + } + #openDropdown(event) { + if (this.#isDropdownVisible) { + this.hideDropdown(); + return; + } + this.#dropdownWasFromKeyboard = event.detail === 0; + if (!this.#openDropdownAC) { + this.#openDropdownAC = new AbortController(); + window.addEventListener("pointerdown", this.#pointerDown.bind(this), { + signal: this.#uiManager.combinedSignal(this.#openDropdownAC) + }); + } + if (this.#dropdown) { + this.#dropdown.classList.remove("hidden"); + return; + } + const root = this.#dropdown = this.#getDropdownRoot(); + this.#button.append(root); + } + #pointerDown(event) { + if (this.#dropdown?.contains(event.target)) { + return; + } + this.hideDropdown(); + } + hideDropdown() { + this.#dropdown?.classList.add("hidden"); + this.#openDropdownAC?.abort(); + this.#openDropdownAC = null; + } + get #isDropdownVisible() { + return this.#dropdown && !this.#dropdown.classList.contains("hidden"); + } + _hideDropdownFromKeyboard() { + if (this.#isMainColorPicker) { + return; + } + if (!this.#isDropdownVisible) { + this.#editor?.unselect(); + return; + } + this.hideDropdown(); + this.#button.focus({ + preventScroll: true, + focusVisible: this.#dropdownWasFromKeyboard + }); + } + updateColor(color) { + if (this.#buttonSwatch) { + this.#buttonSwatch.style.backgroundColor = color; + } + if (!this.#dropdown) { + return; + } + const i = this.#uiManager.highlightColors.values(); + for (const child of this.#dropdown.children) { + child.setAttribute("aria-selected", i.next().value === color); + } + } + destroy() { + this.#button?.remove(); + this.#button = null; + this.#buttonSwatch = null; + this.#dropdown?.remove(); + this.#dropdown = null; + } +} + +;// ./src/display/editor/highlight.js + + + + + + + +class HighlightEditor extends AnnotationEditor { + #anchorNode = null; + #anchorOffset = 0; + #boxes; + #clipPathId = null; + #colorPicker = null; + #focusOutlines = null; + #focusNode = null; + #focusOffset = 0; + #highlightDiv = null; + #highlightOutlines = null; + #id = null; + #isFreeHighlight = false; + #lastPoint = null; + #opacity; + #outlineId = null; + #text = ""; + #thickness; + #methodOfCreation = ""; + static _defaultColor = null; + static _defaultOpacity = 1; + static _defaultThickness = 12; + static _type = "highlight"; + static _editorType = AnnotationEditorType.HIGHLIGHT; + static _freeHighlightId = -1; + static _freeHighlight = null; + static _freeHighlightClipId = ""; + static get _keyboardManager() { + const proto = HighlightEditor.prototype; + return shadow(this, "_keyboardManager", new KeyboardManager([[["ArrowLeft", "mac+ArrowLeft"], proto._moveCaret, { + args: [0] + }], [["ArrowRight", "mac+ArrowRight"], proto._moveCaret, { + args: [1] + }], [["ArrowUp", "mac+ArrowUp"], proto._moveCaret, { + args: [2] + }], [["ArrowDown", "mac+ArrowDown"], proto._moveCaret, { + args: [3] + }]])); + } + constructor(params) { + super({ + ...params, + name: "highlightEditor" + }); + this.color = params.color || HighlightEditor._defaultColor; + this.#thickness = params.thickness || HighlightEditor._defaultThickness; + this.#opacity = params.opacity || HighlightEditor._defaultOpacity; + this.#boxes = params.boxes || null; + this.#methodOfCreation = params.methodOfCreation || ""; + this.#text = params.text || ""; + this._isDraggable = false; + if (params.highlightId > -1) { + this.#isFreeHighlight = true; + this.#createFreeOutlines(params); + this.#addToDrawLayer(); + } else if (this.#boxes) { + this.#anchorNode = params.anchorNode; + this.#anchorOffset = params.anchorOffset; + this.#focusNode = params.focusNode; + this.#focusOffset = params.focusOffset; + this.#createOutlines(); + this.#addToDrawLayer(); + this.rotate(this.rotation); + } + } + get telemetryInitialData() { + return { + action: "added", + type: this.#isFreeHighlight ? "free_highlight" : "highlight", + color: this._uiManager.highlightColorNames.get(this.color), + thickness: this.#thickness, + methodOfCreation: this.#methodOfCreation + }; + } + get telemetryFinalData() { + return { + type: "highlight", + color: this._uiManager.highlightColorNames.get(this.color) + }; + } + static computeTelemetryFinalData(data) { + return { + numberOfColors: data.get("color").size + }; + } + #createOutlines() { + const outliner = new HighlightOutliner(this.#boxes, 0.001); + this.#highlightOutlines = outliner.getOutlines(); + [this.x, this.y, this.width, this.height] = this.#highlightOutlines.box; + const outlinerForOutline = new HighlightOutliner(this.#boxes, 0.0025, 0.001, this._uiManager.direction === "ltr"); + this.#focusOutlines = outlinerForOutline.getOutlines(); + const { + lastPoint + } = this.#focusOutlines; + this.#lastPoint = [(lastPoint[0] - this.x) / this.width, (lastPoint[1] - this.y) / this.height]; + } + #createFreeOutlines({ + highlightOutlines, + highlightId, + clipPathId + }) { + this.#highlightOutlines = highlightOutlines; + const extraThickness = 1.5; + this.#focusOutlines = highlightOutlines.getNewOutline(this.#thickness / 2 + extraThickness, 0.0025); + if (highlightId >= 0) { + this.#id = highlightId; + this.#clipPathId = clipPathId; + this.parent.drawLayer.finalizeDraw(highlightId, { + bbox: highlightOutlines.box, + path: { + d: highlightOutlines.toSVGPath() + } + }); + this.#outlineId = this.parent.drawLayer.drawOutline({ + rootClass: { + highlightOutline: true, + free: true + }, + bbox: this.#focusOutlines.box, + path: { + d: this.#focusOutlines.toSVGPath() + } + }, true); + } else if (this.parent) { + const angle = this.parent.viewport.rotation; + this.parent.drawLayer.updateProperties(this.#id, { + bbox: HighlightEditor.#rotateBbox(this.#highlightOutlines.box, (angle - this.rotation + 360) % 360), + path: { + d: highlightOutlines.toSVGPath() + } + }); + this.parent.drawLayer.updateProperties(this.#outlineId, { + bbox: HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle), + path: { + d: this.#focusOutlines.toSVGPath() + } + }); + } + const [x, y, width, height] = highlightOutlines.box; + switch (this.rotation) { + case 0: + this.x = x; + this.y = y; + this.width = width; + this.height = height; + break; + case 90: + { + const [pageWidth, pageHeight] = this.parentDimensions; + this.x = y; + this.y = 1 - x; + this.width = width * pageHeight / pageWidth; + this.height = height * pageWidth / pageHeight; + break; + } + case 180: + this.x = 1 - x; + this.y = 1 - y; + this.width = width; + this.height = height; + break; + case 270: + { + const [pageWidth, pageHeight] = this.parentDimensions; + this.x = 1 - y; + this.y = x; + this.width = width * pageHeight / pageWidth; + this.height = height * pageWidth / pageHeight; + break; + } + } + const { + lastPoint + } = this.#focusOutlines; + this.#lastPoint = [(lastPoint[0] - x) / width, (lastPoint[1] - y) / height]; + } + static initialize(l10n, uiManager) { + AnnotationEditor.initialize(l10n, uiManager); + HighlightEditor._defaultColor ||= uiManager.highlightColors?.values().next().value || "#fff066"; + } + static updateDefaultParams(type, value) { + switch (type) { + case AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR: + HighlightEditor._defaultColor = value; + break; + case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS: + HighlightEditor._defaultThickness = value; + break; + } + } + translateInPage(x, y) {} + get toolbarPosition() { + return this.#lastPoint; + } + updateParams(type, value) { + switch (type) { + case AnnotationEditorParamsType.HIGHLIGHT_COLOR: + this.#updateColor(value); + break; + case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS: + this.#updateThickness(value); + break; + } + } + static get defaultPropertiesToUpdate() { + return [[AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR, HighlightEditor._defaultColor], [AnnotationEditorParamsType.HIGHLIGHT_THICKNESS, HighlightEditor._defaultThickness]]; + } + get propertiesToUpdate() { + return [[AnnotationEditorParamsType.HIGHLIGHT_COLOR, this.color || HighlightEditor._defaultColor], [AnnotationEditorParamsType.HIGHLIGHT_THICKNESS, this.#thickness || HighlightEditor._defaultThickness], [AnnotationEditorParamsType.HIGHLIGHT_FREE, this.#isFreeHighlight]]; + } + #updateColor(color) { + const setColorAndOpacity = (col, opa) => { + this.color = col; + this.#opacity = opa; + this.parent?.drawLayer.updateProperties(this.#id, { + root: { + fill: col, + "fill-opacity": opa + } + }); + this.#colorPicker?.updateColor(col); + }; + const savedColor = this.color; + const savedOpacity = this.#opacity; + this.addCommands({ + cmd: setColorAndOpacity.bind(this, color, HighlightEditor._defaultOpacity), + undo: setColorAndOpacity.bind(this, savedColor, savedOpacity), + post: this._uiManager.updateUI.bind(this._uiManager, this), + mustExec: true, + type: AnnotationEditorParamsType.HIGHLIGHT_COLOR, + overwriteIfSameType: true, + keepUndo: true + }); + this._reportTelemetry({ + action: "color_changed", + color: this._uiManager.highlightColorNames.get(color) + }, true); + } + #updateThickness(thickness) { + const savedThickness = this.#thickness; + const setThickness = th => { + this.#thickness = th; + this.#changeThickness(th); + }; + this.addCommands({ + cmd: setThickness.bind(this, thickness), + undo: setThickness.bind(this, savedThickness), + post: this._uiManager.updateUI.bind(this._uiManager, this), + mustExec: true, + type: AnnotationEditorParamsType.INK_THICKNESS, + overwriteIfSameType: true, + keepUndo: true + }); + this._reportTelemetry({ + action: "thickness_changed", + thickness + }, true); + } + async addEditToolbar() { + const toolbar = await super.addEditToolbar(); + if (!toolbar) { + return null; + } + if (this._uiManager.highlightColors) { + this.#colorPicker = new ColorPicker({ + editor: this + }); + toolbar.addColorPicker(this.#colorPicker); + } + return toolbar; + } + disableEditing() { + super.disableEditing(); + this.div.classList.toggle("disabled", true); + } + enableEditing() { + super.enableEditing(); + this.div.classList.toggle("disabled", false); + } + fixAndSetPosition() { + return super.fixAndSetPosition(this.#getRotation()); + } + getBaseTranslation() { + return [0, 0]; + } + getRect(tx, ty) { + return super.getRect(tx, ty, this.#getRotation()); + } + onceAdded(focus) { + if (!this.annotationElementId) { + this.parent.addUndoableEditor(this); + } + if (focus) { + this.div.focus(); + } + } + remove() { + this.#cleanDrawLayer(); + this._reportTelemetry({ + action: "deleted" + }); + super.remove(); + } + rebuild() { + if (!this.parent) { + return; + } + super.rebuild(); + if (this.div === null) { + return; + } + this.#addToDrawLayer(); + if (!this.isAttachedToDOM) { + this.parent.add(this); + } + } + setParent(parent) { + let mustBeSelected = false; + if (this.parent && !parent) { + this.#cleanDrawLayer(); + } else if (parent) { + this.#addToDrawLayer(parent); + mustBeSelected = !this.parent && this.div?.classList.contains("selectedEditor"); + } + super.setParent(parent); + this.show(this._isVisible); + if (mustBeSelected) { + this.select(); + } + } + #changeThickness(thickness) { + if (!this.#isFreeHighlight) { + return; + } + this.#createFreeOutlines({ + highlightOutlines: this.#highlightOutlines.getNewOutline(thickness / 2) + }); + this.fixAndSetPosition(); + const [parentWidth, parentHeight] = this.parentDimensions; + this.setDims(this.width * parentWidth, this.height * parentHeight); + } + #cleanDrawLayer() { + if (this.#id === null || !this.parent) { + return; + } + this.parent.drawLayer.remove(this.#id); + this.#id = null; + this.parent.drawLayer.remove(this.#outlineId); + this.#outlineId = null; + } + #addToDrawLayer(parent = this.parent) { + if (this.#id !== null) { + return; + } + ({ + id: this.#id, + clipPathId: this.#clipPathId + } = parent.drawLayer.draw({ + bbox: this.#highlightOutlines.box, + root: { + viewBox: "0 0 1 1", + fill: this.color, + "fill-opacity": this.#opacity + }, + rootClass: { + highlight: true, + free: this.#isFreeHighlight + }, + path: { + d: this.#highlightOutlines.toSVGPath() + } + }, false, true)); + this.#outlineId = parent.drawLayer.drawOutline({ + rootClass: { + highlightOutline: true, + free: this.#isFreeHighlight + }, + bbox: this.#focusOutlines.box, + path: { + d: this.#focusOutlines.toSVGPath() + } + }, this.#isFreeHighlight); + if (this.#highlightDiv) { + this.#highlightDiv.style.clipPath = this.#clipPathId; + } + } + static #rotateBbox([x, y, width, height], angle) { + switch (angle) { + case 90: + return [1 - y - height, x, height, width]; + case 180: + return [1 - x - width, 1 - y - height, width, height]; + case 270: + return [y, 1 - x - width, height, width]; + } + return [x, y, width, height]; + } + rotate(angle) { + const { + drawLayer + } = this.parent; + let box; + if (this.#isFreeHighlight) { + angle = (angle - this.rotation + 360) % 360; + box = HighlightEditor.#rotateBbox(this.#highlightOutlines.box, angle); + } else { + box = HighlightEditor.#rotateBbox([this.x, this.y, this.width, this.height], angle); + } + drawLayer.updateProperties(this.#id, { + bbox: box, + root: { + "data-main-rotation": angle + } + }); + drawLayer.updateProperties(this.#outlineId, { + bbox: HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle), + root: { + "data-main-rotation": angle + } + }); + } + render() { + if (this.div) { + return this.div; + } + const div = super.render(); + if (this.#text) { + div.setAttribute("aria-label", this.#text); + div.setAttribute("role", "mark"); + } + if (this.#isFreeHighlight) { + div.classList.add("free"); + } else { + this.div.addEventListener("keydown", this.#keydown.bind(this), { + signal: this._uiManager._signal + }); + } + const highlightDiv = this.#highlightDiv = document.createElement("div"); + div.append(highlightDiv); + highlightDiv.setAttribute("aria-hidden", "true"); + highlightDiv.className = "internal"; + highlightDiv.style.clipPath = this.#clipPathId; + const [parentWidth, parentHeight] = this.parentDimensions; + this.setDims(this.width * parentWidth, this.height * parentHeight); + bindEvents(this, this.#highlightDiv, ["pointerover", "pointerleave"]); + this.enableEditing(); + return div; + } + pointerover() { + if (!this.isSelected) { + this.parent?.drawLayer.updateProperties(this.#outlineId, { + rootClass: { + hovered: true + } + }); + } + } + pointerleave() { + if (!this.isSelected) { + this.parent?.drawLayer.updateProperties(this.#outlineId, { + rootClass: { + hovered: false + } + }); + } + } + #keydown(event) { + HighlightEditor._keyboardManager.exec(this, event); + } + _moveCaret(direction) { + this.parent.unselect(this); + switch (direction) { + case 0: + case 2: + this.#setCaret(true); + break; + case 1: + case 3: + this.#setCaret(false); + break; + } + } + #setCaret(start) { + if (!this.#anchorNode) { + return; + } + const selection = window.getSelection(); + if (start) { + selection.setPosition(this.#anchorNode, this.#anchorOffset); + } else { + selection.setPosition(this.#focusNode, this.#focusOffset); + } + } + select() { + super.select(); + if (!this.#outlineId) { + return; + } + this.parent?.drawLayer.updateProperties(this.#outlineId, { + rootClass: { + hovered: false, + selected: true + } + }); + } + unselect() { + super.unselect(); + if (!this.#outlineId) { + return; + } + this.parent?.drawLayer.updateProperties(this.#outlineId, { + rootClass: { + selected: false + } + }); + if (!this.#isFreeHighlight) { + this.#setCaret(false); + } + } + get _mustFixPosition() { + return !this.#isFreeHighlight; + } + show(visible = this._isVisible) { + super.show(visible); + if (this.parent) { + this.parent.drawLayer.updateProperties(this.#id, { + rootClass: { + hidden: !visible + } + }); + this.parent.drawLayer.updateProperties(this.#outlineId, { + rootClass: { + hidden: !visible + } + }); + } + } + #getRotation() { + return this.#isFreeHighlight ? this.rotation : 0; + } + #serializeBoxes() { + if (this.#isFreeHighlight) { + return null; + } + const [pageWidth, pageHeight] = this.pageDimensions; + const [pageX, pageY] = this.pageTranslation; + const boxes = this.#boxes; + const quadPoints = new Float32Array(boxes.length * 8); + let i = 0; + for (const { + x, + y, + width, + height + } of boxes) { + const sx = x * pageWidth + pageX; + const sy = (1 - y) * pageHeight + pageY; + quadPoints[i] = quadPoints[i + 4] = sx; + quadPoints[i + 1] = quadPoints[i + 3] = sy; + quadPoints[i + 2] = quadPoints[i + 6] = sx + width * pageWidth; + quadPoints[i + 5] = quadPoints[i + 7] = sy - height * pageHeight; + i += 8; + } + return quadPoints; + } + #serializeOutlines(rect) { + return this.#highlightOutlines.serialize(rect, this.#getRotation()); + } + static startHighlighting(parent, isLTR, { + target: textLayer, + x, + y + }) { + const { + x: layerX, + y: layerY, + width: parentWidth, + height: parentHeight + } = textLayer.getBoundingClientRect(); + const ac = new AbortController(); + const signal = parent.combinedSignal(ac); + const pointerUpCallback = e => { + ac.abort(); + this.#endHighlight(parent, e); + }; + window.addEventListener("blur", pointerUpCallback, { + signal + }); + window.addEventListener("pointerup", pointerUpCallback, { + signal + }); + window.addEventListener("pointerdown", stopEvent, { + capture: true, + passive: false, + signal + }); + window.addEventListener("contextmenu", noContextMenu, { + signal + }); + textLayer.addEventListener("pointermove", this.#highlightMove.bind(this, parent), { + signal + }); + this._freeHighlight = new FreeHighlightOutliner({ + x, + y + }, [layerX, layerY, parentWidth, parentHeight], parent.scale, this._defaultThickness / 2, isLTR, 0.001); + ({ + id: this._freeHighlightId, + clipPathId: this._freeHighlightClipId + } = parent.drawLayer.draw({ + bbox: [0, 0, 1, 1], + root: { + viewBox: "0 0 1 1", + fill: this._defaultColor, + "fill-opacity": this._defaultOpacity + }, + rootClass: { + highlight: true, + free: true + }, + path: { + d: this._freeHighlight.toSVGPath() + } + }, true, true)); + } + static #highlightMove(parent, event) { + if (this._freeHighlight.add(event)) { + parent.drawLayer.updateProperties(this._freeHighlightId, { + path: { + d: this._freeHighlight.toSVGPath() + } + }); + } + } + static #endHighlight(parent, event) { + if (!this._freeHighlight.isEmpty()) { + parent.createAndAddNewEditor(event, false, { + highlightId: this._freeHighlightId, + highlightOutlines: this._freeHighlight.getOutlines(), + clipPathId: this._freeHighlightClipId, + methodOfCreation: "main_toolbar" + }); + } else { + parent.drawLayer.remove(this._freeHighlightId); + } + this._freeHighlightId = -1; + this._freeHighlight = null; + this._freeHighlightClipId = ""; + } + static async deserialize(data, parent, uiManager) { + let initialData = null; + if (data instanceof HighlightAnnotationElement) { + const { + data: { + quadPoints, + rect, + rotation, + id, + color, + opacity, + popupRef + }, + parent: { + page: { + pageNumber + } + } + } = data; + initialData = data = { + annotationType: AnnotationEditorType.HIGHLIGHT, + color: Array.from(color), + opacity, + quadPoints, + boxes: null, + pageIndex: pageNumber - 1, + rect: rect.slice(0), + rotation, + id, + deleted: false, + popupRef + }; + } else if (data instanceof InkAnnotationElement) { + const { + data: { + inkLists, + rect, + rotation, + id, + color, + borderStyle: { + rawWidth: thickness + }, + popupRef + }, + parent: { + page: { + pageNumber + } + } + } = data; + initialData = data = { + annotationType: AnnotationEditorType.HIGHLIGHT, + color: Array.from(color), + thickness, + inkLists, + boxes: null, + pageIndex: pageNumber - 1, + rect: rect.slice(0), + rotation, + id, + deleted: false, + popupRef + }; + } + const { + color, + quadPoints, + inkLists, + opacity + } = data; + const editor = await super.deserialize(data, parent, uiManager); + editor.color = Util.makeHexColor(...color); + editor.#opacity = opacity || 1; + if (inkLists) { + editor.#thickness = data.thickness; + } + editor.annotationElementId = data.id || null; + editor._initialData = initialData; + const [pageWidth, pageHeight] = editor.pageDimensions; + const [pageX, pageY] = editor.pageTranslation; + if (quadPoints) { + const boxes = editor.#boxes = []; + for (let i = 0; i < quadPoints.length; i += 8) { + boxes.push({ + x: (quadPoints[i] - pageX) / pageWidth, + y: 1 - (quadPoints[i + 1] - pageY) / pageHeight, + width: (quadPoints[i + 2] - quadPoints[i]) / pageWidth, + height: (quadPoints[i + 1] - quadPoints[i + 5]) / pageHeight + }); + } + editor.#createOutlines(); + editor.#addToDrawLayer(); + editor.rotate(editor.rotation); + } else if (inkLists) { + editor.#isFreeHighlight = true; + const points = inkLists[0]; + const point = { + x: points[0] - pageX, + y: pageHeight - (points[1] - pageY) + }; + const outliner = new FreeHighlightOutliner(point, [0, 0, pageWidth, pageHeight], 1, editor.#thickness / 2, true, 0.001); + for (let i = 0, ii = points.length; i < ii; i += 2) { + point.x = points[i] - pageX; + point.y = pageHeight - (points[i + 1] - pageY); + outliner.add(point); + } + const { + id, + clipPathId + } = parent.drawLayer.draw({ + bbox: [0, 0, 1, 1], + root: { + viewBox: "0 0 1 1", + fill: editor.color, + "fill-opacity": editor._defaultOpacity + }, + rootClass: { + highlight: true, + free: true + }, + path: { + d: outliner.toSVGPath() + } + }, true, true); + editor.#createFreeOutlines({ + highlightOutlines: outliner.getOutlines(), + highlightId: id, + clipPathId + }); + editor.#addToDrawLayer(); + } + return editor; + } + serialize(isForCopying = false) { + if (this.isEmpty() || isForCopying) { + return null; + } + if (this.deleted) { + return this.serializeDeleted(); + } + const rect = this.getRect(0, 0); + const color = AnnotationEditor._colorManager.convert(this.color); + const serialized = { + annotationType: AnnotationEditorType.HIGHLIGHT, + color, + opacity: this.#opacity, + thickness: this.#thickness, + quadPoints: this.#serializeBoxes(), + outlines: this.#serializeOutlines(rect), + pageIndex: this.pageIndex, + rect, + rotation: this.#getRotation(), + structTreeParentId: this._structTreeParentId + }; + if (this.annotationElementId && !this.#hasElementChanged(serialized)) { + return null; + } + serialized.id = this.annotationElementId; + return serialized; + } + #hasElementChanged(serialized) { + const { + color + } = this._initialData; + return serialized.color.some((c, i) => c !== color[i]); + } + renderAnnotationElement(annotation) { + annotation.updateEdited({ + rect: this.getRect(0, 0) + }); + return null; + } + static canCreateNewEmptyEditor() { + return false; + } +} + +;// ./src/display/editor/draw.js + + + +class DrawingOptions { + #svgProperties = Object.create(null); + updateProperty(name, value) { + this[name] = value; + this.updateSVGProperty(name, value); + } + updateProperties(properties) { + if (!properties) { + return; + } + for (const [name, value] of Object.entries(properties)) { + this.updateProperty(name, value); + } + } + updateSVGProperty(name, value) { + this.#svgProperties[name] = value; + } + toSVGProperties() { + const root = this.#svgProperties; + this.#svgProperties = Object.create(null); + return { + root + }; + } + reset() { + this.#svgProperties = Object.create(null); + } + updateAll(options = this) { + this.updateProperties(options); + } + clone() { + unreachable("Not implemented"); + } +} +class DrawingEditor extends AnnotationEditor { + #drawOutlines = null; + #mustBeCommitted; + _drawId = null; + static _currentDrawId = -1; + static _currentParent = null; + static #currentDraw = null; + static #currentDrawingAC = null; + static #currentDrawingOptions = null; + static #currentPointerId = NaN; + static #currentPointerType = null; + static #currentPointerIds = null; + static #currentMoveTimestamp = NaN; + static _INNER_MARGIN = 3; + constructor(params) { + super(params); + this.#mustBeCommitted = params.mustBeCommitted || false; + if (params.drawOutlines) { + this.#createDrawOutlines(params); + this.#addToDrawLayer(); + } + } + #createDrawOutlines({ + drawOutlines, + drawId, + drawingOptions + }) { + this.#drawOutlines = drawOutlines; + this._drawingOptions ||= drawingOptions; + if (drawId >= 0) { + this._drawId = drawId; + this.parent.drawLayer.finalizeDraw(drawId, drawOutlines.defaultProperties); + } else { + this._drawId = this.#createDrawing(drawOutlines, this.parent); + } + this.#updateBbox(drawOutlines.box); + } + #createDrawing(drawOutlines, parent) { + const { + id + } = parent.drawLayer.draw(DrawingEditor._mergeSVGProperties(this._drawingOptions.toSVGProperties(), drawOutlines.defaultSVGProperties), false, false); + return id; + } + static _mergeSVGProperties(p1, p2) { + const p1Keys = new Set(Object.keys(p1)); + for (const [key, value] of Object.entries(p2)) { + if (p1Keys.has(key)) { + Object.assign(p1[key], value); + } else { + p1[key] = value; + } + } + return p1; + } + static getDefaultDrawingOptions(_options) { + unreachable("Not implemented"); + } + static get typesMap() { + unreachable("Not implemented"); + } + static get isDrawer() { + return true; + } + static get supportMultipleDrawings() { + return false; + } + static updateDefaultParams(type, value) { + const propertyName = this.typesMap.get(type); + if (propertyName) { + this._defaultDrawingOptions.updateProperty(propertyName, value); + } + if (this._currentParent) { + DrawingEditor.#currentDraw.updateProperty(propertyName, value); + this._currentParent.drawLayer.updateProperties(this._currentDrawId, this._defaultDrawingOptions.toSVGProperties()); + } + } + updateParams(type, value) { + const propertyName = this.constructor.typesMap.get(type); + if (propertyName) { + this._updateProperty(type, propertyName, value); + } + } + static get defaultPropertiesToUpdate() { + const properties = []; + const options = this._defaultDrawingOptions; + for (const [type, name] of this.typesMap) { + properties.push([type, options[name]]); + } + return properties; + } + get propertiesToUpdate() { + const properties = []; + const { + _drawingOptions + } = this; + for (const [type, name] of this.constructor.typesMap) { + properties.push([type, _drawingOptions[name]]); + } + return properties; + } + _updateProperty(type, name, value) { + const options = this._drawingOptions; + const savedValue = options[name]; + const setter = val => { + options.updateProperty(name, val); + const bbox = this.#drawOutlines.updateProperty(name, val); + if (bbox) { + this.#updateBbox(bbox); + } + this.parent?.drawLayer.updateProperties(this._drawId, options.toSVGProperties()); + }; + this.addCommands({ + cmd: setter.bind(this, value), + undo: setter.bind(this, savedValue), + post: this._uiManager.updateUI.bind(this._uiManager, this), + mustExec: true, + type, + overwriteIfSameType: true, + keepUndo: true + }); + } + _onResizing() { + this.parent?.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties(this.#drawOutlines.getPathResizingSVGProperties(this.#convertToDrawSpace()), { + bbox: this.#rotateBox() + })); + } + _onResized() { + this.parent?.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties(this.#drawOutlines.getPathResizedSVGProperties(this.#convertToDrawSpace()), { + bbox: this.#rotateBox() + })); + } + _onTranslating(x, y) { + this.parent?.drawLayer.updateProperties(this._drawId, { + bbox: this.#rotateBox(x, y) + }); + } + _onTranslated() { + this.parent?.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties(this.#drawOutlines.getPathTranslatedSVGProperties(this.#convertToDrawSpace(), this.parentDimensions), { + bbox: this.#rotateBox() + })); + } + _onStartDragging() { + this.parent?.drawLayer.updateProperties(this._drawId, { + rootClass: { + moving: true + } + }); + } + _onStopDragging() { + this.parent?.drawLayer.updateProperties(this._drawId, { + rootClass: { + moving: false + } + }); + } + commit() { + super.commit(); + this.disableEditMode(); + this.disableEditing(); + } + disableEditing() { + super.disableEditing(); + this.div.classList.toggle("disabled", true); + } + enableEditing() { + super.enableEditing(); + this.div.classList.toggle("disabled", false); + } + getBaseTranslation() { + return [0, 0]; + } + get isResizable() { + return true; + } + onceAdded(focus) { + if (!this.annotationElementId) { + this.parent.addUndoableEditor(this); + } + this._isDraggable = true; + if (this.#mustBeCommitted) { + this.#mustBeCommitted = false; + this.commit(); + this.parent.setSelected(this); + if (focus && this.isOnScreen) { + this.div.focus(); + } + } + } + remove() { + this.#cleanDrawLayer(); + super.remove(); + } + rebuild() { + if (!this.parent) { + return; + } + super.rebuild(); + if (this.div === null) { + return; + } + this.#addToDrawLayer(); + this.#updateBbox(this.#drawOutlines.box); + if (!this.isAttachedToDOM) { + this.parent.add(this); + } + } + setParent(parent) { + let mustBeSelected = false; + if (this.parent && !parent) { + this._uiManager.removeShouldRescale(this); + this.#cleanDrawLayer(); + } else if (parent) { + this._uiManager.addShouldRescale(this); + this.#addToDrawLayer(parent); + mustBeSelected = !this.parent && this.div?.classList.contains("selectedEditor"); + } + super.setParent(parent); + if (mustBeSelected) { + this.select(); + } + } + #cleanDrawLayer() { + if (this._drawId === null || !this.parent) { + return; + } + this.parent.drawLayer.remove(this._drawId); + this._drawId = null; + this._drawingOptions.reset(); + } + #addToDrawLayer(parent = this.parent) { + if (this._drawId !== null && this.parent === parent) { + return; + } + if (this._drawId !== null) { + this.parent.drawLayer.updateParent(this._drawId, parent.drawLayer); + return; + } + this._drawingOptions.updateAll(); + this._drawId = this.#createDrawing(this.#drawOutlines, parent); + } + #convertToParentSpace([x, y, width, height]) { + const { + parentDimensions: [pW, pH], + rotation + } = this; + switch (rotation) { + case 90: + return [y, 1 - x, width * (pH / pW), height * (pW / pH)]; + case 180: + return [1 - x, 1 - y, width, height]; + case 270: + return [1 - y, x, width * (pH / pW), height * (pW / pH)]; + default: + return [x, y, width, height]; + } + } + #convertToDrawSpace() { + const { + x, + y, + width, + height, + parentDimensions: [pW, pH], + rotation + } = this; + switch (rotation) { + case 90: + return [1 - y, x, width * (pW / pH), height * (pH / pW)]; + case 180: + return [1 - x, 1 - y, width, height]; + case 270: + return [y, 1 - x, width * (pW / pH), height * (pH / pW)]; + default: + return [x, y, width, height]; + } + } + #updateBbox(bbox) { + [this.x, this.y, this.width, this.height] = this.#convertToParentSpace(bbox); + if (this.div) { + this.fixAndSetPosition(); + const [parentWidth, parentHeight] = this.parentDimensions; + this.setDims(this.width * parentWidth, this.height * parentHeight); + } + this._onResized(); + } + #rotateBox() { + const { + x, + y, + width, + height, + rotation, + parentRotation, + parentDimensions: [pW, pH] + } = this; + switch ((rotation * 4 + parentRotation) / 90) { + case 1: + return [1 - y - height, x, height, width]; + case 2: + return [1 - x - width, 1 - y - height, width, height]; + case 3: + return [y, 1 - x - width, height, width]; + case 4: + return [x, y - width * (pW / pH), height * (pH / pW), width * (pW / pH)]; + case 5: + return [1 - y, x, width * (pW / pH), height * (pH / pW)]; + case 6: + return [1 - x - height * (pH / pW), 1 - y, height * (pH / pW), width * (pW / pH)]; + case 7: + return [y - width * (pW / pH), 1 - x - height * (pH / pW), width * (pW / pH), height * (pH / pW)]; + case 8: + return [x - width, y - height, width, height]; + case 9: + return [1 - y, x - width, height, width]; + case 10: + return [1 - x, 1 - y, width, height]; + case 11: + return [y - height, 1 - x, height, width]; + case 12: + return [x - height * (pH / pW), y, height * (pH / pW), width * (pW / pH)]; + case 13: + return [1 - y - width * (pW / pH), x - height * (pH / pW), width * (pW / pH), height * (pH / pW)]; + case 14: + return [1 - x, 1 - y - width * (pW / pH), height * (pH / pW), width * (pW / pH)]; + case 15: + return [y, 1 - x, width * (pW / pH), height * (pH / pW)]; + default: + return [x, y, width, height]; + } + } + rotate() { + if (!this.parent) { + return; + } + this.parent.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties({ + bbox: this.#rotateBox() + }, this.#drawOutlines.updateRotation((this.parentRotation - this.rotation + 360) % 360))); + } + onScaleChanging() { + if (!this.parent) { + return; + } + this.#updateBbox(this.#drawOutlines.updateParentDimensions(this.parentDimensions, this.parent.scale)); + } + static onScaleChangingWhenDrawing() {} + render() { + if (this.div) { + return this.div; + } + const div = super.render(); + div.classList.add("draw"); + const drawDiv = document.createElement("div"); + div.append(drawDiv); + drawDiv.setAttribute("aria-hidden", "true"); + drawDiv.className = "internal"; + const [parentWidth, parentHeight] = this.parentDimensions; + this.setDims(this.width * parentWidth, this.height * parentHeight); + this._uiManager.addShouldRescale(this); + this.disableEditing(); + return div; + } + static createDrawerInstance(_x, _y, _parentWidth, _parentHeight, _rotation) { + unreachable("Not implemented"); + } + static startDrawing(parent, uiManager, _isLTR, event) { + const { + target, + offsetX: x, + offsetY: y, + pointerId, + pointerType + } = event; + if (DrawingEditor.#currentPointerType && DrawingEditor.#currentPointerType !== pointerType) { + return; + } + const { + viewport: { + rotation + } + } = parent; + const { + width: parentWidth, + height: parentHeight + } = target.getBoundingClientRect(); + const ac = DrawingEditor.#currentDrawingAC = new AbortController(); + const signal = parent.combinedSignal(ac); + DrawingEditor.#currentPointerId ||= pointerId; + DrawingEditor.#currentPointerType ??= pointerType; + window.addEventListener("pointerup", e => { + if (DrawingEditor.#currentPointerId === e.pointerId) { + this._endDraw(e); + } else { + DrawingEditor.#currentPointerIds?.delete(e.pointerId); + } + }, { + signal + }); + window.addEventListener("pointercancel", e => { + if (DrawingEditor.#currentPointerId === e.pointerId) { + this._currentParent.endDrawingSession(); + } else { + DrawingEditor.#currentPointerIds?.delete(e.pointerId); + } + }, { + signal + }); + window.addEventListener("pointerdown", e => { + if (DrawingEditor.#currentPointerType !== e.pointerType) { + return; + } + (DrawingEditor.#currentPointerIds ||= new Set()).add(e.pointerId); + if (DrawingEditor.#currentDraw.isCancellable()) { + DrawingEditor.#currentDraw.removeLastElement(); + if (DrawingEditor.#currentDraw.isEmpty()) { + this._currentParent.endDrawingSession(true); + } else { + this._endDraw(null); + } + } + }, { + capture: true, + passive: false, + signal + }); + window.addEventListener("contextmenu", noContextMenu, { + signal + }); + target.addEventListener("pointermove", this._drawMove.bind(this), { + signal + }); + target.addEventListener("touchmove", e => { + if (e.timeStamp === DrawingEditor.#currentMoveTimestamp) { + stopEvent(e); + } + }, { + signal + }); + parent.toggleDrawing(); + uiManager._editorUndoBar?.hide(); + if (DrawingEditor.#currentDraw) { + parent.drawLayer.updateProperties(this._currentDrawId, DrawingEditor.#currentDraw.startNew(x, y, parentWidth, parentHeight, rotation)); + return; + } + uiManager.updateUIForDefaultProperties(this); + DrawingEditor.#currentDraw = this.createDrawerInstance(x, y, parentWidth, parentHeight, rotation); + DrawingEditor.#currentDrawingOptions = this.getDefaultDrawingOptions(); + this._currentParent = parent; + ({ + id: this._currentDrawId + } = parent.drawLayer.draw(this._mergeSVGProperties(DrawingEditor.#currentDrawingOptions.toSVGProperties(), DrawingEditor.#currentDraw.defaultSVGProperties), true, false)); + } + static _drawMove(event) { + DrawingEditor.#currentMoveTimestamp = -1; + if (!DrawingEditor.#currentDraw) { + return; + } + const { + offsetX, + offsetY, + pointerId + } = event; + if (DrawingEditor.#currentPointerId !== pointerId) { + return; + } + if (DrawingEditor.#currentPointerIds?.size >= 1) { + this._endDraw(event); + return; + } + this._currentParent.drawLayer.updateProperties(this._currentDrawId, DrawingEditor.#currentDraw.add(offsetX, offsetY)); + DrawingEditor.#currentMoveTimestamp = event.timeStamp; + stopEvent(event); + } + static _cleanup(all) { + if (all) { + this._currentDrawId = -1; + this._currentParent = null; + DrawingEditor.#currentDraw = null; + DrawingEditor.#currentDrawingOptions = null; + DrawingEditor.#currentPointerType = null; + DrawingEditor.#currentMoveTimestamp = NaN; + } + if (DrawingEditor.#currentDrawingAC) { + DrawingEditor.#currentDrawingAC.abort(); + DrawingEditor.#currentDrawingAC = null; + DrawingEditor.#currentPointerId = NaN; + DrawingEditor.#currentPointerIds = null; + } + } + static _endDraw(event) { + const parent = this._currentParent; + if (!parent) { + return; + } + parent.toggleDrawing(true); + this._cleanup(false); + if (event) { + parent.drawLayer.updateProperties(this._currentDrawId, DrawingEditor.#currentDraw.end(event.offsetX, event.offsetY)); + } + if (this.supportMultipleDrawings) { + const draw = DrawingEditor.#currentDraw; + const drawId = this._currentDrawId; + const lastElement = draw.getLastElement(); + parent.addCommands({ + cmd: () => { + parent.drawLayer.updateProperties(drawId, draw.setLastElement(lastElement)); + }, + undo: () => { + parent.drawLayer.updateProperties(drawId, draw.removeLastElement()); + }, + mustExec: false, + type: AnnotationEditorParamsType.DRAW_STEP + }); + return; + } + this.endDrawing(false); + } + static endDrawing(isAborted) { + const parent = this._currentParent; + if (!parent) { + return null; + } + parent.toggleDrawing(true); + parent.cleanUndoStack(AnnotationEditorParamsType.DRAW_STEP); + if (!DrawingEditor.#currentDraw.isEmpty()) { + const { + pageDimensions: [pageWidth, pageHeight], + scale + } = parent; + const editor = parent.createAndAddNewEditor({ + offsetX: 0, + offsetY: 0 + }, false, { + drawId: this._currentDrawId, + drawOutlines: DrawingEditor.#currentDraw.getOutlines(pageWidth * scale, pageHeight * scale, scale, this._INNER_MARGIN), + drawingOptions: DrawingEditor.#currentDrawingOptions, + mustBeCommitted: !isAborted + }); + this._cleanup(true); + return editor; + } + parent.drawLayer.remove(this._currentDrawId); + this._cleanup(true); + return null; + } + createDrawingOptions(_data) {} + static deserializeDraw(_pageX, _pageY, _pageWidth, _pageHeight, _innerWidth, _data) { + unreachable("Not implemented"); + } + static async deserialize(data, parent, uiManager) { + const { + rawDims: { + pageWidth, + pageHeight, + pageX, + pageY + } + } = parent.viewport; + const drawOutlines = this.deserializeDraw(pageX, pageY, pageWidth, pageHeight, this._INNER_MARGIN, data); + const editor = await super.deserialize(data, parent, uiManager); + editor.createDrawingOptions(data); + editor.#createDrawOutlines({ + drawOutlines + }); + editor.#addToDrawLayer(); + editor.onScaleChanging(); + editor.rotate(); + return editor; + } + serializeDraw(isForCopying) { + const [pageX, pageY] = this.pageTranslation; + const [pageWidth, pageHeight] = this.pageDimensions; + return this.#drawOutlines.serialize([pageX, pageY, pageWidth, pageHeight], isForCopying); + } + renderAnnotationElement(annotation) { + annotation.updateEdited({ + rect: this.getRect(0, 0) + }); + return null; + } + static canCreateNewEmptyEditor() { + return false; + } +} + +;// ./src/display/editor/drawers/inkdraw.js + + +class InkDrawOutliner { + #last = new Float64Array(6); + #line; + #lines; + #rotation; + #thickness; + #points; + #lastSVGPath = ""; + #lastIndex = 0; + #outlines = new InkDrawOutline(); + #parentWidth; + #parentHeight; + constructor(x, y, parentWidth, parentHeight, rotation, thickness) { + this.#parentWidth = parentWidth; + this.#parentHeight = parentHeight; + this.#rotation = rotation; + this.#thickness = thickness; + [x, y] = this.#normalizePoint(x, y); + const line = this.#line = [NaN, NaN, NaN, NaN, x, y]; + this.#points = [x, y]; + this.#lines = [{ + line, + points: this.#points + }]; + this.#last.set(line, 0); + } + updateProperty(name, value) { + if (name === "stroke-width") { + this.#thickness = value; + } + } + #normalizePoint(x, y) { + return Outline._normalizePoint(x, y, this.#parentWidth, this.#parentHeight, this.#rotation); + } + isEmpty() { + return !this.#lines || this.#lines.length === 0; + } + isCancellable() { + return this.#points.length <= 10; + } + add(x, y) { + [x, y] = this.#normalizePoint(x, y); + const [x1, y1, x2, y2] = this.#last.subarray(2, 6); + const diffX = x - x2; + const diffY = y - y2; + const d = Math.hypot(this.#parentWidth * diffX, this.#parentHeight * diffY); + if (d <= 2) { + return null; + } + this.#points.push(x, y); + if (isNaN(x1)) { + this.#last.set([x2, y2, x, y], 2); + this.#line.push(NaN, NaN, NaN, NaN, x, y); + return { + path: { + d: this.toSVGPath() + } + }; + } + if (isNaN(this.#last[0])) { + this.#line.splice(6, 6); + } + this.#last.set([x1, y1, x2, y2, x, y], 0); + this.#line.push(...Outline.createBezierPoints(x1, y1, x2, y2, x, y)); + return { + path: { + d: this.toSVGPath() + } + }; + } + end(x, y) { + const change = this.add(x, y); + if (change) { + return change; + } + if (this.#points.length === 2) { + return { + path: { + d: this.toSVGPath() + } + }; + } + return null; + } + startNew(x, y, parentWidth, parentHeight, rotation) { + this.#parentWidth = parentWidth; + this.#parentHeight = parentHeight; + this.#rotation = rotation; + [x, y] = this.#normalizePoint(x, y); + const line = this.#line = [NaN, NaN, NaN, NaN, x, y]; + this.#points = [x, y]; + const last = this.#lines.at(-1); + if (last) { + last.line = new Float32Array(last.line); + last.points = new Float32Array(last.points); + } + this.#lines.push({ + line, + points: this.#points + }); + this.#last.set(line, 0); + this.#lastIndex = 0; + this.toSVGPath(); + return null; + } + getLastElement() { + return this.#lines.at(-1); + } + setLastElement(element) { + if (!this.#lines) { + return this.#outlines.setLastElement(element); + } + this.#lines.push(element); + this.#line = element.line; + this.#points = element.points; + this.#lastIndex = 0; + return { + path: { + d: this.toSVGPath() + } + }; + } + removeLastElement() { + if (!this.#lines) { + return this.#outlines.removeLastElement(); + } + this.#lines.pop(); + this.#lastSVGPath = ""; + for (let i = 0, ii = this.#lines.length; i < ii; i++) { + const { + line, + points + } = this.#lines[i]; + this.#line = line; + this.#points = points; + this.#lastIndex = 0; + this.toSVGPath(); + } + return { + path: { + d: this.#lastSVGPath + } + }; + } + toSVGPath() { + const firstX = Outline.svgRound(this.#line[4]); + const firstY = Outline.svgRound(this.#line[5]); + if (this.#points.length === 2) { + this.#lastSVGPath = `${this.#lastSVGPath} M ${firstX} ${firstY} Z`; + return this.#lastSVGPath; + } + if (this.#points.length <= 6) { + const i = this.#lastSVGPath.lastIndexOf("M"); + this.#lastSVGPath = `${this.#lastSVGPath.slice(0, i)} M ${firstX} ${firstY}`; + this.#lastIndex = 6; + } + if (this.#points.length === 4) { + const secondX = Outline.svgRound(this.#line[10]); + const secondY = Outline.svgRound(this.#line[11]); + this.#lastSVGPath = `${this.#lastSVGPath} L ${secondX} ${secondY}`; + this.#lastIndex = 12; + return this.#lastSVGPath; + } + const buffer = []; + if (this.#lastIndex === 0) { + buffer.push(`M ${firstX} ${firstY}`); + this.#lastIndex = 6; + } + for (let i = this.#lastIndex, ii = this.#line.length; i < ii; i += 6) { + const [c1x, c1y, c2x, c2y, x, y] = this.#line.slice(i, i + 6).map(Outline.svgRound); + buffer.push(`C${c1x} ${c1y} ${c2x} ${c2y} ${x} ${y}`); + } + this.#lastSVGPath += buffer.join(" "); + this.#lastIndex = this.#line.length; + return this.#lastSVGPath; + } + getOutlines(parentWidth, parentHeight, scale, innerMargin) { + const last = this.#lines.at(-1); + last.line = new Float32Array(last.line); + last.points = new Float32Array(last.points); + this.#outlines.build(this.#lines, parentWidth, parentHeight, scale, this.#rotation, this.#thickness, innerMargin); + this.#last = null; + this.#line = null; + this.#lines = null; + this.#lastSVGPath = null; + return this.#outlines; + } + get defaultSVGProperties() { + return { + root: { + viewBox: "0 0 10000 10000" + }, + rootClass: { + draw: true + }, + bbox: [0, 0, 1, 1] + }; + } +} +class InkDrawOutline extends Outline { + #bbox; + #currentRotation = 0; + #innerMargin; + #lines; + #parentWidth; + #parentHeight; + #parentScale; + #rotation; + #thickness; + build(lines, parentWidth, parentHeight, parentScale, rotation, thickness, innerMargin) { + this.#parentWidth = parentWidth; + this.#parentHeight = parentHeight; + this.#parentScale = parentScale; + this.#rotation = rotation; + this.#thickness = thickness; + this.#innerMargin = innerMargin ?? 0; + this.#lines = lines; + this.#computeBbox(); + } + setLastElement(element) { + this.#lines.push(element); + return { + path: { + d: this.toSVGPath() + } + }; + } + removeLastElement() { + this.#lines.pop(); + return { + path: { + d: this.toSVGPath() + } + }; + } + toSVGPath() { + const buffer = []; + for (const { + line + } of this.#lines) { + buffer.push(`M${Outline.svgRound(line[4])} ${Outline.svgRound(line[5])}`); + if (line.length === 6) { + buffer.push("Z"); + continue; + } + if (line.length === 12) { + buffer.push(`L${Outline.svgRound(line[10])} ${Outline.svgRound(line[11])}`); + continue; + } + for (let i = 6, ii = line.length; i < ii; i += 6) { + const [c1x, c1y, c2x, c2y, x, y] = line.subarray(i, i + 6).map(Outline.svgRound); + buffer.push(`C${c1x} ${c1y} ${c2x} ${c2y} ${x} ${y}`); + } + } + return buffer.join(""); + } + serialize([pageX, pageY, pageWidth, pageHeight], isForCopying) { + const serializedLines = []; + const serializedPoints = []; + const [x, y, width, height] = this.#getBBoxWithNoMargin(); + let tx, ty, sx, sy, x1, y1, x2, y2, rescaleFn; + switch (this.#rotation) { + case 0: + rescaleFn = Outline._rescale; + tx = pageX; + ty = pageY + pageHeight; + sx = pageWidth; + sy = -pageHeight; + x1 = pageX + x * pageWidth; + y1 = pageY + (1 - y - height) * pageHeight; + x2 = pageX + (x + width) * pageWidth; + y2 = pageY + (1 - y) * pageHeight; + break; + case 90: + rescaleFn = Outline._rescaleAndSwap; + tx = pageX; + ty = pageY; + sx = pageWidth; + sy = pageHeight; + x1 = pageX + y * pageWidth; + y1 = pageY + x * pageHeight; + x2 = pageX + (y + height) * pageWidth; + y2 = pageY + (x + width) * pageHeight; + break; + case 180: + rescaleFn = Outline._rescale; + tx = pageX + pageWidth; + ty = pageY; + sx = -pageWidth; + sy = pageHeight; + x1 = pageX + (1 - x - width) * pageWidth; + y1 = pageY + y * pageHeight; + x2 = pageX + (1 - x) * pageWidth; + y2 = pageY + (y + height) * pageHeight; + break; + case 270: + rescaleFn = Outline._rescaleAndSwap; + tx = pageX + pageWidth; + ty = pageY + pageHeight; + sx = -pageWidth; + sy = -pageHeight; + x1 = pageX + (1 - y - height) * pageWidth; + y1 = pageY + (1 - x - width) * pageHeight; + x2 = pageX + (1 - y) * pageWidth; + y2 = pageY + (1 - x) * pageHeight; + break; + } + for (const { + line, + points + } of this.#lines) { + serializedLines.push(rescaleFn(line, tx, ty, sx, sy, isForCopying ? new Array(line.length) : null)); + serializedPoints.push(rescaleFn(points, tx, ty, sx, sy, isForCopying ? new Array(points.length) : null)); + } + return { + lines: serializedLines, + points: serializedPoints, + rect: [x1, y1, x2, y2] + }; + } + static deserialize(pageX, pageY, pageWidth, pageHeight, innerMargin, { + paths: { + lines, + points + }, + rotation, + thickness + }) { + const newLines = []; + let tx, ty, sx, sy, rescaleFn; + switch (rotation) { + case 0: + rescaleFn = Outline._rescale; + tx = -pageX / pageWidth; + ty = pageY / pageHeight + 1; + sx = 1 / pageWidth; + sy = -1 / pageHeight; + break; + case 90: + rescaleFn = Outline._rescaleAndSwap; + tx = -pageY / pageHeight; + ty = -pageX / pageWidth; + sx = 1 / pageHeight; + sy = 1 / pageWidth; + break; + case 180: + rescaleFn = Outline._rescale; + tx = pageX / pageWidth + 1; + ty = -pageY / pageHeight; + sx = -1 / pageWidth; + sy = 1 / pageHeight; + break; + case 270: + rescaleFn = Outline._rescaleAndSwap; + tx = pageY / pageHeight + 1; + ty = pageX / pageWidth + 1; + sx = -1 / pageHeight; + sy = -1 / pageWidth; + break; + } + if (!lines) { + lines = []; + for (const point of points) { + const len = point.length; + if (len === 2) { + lines.push(new Float32Array([NaN, NaN, NaN, NaN, point[0], point[1]])); + continue; + } + if (len === 4) { + lines.push(new Float32Array([NaN, NaN, NaN, NaN, point[0], point[1], NaN, NaN, NaN, NaN, point[2], point[3]])); + continue; + } + const line = new Float32Array(3 * (len - 2)); + lines.push(line); + let [x1, y1, x2, y2] = point.subarray(0, 4); + line.set([NaN, NaN, NaN, NaN, x1, y1], 0); + for (let i = 4; i < len; i += 2) { + const x = point[i]; + const y = point[i + 1]; + line.set(Outline.createBezierPoints(x1, y1, x2, y2, x, y), (i - 2) * 3); + [x1, y1, x2, y2] = [x2, y2, x, y]; + } + } + } + for (let i = 0, ii = lines.length; i < ii; i++) { + newLines.push({ + line: rescaleFn(lines[i].map(x => x ?? NaN), tx, ty, sx, sy), + points: rescaleFn(points[i].map(x => x ?? NaN), tx, ty, sx, sy) + }); + } + const outlines = new InkDrawOutline(); + outlines.build(newLines, pageWidth, pageHeight, 1, rotation, thickness, innerMargin); + return outlines; + } + #getMarginComponents(thickness = this.#thickness) { + const margin = this.#innerMargin + thickness / 2 * this.#parentScale; + return this.#rotation % 180 === 0 ? [margin / this.#parentWidth, margin / this.#parentHeight] : [margin / this.#parentHeight, margin / this.#parentWidth]; + } + #getBBoxWithNoMargin() { + const [x, y, width, height] = this.#bbox; + const [marginX, marginY] = this.#getMarginComponents(0); + return [x + marginX, y + marginY, width - 2 * marginX, height - 2 * marginY]; + } + #computeBbox() { + const bbox = this.#bbox = new Float32Array([Infinity, Infinity, -Infinity, -Infinity]); + for (const { + line + } of this.#lines) { + if (line.length <= 12) { + for (let i = 4, ii = line.length; i < ii; i += 6) { + const [x, y] = line.subarray(i, i + 2); + bbox[0] = Math.min(bbox[0], x); + bbox[1] = Math.min(bbox[1], y); + bbox[2] = Math.max(bbox[2], x); + bbox[3] = Math.max(bbox[3], y); + } + continue; + } + let lastX = line[4], + lastY = line[5]; + for (let i = 6, ii = line.length; i < ii; i += 6) { + const [c1x, c1y, c2x, c2y, x, y] = line.subarray(i, i + 6); + Util.bezierBoundingBox(lastX, lastY, c1x, c1y, c2x, c2y, x, y, bbox); + lastX = x; + lastY = y; + } + } + const [marginX, marginY] = this.#getMarginComponents(); + bbox[0] = Math.min(1, Math.max(0, bbox[0] - marginX)); + bbox[1] = Math.min(1, Math.max(0, bbox[1] - marginY)); + bbox[2] = Math.min(1, Math.max(0, bbox[2] + marginX)); + bbox[3] = Math.min(1, Math.max(0, bbox[3] + marginY)); + bbox[2] -= bbox[0]; + bbox[3] -= bbox[1]; + } + get box() { + return this.#bbox; + } + updateProperty(name, value) { + if (name === "stroke-width") { + return this.#updateThickness(value); + } + return null; + } + #updateThickness(thickness) { + const [oldMarginX, oldMarginY] = this.#getMarginComponents(); + this.#thickness = thickness; + const [newMarginX, newMarginY] = this.#getMarginComponents(); + const [diffMarginX, diffMarginY] = [newMarginX - oldMarginX, newMarginY - oldMarginY]; + const bbox = this.#bbox; + bbox[0] -= diffMarginX; + bbox[1] -= diffMarginY; + bbox[2] += 2 * diffMarginX; + bbox[3] += 2 * diffMarginY; + return bbox; + } + updateParentDimensions([width, height], scale) { + const [oldMarginX, oldMarginY] = this.#getMarginComponents(); + this.#parentWidth = width; + this.#parentHeight = height; + this.#parentScale = scale; + const [newMarginX, newMarginY] = this.#getMarginComponents(); + const diffMarginX = newMarginX - oldMarginX; + const diffMarginY = newMarginY - oldMarginY; + const bbox = this.#bbox; + bbox[0] -= diffMarginX; + bbox[1] -= diffMarginY; + bbox[2] += 2 * diffMarginX; + bbox[3] += 2 * diffMarginY; + return bbox; + } + updateRotation(rotation) { + this.#currentRotation = rotation; + return { + path: { + transform: this.rotationTransform + } + }; + } + get viewBox() { + return this.#bbox.map(Outline.svgRound).join(" "); + } + get defaultProperties() { + const [x, y] = this.#bbox; + return { + root: { + viewBox: this.viewBox + }, + path: { + "transform-origin": `${Outline.svgRound(x)} ${Outline.svgRound(y)}` + } + }; + } + get rotationTransform() { + const [,, width, height] = this.#bbox; + let a = 0, + b = 0, + c = 0, + d = 0, + e = 0, + f = 0; + switch (this.#currentRotation) { + case 90: + b = height / width; + c = -width / height; + e = width; + break; + case 180: + a = -1; + d = -1; + e = width; + f = height; + break; + case 270: + b = -height / width; + c = width / height; + f = height; + break; + default: + return ""; + } + return `matrix(${a} ${b} ${c} ${d} ${Outline.svgRound(e)} ${Outline.svgRound(f)})`; + } + getPathResizingSVGProperties([newX, newY, newWidth, newHeight]) { + const [marginX, marginY] = this.#getMarginComponents(); + const [x, y, width, height] = this.#bbox; + if (Math.abs(width - marginX) <= Outline.PRECISION || Math.abs(height - marginY) <= Outline.PRECISION) { + const tx = newX + newWidth / 2 - (x + width / 2); + const ty = newY + newHeight / 2 - (y + height / 2); + return { + path: { + "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`, + transform: `${this.rotationTransform} translate(${tx} ${ty})` + } + }; + } + const s1x = (newWidth - 2 * marginX) / (width - 2 * marginX); + const s1y = (newHeight - 2 * marginY) / (height - 2 * marginY); + const s2x = width / newWidth; + const s2y = height / newHeight; + return { + path: { + "transform-origin": `${Outline.svgRound(x)} ${Outline.svgRound(y)}`, + transform: `${this.rotationTransform} scale(${s2x} ${s2y}) ` + `translate(${Outline.svgRound(marginX)} ${Outline.svgRound(marginY)}) scale(${s1x} ${s1y}) ` + `translate(${Outline.svgRound(-marginX)} ${Outline.svgRound(-marginY)})` + } + }; + } + getPathResizedSVGProperties([newX, newY, newWidth, newHeight]) { + const [marginX, marginY] = this.#getMarginComponents(); + const bbox = this.#bbox; + const [x, y, width, height] = bbox; + bbox[0] = newX; + bbox[1] = newY; + bbox[2] = newWidth; + bbox[3] = newHeight; + if (Math.abs(width - marginX) <= Outline.PRECISION || Math.abs(height - marginY) <= Outline.PRECISION) { + const tx = newX + newWidth / 2 - (x + width / 2); + const ty = newY + newHeight / 2 - (y + height / 2); + for (const { + line, + points + } of this.#lines) { + Outline._translate(line, tx, ty, line); + Outline._translate(points, tx, ty, points); + } + return { + root: { + viewBox: this.viewBox + }, + path: { + "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`, + transform: this.rotationTransform || null, + d: this.toSVGPath() + } + }; + } + const s1x = (newWidth - 2 * marginX) / (width - 2 * marginX); + const s1y = (newHeight - 2 * marginY) / (height - 2 * marginY); + const tx = -s1x * (x + marginX) + newX + marginX; + const ty = -s1y * (y + marginY) + newY + marginY; + if (s1x !== 1 || s1y !== 1 || tx !== 0 || ty !== 0) { + for (const { + line, + points + } of this.#lines) { + Outline._rescale(line, tx, ty, s1x, s1y, line); + Outline._rescale(points, tx, ty, s1x, s1y, points); + } + } + return { + root: { + viewBox: this.viewBox + }, + path: { + "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`, + transform: this.rotationTransform || null, + d: this.toSVGPath() + } + }; + } + getPathTranslatedSVGProperties([newX, newY], parentDimensions) { + const [newParentWidth, newParentHeight] = parentDimensions; + const bbox = this.#bbox; + const tx = newX - bbox[0]; + const ty = newY - bbox[1]; + if (this.#parentWidth === newParentWidth && this.#parentHeight === newParentHeight) { + for (const { + line, + points + } of this.#lines) { + Outline._translate(line, tx, ty, line); + Outline._translate(points, tx, ty, points); + } + } else { + const sx = this.#parentWidth / newParentWidth; + const sy = this.#parentHeight / newParentHeight; + this.#parentWidth = newParentWidth; + this.#parentHeight = newParentHeight; + for (const { + line, + points + } of this.#lines) { + Outline._rescale(line, tx, ty, sx, sy, line); + Outline._rescale(points, tx, ty, sx, sy, points); + } + bbox[2] *= sx; + bbox[3] *= sy; + } + bbox[0] = newX; + bbox[1] = newY; + return { + root: { + viewBox: this.viewBox + }, + path: { + d: this.toSVGPath(), + "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}` + } + }; + } + get defaultSVGProperties() { + const bbox = this.#bbox; + return { + root: { + viewBox: this.viewBox + }, + rootClass: { + draw: true + }, + path: { + d: this.toSVGPath(), + "transform-origin": `${Outline.svgRound(bbox[0])} ${Outline.svgRound(bbox[1])}`, + transform: this.rotationTransform || null + }, + bbox + }; + } +} + +;// ./src/display/editor/ink.js + + + + + +class InkDrawingOptions extends DrawingOptions { + #viewParameters; + constructor(viewerParameters) { + super(); + this.#viewParameters = viewerParameters; + super.updateProperties({ + fill: "none", + stroke: AnnotationEditor._defaultLineColor, + "stroke-opacity": 1, + "stroke-width": 1, + "stroke-linecap": "round", + "stroke-linejoin": "round", + "stroke-miterlimit": 10 + }); + } + updateSVGProperty(name, value) { + if (name === "stroke-width") { + value ??= this["stroke-width"]; + value *= this.#viewParameters.realScale; + } + super.updateSVGProperty(name, value); + } + clone() { + const clone = new InkDrawingOptions(this.#viewParameters); + clone.updateAll(this); + return clone; + } +} +class InkEditor extends DrawingEditor { + static _type = "ink"; + static _editorType = AnnotationEditorType.INK; + static _defaultDrawingOptions = null; + constructor(params) { + super({ + ...params, + name: "inkEditor" + }); + this._willKeepAspectRatio = true; + } + static initialize(l10n, uiManager) { + AnnotationEditor.initialize(l10n, uiManager); + this._defaultDrawingOptions = new InkDrawingOptions(uiManager.viewParameters); + } + static getDefaultDrawingOptions(options) { + const clone = this._defaultDrawingOptions.clone(); + clone.updateProperties(options); + return clone; + } + static get supportMultipleDrawings() { + return true; + } + static get typesMap() { + return shadow(this, "typesMap", new Map([[AnnotationEditorParamsType.INK_THICKNESS, "stroke-width"], [AnnotationEditorParamsType.INK_COLOR, "stroke"], [AnnotationEditorParamsType.INK_OPACITY, "stroke-opacity"]])); + } + static createDrawerInstance(x, y, parentWidth, parentHeight, rotation) { + return new InkDrawOutliner(x, y, parentWidth, parentHeight, rotation, this._defaultDrawingOptions["stroke-width"]); + } + static deserializeDraw(pageX, pageY, pageWidth, pageHeight, innerMargin, data) { + return InkDrawOutline.deserialize(pageX, pageY, pageWidth, pageHeight, innerMargin, data); + } + static async deserialize(data, parent, uiManager) { + let initialData = null; + if (data instanceof InkAnnotationElement) { + const { + data: { + inkLists, + rect, + rotation, + id, + color, + opacity, + borderStyle: { + rawWidth: thickness + }, + popupRef + }, + parent: { + page: { + pageNumber + } + } + } = data; + initialData = data = { + annotationType: AnnotationEditorType.INK, + color: Array.from(color), + thickness, + opacity, + paths: { + points: inkLists + }, + boxes: null, + pageIndex: pageNumber - 1, + rect: rect.slice(0), + rotation, + id, + deleted: false, + popupRef + }; + } + const editor = await super.deserialize(data, parent, uiManager); + editor.annotationElementId = data.id || null; + editor._initialData = initialData; + return editor; + } + onScaleChanging() { + if (!this.parent) { + return; + } + super.onScaleChanging(); + const { + _drawId, + _drawingOptions, + parent + } = this; + _drawingOptions.updateSVGProperty("stroke-width"); + parent.drawLayer.updateProperties(_drawId, _drawingOptions.toSVGProperties()); + } + static onScaleChangingWhenDrawing() { + const parent = this._currentParent; + if (!parent) { + return; + } + super.onScaleChangingWhenDrawing(); + this._defaultDrawingOptions.updateSVGProperty("stroke-width"); + parent.drawLayer.updateProperties(this._currentDrawId, this._defaultDrawingOptions.toSVGProperties()); + } + createDrawingOptions({ + color, + thickness, + opacity + }) { + this._drawingOptions = InkEditor.getDefaultDrawingOptions({ + stroke: Util.makeHexColor(...color), + "stroke-width": thickness, + "stroke-opacity": opacity + }); + } + serialize(isForCopying = false) { + if (this.isEmpty()) { + return null; + } + if (this.deleted) { + return this.serializeDeleted(); + } + const { + lines, + points, + rect + } = this.serializeDraw(isForCopying); + const { + _drawingOptions: { + stroke, + "stroke-opacity": opacity, + "stroke-width": thickness + } + } = this; + const serialized = { + annotationType: AnnotationEditorType.INK, + color: AnnotationEditor._colorManager.convert(stroke), + opacity, + thickness, + paths: { + lines, + points + }, + pageIndex: this.pageIndex, + rect, + rotation: this.rotation, + structTreeParentId: this._structTreeParentId + }; + if (isForCopying) { + return serialized; + } + if (this.annotationElementId && !this.#hasElementChanged(serialized)) { + return null; + } + serialized.id = this.annotationElementId; + return serialized; + } + #hasElementChanged(serialized) { + const { + color, + thickness, + opacity, + pageIndex + } = this._initialData; + return this._hasBeenMoved || this._hasBeenResized || serialized.color.some((c, i) => c !== color[i]) || serialized.thickness !== thickness || serialized.opacity !== opacity || serialized.pageIndex !== pageIndex; + } + renderAnnotationElement(annotation) { + const { + points, + rect + } = this.serializeDraw(false); + annotation.updateEdited({ + rect, + thickness: this._drawingOptions["stroke-width"], + points + }); + return null; + } +} + +;// ./src/display/editor/stamp.js + + + + +class StampEditor extends AnnotationEditor { + #bitmap = null; + #bitmapId = null; + #bitmapPromise = null; + #bitmapUrl = null; + #bitmapFile = null; + #bitmapFileName = ""; + #canvas = null; + #resizeTimeoutId = null; + #isSvg = false; + #hasBeenAddedInUndoStack = false; + static _type = "stamp"; + static _editorType = AnnotationEditorType.STAMP; + constructor(params) { + super({ + ...params, + name: "stampEditor" + }); + this.#bitmapUrl = params.bitmapUrl; + this.#bitmapFile = params.bitmapFile; + } + static initialize(l10n, uiManager) { + AnnotationEditor.initialize(l10n, uiManager); + } + static get supportedTypes() { + const types = ["apng", "avif", "bmp", "gif", "jpeg", "png", "svg+xml", "webp", "x-icon"]; + return shadow(this, "supportedTypes", types.map(type => `image/${type}`)); + } + static get supportedTypesStr() { + return shadow(this, "supportedTypesStr", this.supportedTypes.join(",")); + } + static isHandlingMimeForPasting(mime) { + return this.supportedTypes.includes(mime); + } + static paste(item, parent) { + parent.pasteEditor(AnnotationEditorType.STAMP, { + bitmapFile: item.getAsFile() + }); + } + altTextFinish() { + if (this._uiManager.useNewAltTextFlow) { + this.div.hidden = false; + } + super.altTextFinish(); + } + get telemetryFinalData() { + return { + type: "stamp", + hasAltText: !!this.altTextData?.altText + }; + } + static computeTelemetryFinalData(data) { + const hasAltTextStats = data.get("hasAltText"); + return { + hasAltText: hasAltTextStats.get(true) ?? 0, + hasNoAltText: hasAltTextStats.get(false) ?? 0 + }; + } + #getBitmapFetched(data, fromId = false) { + if (!data) { + this.remove(); + return; + } + this.#bitmap = data.bitmap; + if (!fromId) { + this.#bitmapId = data.id; + this.#isSvg = data.isSvg; + } + if (data.file) { + this.#bitmapFileName = data.file.name; + } + this.#createCanvas(); + } + #getBitmapDone() { + this.#bitmapPromise = null; + this._uiManager.enableWaiting(false); + if (!this.#canvas) { + return; + } + if (this._uiManager.useNewAltTextWhenAddingImage && this._uiManager.useNewAltTextFlow && this.#bitmap) { + this._editToolbar.hide(); + this._uiManager.editAltText(this, true); + return; + } + if (!this._uiManager.useNewAltTextWhenAddingImage && this._uiManager.useNewAltTextFlow && this.#bitmap) { + this._reportTelemetry({ + action: "pdfjs.image.image_added", + data: { + alt_text_modal: false, + alt_text_type: "empty" + } + }); + try { + this.mlGuessAltText(); + } catch {} + } + this.div.focus(); + } + async mlGuessAltText(imageData = null, updateAltTextData = true) { + if (this.hasAltTextData()) { + return null; + } + const { + mlManager + } = this._uiManager; + if (!mlManager) { + throw new Error("No ML."); + } + if (!(await mlManager.isEnabledFor("altText"))) { + throw new Error("ML isn't enabled for alt text."); + } + const { + data, + width, + height + } = imageData || this.copyCanvas(null, null, true).imageData; + const response = await mlManager.guess({ + name: "altText", + request: { + data, + width, + height, + channels: data.length / (width * height) + } + }); + if (!response) { + throw new Error("No response from the AI service."); + } + if (response.error) { + throw new Error("Error from the AI service."); + } + if (response.cancel) { + return null; + } + if (!response.output) { + throw new Error("No valid response from the AI service."); + } + const altText = response.output; + await this.setGuessedAltText(altText); + if (updateAltTextData && !this.hasAltTextData()) { + this.altTextData = { + alt: altText, + decorative: false + }; + } + return altText; + } + #getBitmap() { + if (this.#bitmapId) { + this._uiManager.enableWaiting(true); + this._uiManager.imageManager.getFromId(this.#bitmapId).then(data => this.#getBitmapFetched(data, true)).finally(() => this.#getBitmapDone()); + return; + } + if (this.#bitmapUrl) { + const url = this.#bitmapUrl; + this.#bitmapUrl = null; + this._uiManager.enableWaiting(true); + this.#bitmapPromise = this._uiManager.imageManager.getFromUrl(url).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone()); + return; + } + if (this.#bitmapFile) { + const file = this.#bitmapFile; + this.#bitmapFile = null; + this._uiManager.enableWaiting(true); + this.#bitmapPromise = this._uiManager.imageManager.getFromFile(file).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone()); + return; + } + const input = document.createElement("input"); + input.type = "file"; + input.accept = StampEditor.supportedTypesStr; + const signal = this._uiManager._signal; + this.#bitmapPromise = new Promise(resolve => { + input.addEventListener("change", async () => { + if (!input.files || input.files.length === 0) { + this.remove(); + } else { + this._uiManager.enableWaiting(true); + const data = await this._uiManager.imageManager.getFromFile(input.files[0]); + this._reportTelemetry({ + action: "pdfjs.image.image_selected", + data: { + alt_text_modal: this._uiManager.useNewAltTextFlow + } + }); + this.#getBitmapFetched(data); + } + resolve(); + }, { + signal + }); + input.addEventListener("cancel", () => { + this.remove(); + resolve(); + }, { + signal + }); + }).finally(() => this.#getBitmapDone()); + input.click(); + } + remove() { + if (this.#bitmapId) { + this.#bitmap = null; + this._uiManager.imageManager.deleteId(this.#bitmapId); + this.#canvas?.remove(); + this.#canvas = null; + if (this.#resizeTimeoutId) { + clearTimeout(this.#resizeTimeoutId); + this.#resizeTimeoutId = null; + } + } + super.remove(); + } + rebuild() { + if (!this.parent) { + if (this.#bitmapId) { + this.#getBitmap(); + } + return; + } + super.rebuild(); + if (this.div === null) { + return; + } + if (this.#bitmapId && this.#canvas === null) { + this.#getBitmap(); + } + if (!this.isAttachedToDOM) { + this.parent.add(this); + } + } + onceAdded(focus) { + this._isDraggable = true; + if (focus) { + this.div.focus(); + } + } + isEmpty() { + return !(this.#bitmapPromise || this.#bitmap || this.#bitmapUrl || this.#bitmapFile || this.#bitmapId); + } + get isResizable() { + return true; + } + render() { + if (this.div) { + return this.div; + } + let baseX, baseY; + if (this.width) { + baseX = this.x; + baseY = this.y; + } + super.render(); + this.div.hidden = true; + this.div.setAttribute("role", "figure"); + this.addAltTextButton(); + if (this.#bitmap) { + this.#createCanvas(); + } else { + this.#getBitmap(); + } + if (this.width && !this.annotationElementId) { + const [parentWidth, parentHeight] = this.parentDimensions; + this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight); + } + this._uiManager.addShouldRescale(this); + return this.div; + } + _onResized() { + this.onScaleChanging(); + } + onScaleChanging() { + if (!this.parent) { + return; + } + if (this.#resizeTimeoutId !== null) { + clearTimeout(this.#resizeTimeoutId); + } + const TIME_TO_WAIT = 200; + this.#resizeTimeoutId = setTimeout(() => { + this.#resizeTimeoutId = null; + this.#drawBitmap(); + }, TIME_TO_WAIT); + } + #createCanvas() { + const { + div + } = this; + let { + width, + height + } = this.#bitmap; + const [pageWidth, pageHeight] = this.pageDimensions; + const MAX_RATIO = 0.75; + if (this.width) { + width = this.width * pageWidth; + height = this.height * pageHeight; + } else if (width > MAX_RATIO * pageWidth || height > MAX_RATIO * pageHeight) { + const factor = Math.min(MAX_RATIO * pageWidth / width, MAX_RATIO * pageHeight / height); + width *= factor; + height *= factor; + } + const [parentWidth, parentHeight] = this.parentDimensions; + this.setDims(width * parentWidth / pageWidth, height * parentHeight / pageHeight); + this._uiManager.enableWaiting(false); + const canvas = this.#canvas = document.createElement("canvas"); + canvas.setAttribute("role", "img"); + this.addContainer(canvas); + this.width = width / pageWidth; + this.height = height / pageHeight; + if (this._initialOptions?.isCentered) { + this.center(); + } else { + this.fixAndSetPosition(); + } + this._initialOptions = null; + if (!this._uiManager.useNewAltTextWhenAddingImage || !this._uiManager.useNewAltTextFlow || this.annotationElementId) { + div.hidden = false; + } + this.#drawBitmap(); + if (!this.#hasBeenAddedInUndoStack) { + this.parent.addUndoableEditor(this); + this.#hasBeenAddedInUndoStack = true; + } + this._reportTelemetry({ + action: "inserted_image" + }); + if (this.#bitmapFileName) { + canvas.setAttribute("aria-label", this.#bitmapFileName); + } + } + copyCanvas(maxDataDimension, maxPreviewDimension, createImageData = false) { + if (!maxDataDimension) { + maxDataDimension = 224; + } + const { + width: bitmapWidth, + height: bitmapHeight + } = this.#bitmap; + const outputScale = new OutputScale(); + let bitmap = this.#bitmap; + let width = bitmapWidth, + height = bitmapHeight; + let canvas = null; + if (maxPreviewDimension) { + if (bitmapWidth > maxPreviewDimension || bitmapHeight > maxPreviewDimension) { + const ratio = Math.min(maxPreviewDimension / bitmapWidth, maxPreviewDimension / bitmapHeight); + width = Math.floor(bitmapWidth * ratio); + height = Math.floor(bitmapHeight * ratio); + } + canvas = document.createElement("canvas"); + const scaledWidth = canvas.width = Math.ceil(width * outputScale.sx); + const scaledHeight = canvas.height = Math.ceil(height * outputScale.sy); + if (!this.#isSvg) { + bitmap = this.#scaleBitmap(scaledWidth, scaledHeight); + } + const ctx = canvas.getContext("2d"); + ctx.filter = this._uiManager.hcmFilter; + let white = "white", + black = "#cfcfd8"; + if (this._uiManager.hcmFilter !== "none") { + black = "black"; + } else if (window.matchMedia?.("(prefers-color-scheme: dark)").matches) { + white = "#8f8f9d"; + black = "#42414d"; + } + const boxDim = 15; + const boxDimWidth = boxDim * outputScale.sx; + const boxDimHeight = boxDim * outputScale.sy; + const pattern = new OffscreenCanvas(boxDimWidth * 2, boxDimHeight * 2); + const patternCtx = pattern.getContext("2d"); + patternCtx.fillStyle = white; + patternCtx.fillRect(0, 0, boxDimWidth * 2, boxDimHeight * 2); + patternCtx.fillStyle = black; + patternCtx.fillRect(0, 0, boxDimWidth, boxDimHeight); + patternCtx.fillRect(boxDimWidth, boxDimHeight, boxDimWidth, boxDimHeight); + ctx.fillStyle = ctx.createPattern(pattern, "repeat"); + ctx.fillRect(0, 0, scaledWidth, scaledHeight); + ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, scaledWidth, scaledHeight); + } + let imageData = null; + if (createImageData) { + let dataWidth, dataHeight; + if (outputScale.symmetric && bitmap.width < maxDataDimension && bitmap.height < maxDataDimension) { + dataWidth = bitmap.width; + dataHeight = bitmap.height; + } else { + bitmap = this.#bitmap; + if (bitmapWidth > maxDataDimension || bitmapHeight > maxDataDimension) { + const ratio = Math.min(maxDataDimension / bitmapWidth, maxDataDimension / bitmapHeight); + dataWidth = Math.floor(bitmapWidth * ratio); + dataHeight = Math.floor(bitmapHeight * ratio); + if (!this.#isSvg) { + bitmap = this.#scaleBitmap(dataWidth, dataHeight); + } + } + } + const offscreen = new OffscreenCanvas(dataWidth, dataHeight); + const offscreenCtx = offscreen.getContext("2d", { + willReadFrequently: true + }); + offscreenCtx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, dataWidth, dataHeight); + imageData = { + width: dataWidth, + height: dataHeight, + data: offscreenCtx.getImageData(0, 0, dataWidth, dataHeight).data + }; + } + return { + canvas, + width, + height, + imageData + }; + } + #scaleBitmap(width, height) { + const { + width: bitmapWidth, + height: bitmapHeight + } = this.#bitmap; + let newWidth = bitmapWidth; + let newHeight = bitmapHeight; + let bitmap = this.#bitmap; + while (newWidth > 2 * width || newHeight > 2 * height) { + const prevWidth = newWidth; + const prevHeight = newHeight; + if (newWidth > 2 * width) { + newWidth = newWidth >= 16384 ? Math.floor(newWidth / 2) - 1 : Math.ceil(newWidth / 2); + } + if (newHeight > 2 * height) { + newHeight = newHeight >= 16384 ? Math.floor(newHeight / 2) - 1 : Math.ceil(newHeight / 2); + } + const offscreen = new OffscreenCanvas(newWidth, newHeight); + const ctx = offscreen.getContext("2d"); + ctx.drawImage(bitmap, 0, 0, prevWidth, prevHeight, 0, 0, newWidth, newHeight); + bitmap = offscreen.transferToImageBitmap(); + } + return bitmap; + } + #drawBitmap() { + const [parentWidth, parentHeight] = this.parentDimensions; + const { + width, + height + } = this; + const outputScale = new OutputScale(); + const scaledWidth = Math.ceil(width * parentWidth * outputScale.sx); + const scaledHeight = Math.ceil(height * parentHeight * outputScale.sy); + const canvas = this.#canvas; + if (!canvas || canvas.width === scaledWidth && canvas.height === scaledHeight) { + return; + } + canvas.width = scaledWidth; + canvas.height = scaledHeight; + const bitmap = this.#isSvg ? this.#bitmap : this.#scaleBitmap(scaledWidth, scaledHeight); + const ctx = canvas.getContext("2d"); + ctx.filter = this._uiManager.hcmFilter; + ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, scaledWidth, scaledHeight); + } + getImageForAltText() { + return this.#canvas; + } + #serializeBitmap(toUrl) { + if (toUrl) { + if (this.#isSvg) { + const url = this._uiManager.imageManager.getSvgUrl(this.#bitmapId); + if (url) { + return url; + } + } + const canvas = document.createElement("canvas"); + ({ + width: canvas.width, + height: canvas.height + } = this.#bitmap); + const ctx = canvas.getContext("2d"); + ctx.drawImage(this.#bitmap, 0, 0); + return canvas.toDataURL(); + } + if (this.#isSvg) { + const [pageWidth, pageHeight] = this.pageDimensions; + const width = Math.round(this.width * pageWidth * PixelsPerInch.PDF_TO_CSS_UNITS); + const height = Math.round(this.height * pageHeight * PixelsPerInch.PDF_TO_CSS_UNITS); + const offscreen = new OffscreenCanvas(width, height); + const ctx = offscreen.getContext("2d"); + ctx.drawImage(this.#bitmap, 0, 0, this.#bitmap.width, this.#bitmap.height, 0, 0, width, height); + return offscreen.transferToImageBitmap(); + } + return structuredClone(this.#bitmap); + } + static async deserialize(data, parent, uiManager) { + let initialData = null; + if (data instanceof StampAnnotationElement) { + const { + data: { + rect, + rotation, + id, + structParent, + popupRef + }, + container, + parent: { + page: { + pageNumber + } + } + } = data; + const canvas = container.querySelector("canvas"); + const imageData = uiManager.imageManager.getFromCanvas(container.id, canvas); + canvas.remove(); + const altText = (await parent._structTree.getAriaAttributes(`${AnnotationPrefix}${id}`))?.get("aria-label") || ""; + initialData = data = { + annotationType: AnnotationEditorType.STAMP, + bitmapId: imageData.id, + bitmap: imageData.bitmap, + pageIndex: pageNumber - 1, + rect: rect.slice(0), + rotation, + id, + deleted: false, + accessibilityData: { + decorative: false, + altText + }, + isSvg: false, + structParent, + popupRef + }; + } + const editor = await super.deserialize(data, parent, uiManager); + const { + rect, + bitmap, + bitmapUrl, + bitmapId, + isSvg, + accessibilityData + } = data; + if (bitmapId && uiManager.imageManager.isValidId(bitmapId)) { + editor.#bitmapId = bitmapId; + if (bitmap) { + editor.#bitmap = bitmap; + } + } else { + editor.#bitmapUrl = bitmapUrl; + } + editor.#isSvg = isSvg; + const [parentWidth, parentHeight] = editor.pageDimensions; + editor.width = (rect[2] - rect[0]) / parentWidth; + editor.height = (rect[3] - rect[1]) / parentHeight; + editor.annotationElementId = data.id || null; + if (accessibilityData) { + editor.altTextData = accessibilityData; + } + editor._initialData = initialData; + editor.#hasBeenAddedInUndoStack = !!initialData; + return editor; + } + serialize(isForCopying = false, context = null) { + if (this.isEmpty()) { + return null; + } + if (this.deleted) { + return this.serializeDeleted(); + } + const serialized = { + annotationType: AnnotationEditorType.STAMP, + bitmapId: this.#bitmapId, + pageIndex: this.pageIndex, + rect: this.getRect(0, 0), + rotation: this.rotation, + isSvg: this.#isSvg, + structTreeParentId: this._structTreeParentId + }; + if (isForCopying) { + serialized.bitmapUrl = this.#serializeBitmap(true); + serialized.accessibilityData = this.serializeAltText(true); + return serialized; + } + const { + decorative, + altText + } = this.serializeAltText(false); + if (!decorative && altText) { + serialized.accessibilityData = { + type: "Figure", + alt: altText + }; + } + if (this.annotationElementId) { + const changes = this.#hasElementChanged(serialized); + if (changes.isSame) { + return null; + } + if (changes.isSameAltText) { + delete serialized.accessibilityData; + } else { + serialized.accessibilityData.structParent = this._initialData.structParent ?? -1; + } + } + serialized.id = this.annotationElementId; + if (context === null) { + return serialized; + } + context.stamps ||= new Map(); + const area = this.#isSvg ? (serialized.rect[2] - serialized.rect[0]) * (serialized.rect[3] - serialized.rect[1]) : null; + if (!context.stamps.has(this.#bitmapId)) { + context.stamps.set(this.#bitmapId, { + area, + serialized + }); + serialized.bitmap = this.#serializeBitmap(false); + } else if (this.#isSvg) { + const prevData = context.stamps.get(this.#bitmapId); + if (area > prevData.area) { + prevData.area = area; + prevData.serialized.bitmap.close(); + prevData.serialized.bitmap = this.#serializeBitmap(false); + } + } + return serialized; + } + #hasElementChanged(serialized) { + const { + pageIndex, + accessibilityData: { + altText + } + } = this._initialData; + const isSamePageIndex = serialized.pageIndex === pageIndex; + const isSameAltText = (serialized.accessibilityData?.alt || "") === altText; + return { + isSame: !this._hasBeenMoved && !this._hasBeenResized && isSamePageIndex && isSameAltText, + isSameAltText + }; + } + renderAnnotationElement(annotation) { + annotation.updateEdited({ + rect: this.getRect(0, 0) + }); + return null; + } +} + +;// ./src/display/editor/annotation_editor_layer.js + + + + + + + +class AnnotationEditorLayer { + #accessibilityManager; + #allowClick = false; + #annotationLayer = null; + #clickAC = null; + #editorFocusTimeoutId = null; + #editors = new Map(); + #hadPointerDown = false; + #isDisabling = false; + #isEnabling = false; + #drawingAC = null; + #focusedElement = null; + #textLayer = null; + #textSelectionAC = null; + #uiManager; + static _initialized = false; + static #editorTypes = new Map([FreeTextEditor, InkEditor, StampEditor, HighlightEditor].map(type => [type._editorType, type])); + constructor({ + uiManager, + pageIndex, + div, + structTreeLayer, + accessibilityManager, + annotationLayer, + drawLayer, + textLayer, + viewport, + l10n + }) { + const editorTypes = [...AnnotationEditorLayer.#editorTypes.values()]; + if (!AnnotationEditorLayer._initialized) { + AnnotationEditorLayer._initialized = true; + for (const editorType of editorTypes) { + editorType.initialize(l10n, uiManager); + } + } + uiManager.registerEditorTypes(editorTypes); + this.#uiManager = uiManager; + this.pageIndex = pageIndex; + this.div = div; + this.#accessibilityManager = accessibilityManager; + this.#annotationLayer = annotationLayer; + this.viewport = viewport; + this.#textLayer = textLayer; + this.drawLayer = drawLayer; + this._structTree = structTreeLayer; + this.#uiManager.addLayer(this); + } + get isEmpty() { + return this.#editors.size === 0; + } + get isInvisible() { + return this.isEmpty && this.#uiManager.getMode() === AnnotationEditorType.NONE; + } + updateToolbar(mode) { + this.#uiManager.updateToolbar(mode); + } + updateMode(mode = this.#uiManager.getMode()) { + this.#cleanup(); + switch (mode) { + case AnnotationEditorType.NONE: + this.disableTextSelection(); + this.togglePointerEvents(false); + this.toggleAnnotationLayerPointerEvents(true); + this.disableClick(); + return; + case AnnotationEditorType.INK: + this.disableTextSelection(); + this.togglePointerEvents(true); + this.enableClick(); + break; + case AnnotationEditorType.HIGHLIGHT: + this.enableTextSelection(); + this.togglePointerEvents(false); + this.disableClick(); + break; + default: + this.disableTextSelection(); + this.togglePointerEvents(true); + this.enableClick(); + } + this.toggleAnnotationLayerPointerEvents(false); + const { + classList + } = this.div; + for (const editorType of AnnotationEditorLayer.#editorTypes.values()) { + classList.toggle(`${editorType._type}Editing`, mode === editorType._editorType); + } + this.div.hidden = false; + } + hasTextLayer(textLayer) { + return textLayer === this.#textLayer?.div; + } + setEditingState(isEditing) { + this.#uiManager.setEditingState(isEditing); + } + addCommands(params) { + this.#uiManager.addCommands(params); + } + cleanUndoStack(type) { + this.#uiManager.cleanUndoStack(type); + } + toggleDrawing(enabled = false) { + this.div.classList.toggle("drawing", !enabled); + } + togglePointerEvents(enabled = false) { + this.div.classList.toggle("disabled", !enabled); + } + toggleAnnotationLayerPointerEvents(enabled = false) { + this.#annotationLayer?.div.classList.toggle("disabled", !enabled); + } + async enable() { + this.#isEnabling = true; + this.div.tabIndex = 0; + this.togglePointerEvents(true); + const annotationElementIds = new Set(); + for (const editor of this.#editors.values()) { + editor.enableEditing(); + editor.show(true); + if (editor.annotationElementId) { + this.#uiManager.removeChangedExistingAnnotation(editor); + annotationElementIds.add(editor.annotationElementId); + } + } + if (!this.#annotationLayer) { + this.#isEnabling = false; + return; + } + const editables = this.#annotationLayer.getEditableAnnotations(); + for (const editable of editables) { + editable.hide(); + if (this.#uiManager.isDeletedAnnotationElement(editable.data.id)) { + continue; + } + if (annotationElementIds.has(editable.data.id)) { + continue; + } + const editor = await this.deserialize(editable); + if (!editor) { + continue; + } + this.addOrRebuild(editor); + editor.enableEditing(); + } + this.#isEnabling = false; + } + disable() { + this.#isDisabling = true; + this.div.tabIndex = -1; + this.togglePointerEvents(false); + const changedAnnotations = new Map(); + const resetAnnotations = new Map(); + for (const editor of this.#editors.values()) { + editor.disableEditing(); + if (!editor.annotationElementId) { + continue; + } + if (editor.serialize() !== null) { + changedAnnotations.set(editor.annotationElementId, editor); + continue; + } else { + resetAnnotations.set(editor.annotationElementId, editor); + } + this.getEditableAnnotation(editor.annotationElementId)?.show(); + editor.remove(); + } + if (this.#annotationLayer) { + const editables = this.#annotationLayer.getEditableAnnotations(); + for (const editable of editables) { + const { + id + } = editable.data; + if (this.#uiManager.isDeletedAnnotationElement(id)) { + continue; + } + let editor = resetAnnotations.get(id); + if (editor) { + editor.resetAnnotationElement(editable); + editor.show(false); + editable.show(); + continue; + } + editor = changedAnnotations.get(id); + if (editor) { + this.#uiManager.addChangedExistingAnnotation(editor); + if (editor.renderAnnotationElement(editable)) { + editor.show(false); + } + } + editable.show(); + } + } + this.#cleanup(); + if (this.isEmpty) { + this.div.hidden = true; + } + const { + classList + } = this.div; + for (const editorType of AnnotationEditorLayer.#editorTypes.values()) { + classList.remove(`${editorType._type}Editing`); + } + this.disableTextSelection(); + this.toggleAnnotationLayerPointerEvents(true); + this.#isDisabling = false; + } + getEditableAnnotation(id) { + return this.#annotationLayer?.getEditableAnnotation(id) || null; + } + setActiveEditor(editor) { + const currentActive = this.#uiManager.getActive(); + if (currentActive === editor) { + return; + } + this.#uiManager.setActiveEditor(editor); + } + enableTextSelection() { + this.div.tabIndex = -1; + if (this.#textLayer?.div && !this.#textSelectionAC) { + this.#textSelectionAC = new AbortController(); + const signal = this.#uiManager.combinedSignal(this.#textSelectionAC); + this.#textLayer.div.addEventListener("pointerdown", this.#textLayerPointerDown.bind(this), { + signal + }); + this.#textLayer.div.classList.add("highlighting"); + } + } + disableTextSelection() { + this.div.tabIndex = 0; + if (this.#textLayer?.div && this.#textSelectionAC) { + this.#textSelectionAC.abort(); + this.#textSelectionAC = null; + this.#textLayer.div.classList.remove("highlighting"); + } + } + #textLayerPointerDown(event) { + this.#uiManager.unselectAll(); + const { + target + } = event; + if (target === this.#textLayer.div || (target.getAttribute("role") === "img" || target.classList.contains("endOfContent")) && this.#textLayer.div.contains(target)) { + const { + isMac + } = util_FeatureTest.platform; + if (event.button !== 0 || event.ctrlKey && isMac) { + return; + } + this.#uiManager.showAllEditors("highlight", true, true); + this.#textLayer.div.classList.add("free"); + this.toggleDrawing(); + HighlightEditor.startHighlighting(this, this.#uiManager.direction === "ltr", { + target: this.#textLayer.div, + x: event.x, + y: event.y + }); + this.#textLayer.div.addEventListener("pointerup", () => { + this.#textLayer.div.classList.remove("free"); + this.toggleDrawing(true); + }, { + once: true, + signal: this.#uiManager._signal + }); + event.preventDefault(); + } + } + enableClick() { + if (this.#clickAC) { + return; + } + this.#clickAC = new AbortController(); + const signal = this.#uiManager.combinedSignal(this.#clickAC); + this.div.addEventListener("pointerdown", this.pointerdown.bind(this), { + signal + }); + const pointerup = this.pointerup.bind(this); + this.div.addEventListener("pointerup", pointerup, { + signal + }); + this.div.addEventListener("pointercancel", pointerup, { + signal + }); + } + disableClick() { + this.#clickAC?.abort(); + this.#clickAC = null; + } + attach(editor) { + this.#editors.set(editor.id, editor); + const { + annotationElementId + } = editor; + if (annotationElementId && this.#uiManager.isDeletedAnnotationElement(annotationElementId)) { + this.#uiManager.removeDeletedAnnotationElement(editor); + } + } + detach(editor) { + this.#editors.delete(editor.id); + this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv); + if (!this.#isDisabling && editor.annotationElementId) { + this.#uiManager.addDeletedAnnotationElement(editor); + } + } + remove(editor) { + this.detach(editor); + this.#uiManager.removeEditor(editor); + editor.div.remove(); + editor.isAttachedToDOM = false; + } + changeParent(editor) { + if (editor.parent === this) { + return; + } + if (editor.parent && editor.annotationElementId) { + this.#uiManager.addDeletedAnnotationElement(editor.annotationElementId); + AnnotationEditor.deleteAnnotationElement(editor); + editor.annotationElementId = null; + } + this.attach(editor); + editor.parent?.detach(editor); + editor.setParent(this); + if (editor.div && editor.isAttachedToDOM) { + editor.div.remove(); + this.div.append(editor.div); + } + } + add(editor) { + if (editor.parent === this && editor.isAttachedToDOM) { + return; + } + this.changeParent(editor); + this.#uiManager.addEditor(editor); + this.attach(editor); + if (!editor.isAttachedToDOM) { + const div = editor.render(); + this.div.append(div); + editor.isAttachedToDOM = true; + } + editor.fixAndSetPosition(); + editor.onceAdded(!this.#isEnabling); + this.#uiManager.addToAnnotationStorage(editor); + editor._reportTelemetry(editor.telemetryInitialData); + } + moveEditorInDOM(editor) { + if (!editor.isAttachedToDOM) { + return; + } + const { + activeElement + } = document; + if (editor.div.contains(activeElement) && !this.#editorFocusTimeoutId) { + editor._focusEventsAllowed = false; + this.#editorFocusTimeoutId = setTimeout(() => { + this.#editorFocusTimeoutId = null; + if (!editor.div.contains(document.activeElement)) { + editor.div.addEventListener("focusin", () => { + editor._focusEventsAllowed = true; + }, { + once: true, + signal: this.#uiManager._signal + }); + activeElement.focus(); + } else { + editor._focusEventsAllowed = true; + } + }, 0); + } + editor._structTreeParentId = this.#accessibilityManager?.moveElementInDOM(this.div, editor.div, editor.contentDiv, true); + } + addOrRebuild(editor) { + if (editor.needsToBeRebuilt()) { + editor.parent ||= this; + editor.rebuild(); + editor.show(); + } else { + this.add(editor); + } + } + addUndoableEditor(editor) { + const cmd = () => editor._uiManager.rebuild(editor); + const undo = () => { + editor.remove(); + }; + this.addCommands({ + cmd, + undo, + mustExec: false + }); + } + getNextId() { + return this.#uiManager.getId(); + } + get #currentEditorType() { + return AnnotationEditorLayer.#editorTypes.get(this.#uiManager.getMode()); + } + combinedSignal(ac) { + return this.#uiManager.combinedSignal(ac); + } + #createNewEditor(params) { + const editorType = this.#currentEditorType; + return editorType ? new editorType.prototype.constructor(params) : null; + } + canCreateNewEmptyEditor() { + return this.#currentEditorType?.canCreateNewEmptyEditor(); + } + pasteEditor(mode, params) { + this.#uiManager.updateToolbar(mode); + this.#uiManager.updateMode(mode); + const { + offsetX, + offsetY + } = this.#getCenterPoint(); + const id = this.getNextId(); + const editor = this.#createNewEditor({ + parent: this, + id, + x: offsetX, + y: offsetY, + uiManager: this.#uiManager, + isCentered: true, + ...params + }); + if (editor) { + this.add(editor); + } + } + async deserialize(data) { + return (await AnnotationEditorLayer.#editorTypes.get(data.annotationType ?? data.annotationEditorType)?.deserialize(data, this, this.#uiManager)) || null; + } + createAndAddNewEditor(event, isCentered, data = {}) { + const id = this.getNextId(); + const editor = this.#createNewEditor({ + parent: this, + id, + x: event.offsetX, + y: event.offsetY, + uiManager: this.#uiManager, + isCentered, + ...data + }); + if (editor) { + this.add(editor); + } + return editor; + } + #getCenterPoint() { + const { + x, + y, + width, + height + } = this.div.getBoundingClientRect(); + const tlX = Math.max(0, x); + const tlY = Math.max(0, y); + const brX = Math.min(window.innerWidth, x + width); + const brY = Math.min(window.innerHeight, y + height); + const centerX = (tlX + brX) / 2 - x; + const centerY = (tlY + brY) / 2 - y; + const [offsetX, offsetY] = this.viewport.rotation % 180 === 0 ? [centerX, centerY] : [centerY, centerX]; + return { + offsetX, + offsetY + }; + } + addNewEditor() { + this.createAndAddNewEditor(this.#getCenterPoint(), true); + } + setSelected(editor) { + this.#uiManager.setSelected(editor); + } + toggleSelected(editor) { + this.#uiManager.toggleSelected(editor); + } + unselect(editor) { + this.#uiManager.unselect(editor); + } + pointerup(event) { + const { + isMac + } = util_FeatureTest.platform; + if (event.button !== 0 || event.ctrlKey && isMac) { + return; + } + if (event.target !== this.div) { + return; + } + if (!this.#hadPointerDown) { + return; + } + this.#hadPointerDown = false; + if (this.#currentEditorType?.isDrawer && this.#currentEditorType.supportMultipleDrawings) { + return; + } + if (!this.#allowClick) { + this.#allowClick = true; + return; + } + if (this.#uiManager.getMode() === AnnotationEditorType.STAMP) { + this.#uiManager.unselectAll(); + return; + } + this.createAndAddNewEditor(event, false); + } + pointerdown(event) { + if (this.#uiManager.getMode() === AnnotationEditorType.HIGHLIGHT) { + this.enableTextSelection(); + } + if (this.#hadPointerDown) { + this.#hadPointerDown = false; + return; + } + const { + isMac + } = util_FeatureTest.platform; + if (event.button !== 0 || event.ctrlKey && isMac) { + return; + } + if (event.target !== this.div) { + return; + } + this.#hadPointerDown = true; + if (this.#currentEditorType?.isDrawer) { + this.startDrawingSession(event); + return; + } + const editor = this.#uiManager.getActive(); + this.#allowClick = !editor || editor.isEmpty(); + } + startDrawingSession(event) { + this.div.focus(); + if (this.#drawingAC) { + this.#currentEditorType.startDrawing(this, this.#uiManager, false, event); + return; + } + this.#uiManager.setCurrentDrawingSession(this); + this.#drawingAC = new AbortController(); + const signal = this.#uiManager.combinedSignal(this.#drawingAC); + this.div.addEventListener("blur", ({ + relatedTarget + }) => { + if (relatedTarget && !this.div.contains(relatedTarget)) { + this.#focusedElement = null; + this.commitOrRemove(); + } + }, { + signal + }); + this.#currentEditorType.startDrawing(this, this.#uiManager, false, event); + } + pause(on) { + if (on) { + const { + activeElement + } = document; + if (this.div.contains(activeElement)) { + this.#focusedElement = activeElement; + } + return; + } + if (this.#focusedElement) { + setTimeout(() => { + this.#focusedElement?.focus(); + this.#focusedElement = null; + }, 0); + } + } + endDrawingSession(isAborted = false) { + if (!this.#drawingAC) { + return null; + } + this.#uiManager.setCurrentDrawingSession(null); + this.#drawingAC.abort(); + this.#drawingAC = null; + this.#focusedElement = null; + return this.#currentEditorType.endDrawing(isAborted); + } + findNewParent(editor, x, y) { + const layer = this.#uiManager.findParent(x, y); + if (layer === null || layer === this) { + return false; + } + layer.changeParent(editor); + return true; + } + commitOrRemove() { + if (this.#drawingAC) { + this.endDrawingSession(); + return true; + } + return false; + } + onScaleChanging() { + if (!this.#drawingAC) { + return; + } + this.#currentEditorType.onScaleChangingWhenDrawing(this); + } + destroy() { + this.commitOrRemove(); + if (this.#uiManager.getActive()?.parent === this) { + this.#uiManager.commitOrRemove(); + this.#uiManager.setActiveEditor(null); + } + if (this.#editorFocusTimeoutId) { + clearTimeout(this.#editorFocusTimeoutId); + this.#editorFocusTimeoutId = null; + } + for (const editor of this.#editors.values()) { + this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv); + editor.setParent(null); + editor.isAttachedToDOM = false; + editor.div.remove(); + } + this.div = null; + this.#editors.clear(); + this.#uiManager.removeLayer(this); + } + #cleanup() { + for (const editor of this.#editors.values()) { + if (editor.isEmpty()) { + editor.remove(); + } + } + } + render({ + viewport + }) { + this.viewport = viewport; + setLayerDimensions(this.div, viewport); + for (const editor of this.#uiManager.getEditors(this.pageIndex)) { + this.add(editor); + editor.rebuild(); + } + this.updateMode(); + } + update({ + viewport + }) { + this.#uiManager.commitOrRemove(); + this.#cleanup(); + const oldRotation = this.viewport.rotation; + const rotation = viewport.rotation; + this.viewport = viewport; + setLayerDimensions(this.div, { + rotation + }); + if (oldRotation !== rotation) { + for (const editor of this.#editors.values()) { + editor.rotate(rotation); + } + } + } + get pageDimensions() { + const { + pageWidth, + pageHeight + } = this.viewport.rawDims; + return [pageWidth, pageHeight]; + } + get scale() { + return this.#uiManager.viewParameters.realScale; + } +} + +;// ./src/display/draw_layer.js + + +class DrawLayer { + #parent = null; + #id = 0; + #mapping = new Map(); + #toUpdate = new Map(); + constructor({ + pageIndex + }) { + this.pageIndex = pageIndex; + } + setParent(parent) { + if (!this.#parent) { + this.#parent = parent; + return; + } + if (this.#parent !== parent) { + if (this.#mapping.size > 0) { + for (const root of this.#mapping.values()) { + root.remove(); + parent.append(root); + } + } + this.#parent = parent; + } + } + static get _svgFactory() { + return shadow(this, "_svgFactory", new DOMSVGFactory()); + } + static #setBox(element, [x, y, width, height]) { + const { + style + } = element; + style.top = `${100 * y}%`; + style.left = `${100 * x}%`; + style.width = `${100 * width}%`; + style.height = `${100 * height}%`; + } + #createSVG() { + const svg = DrawLayer._svgFactory.create(1, 1, true); + this.#parent.append(svg); + svg.setAttribute("aria-hidden", true); + return svg; + } + #createClipPath(defs, pathId) { + const clipPath = DrawLayer._svgFactory.createElement("clipPath"); + defs.append(clipPath); + const clipPathId = `clip_${pathId}`; + clipPath.setAttribute("id", clipPathId); + clipPath.setAttribute("clipPathUnits", "objectBoundingBox"); + const clipPathUse = DrawLayer._svgFactory.createElement("use"); + clipPath.append(clipPathUse); + clipPathUse.setAttribute("href", `#${pathId}`); + clipPathUse.classList.add("clip"); + return clipPathId; + } + #updateProperties(element, properties) { + for (const [key, value] of Object.entries(properties)) { + if (value === null) { + element.removeAttribute(key); + } else { + element.setAttribute(key, value); + } + } + } + draw(properties, isPathUpdatable = false, hasClip = false) { + const id = this.#id++; + const root = this.#createSVG(); + const defs = DrawLayer._svgFactory.createElement("defs"); + root.append(defs); + const path = DrawLayer._svgFactory.createElement("path"); + defs.append(path); + const pathId = `path_p${this.pageIndex}_${id}`; + path.setAttribute("id", pathId); + path.setAttribute("vector-effect", "non-scaling-stroke"); + if (isPathUpdatable) { + this.#toUpdate.set(id, path); + } + const clipPathId = hasClip ? this.#createClipPath(defs, pathId) : null; + const use = DrawLayer._svgFactory.createElement("use"); + root.append(use); + use.setAttribute("href", `#${pathId}`); + this.updateProperties(root, properties); + this.#mapping.set(id, root); + return { + id, + clipPathId: `url(#${clipPathId})` + }; + } + drawOutline(properties, mustRemoveSelfIntersections) { + const id = this.#id++; + const root = this.#createSVG(); + const defs = DrawLayer._svgFactory.createElement("defs"); + root.append(defs); + const path = DrawLayer._svgFactory.createElement("path"); + defs.append(path); + const pathId = `path_p${this.pageIndex}_${id}`; + path.setAttribute("id", pathId); + path.setAttribute("vector-effect", "non-scaling-stroke"); + let maskId; + if (mustRemoveSelfIntersections) { + const mask = DrawLayer._svgFactory.createElement("mask"); + defs.append(mask); + maskId = `mask_p${this.pageIndex}_${id}`; + mask.setAttribute("id", maskId); + mask.setAttribute("maskUnits", "objectBoundingBox"); + const rect = DrawLayer._svgFactory.createElement("rect"); + mask.append(rect); + rect.setAttribute("width", "1"); + rect.setAttribute("height", "1"); + rect.setAttribute("fill", "white"); + const use = DrawLayer._svgFactory.createElement("use"); + mask.append(use); + use.setAttribute("href", `#${pathId}`); + use.setAttribute("stroke", "none"); + use.setAttribute("fill", "black"); + use.setAttribute("fill-rule", "nonzero"); + use.classList.add("mask"); + } + const use1 = DrawLayer._svgFactory.createElement("use"); + root.append(use1); + use1.setAttribute("href", `#${pathId}`); + if (maskId) { + use1.setAttribute("mask", `url(#${maskId})`); + } + const use2 = use1.cloneNode(); + root.append(use2); + use1.classList.add("mainOutline"); + use2.classList.add("secondaryOutline"); + this.updateProperties(root, properties); + this.#mapping.set(id, root); + return id; + } + finalizeDraw(id, properties) { + this.#toUpdate.delete(id); + this.updateProperties(id, properties); + } + updateProperties(elementOrId, properties) { + if (!properties) { + return; + } + const { + root, + bbox, + rootClass, + path + } = properties; + const element = typeof elementOrId === "number" ? this.#mapping.get(elementOrId) : elementOrId; + if (!element) { + return; + } + if (root) { + this.#updateProperties(element, root); + } + if (bbox) { + DrawLayer.#setBox(element, bbox); + } + if (rootClass) { + const { + classList + } = element; + for (const [className, value] of Object.entries(rootClass)) { + classList.toggle(className, value); + } + } + if (path) { + const defs = element.firstChild; + const pathElement = defs.firstChild; + this.#updateProperties(pathElement, path); + } + } + updateParent(id, layer) { + if (layer === this) { + return; + } + const root = this.#mapping.get(id); + if (!root) { + return; + } + layer.#parent.append(root); + this.#mapping.delete(id); + layer.#mapping.set(id, root); + } + remove(id) { + this.#toUpdate.delete(id); + if (this.#parent === null) { + return; + } + this.#mapping.get(id).remove(); + this.#mapping.delete(id); + } + destroy() { + this.#parent = null; + for (const root of this.#mapping.values()) { + root.remove(); + } + this.#mapping.clear(); + this.#toUpdate.clear(); + } +} + +;// ./src/pdf.js + + + + + + + + + + + + + + +const pdfjsVersion = "4.10.38"; +const pdfjsBuild = "f9bea397f"; +{ + globalThis.pdfjsTestingUtils = { + HighlightOutliner: HighlightOutliner + }; +} + +var __webpack_exports__AbortException = __webpack_exports__.AbortException; +var __webpack_exports__AnnotationEditorLayer = __webpack_exports__.AnnotationEditorLayer; +var __webpack_exports__AnnotationEditorParamsType = __webpack_exports__.AnnotationEditorParamsType; +var __webpack_exports__AnnotationEditorType = __webpack_exports__.AnnotationEditorType; +var __webpack_exports__AnnotationEditorUIManager = __webpack_exports__.AnnotationEditorUIManager; +var __webpack_exports__AnnotationLayer = __webpack_exports__.AnnotationLayer; +var __webpack_exports__AnnotationMode = __webpack_exports__.AnnotationMode; +var __webpack_exports__ColorPicker = __webpack_exports__.ColorPicker; +var __webpack_exports__DOMSVGFactory = __webpack_exports__.DOMSVGFactory; +var __webpack_exports__DrawLayer = __webpack_exports__.DrawLayer; +var __webpack_exports__FeatureTest = __webpack_exports__.FeatureTest; +var __webpack_exports__GlobalWorkerOptions = __webpack_exports__.GlobalWorkerOptions; +var __webpack_exports__ImageKind = __webpack_exports__.ImageKind; +var __webpack_exports__InvalidPDFException = __webpack_exports__.InvalidPDFException; +var __webpack_exports__MissingPDFException = __webpack_exports__.MissingPDFException; +var __webpack_exports__OPS = __webpack_exports__.OPS; +var __webpack_exports__OutputScale = __webpack_exports__.OutputScale; +var __webpack_exports__PDFDataRangeTransport = __webpack_exports__.PDFDataRangeTransport; +var __webpack_exports__PDFDateString = __webpack_exports__.PDFDateString; +var __webpack_exports__PDFWorker = __webpack_exports__.PDFWorker; +var __webpack_exports__PasswordResponses = __webpack_exports__.PasswordResponses; +var __webpack_exports__PermissionFlag = __webpack_exports__.PermissionFlag; +var __webpack_exports__PixelsPerInch = __webpack_exports__.PixelsPerInch; +var __webpack_exports__RenderingCancelledException = __webpack_exports__.RenderingCancelledException; +var __webpack_exports__TextLayer = __webpack_exports__.TextLayer; +var __webpack_exports__TouchManager = __webpack_exports__.TouchManager; +var __webpack_exports__UnexpectedResponseException = __webpack_exports__.UnexpectedResponseException; +var __webpack_exports__Util = __webpack_exports__.Util; +var __webpack_exports__VerbosityLevel = __webpack_exports__.VerbosityLevel; +var __webpack_exports__XfaLayer = __webpack_exports__.XfaLayer; +var __webpack_exports__build = __webpack_exports__.build; +var __webpack_exports__createValidAbsoluteUrl = __webpack_exports__.createValidAbsoluteUrl; +var __webpack_exports__fetchData = __webpack_exports__.fetchData; +var __webpack_exports__getDocument = __webpack_exports__.getDocument; +var __webpack_exports__getFilenameFromUrl = __webpack_exports__.getFilenameFromUrl; +var __webpack_exports__getPdfFilenameFromUrl = __webpack_exports__.getPdfFilenameFromUrl; +var __webpack_exports__getXfaPageViewport = __webpack_exports__.getXfaPageViewport; +var __webpack_exports__isDataScheme = __webpack_exports__.isDataScheme; +var __webpack_exports__isPdfFile = __webpack_exports__.isPdfFile; +var __webpack_exports__noContextMenu = __webpack_exports__.noContextMenu; +var __webpack_exports__normalizeUnicode = __webpack_exports__.normalizeUnicode; +var __webpack_exports__setLayerDimensions = __webpack_exports__.setLayerDimensions; +var __webpack_exports__shadow = __webpack_exports__.shadow; +var __webpack_exports__stopEvent = __webpack_exports__.stopEvent; +var __webpack_exports__version = __webpack_exports__.version; +export { __webpack_exports__AbortException as AbortException, __webpack_exports__AnnotationEditorLayer as AnnotationEditorLayer, __webpack_exports__AnnotationEditorParamsType as AnnotationEditorParamsType, __webpack_exports__AnnotationEditorType as AnnotationEditorType, __webpack_exports__AnnotationEditorUIManager as AnnotationEditorUIManager, __webpack_exports__AnnotationLayer as AnnotationLayer, __webpack_exports__AnnotationMode as AnnotationMode, __webpack_exports__ColorPicker as ColorPicker, __webpack_exports__DOMSVGFactory as DOMSVGFactory, __webpack_exports__DrawLayer as DrawLayer, __webpack_exports__FeatureTest as FeatureTest, __webpack_exports__GlobalWorkerOptions as GlobalWorkerOptions, __webpack_exports__ImageKind as ImageKind, __webpack_exports__InvalidPDFException as InvalidPDFException, __webpack_exports__MissingPDFException as MissingPDFException, __webpack_exports__OPS as OPS, __webpack_exports__OutputScale as OutputScale, __webpack_exports__PDFDataRangeTransport as PDFDataRangeTransport, __webpack_exports__PDFDateString as PDFDateString, __webpack_exports__PDFWorker as PDFWorker, __webpack_exports__PasswordResponses as PasswordResponses, __webpack_exports__PermissionFlag as PermissionFlag, __webpack_exports__PixelsPerInch as PixelsPerInch, __webpack_exports__RenderingCancelledException as RenderingCancelledException, __webpack_exports__TextLayer as TextLayer, __webpack_exports__TouchManager as TouchManager, __webpack_exports__UnexpectedResponseException as UnexpectedResponseException, __webpack_exports__Util as Util, __webpack_exports__VerbosityLevel as VerbosityLevel, __webpack_exports__XfaLayer as XfaLayer, __webpack_exports__build as build, __webpack_exports__createValidAbsoluteUrl as createValidAbsoluteUrl, __webpack_exports__fetchData as fetchData, __webpack_exports__getDocument as getDocument, __webpack_exports__getFilenameFromUrl as getFilenameFromUrl, __webpack_exports__getPdfFilenameFromUrl as getPdfFilenameFromUrl, __webpack_exports__getXfaPageViewport as getXfaPageViewport, __webpack_exports__isDataScheme as isDataScheme, __webpack_exports__isPdfFile as isPdfFile, __webpack_exports__noContextMenu as noContextMenu, __webpack_exports__normalizeUnicode as normalizeUnicode, __webpack_exports__setLayerDimensions as setLayerDimensions, __webpack_exports__shadow as shadow, __webpack_exports__stopEvent as stopEvent, __webpack_exports__version as version }; + +//# sourceMappingURL=pdf.mjs.map \ No newline at end of file diff --git a/public/pdfjs/build/pdf.sandbox.mjs b/public/pdfjs/build/pdf.sandbox.mjs new file mode 100644 index 0000000..9c5997d --- /dev/null +++ b/public/pdfjs/build/pdf.sandbox.mjs @@ -0,0 +1,242 @@ +/** + * @licstart The following is the entire license notice for the + * JavaScript code in this page + * + * Copyright 2024 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @licend The above is the entire license notice for the + * JavaScript code in this page + */ + +/******/ // The require scope +/******/ var __webpack_require__ = {}; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = globalThis.pdfjsSandbox = {}; + +// EXPORTS +__webpack_require__.d(__webpack_exports__, { + QuickJSSandbox: () => (/* binding */ QuickJSSandbox) +}); + +;// ./external/quickjs/quickjs-eval.js +var Module=(()=>{var _scriptDir=typeof document!=='undefined'&&document.currentScript?document.currentScript.src:undefined;return function(moduleArg={}){var d=moduleArg,k,n;d.ready=new Promise((a,b)=>{k=a;n=b;});var p=Object.assign({},d),q="";"undefined"!=typeof document&&document.currentScript&&(q=document.currentScript.src);_scriptDir&&(q=_scriptDir);q.startsWith("blob:")?q="":q=q.substr(0,q.replace(/[?#].*/,"").lastIndexOf("/")+1);var aa=d.print||console.log.bind(console),u=d.printErr||console.error.bind(console);Object.assign(d,p);p=null;var v;d.wasmBinary&&(v=d.wasmBinary);"object"!=typeof WebAssembly&&w("no native wasm support detected");var x,y=!1,z,A,B,C;function D(){var a=x.buffer;d.HEAP8=z=new Int8Array(a);d.HEAP16=new Int16Array(a);d.HEAPU8=A=new Uint8Array(a);d.HEAPU16=new Uint16Array(a);d.HEAP32=B=new Int32Array(a);d.HEAPU32=C=new Uint32Array(a);d.HEAPF32=new Float32Array(a);d.HEAPF64=new Float64Array(a);}var E=[],F=[],G=[];function ba(){var a=d.preRun.shift();E.unshift(a);}var H=0,I=null,J=null;function w(a){d.onAbort?.(a);a="Aborted("+a+")";u(a);y=!0;a=new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");n(a);throw a;}var K=a=>a.startsWith("data:application/octet-stream;base64,"),L;L="data:application/octet-stream;base64,AGFzbQEAAAABzgZtYAJ/fwBgA39/fwF/YAR/fn9/AX5gAn9/AX9gBX9+f39/AX5gAX8Bf2ADf39/AGAEf39/fwF/YAJ/fgF+YAF/AGABfAF8YAV/f39/fwF/YAJ/fgBgAn9+AX9gAn9/AX5gA39/fgF/YAN/fn8BfmAGf35/f39/AX5gA39+fwBgA39+fwF/YAZ/f39/f38Bf2AEf39/fwBgBn9+fn9/fwF+YAR/f35/AX9gA39+fgF+YAN/f38BfmADf35+AX9gBH9/f38BfmAFf35+fn4AYAJ8fAF8YAF/AX5gBH9/f34Bf2AFf35+f38BfmAFf39/f38AYAd/fn9+fn5/AX9gBX9/f35+AX9gB39/f39/f38Bf2AAAGAFf35/fn8Bf2AEf35+fwBgBH9+fn8BfmAFf35+fn8Bf2AFf39/f38BfmAEf39+fgF/YAF+AX9gBH9+f34BfmAEf35/fwBgBH9+fn8Bf2AJf39/f39/f39/AX9gCH9/f39/f39/AX9gA39+fgBgBH9+f38Bf2AGf35/fn5/AX9gBX9+fn9/AGABfgF+YAd/fn9/f39/AX5gAX8BfGADf39+AGAEf35/fgF/YAV/f35/fwF/YAR/fn5+AX9gBn9/f39/fwF+YAN+f38Bf2AHf39/f39/fwBgAnx/AXxgA39/fgF+YAJ+fwF/YAN8fH8BfGAEf39+fwBgBH9+fn4BfmAAAX9gBn98f39/fwF/YAABfGAFf35/fn8BfmAGf39+fn5+AX9gAn5/AGACf3wAYAV/f39/fgF+YAR/f35/AX5gBH9+f34AYAd/fn5+f39/AX5gBH5+fn4Bf2AKf39/f39/f39/fwF/YAd/f39/f39+AX5gBX9+f39/AGAHfH9/f39/fwBgBX98f39/AX5gAXwBf2AFf39+f38AYAZ/fn5+fn8Bf2AGf35/f39/AX9gBH98f38Bf2AGf39/f39/AGAEf39/fgF+YAV/fn9/fwF/YAV/fn5+fgF/YAJ/fwF8YAV/fn5/fwF/YAV/f35+fgF+YAV/f35+fwF/YAJ8fwF/YAJ8fAF/YAh/fn5+fn9+fgF+YAN/fnwBfmAAAX5gB39/f35+fn8Bf2ACfn4BfGADfn5+AX9gA39/fAACSQwBYQFhABUBYQFiACUBYQFjAAcBYQFkAAYBYQFlAEgBYQFmAAABYQFn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IAILLQEBfwJAIAAoAgAiAUUNACAAKAIQIgBFDQAgASgCACAAQQAgASgCBBEBABoLCzEAIAFBAE4EQCAAQbYBEA0gACABEDggACgCQCIAKAKkAiABQRRsaiAAKAKEAjYCBAsLJwEBfyMAQRBrIgIkACACIAE2AgwgACACQQxqQQQQchogAkEQaiQACxcAIAAgASACQoCAgIAwIAMgBEECENIBCxgBAX4gASkDACEDIAEgAjcDACAAIAMQDAszAQF/IAIEQCAAIQMDQCADIAEtAAA6AAAgA0EBaiEDIAFBAWohASACQQFrIgINAAsLIAALwQUCAn4GfyMAQeAAayIJJAAgA0EAIANBAEobIQsDQCAKIAtHBEAgACACIApBBHRqIgMoAgAQsAUhBiADLQAEIQdCgICAgDAhBAJAAkACQAJAAkACQAJAAkACQAJAIAMtAAUOCgECAgUHAwQIBQAGCyAAIAMoAggQsAUhCAJ+AkACQAJAIAMoAgxBAWoOAwIAAQkLIAAgACkDwAEiBCAIIARBABARDAILIAAgACgCKCkDECIEIAggBEEAEBEMAQsgACABIAggAUEAEBELIQQgACAIEBAgBkHLAUYEQEEBIQcMCAsgBkHUAUcNB0EAIQcMBwsCQCAGQcsBRgRAQQEhBwwBCyAGQdQBRw0AQQAhBwsgACABIAZBAiADIAcQgAMaDAcLIAAgASAGQoCAgIAwIAMoAggEfiAJIAMoAgA2AhAgCUEgaiIIQcAAQeEqIAlBEGoQSBogACADKAIIIAhBAEEKQQggAy0ABUECRhsgAy4BBhCCAQVCgICAgDALIgQgAygCDAR+IAkgAygCADYCACAJQSBqIghBwABB2iogCRBIGiAAIAMoAgwgCEEBQQtBCSADLQAFQQJGGyADLgEGEIIBBUKAgICAMAsiBSAHQYA6chBqGiAAIAQQDCAAIAUQDAwGCyADKQMIIgRCgICAgAh8Qv////8PWARAIARC/////w+DIQQMBQtCgICAgMB+IAS5vSIEQoCAgIDAgYD8/wB9IARC////////////AINCgICAgICAgPj/AFYbIQQMBAtCgICAgMB+IAMpAwgiBEKAgICAwIGA/P8AfSAEQv///////////wCDQoCAgICAgID4/wBWGyEEDAMLIAAgASAGQQIgAyAHEIADGgwDCxABAAsgAzUCCCEECyAAIAEgBiAEIAcQFRoLIAAgBhAQIApBAWohCgwBCwsgCUHgAGokAAuMAgICfgF/AkACQAJAAkACQAJAAkACQAJAQQcgAUIgiKciBCAEQQdrQW5JG0EKag4SAgAGBAAAAAAAAQMFAAAAAAEDAAsgAEGbHkEAEBJCgICAgOAADwsgBEF1SQ0GIAGnIgAgACgCAEEBajYCAAwGCyAAQSEQhgEhAgwECyAAQQQQhgEhAgwDCyAAIABBBRCGASICQTAgAacpAgRC/////weDQQAQFRoMAgsgAEEGEIYBIQIMAQsgAEEHEIYBIQILQoCAgIDgACEDIAJCgICAgHCDQoCAgIDgAFIEfiAEQXVPBEAgAaciBCAEKAIAQQFqNgIACyAAIAIgARC9ASACBUKAgICA4AALDwsgAQsyAQF/AkAgAUIgiKdBdUkNACABpyICIAIoAgAiAkEBazYCACACQQFKDQAgACABEJcFCwsLACAAQeweQQAQEgujBAELfyAAKAIAIQUjAEEQayIIIAI2AgxBfyEJAkADQAJAIAggAiIDQQRqIgI2AgwgAygCACIHQX9GDQAgACgCBCEKA0AgASIEIApODQMgBCAEIAVqIgwtAAAiBkECdEHgrgFqIg0tAABqIgEgCkoNAyAGQcYBRgRAIAwoAAEhCQwBCwsgBiAHRwRAIAdBGHYgBkYgBiAHQRB2Qf8BcUZyIAYgB0H/AXFGckUgBiAHQQh2Qf8BcUdxIAZFIAdBgAJJcnINAyAAIAY2AhALIARBAWohBAJAAkACQAJAAkACQAJAAkAgDS0AA0EFaw4YAAkACQkBCQkBCQkBAQECAgICBAUGBwkDCQsgBCAFai0AACEEIAggA0EIaiICNgIMIAMoAgQiA0F/RgRAIAAgBDYCFAwJCyADIARGDQgMCQsgBCAFai8AACEEIAggA0EIaiICNgIMIAMoAgQiA0F/RgRAIAAgBDYCFAwICyADIARGDQcMCAsgACAEIAVqKAAANgIYDAYLIAAgBCAFaiIDKAAANgIYIAAgAy8ABDYCHAwFCyAAIAQgBWooAAA2AiAMBAsgACAEIAVqIgMoAAA2AiAgACADLQAENgIcDAMLIAAgBCAFaiIDKAAANgIgIAAgAy8ABDYCHAwCCyAAIAQgBWoiAygAADYCICAAIAMoAAQ2AhggACADLQAINgIcDAELCyAAIAk2AgwgACABNgIIQQEhCwsgCwskAQF/IAAoAhAiAkEQaiABIAIoAgARAwAiAUUEQCAAEHALIAELCwAgACABQQAQjQQLJwEBfyMAQRBrIgIkACACIAE7AQ4gACACQQ5qQQIQchogAkEQaiQAC9QBAgR/An5BfyECAkACQAJAAkACQAJAAkAgAUIgiKciA0EKag4RAwUFAgUFBQUFBAABAQEFBQYFCyABp0EARw8LIAGnDwsgAacpAgQhByAAIAEQDCAHQv////8Hg0IAUg8LIAGnKAIMIQQgACABEAwgBEH/////B2pBfkkPCyABpywABSEFIAAgARAMIAVBAE4PCyADQQdrQW1NBEAgAUKAgICAwIGA/P8AfEL///////////8Ag0IBfUKAgICAgICA+P8AVA8LIAAgARAMQQEhAgsgAgs/AQJ/IwBBEGsiAiQAAn8gASAAKAIQRwRAIAIgATYCACAAQcyQASACEBNBfwwBCyAAEA8LIQMgAkEQaiQAIAMLCwAgACABQQEQ6QULGQAgAEEAEFAaIABCgICAgPD/////ADcCBAvDCgIFfxF+IwBB4ABrIgUkACAEQv///////z+DIQwgAiAEhUKAgICAgICAgIB/gyEKIAJC////////P4MiDUIgiCEOIARCMIinQf//AXEhBwJAAkAgAkIwiKdB//8BcSIJQf//AWtBgoB+TwRAIAdB//8Ba0GBgH5LDQELIAFQIAJC////////////AIMiC0KAgICAgIDA//8AVCALQoCAgICAgMD//wBRG0UEQCACQoCAgICAgCCEIQoMAgsgA1AgBEL///////////8AgyICQoCAgICAgMD//wBUIAJCgICAgICAwP//AFEbRQRAIARCgICAgICAIIQhCiADIQEMAgsgASALQoCAgICAgMD//wCFhFAEQCACIAOEUARAQoCAgICAgOD//wAhCkIAIQEMAwsgCkKAgICAgIDA//8AhCEKQgAhAQwCCyADIAJCgICAgICAwP//AIWEUARAIAEgC4QhGUIAIQEgGVAEQEKAgICAgIDg//8AIQoMAwsgCkKAgICAgIDA//8AhCEKDAILIAEgC4RQBEBCACEBDAILIAIgA4RQBEBCACEBDAILIAtC////////P1gEQCAFQdAAaiABIA0gASANIA1QIgYbeSAGQQZ0rXynIgZBD2sQYkEQIAZrIQYgBSkDWCINQiCIIQ4gBSkDUCEBCyACQv///////z9WDQAgBUFAayADIAwgAyAMIAxQIggbeSAIQQZ0rXynIghBD2sQYiAGIAhrQRBqIQYgBSkDSCEMIAUpA0AhAwsgA0IPhiILQoCA/v8PgyICIAFCIIgiBH4iECALQiCIIhMgAUL/////D4MiAX58Ig9CIIYiESABIAJ+fCILIBFUrSACIA1C/////w+DIg1+IhUgBCATfnwiESAMQg+GIhIgA0IxiIRC/////w+DIgMgAX58IhQgDyAQVK1CIIYgD0IgiIR8Ig8gAiAOQoCABIQiDH4iFiANIBN+fCIOIBJCIIhCgICAgAiEIgIgAX58IhAgAyAEfnwiEkIghnwiF3whASAHIAlqIAZqQf//AGshBgJAIAIgBH4iGCAMIBN+fCIEIBhUrSAEIAQgAyANfnwiBFatfCACIAx+fCAEIAQgESAVVK0gESAUVq18fCIEVq18IAMgDH4iAyACIA1+fCICIANUrUIghiACQiCIhHwgBCACQiCGfCICIARUrXwgAiACIBAgElatIA4gFlStIA4gEFatfHxCIIYgEkIgiIR8IgJWrXwgAiACIA8gFFStIA8gF1atfHwiAlatfCIEQoCAgICAgMAAg1BFBEAgBkEBaiEGDAELIAtCP4ghGiAEQgGGIAJCP4iEIQQgAkIBhiABQj+IhCECIAtCAYYhCyAaIAFCAYaEIQELIAZB//8BTgRAIApCgICAgICAwP//AIQhCkIAIQEMAQsCfiAGQQBMBEBBASAGayIHQf8ATQRAIAVBMGogCyABIAZB/wBqIgYQYiAFQSBqIAIgBCAGEGIgBUEQaiALIAEgBxCNAiAFIAIgBCAHEI0CIAUpAzAgBSkDOIRCAFKtIAUpAyAgBSkDEISEIQsgBSkDKCAFKQMYhCEBIAUpAwAhAiAFKQMIDAILQgAhAQwCCyAEQv///////z+DIAatQjCGhAsgCoQhCiALUCABQgBZIAFCgICAgICAgICAf1EbRQRAIAogAkIBfCIBUK18IQoMAQsgCyABQoCAgICAgICAgH+FhFBFBEAgAiEBDAELIAogAiACQgGDfCIBIAJUrXwhCgsgACABNwMAIAAgCjcDCCAFQeAAaiQACykBAX8gAgRAIAAhAwNAIAMgAToAACADQQFqIQMgAkEBayICDQALCyAACwwAIAAoAkBBfxDRAwtqAQJ/AkAgACgC2AIiA0UNACAAKALgAiIEIAAoAtwCTg0AIAAoAugCIAFLDQAgACgC5AIgAkYNACADIARBA3RqIgMgAjYCBCADIAE2AgAgACABNgLoAiAAIARBAWo2AuACIAAgAjYC5AILCzUAIAAgAkEwIAJBABARIgJCgICAgHCDQoCAgIDgAFEEQCABQgA3AwBBfw8LIAAgASACEKEBC3kCAn8BfiABQiCIpyIDIAGnIgJBAEhyRQRAIAJBgICAgHhyDwsgA0F4RgRAIAAgACgCECACEMYCEBYPCyAAIAEQiQQiAUKAgICAcIMiBEKAgICA4ABRBEBBAA8LIARCgICAgIB/UQRAIAAgARCIAg8LIAAgAacQkQQLGQAgAQRAIAAgAUEQa61CgICAgJB/hBAMCwupAQEEfyAAQQA2AgQgAVAEQCAAQYCAgIB4NgIIIABBABBQGkEADwsCQCABQv////8PWARAIABBARBQDQEgACgCECABIAGnZyICrYY+AgAgAEEgIAJrNgIIQQAPCyAAQQIQUA0AIAAoAhAiAyABpyIEIAFCIIinIgVnIgJ0NgIAIAMgBSACdCAEQQF2IAJBf3N2cjYCBCAAQcAAIAJrNgIIQQAPCyAAECpBIAsQACAAIAAoAigpAwhBARBHCxQBAn4gACABECUhAyAAIAEQDCADC0sBAn8gAUKAgICAcFoEfyABpyIDLwEGIgJBDUYEQEEBDwsgAkEsRgRAIAMoAiAtABAPCyAAKAIQKAJEIAJBGGxqKAIQQQBHBUEACwsjAQF+IAAgASACQoCAgIAwIAMgBEECENIBIQUgACABEAwgBQuDAgIDfwF+QoCAgIDgACEEIAAoAhQEfkKAgICA4AAFIAAoAgQhASAAKAIIIgJFBEAgACgCACgCECICQRBqIAEgAigCBBEAACAAQQA2AgQgACgCAEEvECkPCyAAKAIMIAJKBEAgACgCACgCECIDQRBqIAEgAiAAKAIQIgF0IAFrQRFqIAMoAggRAQAiAUUEQCAAKAIEIQELIAAgATYCBAsgASAAKAIQIgIEfyACBSABIAAoAghqQQA6ABAgACgCEAtBH3StIAEpAgRC/////3eDhCIENwIEIAEgBEKAgICAeIMgADUCCEL/////B4OENwIEIABBADYCBCABrUKAgICAkH+ECwsPACAAKAJAQYACaiABEBsLEwAgACABIAIgAyABQYCAARDQAQsNACAAIAEgAkEGEMgCCx8BAX8gACgCJCIBIAEoAgBBAWo2AgAgACABQQIQ5wULZwECfwJ/IAAoAggiAiAAKAIMTgRAQX8gACACQQFqIAEQxAINARoLIAAgACgCCCIDQQFqNgIIIAAoAgRBEGohAgJAIAAoAhAEQCACIANBAXRqIAE7AQAMAQsgAiADaiABOgAAC0EACwt6AQN/AkACQCAAIgFBA3FFDQAgAS0AAEUEQEEADwsDQCABQQFqIgFBA3FFDQEgAS0AAA0ACwwBCwNAIAEiAkEEaiEBIAIoAgAiA0F/cyADQYGChAhrcUGAgYKEeHFFDQALA0AgAiIBQQFqIQIgAS0AAA0ACwsgASAAawsNACAAIAEgAkEAEJkDCywBAX8jAEEQayIDJAAgAyACNgIMIABB3ABqQYABIAEgAhDJAhogA0EQaiQAC+gDAQl/IwBBIGsiBSQAIAEgAiABKAIMIAIoAgxJIgYbIgcoAgQgAiABIAYbIggoAgRzIQoCQAJAIAcoAgwiAkUEQAJAIAgoAggiAUH/////B0cEQCAHKAIIIgJB/////wdHDQELIAAQKkEAIQIMAwsgAUH+////B0cgAkH+////B0dxRQRAAkAgAUH+////B0YEQCACQYCAgIB4Rg0BDAQLIAFBgICAgHhHIAJB/v///wdHcg0DCyAAECpBASECDAMLIAAgChCAAUEAIQIMAgsgCCgCDCILIQkgAiEGIARBB3FBBkYEQCACIANBIWpBBXYiASABIAJKGyEGIAsgASABIAtKGyEJCwJ/AkAgACAIRg0AIAAgB0YNACAADAELIAAoAgAhASAFQgA3AhggBUKAgICAgICAgIB/NwIQIAUgATYCDCAAIQwgBUEMagshASAHKAIQIQAgCCgCECENAn8gASAGIAlqEFAEQCABECpBIAwBCyABKAIQIA0gC0ECdGogCUECdGsgCSAAIAJBAnRqIAZBAnRrIAYQ8AEgASAKNgIEIAEgBygCCCAIKAIIajYCCCABIAMgBBCbAgshAiABIAVBDGoiAEcNASAMIAAQvwQMAQsgACAKEH9BACECCyAFQSBqJAAgAgsKACAAIAFBARBHCygBAX8gAkIgiKdBdU8EQCACpyIDIAMoAgBBAWo2AgALIAAgASACEG0LlQUCA38BfgJAAkACQAJAAkACQANAIAIoAhAiBEEwaiEFIAQgBCgCGCADcUF/c0ECdGooAgAhBANAIARFDQQgAyAFIARBAWtBA3QiBmoiBCgCBEcEQCAEKAIAQf///x9xIQQMAQsLIAIoAhQgBmohBSAEKAIAIQYgAUUNASABQoCAgIAwNwMYIAFCgICAgDA3AxAgAUKAgICAMDcDCCABIAZBGnZBB3EiBjYCAAJAAkACQAJAIAQoAgBBHnZBAWsOAwABAgMLIAEgBkEQcjYCACAFKAIAIgAEQCAAIAAoAgBBAWo2AgAgASAArUKAgICAcIQ3AxALIAUoAgQiAEUNCSAAIAAoAgBBAWo2AgAgASAArUKAgICAcIQ3AxhBAQ8LIAUoAgAoAhApAwAiB0KAgICAcINCgICAgMAAUQ0EIAdCIIinQXVPBEAgB6ciACAAKAIAQQFqNgIACyABIAc3AwgMCAsgACACIAMgBSAEEMECRQ0BDAYLCyAFKQMAIgdCIIinQXVPBEAgB6ciACAAKAIAQQFqNgIACyABIAc3AwgMBQtBASEEIAZBgICAgHxxQYCAgIB4Rw0CIAUoAgAoAhA1AgRCIIZCgICAgMAAUg0CCyAAIAMQ0QEMAgtBACEEIAItAAUiBUEEcUUNACAFQQhxBEAgA0EATg0BIANB/////wdxIgMgAigCKCIFSSEEIAFFIAMgBU9yDQEgAUKAgICAMDcDGCABQoCAgIAwNwMQIAFBBzYCACABIAAgAq1CgICAgHCEIAMQpgE3AwgMAwsgACgCECgCRCACLwEGQRhsaigCFCIFRQ0AIAUoAgAiBUUNACAAIAEgAq1CgICAgHCEIAMgBREXACEECyAEDwtBfw8LQQELDQAgACABIAJBARDIAgsmAQF/AkAgACgCEEGDf0cNACAAKAIgIAFHDQAgACgCJEUhAgsgAgsdACAAIAEpAxAQDCAAIAEpAxgQDCAAIAEpAwgQDAumAQEDfyAAKAIQIgMoAuABIAGnQQAgAUL/////b1YbIgRBgYDc8XlsQf//o44GayIFQSAgAygC1AFrdkECdGohAwJAAkADQCADKAIAIgMEQAJAIAMoAhQgBUcNACADKAIsIARHDQAgAygCIEUNAwsgA0EoaiEDDAELCyAAIARBAhDyBCIDDQFCgICAgOAADwsgAyADKAIAQQFqNgIACyAAIAMgAhDnBQsqAQJ/IwBBEGsiBCQAIAQgAzYCDCAAIAEgAiADEMkCIQUgBEEQaiQAIAULSAAgACABRwRAIAAgASgCDBBQBEAgABAqQSAPCyAAIAEoAgQ2AgQgACABKAIINgIIIAAoAhAgASgCECABKAIMQQJ0EB4aC0EACywAIAFCgICAgGCDQoCAgIAgUQRAIABB6z9BABASQoCAgIDgAA8LIAAgARAlC6sCAQR/AkAgAiADTw0AIAMgAmshBSABQRBqIQQgAS0AB0GAAXEEQEEAIQMgBUEAIAVBAEobIQYgBCACQQF0aiEBQQAhAgNAIAIgBkZFBEAgAyABIAJBAXRqLwEAciEDIAJBAWohAgwBCwsCQCAAKAIIIAVqIgIgACgCDCIHSgRAQX8hBCAAIAIgAxDEAkUNAQwDCyAAKAIQIANBgAJIcg0AQX8hBCAAIAcQ4AMNAgsCQCAAKAIQRQRAQQAhAgNAIAIgBkYNAiAAKAIEIAAoAgggAmpqIAEgAkEBdGotAAA6ABAgAkEBaiECDAALAAsgACgCBCAAKAIIQQF0akEQaiABIAVBAXQQHhoLIAAgACgCCCAFajYCCEEADwsgACACIARqIAUQiwIhBAsgBAuJAQECfyABKAJ8IgRB//8DTgRAIABBlyhBABA6QX8PC0F/IQMgACABQfQAakEQIAFB+ABqIARBAWoQZAR/QX8FIAEgASgCfCIDQQFqNgJ8IAEoAnQgA0EEdGoiA0IANwIAIANCADcCCCADIAAgAhAWNgIAIAMgAygCDEGAfnI2AgwgASgCfEEBawsLRwEBfyABQiCIp0F1TwRAIAGnIgMgAygCAEEBajYCAAsgAkIgiKdBdU8EQCACpyIDIAMoAgBBAWo2AgALIAAgASACQQEQtAEL+wQBAn8CQAJAIAFCgICAgHBUIAJC/////w9Wcg0AIAKnIQMCQAJAAkACQAJAAkACQAJAAkACQAJAIAGnIgQvAQZBAmsOHgALCwsLCwALCwsLCwsLCwsLCwsCAQIDBAUGBwgJCgsLIAQoAiggA00NCiAEKAIkIANBA3RqKQMAIgFCIIinQXVJDQsgAaciACAAKAIAQQFqNgIAIAEPCyAEKAIoIANNDQkgBCgCJCADajAAAEL/////D4MPCyAEKAIoIANNDQggBCgCJCADajEAAA8LIAQoAiggA00NByAEKAIkIANBAXRqMgEAQv////8Pgw8LIAQoAiggA00NBiAEKAIkIANBAXRqMwEADwsgBCgCKCADTQ0FIAQoAiQgA0ECdGo1AgAPCyAEKAIoIANNDQQgBCgCJCADQQJ0aigCACIAQQBOBEAgAK0PC0KAgICAwH4gALi9IgFCgICAgMCBgPz/AH0gAUKAgICAgICA+P8AVhsPCyAEKAIoIANNDQMgACAEKAIkIANBA3RqKQMAEL8CDwsgBCgCKCADTQ0CIAAgBCgCJCADQQN0aikDABCIBA8LIAQoAiggA00NAUKAgICAwH4gBCgCJCADQQJ0aioCALu9IgFCgICAgMCBgPz/AH0gAUL///////////8Ag0KAgICAgICA+P8AVhsPCyAEKAIoIANNDQBCgICAgMB+IAQoAiQgA0EDdGopAwAiAUKAgICAwIGA/P8AfSABQv///////////wCDQoCAgICAgID4/wBWGw8LIAAgAhAwIQMgACACEAwgA0UEQEKAgICA4AAPCyAAIAEgAyABQQAQESEBIAAgAxAQCyABC4IDAgR/An4CQCAAKQNwIgVQRSAFIAApA3ggACgCBCIBIAAoAiwiAmusfCIGV3FFBEAjAEEQayICJABBfyEBAkACfyAAIAAoAkgiA0EBayADcjYCSCAAKAIUIAAoAhxHBEAgAEEAQQAgACgCJBEBABoLIABBADYCHCAAQgA3AxAgACgCACIDQQRxBEAgACADQSByNgIAQX8MAQsgACAAKAIsIAAoAjBqIgQ2AgggACAENgIEIANBG3RBH3ULDQAgACACQQ9qQQEgACgCIBEBAEEBRw0AIAItAA8hAQsgAkEQaiQAIAEiA0EATg0BIAAoAgQhASAAKAIsIQILIABCfzcDcCAAIAE2AmggACAGIAIgAWusfDcDeEF/DwsgBkIBfCEGIAAoAgQhASAAKAIIIQICQCAAKQNwIgVQDQAgBSAGfSIFIAIgAWusWQ0AIAEgBadqIQILIAAgAjYCaCAAIAYgACgCLCIAIAFrrHw3A3ggACABTwRAIAFBAWsgAzoAAAsgAwtPAQF/An9BACAAKAIMIAFGDQAaIAAoAgAiAigCACAAKAIQIAFBAnQgAigCBBEBACECIAEEQEF/IAJFDQEaCyAAIAE2AgwgACACNgIQQQALC9EBAQZ/IABBAWohBQJAAkAgAC0AACIDwCIHQQBOBEAgBSEBDAELQX8hBCAHQUBrQf8BcSIDQT1LDQEgA0ECdEGU9gFqKAIAIgYgAU4NASAGQQFrIQggACAGakEBaiEBIAcgBkHz9QFqLQAAcSEDQQAhAANAIAAgBkcEQCAFLAAAIgRBv39KBEBBfw8FIARBP3EgA0EGdHIhAyAAQQFqIQAgBUEBaiEFDAILAAsLQX8hBCADIAhBAnRBgPYBaigCAEkNAQsgAiABNgIAIAMhBAsgBAsLACAAIAFBABDpBQsJACAAQQEQrQELugEBAn8CQAJAIAJC/////wdYBEAgACABIAKnQYCAgIB4chBuIgRBAEwNASAAIAEgAhBOIgJCgICAgHCDQoCAgIDgAFINAkF/IQQMAgsgACACEIsDIgVFBEBBfyEEDAELAkAgACABIAUQbiIEQQBMBEBCgICAgDAhAgwBCyAAIAEgBSABQQAQESICQoCAgIBwg0KAgICA4ABSDQBBfyEECyAAIAUQEAwBC0KAgICAMCECCyADIAI3AwAgBAsbAQF/IAAgARA1BH9BAAUgAEH7OUEAEBJBfwsLYwEBfyACQiCIp0F1TwRAIAKnIgUgBSgCAEEBajYCAAsCQCAAIAEgAhDTBSIFDQACQCABKAIAIgBBAEgEQCAAIARqIgBBACAAQQBKGyEDDAELIAAgA0wNAQsgASADNgIACyAFCxgAIAAtAABBIHFFBEAgASACIAAQlwQaCwsPACAAKAJAQYACaiABEA4LrgIAAkACQAJAAkAgAkEDTARAAkACQAJAAkACQAJAAkACQAJAIAFB2ABrDgkAAQIDBAUGBwgKCyAAIAJBO2tB/wFxEA4PCyAAIAJBN2tB/wFxEA4PCyAAIAJBM2tB/wFxEA4PCyAAIAJBL2tB/wFxEA4PCyAAIAJBK2tB/wFxEA4PCyAAIAJBJ2tB/wFxEA4PCyAAIAJBI2tB/wFxEA4PCyAAIAJBH2tB/wFxEA4PCyAAIAJBG2tB/wFxEA4PCyACQf8BSw0BAkACQAJAIAFB2ABrDgMAAQIECyAAQcIBEA4MBQsgAEHDARAODAQLIABBxAEQDgwDCyABQSJGDQELIAAgAUH/AXEQDiAAIAJB//8DcRAmDwsgACACQRJrQf8BcRAODwsgACACQf8BcRAOCzgBAX8CQAJAIAFCgICAgHBUDQAgAaciAy8BBiACRw0AIAMoAiAiAw0BCyAAIAIQigNBACEDCyADC0EBAX8gAQRAA0AgAiADRkUEQCAAIAEgA0EDdGooAgQQECADQQFqIQMMAQsLIAAoAhAiAEEQaiABIAAoAgQRAAALCywBAX8gACgCECICQRBqIAEgAigCABEDACICBEAgAkEAIAEQLA8LIAAQcCACC20BAX8jAEGAAmsiBSQAIARBgMAEcSACIANMckUEQCAFIAFB/wFxIAIgA2siA0GAAiADQYACSSIBGxAsGiABRQRAA0AgACAFQYACEFcgA0GAAmsiA0H/AUsNAAsLIAAgBSADEFcLIAVBgAJqJAALvgECAn4BfwJAAkAgAUKAgICAcINCgICAgDBRBEAgACgCKCACQQN0aikDACIDQiCIp0F0Sw0BDAILIAAgAUE8IAFBABARIgNCgICAgHCDQoCAgIDgAFEEQCADDwsgA0L/////b1YNASAAIAMQDCAAIAEQ/AIiBUUEQEKAgICA4AAPCyAFKAIoIAJBA3RqKQMAIgNCIIinQXVJDQELIAOnIgUgBSgCAEEBajYCAAsgACADIAIQRyEEIAAgAxAMIAQLDAAgAEHZ6gBBABASCw0AIAAgASABED0Q6gELdQEBfiAAIAEgBH4gAiADfnwgA0IgiCICIAFCIIgiBH58IANC/////w+DIgMgAUL/////D4MiAX4iBUIgiCADIAR+fCIDQiCIfCABIAJ+IANC/////w+DfCIBQiCIfDcDCCAAIAVC/////w+DIAFCIIaENwMAC1ABAX4CQCADQcAAcQRAIAEgA0FAaq2GIQJCACEBDAELIANFDQAgAiADrSIEhiABQcAAIANrrYiEIQIgASAEhiEBCyAAIAE3AwAgACACNwMIC2QAAkACQCABQQBIDQAgACgCrAIgAUwNACAAKAKkAiABQRRsaiIAIAAoAgAgAmoiADYCACAAQQBIDQEgAA8LQdwXQajsAEHzpwFBr8MAEAAAC0HphQFBqOwAQfanAUGvwwAQAAALcAECfyAEIAMoAgBKBH8jAEEQayIFJAAgACABKAIAIAQgAygCAEEDbEECbSIAIAAgBEgbIgAgAmwgBUEMahCnASIEBH8gAyAFKAIMIAJu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EGsiBCQAAkAgAkIgiKciA0ECTQRAIAEgAqe3OQMAQQAhAwwBCyADQQdrQW1NBEAgASACQoCAgIDAgYD8/wB8NwMAQQAhAwwBCwJ/IAAgAhCWASICQoCAgIBwg0KAgICA4ABRBEBEAAAAAAAA+H8hBUF/DAELAnwCQAJAQQcgAkIgiKciAyADQQdrQW5JGyIDQXZHBEAgA0EHRg0CIAMNASACp7cMAwsgAqdBBGogBEEIahCxBCAAIAIQDCAEKwMIIQVBAAwDCxABAAsgAkKAgICAwIGA/P8AfL8LIQVBAAshAyABIAU5AwALIARBEGokACADC90BAQN/AkAgAUKAgICAcFoEQCABpyEDA0ACQCADLQAFQQRxRQ0AIAAoAhAoAkQgAy8BBkEYbGooAhQiBEUNACAEKAIQIgRFDQAgAyADKAIAQQFqNgIAIAAgA61CgICAgHCEIgEgAiAEERMAIQUgACABEAwgBQ8LIAMgAygCAEEBajYCACAAQQAgAyACEEMhBCAAIAOtQoCAgIBwhBAMIAQNAgJAIAMvAQZBFWtB//8DcUEKSw0AIAAgAhCTAyIERQ0AIARBH3UPCyADKAIQKAIsIgMNAAsLQQAhBAsgBAvNCQIEfwV+IwBB8ABrIgYkACAEQv///////////wCDIQkCQAJAIAFQIgUgAkL///////////8AgyIKQoCAgICAgMD//wB9QoCAgICAgMCAgH9UIApQG0UEQCADQgBSIAlCgICAgICAwP//AH0iC0KAgICAgIDAgIB/ViALQoCAgICAgMCAgH9RGw0BCyAFIApCgICAgICAwP//AFQgCkKAgICAgIDA//8AURtFBEAgAkKAgICAgIAghCEEIAEhAwwCCyADUCAJQoCAgICAgMD//wBUIAlCgICAgICAwP//AFEbRQRAIARCgICAgICAIIQhBAwCCyABIApCgICAgICAwP//AIWEUARAQoCAgICAgOD//wAgAiABIAOFIAIgBIVCgICAgICAgICAf4WEUCIFGyEEQgAgASAFGyEDDAILIAMgCUKAgICAgIDA//8AhYRQDQEgASAKhFAEQCADIAmEQgBSDQIgASADgyEDIAIgBIMhBAwCCyADIAmEUEUNACABIQMgAiEEDAELIAMgASABIANUIAkgClYgCSAKURsiCBshCiAEIAIgCBsiDEL/////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/wBWGwsQTgtTAQF/IAAoAhAiBEEQaiABIAIgBCgCCBEBACIBIAJFckUEQCAAEHAgAQ8LIAMEQCADIAEgACgCECgCDBEFACIAIAJrIgJBACAAIAJPGzYCAAsgAQsNACAAQQAgAUEAEJoDC/kBAgN+An8jAEEQayIFJAACfiABvSIEQv///////////wCDIgJCgICAgICAgAh9Qv/////////v/wBYBEAgAkI8hiEDIAJCBIhCgICAgICAgIA8fAwBCyACQoCAgICAgID4/wBaBEAgBEI8hiEDIARCBIhCgICAgICAwP//AIQMAQsgAlAEQEIADAELIAUgAkIAIAKnZ0EgaiACQiCIp2cgAkKAgICAEFQbIgZBMWoQYiAFKQMAIQMgBSkDCEKAgICAgIDAAIVBjPgAIAZrrUIwhoQLIQIgACADNwMAIAAgAiAEQoCAgICAgICAgH+DhDcDCCAFQRBqJAALKgEBfyMAQRBrIgMkACADIAI2AgwgACABIAJBpANBABCUBBogA0EQaiQAC0cAIAAgAUkEQCAAIAEgAhAeGg8LIAIEQCAAIAJqIQAgASACaiEBA0AgAEEBayIAIAFBAWsiAS0AADoAACACQQFrIgINAAsLCyABAX4gACAAIAIgASADQQRBABCCASIFIAEgBBC/ASAFC4sMAQZ/IwBBIGsiAyQAAkACQAJAAkACQAJ/IAAoAhAiAkGDf0cEQEEAIAJBV0cNARogACgCQCIELQBsQQFxRQRAIABBl+AAQQAQEwwDCyAEKAJkRQRAIABB6jtBABATDAMLQX8hBSAAEA8NBQJAAkACQAJAIAAoAhAiBEEpaw4EAgEBAgALIARB3QBGIARBOmtBAklyIARB/QBGcg0BCyAAKAIwDQBBACECIARBKkYEQCAAEA8NCEEBIQILIAAgARCtAUUNAQwHCyAAQQYQDUEAIQILIAAoAkAtAGwhASACBEAgABAtIQUgABAtIQIgAEGAAUH/ACABQQNGGxANIABBDhANIABBBhANIABBBhANIAAgBRAaIABBhgEQDSABQQNHIgdFBEAgAEGMARANCyAAQYMBEA0gAEHCABANIABB6gAQFyAAQesAQX8QGCEGIAAgAhAaQYoBIQQgACAHBH9BigEFIABBwQAQDSAAQcEAEBdBiwELEA0gAEEREA0gAEHrAEF/EBghBCAAQQ4QDSAAQewAIAUQGBogACAEEBogAEEBEA0gAEECEDggAEGsARANIABB6wBBfxAYIQQgAUEDRyIFRQRAIABBjAEQDQsgAEGHARANIABBABBYIABB6wBBfxAYIQcgBUUEQCAAQYwBEA0LIABBgwEQDSAAQcIAEA0gAEHqABAXIABB6gAgAhAYGiAAQcEAEA0gAEHBABAXIAAgBxAaIABBDxANIABBDxANIABBDxANIABBARCwAiAAIAQQGiAAQYcBEA0gAEEBEFggAEHrAEF/EBghBCABQQNHIgFFBEAgAEGMARANCyAAQYMBEA0gAEHCABANIABB6gAQFyAAQeoAIAIQGBogAEHsACAGEBgaIAAgBBAaIABBhwEQDSAAQQIQWCAAQesAQX8QGCEEIAFFBEAgAEGMARANCyAAIAQQGiAAQTAQDUEAIQUgAEEAEBcgAEEEEFggACAGEBogAEHBABANIABBwQAQFyAAQQ8QDSAAQQ8QDSAAQQ8QDQwGCyABQQNGBEAgAEGMARANCyAAQYkBEA0gAEHqAEF/EBghASAAQQEQsAIMBAsgACgCIAshBEF/IQUgAEGifyABQQRyEMADDQMgACgCECICQaZ/RgRAIAFBe3EhBiAAEC0hAgNAIAAQDw0FIABBERANIABBsQEQDSAAQeoAIAIQGBogAEEOEA0gAEEIIAYQ9gENBSAAKAIQQaZ/Rg0ACyAAIAIQGiAAKAIQIQILIAJBP0YEQCAAEA8NBCAAQeoAQX8QGCECIAAQUw0EIABBOhAoDQQgAEHsAEF/EBghBiAAIAIQGiAAIAFBAXEQrQENBCAAIAYQGiAAKAIQIQILIAJBPUciBiACQfsAaiIFQQtLcUUEQCAAEA8NASAAIANBHGogA0EYaiADQRRqIANBEGpBACAGIAIQrgFBAEgNASAAIAEQrQEEQCAAKAIAIAMoAhQQEAwCCwJAIAJBPUYEQEE8IQEgAygCFCECIAMoAhwiBUE8RwRAIAIhBCAFIQEMAgsgAiAERwRAIAIhBAwCCyAAIAQQngEMAQsgACAFQbDJAWotAAAQDSADKAIUIQQgAygCHCEBC0EAIQUgACABIAMoAhggBCADKAIQQQJBABDBAQwEC0EAIQUgAkHvAGpBAksNAyAAEA8NACAAIANBHGogA0EYaiADQRRqIANBEGogA0EMakEBIAIQrgFBAEgNACAAQREQDSACQZN/RgRAIABBsQEQDQsgAEHrAEHqACACQZJ/RhtBfxAYIQIgAEEOEA0gACABEK0BRQ0BIAAoAgAgAygCFBAQC0F/IQUMAgsCQCADKAIcIgFBPEcNACADKAIUIARHDQAgACAEEJ4BCyADKAIMQQFrIgRBA08NAiAAIARBFWpB/wFxEA0gACABIAMoAhggAygCFCADKAIQQQFBABDBASAAQewAQX8QGCEBIAAgAhAaIAMoAgwhBQNAIAUEQCAAQQ8QDSADIAMoAgxBAWsiBTYCDAwBCwsLIAAgARAaQQAhBQsgA0EgaiQAIAUPCxABAAuSBQEHfwJAAkACQCAAKAJAIgsoApgCIg5BAEgNAEECIQ0CQAJAIAsoAoACIA5qIgwtAAAiCEHHAGsOBAQCAgEACyAIQcEARg0CIAhBvwFHBEAgCEG4AUcNAiAMKAABIglBCEYNAiAMLwAFIQogCUE7RwRAIAlB8gBGDQMgCUHOAEcNBQsgCy0AbkEBcUUNBCAAQdDaAEEAEBNBfw8LIAwvAAUhCiAMKAABIQlBASENDAMLQQMhDQwCCyAHQbt/RgRAIABBkd4AQQAQE0F/DwsgB0F+cUGUf0YEQCAAQdjiAEEAEBNBfw8LIAdBX3FB2wBGBEAgAEGLHUEAEBNBfw8LIABBst4AQQAQE0F/DwsgDCgAASEJQQEhDQtBfyEHIAtBfzYCmAIgCyAONgKEAgJAAkAgBgRAAkACQAJAAkAgCEHHAGsOBAEDAwIACwJAIAhBwQBHBEAgCEG/AUYNASAIQbgBRw0EIAAQLSEHIABBuwEQDSAAIAkQFyAAIAcQOCAAIAoQFCALIAdBARBjGkE8IQggAEE8EA0MBwsgAEHCABANIAAgCRAXQcEAIQgMBgsgAEHAARANIAAgCRAXIAAgChAUQb8BIQgMBQsgAEHzABANIABBExANQccAIQgMAwsgAEHyABANIABBFBANQcoAIQgMAgsQAQALAkACQAJAIAhBxwBrDgQBBAQCAAsgCEG4AUcNAyAAEC0hByAAQbsBEA0gACAJEBcgACAHEDggACAKEBQgCyAHQQEQYxpBPCEIDAMLIABB8wAQDUHHACEIDAILIABB8gAQDUHKACEIDAELIAAgCBANCyABIAg2AgAgAiAKNgIAIAMgCTYCACAEIAc2AgAgBQRAIAUgDTYCAAtBAAtaAQN/IwBBEGsiASQAAkAgACgCECIDQap/Rg0AIANBO0cEQCADQf0ARg0BIAAoAjANASABQTs2AgAgAEHMkAEgARATQX8hAgwBCyAAEA8hAgsgAUEQaiQAIAIL2QIBA38jAEFAaiIGJAACfyACIAEoAgBPBEAgBiACNgI0IAYgAzYCMCAAQdmKASAGQTBqEDpBfwwBCwJAIAEoAgQgBE4NACABIAQ2AgQgBEH//wNIDQAgBiACNgIEIAYgAzYCACAAQYGLASAGEDpBfwwBCyABKAIIIAJBAXRqIgcvAQAiA0H//wNHBEAgAyAERwRAIAYgAjYCKCAGIAQ2AiQgBiADNgIgIABBsooBIAZBIGoQOkF/DAILQQAgASgCDCACQQJ0aigCACIBIAVGDQEaIAYgAjYCGCAGIAU2AhQgBiABNgIQIABBh4oBIAZBEGoQOkF/DAELIAcgBDsBACABKAIMIAJBAnRqIAU2AgBBfyAAIAFBEGpBBCABQRhqIAEoAhRBAWoQZA0AGiABIAEoAhQiAEEBajYCFCABKAIQIABBAnRqIAI2AgBBAAshCCAGQUBrJAAgCAs7AAJ/IAAgAUGAgARPBH9BfyAAIAFBgIAEa0EKdkGAsANqEIcBDQEaIAFB/wdxQYC4A3IFIAELEIcBCwvBAQIGfwF+IwBBIGsiBSQAAn4CQCACQoCAgIBwg0KAgICAkH9SBEAgACACEDQiAkKAgICAcINCgICAgOAAUQ0BCyAAIAVBCGoiBCABED0iByADED0iCGogAqciBigCBCIJQf////8HcWogCUEfdhCZAw0AIAQgASAHEIsCGiAEIAZBACAGKAIEQf////8HcRBLGiAEIAMgCBCLAhogACACEAwgBBA3DAELIAAgAhAMQoCAgIDgAAshCiAFQSBqJAAgCgspAQF/IAJCIIinQXVPBEAgAqciAyADKAIAQQFqNgIACyAAIAEgAhDTBQufBAMEfwJ8AX4jAEEwayIHJABBByACQiCIpyIEIARBB2tBbkkbIQVBACEEAkACQAJAAnwCQAJAAkACQAJAAkACQEEHIAFCIIinIgYgBkEHa0FuSRsiBkEKag4SCAkDAgkJCQkJBAUAAQEJCQkGCQsgBUEBRw0IIAGnIAKnRiEEDAkLIAUgBkYhBAwHCyAFQXlHDQYgAacgAqcQvAJFIQQMBgsgAacgAqdGIAVBeEZxIQQMBQsgBUF/Rw0EIAGnIAKnRiEEDAQLIAGntyEIIAVBB0cEQCAFDQQgAqe3DAILIAJCgICAgMCBgPz/AHy/DAELIAFCgICAgMCBgPz/AHy/IQggBQRAIAVBB0cNAyACQoCAgIDAgYD8/wB8vwwBCyACp7cLIQkCQCADBEACQAJAIAi9IgFC////////////AIMiAkKBgICAgICA+P8AWgRAIAm9Qv///////////wCDQoGAgICAgID4/wBUIQQMAQsgCb0iCkL///////////8Ag0KBgICAgICA+P8AVA0BCyAEIAJCgICAgICAgPj/AFZzIQQMBQsgA0ECRw0BCyAIIAlhIQQMAwsgASAKUSEEDAILIAVBdkcNACAAIAdBHGoiBiABEK0CIgMgACAHQQhqIAIQrQIiBRC9AiEEIAMgBkYEQCAGEBkLIAUgB0EIaiIDRw0AIAMQGQsgACABEAwgACACEAwLIAdBMGokACAECy8BAX8jAEHQAGsiAyQAIAMgACADQRBqIAEQgQE2AgAgACACIAMQEiADQdAAaiQACw0AIAAgASABED0QnQMLHQEBfyAAIAFB/wFxEA4gACgCBCEDIAAgAhAbIAMLEgAgACABIAIgAyAEQZQDELEDC1IBAX8gACgCDCIDRQRAQQAPCyAAIAAoAghB/////wNBgYCAgHwgASABQYGAgIB8TBsiASABQf////8DThtqNgIIIABB/////wMgAiADQQAQ3AILHwEBfyAAKAIMIgNFBEBBAA8LIAAgASACIANBABDcAgsgACABQgA3AgwgAUKAgICAgICAgIB/NwIEIAEgADYCAAtmAQF/An9BACAAKAIIIgIgAU8NABpBfyAAKAIMDQAaIAAoAhQgACgCACACQQNsQQF2IgIgASABIAJJGyIBIAAoAhARAQAiAkUEQCAAQQE2AgxBfw8LIAAgATYCCCAAIAI2AgBBAAsLZwECfwJAIAFCgICAgHBUDQAgAaciAy8BBkEEayIEQR1LQQEgBHRBz4CAgAJxRXINACAAIAMpAyAQDCADIAI3AyAPCyAAIAIQDCABQoCAgIBwg0KAgICA4ABSBEAgAEHu0gBBABASCwshAQF/IAAgASAAIAIQtgEiAiADIAQQFSEFIAAgAhAQIAULRwIBfgF/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PEdxRQRAIAAgBigCNBCeAQsgACAEEBoLIAAgBigCMCAGKAIsIAYoAjQgBigCPEEBIAwQwQELIAAoAhBB3QBGDQAgB0Glf0YEQEGQ0wAhCAwECyAAQSwQKEUNAQwFCwsgAEGFARANIAAoAkAiASABKAKwAigCADYCsAIgABAPDQMLAkAgBUUNACAAKAIQQT1HDQBBfyEHIABB7ABBfxAYIQEgABAPDQQgACAKEBogAwRAIABBDhANCyAAEFMNBCAAQewAIAsQGBogACABEBpBASEHDAQLIANFBEAgAEHDPUEAEBMMAwsgACgCQCgCgAIgDWpBswEgDiANaxAsGiAAKAJAKAKkAiAKQRRsaiIAIAAoAgBBAWs2AgBBACEHDAMLIAAgCEEAEBMMAQsgACgCACAGKAI0EBALQX8hBwsgBkFAayQAIAcLKwAgACgCQCgCpAFBAE4EQCAAQQYQDSAAQdkAEA0gACAAKAJALwGkARAUCwsTACAAIAEgAiADIARBAEEAEN0BC6YBAQF/IwBBEGsiAyQAIAMgAjcDCAJAIAAgAUGHASABQQAQESICQoCAgIBwg0KAgICA4ABRDQAgACACEDUEQCAAIAIgAUEBIANBCGoQNiICQv////9vViACQoCAgICwf4NCgICAgCBRcg0BIAAgAhAMIABBpcEAQQAQEkKAgICA4AAhAgwBCyAAIAIQDCAAIAEgACADQQhqEIoFIQILIANBEGokACACC6MBAgN/AX4gAEEQaiECIAEoAgAiBEEBaiEDAkAgACkCBCIFQoCAgIAIg1BFBEAgAiAEQQF0ai8BACIAQYD4A3FBgLADRyADIAWnQf////8HcU5yDQEgAiADQQF0ai8BACICQYD4A3FBgLgDRw0BIABBCnRBgPg/cSACQf8HcXJBgIAEaiEAIARBAmohAwwBCyACIARqLQAAIQALIAEgAzYCACAACxIAIAFB2AFOBEAgACABEIYFCwthACAAIAEgAkKAgICACHxC/////w9YBH4gAkL/////D4MFQoCAgIDAfiACub0iAkKAgICAwIGA/P8AfSACQv///////////wCDQoCAgICAgID4/wBWGwsgAyAEQQdyEJQBCzkAIABB/wBNBEAgAEEDdkH8////AXFBsP8BaigCACAAdkEBcQ8LIABBfnFBjMAARiAAEJYGQQBHcgs1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QfD//wFxayIYJAAgBSEVIAYEQCAHIAQgByAIGyIIQQAgCEEAShsiCGsiCUEAIAcgCU8bIREDQAJAIAggCkYEQANAIAggEUYNAiAYIAhBA3RqQoCAgIAwNwMAIAhBAWohCAwACwALIAUgCkEDdCIJaikDACIBQiCIp0F1TwRAIAGnIhUgFSgCAEEBajYCAAsgCSAYaiABNwMAIBFBAWohESAKQQFqIQoMAQsLIA4gBzYCVCAYIRULIA4gFTYCQCAOIBggBkEDdGoiETYCREEAIQgDQCAIIBdHBEAgESAIQQN0akKAgICAMDcDACAIQQFqIQgMAQsLIBIoAhQhBiAOIBYoAowBNgIwIBYgDkEwaiIUNgKMASASKAIwIQkgESAXQQN0aiIIIRcLQQAMAQtBAQshBwNAAkACQAJAAkAgB0UEQCAEQQN0ISMgA0KAgICAcIMhOyARQQhqIRogEUEQaiEbIBFBGGohHCAVQQhqIR0gFUEQaiEeIBVBGGohHyAUQRhqISQgAkIgiKciIEF+cSElIANCIIinISYgBK0hOiADpyEhIA5BMGohJyAOQegAaiEiIAghBwJAA0ACQCAGQQFqIQxCgICAgDAhNQJ/AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJ/AkACQAJAAkACQAJAAkACQAJAAkACQAJA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AAsgCCABNwMQIAhBGGohBwzeAQsgCCAIQQhrIgYpAwA3AwAgCEEQaykDACIBQiCIp0F1TwRAIAGnIgcgBygCAEEBajYCAAsgBiABNwMAIAhBCGohBwzdAQsgCCAIQQhrIgYpAwAiATcDACAGIAhBEGsiBikDADcDACABQiCIp0F1TwRAIAGnIgcgBygCAEEBajYCAAsgBiABNwMAIAhBCGohBwzcAQsgCCAIQQhrIgYpAwAiATcDACAIQRBrIgcpAwAhNSAHIAhBGGsiBykDADcDACAGIDU3AwAgAUIgiKdBdU8EQCABpyIGIAYoAgBBAWo2AgALIAcgATcDACAIQQhqIQcM2wELIAggCEEIayIGKQMAIgE3AwAgCEEQayIHKQMAITUgByAIQRhrIgcpAwA3AwAgBiA1NwMAIAcgCEEgayIGKQMANwMAIAFCIIinQXVPBEAgAaciByAHKAIAQQFqNgIACyAGIAE3AwAgCEEIaiEHDNoBCyAIQRBrIgYpAwAhASAGIAhBGGsiBikDADcDACAGIAE3AwAM0wELIAhBGGsiBikDACEBIAYgCEEQayIGKQMANwMAIAhBCGsiBykDACE1IAcgATcDACAGIDU3AwAM0gELIAhBIGsiBikDACEBIAYgCEEYayIGKQMANwMAIAhBEGsiBykDACE1IAcgCEEIayIHKQMANwMAIAYgNTcDACAHIAE3AwAM0QELIAhBKGsiBikDACEBIAYgCEEgayIGKQMANwMAIAhBGGsiBykDACE1IAcgCEEQayIHKQMANwMAIAYgNTcDACAHIAhBCGsiBikDADcDACAGIAE3AwAM0AELIAhBCGsiBikDACEBIAYgCEEQayIGKQMANwMAIAhBGGsiBykDACE1IAcgATcDACAGIDU3AwAMzwELIAhBEGsiBikDACEBIAYgCEEYayIGKQMANwMAIAhBIGsiBykDACE1IAcgATcDACAGIDU3AwAMzgELIAhBEGsiBikDACEBIAYgCEEYayIGKQMANwMAIAhBIGsiBykDACE1IAcgCEEoayIHKQMANwMAIAYgNTcDACAHIAE3AwAMzQELIAhBCGsiBikDACEBIAYgCEEQayIGKQMANwMAIAYgATcDAAzMAQsgCEEgayIGKQMAIQEgBiAIQRBrIgYpAwA3AwAgCEEIayIHKQMAITUgByAIQRhr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xH4iAUIAUg0EIA0gD3JBAE4NBSALQoCAgIDA/v8DNwMADA0LIAtCgICAgMB+IA+3IA23o70iAUKAgICAwIGA/P8AfSABQv///////////wCDQoCAgICAgID4/wBWGzcDAAwMCyANQQBKIA9BAE5xRQRAIAsCfiAPtyANtxCZBCI8vSIBAn8gPJlEAAAAAAAA4EFjBEAgPKoMAQtBgICAgHgLIgq3vVEEQCAKrQwBC0KAgICAwH4gAUKAgICAwIGA/P8AfSABQv///////////wCDQoCAgICAgID4/wBWGws3AwAMDAsgDyANcK0hAQwCCyAPtyE8IAsCfgJ8IA23Ij29QoCAgICAgID4/wCDQoCAgICAgID4/wBRBEBEAAAAAAAA+H8gPJlEAAAAAAAA8D9hDQEaCyA8ID0QowMLIjy9IgECfyA8mUQAAAAAAADgQWMEQCA8qgwBC0GAgICAeAsiCre9UQRAIAqtDAELQoCAgIDAfiABQoCAgIDAgYD8/wB9IAFC////////////AINCgICAgICAgPj/AFYbCzcDAAwKCyA1xCABxH0hAQsgAUKAgICACHxC/////w9WDQEgASE2CyA2Qv////8PgwwBC0KAgICAwH4gAbm9IgFCgICAgMCBgPz/AH0gAUL///////////8Ag0KAgICAgICA+P8AVhsLNwMADAULIBBBdkcgD0F2R3FFBEAgCSAKIAsgNSABIAkoAhAoAqwCESMADQcMBQsgCSAHQQhqIDUQbQ0FIAkgByABEG0NBgsCQAJAAkACQCAKQZsBaw4GAAECBAUDBAsgBysDCCAHKwMAogwFCyAHKwMIIAcrAwCjDAQLIAcrAwggBysDABCZBAwDCyAHKwMIITwgBysDACI9vUKAgICAgICA+P8Ag0KAgICAgICA+P8AUQRARAAAAAAAAPh/IDyZRAAAAAAAAPA/YQ0DGgsgPCA9EKMDDAILEAEACyAHKwMIIAcrAwChCyE8IAtCgICAgMB+IDy9IgFCgICAgMCBgPz/AH0gAUL///////////8Ag0KAgICAgICA+P8AVhs3AwALQQAMAgsgCSABEAwLIAtCgICAgDA3AwAgDUKAgICAMDcDAEF/CyEwIAdBEGokACAwDUsgCEEIayEHDEcLIAhBBGsoAgAiB0UgB0EHa0FuSXINQCAIIQcgCSAIQY4BEOEBRQ1GDEoLAkACfCAIQQhrIgcpAwAiAUIgiKciCkUEQEQAAAAAAAAAgCABpyIGRQ0BGkQAAAAAAADgQSAGQYCAgIB4Rg0BGiAHQgAgAX1C/////w+DNwMADEILIApBB2tBbUsNASABQoCAgIDA/v8Dfb8LITwgB0KAgICAwH4gPL0iAUKAgICAwIGA/P8AfSABQv///////////wCDQoCAgICAgID4/wBWGzcDAAxACyAIIQcgCSAIQY0BEOEBRQ1FDEkLIAhBCGsiBykDACIBQv////8PViABQv////8HUXJFBEAgByABQgF8Qv////8PgzcDAAw/CyAIIQcgCSAIQZABEOEBRQ1EDEgLIAhBCGsiBykDACIBQv////8PViABQoCAgIAIUXJFBEAgByABQgF9Qv////8PgzcDAAw+CyAIIQcgCSAIQY8BEOEBRQ1DDEcLIAkgCEEIayIHKQMAEGUiAUKAgICAcINCgICAgOAAUQRAIAdCgICAgDA3AwAMRwsgByABNwMAIAFCIIinQXVPBEAgAaciByAHKAIAQQFqNgIACyAIIAE3AwAgCSAIQQhqIgcgCkECaxDhAUUNQgxGCyAGQQJqIQwgESAGLQABQQN0aiIHKQMAIgFC/////w9WIAFC/////wdRckUEQCAHIAFCAXxC/////w+DNwMADDwLIAFCIIinQXVPBEAgAaciCiAKKAIAQQFqNgIACyAOIAE3A2AgCSAiQZABEOEBDUUgCSAHIA4pA2AQHQw7CyAGQQJqIQwgESAGLQABQQN0aiIHKQMAIgFC/////w9WIAFCgICAgAhRckUEQCAHIAFCAX1C/////w+DNwMADDsLIAFCIIinQXVPBEAgAaciCiAKKAIAQQFqNgIACyAOIAE3A2AgCSAiQY8BEOEBDUQgCSAHIA4pA2AQHQw6CyAIQQhrIgcpAwAiAUL/////D1gEQCAHIAFC/////w+FNwMADDoLIAghByMAQRBrIgokAAJ/AkACQCAJIAhBCGsiCykDABBlIgFCgICAgHCDIjVCgICAgOAAUQ0AIDVCgICAgOB+UQRAIAkgC0GWASABIAkoAhAoAqgCER8ADQEMAgsgCSAKQQxqIAEQlQENACALIAo1AgxC/////w+FNwMADAELIAtCgICAgDA3AwBBfwwBC0EACyExIApBEGokACAxRQ0/DEMLIAhBCGsiBykDACIBIAhBEGsiCikDACI1hEL/////D1gEQCAKIDWnIAGndK03AwAMPwsgCSAIQaEBELUCRQ0+DEILIAhBCGsiBykDACIBIAhBEGsiCikDACI1hEL/////D1gEQCAKAn4gNacgAad2IgZBAE4EQCAGrQwBC0KAgICAwH4gBri9IgFCgICAgMCBgPz/AH0gAUKAgICAgICA+P8AVhsLNwMADD4LIwBBEGsiCiQAIAhBCGsiDSkDACEBAn8CQAJAIAkgCEEQayILKQMAEGUiNUKAgICAcIMiNkKAgICA4ABRDQAgCSABEGUiAUKAgICAcIMiN0KAgICA4ABRBEAgNSEBDAELIDZCgICAgOB+UiA3QoCAgIDgflJxDQEgCUGlgAFBABASIAkgNRAMCyAJIAEQDCALQoCAgIAwNwMAIA1CgICAgDA3AwBBfwwBCyAJIApBDGogNRCVARogCSAKQQhqIAEQlQEaIAsCfiAKKAIMIAooAgh2IgtBAE4EQCALrQwBC0KAgICAwH4gC7i9IgFCgICAgMCBgPz/AH0gAUKAgICAgICA+P8AVhsLNwMAQQALITIgCkEQaiQAIDJFDT0MQQsgCEEIayIHKQMAIgEgCEEQayIKKQMAIjWEQv////8PWARAIAogNacgAad1rTcDAAw9CyAJIAhBogEQtQJFDTwMQAsgCEEIayIHKQMAIgEgCEEQayIKKQMAIjWEQv////8PWARAIAogASA1gzcDAAw8CyAJIAhBrgEQtQJFDTsMPwsgCEEIayIHKQMAIAhBEGsiCikDAIQiAUL/////D1gEQCAKIAE3AwAMOwsgCSAIQbABELUCRQ06DD4LIAhBCGsiBykDACIBIAhBEGsiCikDACI1hEL/////D1gEQCAKIAEgNYU3AwAMOgsgCSAIQa8BELUCRQ05DD0LIAhBCGsiBykDACIBIAhBEGsiCikDACI1hEL/////D1gEQCAKIDWnIAGnSK1CgICAgBCENwMADDkLIAkgCEGkARCEA0UNOAw8CyAIQQhrIgcpAwAiASAIQRBrIgopAwAiNYRC/////w9YBEAgCiA1pyABp0ytQoCAgIAQhDcDAAw4CyAJIAhBpQEQhANFDTcMOwsgCEEIayIHKQMAIgEgCEEQayIKKQMAIjWEQv////8PWARAIAogNacgAadKrUKAgICAEIQ3AwAMNwsgCSAIQaYBEIQDRQ02DDoLIAhBCGsiBykDACIBIAhBEGsiCikDACI1hEL/////D1gEQCAKIDWnIAGnTq1CgICAgBCENwMADDYLIAkgCEGnARCEA0UNNQw5CyAIQQhrIgcpAwAiASAIQRBrIgopAwAiNYRC/////w9YBEAgCiA1pyABp0atQoCAgIAQhDcDAAw1CyAJIAhBABC+BUUNNAw4CyAIQQhrIgcpAwAiASAIQRBrIgopAwAiNYRC/////w9YBEAgCiA1pyABp0etQoCAgIAQhDcDAAw0CyAJIAhBARC+BUUNMww3CyAIQQhrIgcpAwAiASAIQRBrIgYpAwAiNYRC/////w9YBEAgBiA1pyABp0atQoCAgIAQhDcDAAwzCyAJIAhBABC9BQwyCyAIQQhrIgcpAwAiASAIQRBrIgYpAwAiNYRC/////w9YBEAgBiA1pyABp0etQoCAgIAQhDcDAAwyCyAJIAhBARC9BQwxCyAIQQhrIgcpAwAiAUL/////b1gEQCAJQZ/jAEEAEBIMNQsgCSAIQRBrIg0pAwAiNRAwIgpFDTQgCSABIAoQbiELIAkgChAQIAtBAEgNNCAJIDUQDCAJIAEQDCANIAtBAEetQoCAgIAQhDcDAAwwCyAIQRBrIg0pAwAiAUL/////b1gEQCAJQZ/jAEEAEBIMNAsgCEEIayIHKQMAIjVCgICAgHBaBEAgCSABIDUQzgUiC0EASA00DBsLIAkgNRAwIgpFDTMgAacoAhAiBkEwaiELIAYgBigCGCAKcUF/c0ECdGooAgAhCANAIAhFBEBBACEIDBsLIAsgCEEDdGoiBkEIayEIIAZBBGsoAgAgCkYNGiAIKAIAQf///x9xIQgMAAsACyAJIAhBEGsiCikDACIBIAhBCGsiBykDACI1EOIFIgtBAEgNMiAJIAEQDCAJIDUQDCAKIAtBAEetQoCAgIAQhDcDAAwuCyAJIAhBCGsiBikDACIBEPoDIQcgCSABEAwgBiAJIAcQKTcDAAwnCyAIQRBrIg0pAwAhASAJIAhBCGsiBykDACI1EDAiCkUNMCAJIAEgCkGAgAIQzQEhCyAJIAoQECALQQBIDTAgCSABEAwgCSA1EAwgDSALQQBHrUKAgICAEIQ3AwAMLAsgBkEFaiEMIAkgCSkDwAEgBigAAUEAEM0BIgdBAEgNLyAIIAdBAEetQoCAgIAQhDcDACAIQQhqIQcMKwsgCEEIayIHKQMAIgFC/////29WDSQgCSABECAiAUKAgICAcINCgICAgOAAUQ0uIAkgBykDABAMIAcgATcDAAwkCyAIQQhrIgcpAwAiAUIgiKdBCGoiCkEITUEAQQEgCnRBgwJxGw0jIAkgARCJBCIBQoCAgIBwg0KAgICA4ABRDS0gCSAHKQMAEAwgByABNwMADCMLIAhBEGspAwBCgICAgBCEQoCAgIBwg0KAgICAMFEEQCAJQfIJQQAQEgwtCyAIQQhrIgcpAwAiAUIgiKdBCGoiCkEITUEAQQEgCnRBgwJxGw0iIAkgARCJBCIBQoCAgIBwg0KAgICA4ABRDSwgCSAHKQMAEAwgByABNwMADCILIAZBCmohDCAGLQAJIQsgBigABSEPIAkgCEEIayIHKQMAIgEgBigAASINEG4iEEEASA0rAkAgEEUNACALBEBBACELIAkgAUHWASABQQAQESI1QoCAgIBwg0KAgICA4ABRDS0gNUKAgICAcFoEQCAJIAkgNSANIDVBABARECchCwsgCSA1EAwgC0EASA0tIAsNAQsCQAJAAkACQAJAAkACQCAKQfQAaw4GAAECAwQFBgsgCSABIA0gAUEAEBEiAUKAgICAcINCgICAgOAAUQ0yIAkgByABEB0MBQsgCSABIA0gCEEQayIIKQMAIAFBgIACENABITMgCSAHKQMAEAwgM0EATg0EDDELIAkgASANQQAQzQEiCkEASA0wIAkgBykDABAMIAcgCkEAR61CgICAgBCENwMADAMLIAggCSANEFI3AwAgCEEIaiEIDAILIAkgASANIAFBABARIgFCgICAgHCDQoCAgIDgAFENLiAIIAE3AwAgCEEIaiEIDAELIAkgASANIAFBABARIgFCgICAgHCDQoCAgIDgAFENLSAJIAcpAwAQDCAHQoCAgIAwNwMAIAggATcDACAIQQhqIQgLIAwgD2pBBWshDAwiCyAJIAcpAwAQDAwnCyAIQQhrKQMAIjVCgICAgHCDQoCAgIAwUQ0PDAULIAhBCGspAwAiNUKAgICAcINCgICAgCBRDQ4MBAsgCSAIQQhrKQMAIjUQ+gNBxgBGDQEMAwsgCSAIQQhrKQMAIjUQ+gNBG0cNAgsgCSA1EAwMCwsgCEEIaykDACI1QoCAgIBgg0KAgICAIFENCgsgCSA1EAwgCEEIa0KAgICAEDcDAAwaCyASKAIUIQcgDiAKNgIEIA4gB0F/cyAMajYCACAJQYUQIA4QOgwjCyAGQQNqIQwMGAtCAyE1DCMLQgAhNQwiC0IBITUMIQtCAiE1DCALIAhBCGsiCCkDACE1DCALIAsgDigCYDYCFCAOQYABaiEGDA0LQaj2AEGo7ABBgfsAQasiEAAACyAIQQhrQoGAgIAQNwMADBALIAkgChAQIAhBAEchCwsgCSABEAwgCSA1EAwgDSALQQBHrUKAgICAEIQ3AwAMFAsgByEIDBcLIAkgCBC/BUUNEgwWCyAJIAFBARCQARogCSABEAwgCSA1EAwMFQsgASE5DAILQoCAgIAwITgLIAkgB0EAEBILIAkgNhAMIAkgOBAMIAkgNxAMIAkgORAMIAkgNRAMIAtCgICAgDA3AwAgDUKAgICAMDcDAAwRCyAJIAcpAwAQDCAHQoCAgIAwNwMAIApBAEgNECAJIDUQDEKAgICAMCE1CyAIIAE3AwggCCA1NwMAIAhBEGohBwwLCyALIAYoAgA2AgwLIA8gATcDAAwDCyANLQAFQQFxDQELIAkgB0GDjwEQtQEMCwsgCSgCyAEoAhAiCkEwaiELIAogCigCGCAHcUF/c0ECdGooAgAhCgNAIApFDQEgCyAKQQN0aiINQQhrIQogByANQQRrKAIARwRAIAooAgBB////H3EhCgwBCwsgCg0BCyAIIQcMBQsgCSAHEMwFDAgLIAkQIgwHCyAJIAEQDAsgCEKAgICA4AA3AwAgCEEIaiEIDAULIAsgBDYCKCALIAo2AiQgCSkDqAEiNUIgiKdBdU8EQCA1pyIGIAYoAgBBAWo2AgALIAkgAUHMASA1QQMQFRogCSABQc8AQoCAgIAwIAkpA7ABIjUgNUGAMBBqGiAIIAE3AwAgCEEIaiEHC0EACyE0IAchCCAMIQYgNEUNAQsLIAchCAtBASEHDAULAkAgFikDgAEiNUKAgICAcFQNACA1pyIGLwEGQQNHDQAgBigCECIGQTBqIQcgBiAGKAIYQX9zQQJ0QaR+cmooAgAhBgJAA0AgBkUNASAHIAZBA3RqIgpBCGshBiAKQQRrKAIAQTZHBEAgBigCAEH///8fcSEGDAELCyAGDQELIBQgDDYCICAJIDVBAEEAQQAQtAIgFikDgAEhNQtBACEGAkAgNUKAgICAcFQNACA1pyIHLwEGQQNHDQAgBy0ABUEFdkEBcSEGCwJAIAYNACAIIQYDQCAGIgggF00NASAJIAhBCGsiBikDACIBEAwgAUKAgICAcINCgICAgNAAUg0AIAGnIgcNBSAJIAhBEGsiBikDABAMIAkgCEEYaykDAEEBEJABGgwACwALQoCAgIDgACE1IBItABFBMHFFDQELIBQgCDYCLCAUIAw2AiAMAQsgFCgCHCAUQRhqRwRAIBYgFBC8BQsDQCAIIBhNDQEgCSAYKQMAEAwgGEEIaiEYDAALAAsgFiAUKAIANgKMAQwCCyAGIBYpA4ABNwMAIBZCgICAgCA3A4ABIBIoAhQgB2ohBkEAIQcMAAsACyAOQaABaiQAIDULigEBAn8gASgCECIDLQAQRQRAQQAPCwJAIAMoAgBBAUcEQCACBH8gAigCACADa0Ewa0EDdQVBAAshBCAAIAMQ1wUiA0UEQEF/DwsgACgCECABKAIQEIwCIAEgAzYCECACRQ0BIAIgAyAEQQN0akEwajYCAEEADwsgACgCECADEIMEIANBADoAEAtBAAv8CwEHfwJAIABFDQAgAEEIayIDIABBBGsoAgAiAUF4cSIAaiEFAkAgAUEBcQ0AIAFBAnFFDQEgAyADKAIAIgFrIgNB1N4EKAIASQ0BIAAgAWohAAJAAkBB2N4EKAIAIANHBEAgAygCDCECIAFB/wFNBEAgAUEDdiEBIAMoAggiBCACRgRAQcTeBEHE3gQoAgBBfiABd3E2AgAMBQsgBCACNgIMIAIgBDYCCAwECyADKAIYIQYgAiADRwRAIAMoAggiASACNgIMIAIgATYCCAwDCyADKAIUIgEEfyADQRRqBSADKAIQIgFFDQIgA0EQagshBANAIAQhByABIgJBFGohBCACKAIUIgENACACQRBqIQQgAigCECIBDQALIAdBADYCAAwCCyAFKAIEIgFBA3FBA0cNAkHM3gQgADYCACAFIAFBfnE2AgQgAyAAQQFyNgIEIAUgADYCAA8LQQAhAgsgBkUNAAJAIAMoAhwiAUECdEH04ARqIgQoAgAgA0YEQCAEIAI2AgAgAg0BQcjeBEHI3gQoAgBBfiABd3E2AgAMAgsgBkEQQRQgBigCECADRhtqIAI2AgAgAkUNAQsgAiAGNgIYIAMoAhAiAQRAIAIgATYCECABIAI2AhgLIAMoAhQiAUUNACACIAE2AhQgASACNgIYCyADIAVPDQAgBSgCBCIBQQFxRQ0AAkACQAJAAkAgAUECcUUEQEHc3gQoAgAgBUYEQEHc3gQgAzYCAEHQ3gRB0N4EKAIAIABqIgA2AgAgAyAAQQFyNgIEIANB2N4EKAIARw0GQczeBEEANgIAQdjeBEEANgIADwtB2N4EKAIAIAVGBEBB2N4EIAM2AgBBzN4EQczeBCgCACAAaiIANgIAIAMgAEEBcjYCBCAAIANqIAA2AgAPCyABQXhxIABqIQAgBSgCDCECIAFB/wFNBEAgAUEDdiEBIAUoAggiBCACRgRAQcTeBEHE3gQoAgBBfiABd3E2AgAMBQsgBCACNgIMIAIgBDYCCAwECyAFKAIYIQYgAiAFRwRAQdTeBCgCABogBSgCCCIBIAI2AgwgAiABNgIIDAMLIAUoAhQiAQR/IAVBFGoFIAUoAhAiAUUNAiAFQRBqCyEEA0AgBCEHIAEiAkEUaiEEIAIoAhQiAQ0AIAJBEGohBCACKAIQIgENAAsgB0EANgIADAILIAUgAUF+cTYCBCADIABBAXI2AgQgACADaiAANgIADAMLQQAhAgsgBkUNAAJAIAUoAhwiAUECdEH04ARqIgQoAgAgBUYEQCAEIAI2AgAgAg0BQcjeBEHI3gQoAgBBfiABd3E2AgAMAgsgBkEQQRQgBigCECAFRhtqIAI2AgAgAkUNAQsgAiAGNgIYIAUoAhAiAQRAIAIgATYCECABIAI2AhgLIAUoAhQiAUUNACACIAE2AhQgASACNgIYCyADIABBAXI2AgQgACADaiAANgIAIANB2N4EKAIARw0AQczeBCAANgIADwsgAEH/AU0EQCAAQXhxQezeBGohAQJ/QcTeBCgCACIEQQEgAEEDdnQiAHFFBEBBxN4EIAAgBHI2AgAgAQwBCyABKAIICyEAIAEgAzYCCCAAIAM2AgwgAyABNgIMIAMgADYCCA8LQR8hAiAAQf///wdNBEAgAEEmIABBCHZnIgFrdkEBcSABQQF0a0E+aiECCyADIAI2AhwgA0IANwIQIAJBAnRB9OAEaiEHAn8CQAJ/QcjeBCgCACIBQQEgAnQiBHFFBEBByN4EIAEgBHI2AgBBGCECIAchBEEIDAELIABBGSACQQF2a0EAIAJBH0cbdCECIAcoAgAhBANAIAQiASgCBEF4cSAARg0CIAJBHXYhBCACQQF0IQIgASAEQQRxakEQaiIHKAIAIgQNAAtBGCECIAEhBEEICyEAIAMiAQwBCyABKAIIIgQgAzYCDEEIIQIgAUEIaiEHQRghAEEACyEFIAcgAzYCACACIANqIAQ2AgAgAyABNgIMIAAgA2ogBTYCAEHk3gRB5N4EKAIAQQFrIgBBfyAAGzYCAAsLqAEAAkAgAUGACE4EQCAARAAAAAAAAOB/oiEAIAFB/w9JBEAgAUH/B2shAQwCCyAARAAAAAAAAOB/oiEAQf0XIAEgAUH9F04bQf4PayEBDAELIAFBgXhKDQAgAEQAAAAAAABgA6IhACABQbhwSwRAIAFByQdqIQEMAQsgAEQAAAAAAABgA6IhAEHwaCABIAFB8GhMG0GSD2ohAQsgACABQf8Haq1CNIa/ogudAQEFfyAAQf8ASwRAQfECIQICQANAIAIgA0gNASAAIAIgA2pBAXYiBEECdEGggAJqKAIAIgVBD3YiBkkEQCAEQQFrIQIMAQsgACAFQQh2Qf8AcSAGak8EQCAEQQFqIQMMAQsLIAAgBCAFIAEQnAYhAAsgAA8LIAEEQCAAQSByIAAgAEHBAGtBGkkbDwsgAEEgayAAIABB4QBrQRpJGwuOCAEPfyMAQeAEayINJAAgACACELgDIQ4gACACQYABchC4AyESAkAgAkUgAUECSXINACANIAE2AgQgDSAANgIAIA1BADYCCEEAIAJrIQ8gDUEMciEJA0AgCSANTQ0BQTIgCUEEaygCACIMIAxBMkwbIRMgCUEIaygCACEHIAlBDGsiCSgCACEAA0ACQCAHQQdJDQAgDCATRgRAIAIgB2wiBiACayEKIAdBAXYgAmwhByAAIAIQuAMhCANAIAcEQCAHIAJrIgchBQNAIAVBAXQgAmoiASAGTw0CIAEgCkkEQCABIAJBACAAIAFqIgEgASACaiAEIAMRAQBBAEwbaiEBCyAAIAVqIgUgACABaiIMIAQgAxEBAEEASg0CIAUgDCACIAgRBgAgASEFDAALAAsLA0AgBiACayIGRQRAQQAhBwwDCyAAIAAgBmogAiAIEQYAIAYgAmshB0EAIQUDQCAFQQF0IAJqIgEgBk8NASABIAdJBEAgASACQQAgACABaiIBIAEgAmogBCADEQEAQQBMG2ohAQsgACAFaiIFIAAgAWoiCiAEIAMRAQBBAEoNASAFIAogAiAIEQYAIAEhBQwACwALAAsgACAHQQJ2IAJsIgVqIgYgACAFQQF0aiIBIAQgAxEBACEKIAEgACAFQQNsaiIFIAQgAxEBACEIAkAgCkEASARAIAhBAEgNASAFIAYgBiAFIAQgAxEBAEEASBshAQwBCyAIQQBKDQAgBiAFIAYgBSAEIAMRAQBBAEgbIQELIAxBAWohDCAAIAEgAiAOEQYAQQEhBiAAIAIgB2xqIgghBSAIIQogACACaiILIQFBASEQA0ACQAJAIAEgBU8NACAAIAEgBCADEQEAIhFBAEgNACARDQEgCyABIAIgDhEGACACIAtqIQsgEEEBaiEQDAELAkADQCABIAUgD2oiBU8NASAAIAUgBCADEQEAIhFBAEwEQCARDQEgCiAPaiIKIAUgAiAOEQYAIAdBAWshBwwBCwsgASAFIAIgDhEGAAwBCyAAIAEgCyAAayIFIAEgC2siCyAFIAtJGyIFayAFIBIRBgAgASAIIAggCmsiCyAKIAFrIgUgBSALSxsiAWsgASASEQYAIAcgBmshASAIIAVrIQUCQCABIAYgEGsiB0kEQCAAIQYgByEIIAUhACABIQcMAQsgBSEGIAEhCAsgCSAMNgIIIAkgCDYCBCAJIAY2AgAgCUEMaiEJDAMLIAEgAmohASAGQQFqIQYMAAsACwsgACACIAdsaiEHIAAhBgNAIAIgBmoiBiEBIAYgB08NAQNAIAAgAU8NASABIA9qIgUgASAEIAMRAQBBAEwNASABIAUgAiAOEQYAIAUhAQwACwALAAsACyANQeAEaiQAC2AAIARB9AAgA0HEAGsgA0G3AUYbQf8BcRAOIAQgACACEBYQGyAFIAEgBSgCABDRAyIANgIAIAQgABAbIAQgBkH/AXEQDiABIAUoAgBBARBjGiABIAEoAtACQQFqNgLQAguiCQIGfwF+IwBBEGsiBCQAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgACgCECICQc0Aag4DBAEDAAsgAkHsAGpBAkkNAQJAIAJBK2sOAwEGAQALIAJBWEYNBCACQf4ARg0AIAJBIUcNBQtBfyEDIAAQDw0KIABBEBDZAQ0KAkACQAJAAkACQAJAIAJBK2sOAwIFAQALIAJBtH9GDQMgAkEhRg0CIAJB/gBHDQQgAEGWARANDA4LIABBjQEQDQwNCyAAQY4BEA0MDAsgAEGXARANDAsLIABBDhANIABBBhANDAoLEAEACyAAEA8NByAAQQAQ2QENByAAIARBDGogBEEIaiAEIARBBGpBAEEBIAIQrgENByAAIAJBBWtB/wFxEA0gACAEKAIMIAQoAgggBCgCACAEKAIEQQJBABDBAQwEC0F/IQMgABAPDQggAEEQENkBDQhBACEDAkAgACgCQCIBKAKYAiICQQBIDQAgASgCgAIgAmoiAS0AAEG4AUcNACABQbcBOgAACyAAQZgBEA0MCAsgACgCQCEBQX8hAyAAEA8NByAAQRAQ2QENB0EAIQMgASgCmAIiAkEASA0EAkACQAJAAkACQAJ/AkACQCABKAKAAiACaiIGLQAAIgVBvwFrDgYFDAwMAQQACwJAIAVBxwBrDgQDDAwGAAsgBUHBAEcNBkF/DAELIAYoAAYLIQUgBigAASEDIAEgAjYChAIgACAAKAIAIAMQUiIIQQEQwAEhByAAKAIAIAgQDCAAKAIAIAMQEEF/IQMgBw0MIABBmQEQDUEAIQMgBUEATgRAIABB7ABBfxAYIQIgACAFEBogAEEOEA0gAEEKEA0gACACEBoLIAFBfzYCmAIMDAsgAUF/NgKYAiABIAI2AoQCIABBmQEQDQwKCyAGKAACIQMgASACNgKEAiAAQZkBEA0gAEHsAEF/EBghAiAAIAMQGiAAQQ4QDSAAQQoQDSAAIAIQGiABQX82ApgCDAkLIABB+eMAQQAQEwwHCyABQX82ApgCIAEgAjYChAIgAEEwEA0gAEEAEBcgAEEDEFgMCAsgBUG4AUYNAwwECyAAKAJAIgEtAGxBAnFFBEAgAEH83wBBABATDAULIAEoAmRFBEAgAEHOO0EAEBMMBQtBfyEDIAAQDw0GIABBEBDZAQ0GIAAoAkBBATYCmAMgAEGMARANDAULQX8hAyAAIAFBBHFBAnIQxAMNBSAAKAIwDQAgACgCECICQX5xQZR/Rw0AIAAgBEEMaiAEQQhqIAQgBEEEakEAQQEgAhCuAQ0FIAAgAkEDa0H/AXEQDSAAIAQoAgwgBCgCCCAEKAIAIAQoAgRBA0EAEMEBIAAQDw0FC0EAIQMgAUEYcUUNBCAAKAIQQaN/Rw0EIAFBEHEEQCAAKAIAQduQAUEAEIoCDAMLQX8hAyAAEA8NBCAAQQgQ2QENBCAAQaABEA0MAwsgBigAASICQQhGIAJB8gBGcg0AIAEtAG5BAXEEQCAAQZPbAEEAEBMMAgsgBkG6AToAAAwCCyAAQQ4QDSAAQQoQDQwCC0F/IQMMAQtBACEDCyAEQRBqJAAgAwt6AQN/IAAoAkAiAQRAIAEoArwBIQIgAEG1ARANIAAgAkH//wNxEBQgASABKALMASIDIAJBA3RqKAIAIgA2ArwBA0ACQCAAQQBIBEBBfyEADAELIAMgAEEDdGoiAigCBCIAQQBODQAgAigCACEADAELCyABIAA2AsABCwvgKgERfyMAQZABayIEJAAgACgCACENAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAAoAhAiAkGDf0cNACAAKAIoDQEgAEEAEHNBOkcEQCAAKAIQIQIMAQsgDSAAKAIgEBYhCSAAKAJAQbACaiEDAkADQCADKAIAIgNFDQEgAygCBCAJRw0ACyAAQf7VAEEAEBMMGAsgABAPDRcgAEE6ECgNFyAAKAIQIgJBxwBqQQNJDQAgABAtIQUgBCAAKAJAIgIoArACNgJQIAIgBEHQAGo2ArACIARBfzYCZCAEQv////8PNwJcIAQgBTYCWCAEIAk2AlQgBCACKAK8ATYCaEEAIQMgBEEANgJsIAAgAUEedEEfdUEAQQMgAi0AbkEBcRtxENsBDRcgACAFEBogACgCQCIAIAAoArACKAIANgKwAgwZCwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAJB0gBqDiQDEgEiEhISEhISEgUEBgcHCBISAgkSEgwQCw8hEREREhISEiEACyACQYN/Rg0MIAJBO0YNCSACQfsARw0RIAAQ6QINIgwjCyAAKAJAIgEoAiAEQCAAQYo6QQAQEwwiCyABLQBtQQh0QYAORgRAIABBzMUAQQAQEwwiCyAAEA8NIUEAIQMgAAJ/QQAgACgCECICQTtGDQAaQQAgAkH9AEYNABpBACAAKAIwDQAaIAAQiwENIkEBCxCwAiAAEK8BDSEMIwsgABAPDSAgACgCMARAIABBxhBBABATDCELIAAQiwENICAAQS8QDSAAEK8BRQ0hDCALIAAQDw0fIAAQdBogABDDASAAEPgBDR8gAEHqAEF/EBghASAAIAAoAkAtAG5Bf3NBAXEiAxDbAQ0fAkAgACgCEEGvf0cEQCABIQIMAQsgAEHsAEF/EBghAiAAEA8NICAAIAEQGiAAIAMQ2wENIAsgACACEBoMHAsgABAtIQEgABAtIQIgBCAAKAJAIgMoArACNgJQIAMgBEHQAGo2ArACIARCgICAgHA3AmAgBCABNgJcIAQgAjYCWCAEIAk2AlQgAygCvAEhAyAEQQA2AmwgBCADNgJoIAAQDw0eIAAQwwEgACABEBogABD4AQ0eIABB6gAgAhAYGiAAEKQCDR4gAEHsACABEBgaIAAgAhAaIAAoAkAiACAAKAKwAigCADYCsAIMHwsgABAtIQEgABAtIQIgABAtIQMgBCAAKAJAIgUoArACNgJQIAUgBEHQAGo2ArACIARCgICAgHA3AmAgBCABNgJcIAQgAjYCWCAEIAk2AlQgBSgCvAEhBSAEQQA2AmwgBCAFNgJoIAAQDw0dIAAgAxAaIAAQwwEgABCkAg0dIAAgARAaIABBun8QKA0dIAAQ+AENHSAAKAIQQTtGBEAgABAPDR4LIABB6wAgAxAYGiAAIAIQGiAAKAJAIgAgACgCsAIoAgA2ArACDB4LIAAQDw0cIAAQwwEgBEEANgIYAkAgACgCECICQVhHBEBBASEBIAJBKEcNASAAIARBGGpBABCcARoMAQsgACgCQC0AbEECcUUEQCAAQaMkQQAQEwweCyAAEA8NHSAAKAJAQQE2ApgDQQAhAQsgAEEoECgNHEEBIQIgBC0AGEEBcUUEQCAAKAIAIQggACgCQCILKAK8ASEOIAAQLSEHIAAQLSEQIAAQLSERIAAQLSESIAAQdBogBCAAKAJAIgMoArACNgJQIAMgBEHQAGo2ArACIARBADYCbCAEQoGAgIBwNwJgIAQgBzYCXCAEIBE2AlggBCAJNgJUIAQgDjYCaCAAQewAQX8QGCEPIAAoAkAoAoQCIQogACASEBogACgCECEDQVEhBQJAAkACQAJAAkACQCAAQQQQygMOAgABIwsgA0FJRiEMIANBUUYhAiACIANBsX9GckUgA0FJR3ENASADIQULIAAQDw0hIAAoAhAiA0H7AEYgA0HbAEZyDQMCQCADQYN/RgRAIAAoAihFDQELIABB4eYAQQAQEwwiCyAIIAAoAiAQFiEGIAAQDwRAIAAoAgAgBhAQDCILIAAgBiAFEKMCRQ0BIAAoAgAgBhAQDCELAkAgAUUNACAAQYYBEEVFDQAgAEEAEHNBWUcNACAAQZ6QAUEAEBMMIQsCQAJAIAAoAhBBIHJB+wBHDQAgACAEQUBrQQAQnAEiAkFZRyACQbd/R3ENACAAQQBBAEEBIAQoAkBBAnFBARDCAUEATg0BDCILIAAQogINISAAIARByABqIARBxABqIARBzABqIARBPGpBAEEAQbt/EK4BDSEgACAEKAJIIAQoAkQgBCgCTCAEKAI8QQRBABDBAQsgAyEFDAELIABBvQFBvQFBuQEgAhsgDBsQDSAAIAYQFyAAIAsvAbwBEBQLQQAhAwwaC0EBIQMgACAFQQBBAUF/QQAQwgFBAE4NGQwdCyAAKAJAKAK8ASEGIAAQdBogACgCECIBQTtGDRdBUSECAkAgAEEEEMoDDgIAFh0LIAFBsX9GIAFBUUZyDRQgASICQUlGDRUgAEEAEOwEDRwgAEEOEA0MFgsgABAPDRsCQCAAKAIwDQAgACgCEEGDf0cNACAAKAIoDQAgACgCICEFCyAAKAJAIgNBsAJqIQEgAygCvAEhByACQbx/RiEGAkADQCABKAIAIgEEQCAAIAcgASgCGBChAiABKAIYIQcCQCAGRQRAIAEoAgwiA0F/Rg0BIAVFDQQgASgCBCAFRw0BDBYLIAEoAggiA0F/Rg0AIAVFDQMgASgCBCAFRg0VCyABKAIcBH8gAEGFARANQQMFQQALIQMDQCADIAEoAhBORQRAIABBDhANIANBAWohAwwBCwsgASgCFEF/Rg0BIABBBhANIABB7gAgASgCFBAYGiAAQQ4QDQwBCwsgBUUEQCACQbx/Rg0NIABBiDdBABATDB0LIABBvuEAQQAQEwwcCyAAQewAIAMQGBoMEgsgABAPDRogABDDASAAEPgBDRogABB0GiAAEC0hAiAEIAAoAkAiAygCsAI2AlAgAyAEQdAAajYCsAJBfyEBIARBfzYCZCAEQv////8fNwJcIAQgAjYCWCAEIAk2AlQgAygCvAEhAyAEQQA2AmwgBCADNgJoIABB+wAQKA0aQX8hBQNAIAFBAEghAwNAAkACQAJAIAAoAhAiB0HBAGoOAgABAgsgAwR/QX8FIABB7ABBfxAYCyEDIAAgARAaA0AgABAPDR8gAEEREA0gABCLAQ0fIABBOhAoDR8gAEGsARANIAAoAhBBv39GBEAgAEHrACADEBghAwwBCwsgAEHqAEF/EBghASAAIAMQGgwDCyAAEA8NHSAAQToQKA0dIAVBAE4EQEGrGyEDDBMLIAFBAEgEQCAAQewAQX8QGCEBCyAAQbYBEA0gAEEAEDggACgCQCgChAJBBGshBQwCCyAHQf0ARwRAIAMEQEGCGyEDDBMLIABBBxDbAUUNAQwdCwsLIABB/QAQKA0aAkAgBUEATgRAIAAoAkAiAygCgAIgBWogATYAACADKAKkAiABQRRsaiAFQQRqNgIEDAELIAAgARAaCyAAIAIQGiAAQQ4QDSAAKAJAIgEgASgCsAIoAgA2ArACDBcLIAAQwwEgABAPDRkgABAtIQIgABAtIQEgABAtIQMgABAtIQUgAEHtACACEBgaIAQgACgCQCIHKAKwAjYCUCAHIARB0ABqNgKwAiAEQv////8fNwJcIARCgICAgHA3AlQgBygCvAEhByAEQQA2AmwgBCAHNgJoIAQgAzYCZCAAEOkCDRkgACgCQCIHIAcoArACKAIANgKwAiAAEOgCBEAgAEEOEA0gAEEGEA0gAEHuACADEBgaIABBDhANIABB7AAgBRAYGgsCQAJAAkAgACgCEEE9ag4CABABCyAAEA8NGyAAEHQaIAAgAhAaIAAoAhBB+wBGBEAgAEEOEA0MDwsgAEEoECgNGyAAKAIQIgJB+wBGIAJB2wBGcg0BAkAgAkGDf0YEQCAAKAIoRQ0BCyAAQfblAEEAEBMMHAsgDSAAKAIgEBYhAgJAIAAQD0UEQCAAIAJBQxCjAkEATg0BCyANIAIQEAwcCyAAQbkBEA0gACACEDggACAAKAJALwG8ARAUDA0LIABBvAxBABATDBoLIABBUUEAQQFBf0EBEMIBQQBODQsMGQsgABAPRQ0ZDBgLIAAoAkAtAG5BAXEEQCAAQcnGAEEAEBMMGAsgABAPDRcgABD4AQ0XIAAQdBogACAAKAJAQdUAQQAQnQEiAUEASA0XIABB8QAQDSAAQdkAEA0gACABQf//A3EQFCAAEMMBIAAQpAINFwwUCyABQQFxRQ0BIAFBBHENByAAQQAQc0EqRg0BDAcLIAAoAigEQCAAENwBDBYLQVEhAgJAIAAgARDKAw4CABQWCyAAQYYBEEVFDQQgAEEBEHNBRUcNBCABQQRxDQYLIABBhBJBABATDBQLIAFBBHFFBEAgAEHIEUEAEBMMFAtBfyEBQQAhAyAAQQBBABDsAkUNFQwWCyAAEA8NEiAAEK8BRQ0TDBILIAQgACgCACAEQdAAaiAAKAIgEIEBNgIQIABB+yogBEEQahATDBELIAAQiwENEAJAIAAoAkAoAqQBQQBOBEAgAEHZABANIAAgACgCQC8BpAEQFAwBCyAAQQ4QDQsgABCvAUUNEQwQCyAAQYvIAEEAEBMMDwtBACEDIABBAUEAIAAoAhggACgCFBDEAQ0ODBALIABBKRAoDQ0LIABB7QAgARAYGiAAEHQaIAQgACgCQCICKAKwAjYCUCACIARB0ABqNgKwAiAEQv////8fNwJcIARCgICAgHA3AlQgAigCvAEhAiAEQQA2AmwgBCACNgJoIAQgAzYCZCAAEOkCDQwgACgCQCICIAIoArACKAIANgKwAiAAENoBIAAQ2gEgABDoAgRAIABBDhANIABBBhANIABB7gAgAxAYGiAAQQ4QDSAAQewAIAUQGBoLIAEhAgsgACACEBogAEHuACADEBgaIABBLxANIAAgAxAaIAAoAhBBREYEQCAAEA8NDCAEIAAoAkAiAigCsAI2AlAgAiAEQdAAajYCsAIgBEF/NgJkIARC/////y83AlwgBEKAgICAcDcCVCACKAK8ASEDQQAhASAEQQA2AmwgBCADNgJoIAIoAqQBQQBOBEAgACgCACACQdIAEEwiAUEASA0NIABB2AAQDSAAIAAoAkAvAaQBEBQgAEHZABANIAAgAUH//wNxEBQgABDDAQsgABDpAg0MIAAoAkAiAygCpAFBAE4EQCAAQdgAEA0gACABQf//A3EQFCAAQdkAEA0gACAAKAJALwGkARAUIAAoAkAhAwsgAyADKAKwAigCADYCsAILIABB7wAQDSAAIAUQGgwMCyAAIANBABATDAoLIABB7AAgAxAYGiAFRQ0AIAAQDw0JCyAAEK8BRQ0JDAgLIAEhAgsgABAPDQYgAEEAIAJBABDMAw0GCyAAIAAoAkAoArwBIAYQoQILIABBOxAoDQQgABAtIQUgABAtIQMgABAtIQEgABAtIQcgBCAAKAJAIgIoArACNgIcIAIgBEEcajYCsAIgBEKAgICAcDcCLCAEIAM2AiggBCAHNgIkIAQgCTYCICACKAK8ASECIARBADYCOCAEIAI2AjQgASECIAAoAhBBO0cEQCAAIAUQGiAAEIsBDQUgAEHqACAHEBgaIAUhAgsgAEE7ECgNBAJAIAAoAhBBKUYEQCAEIAI2AihBACEFIAIhAwwBCyAAQewAIAEQGBogACgCQCgChAIhBSAAIAMQGiAAEIsBDQUgAEEOEA0gASACRg0AIABB7AAgAhAYGgsgAEEpECgNBCAAKAJAKAKEAiEKIAAgARAaIAAQpAINBCAAIAAoAkAoArwBIAYQoQICQCABIAJGIAIgA0ZyRQRAIAAoAkAiAUGAAmoiBiABKAKEAiIIIAogBWsiAmoQvAEaIAYgASgCgAIgBWogAhByGiABKAKAAiAFakGzASACECwaIAAoAkAiAiABKAKEAkEFazYCmAIgAyACKAKsAiIBIAEgA0gbIQYgCCAFayEIA0AgAyAGRg0CIAIoAqQCIANBFGxqIgsoAgQiASAFSCABIApOckUEQCALIAEgCGo2AgQLIANBAWohAwwACwALIABB7AAgAxAYGgsgACAHEBogACgCQCIBIAEoArACKAIANgKwAgwBCyAAQewAIBAQGBogACgCQCgChAIhDCAAIA8QGgJAIAAoAhAiAkE9Rw0AAkAgABAPRQRAIABBABCtAUUNAQsgCCAGEBAMBQsgBkUNACAAQbkBEA0gACAGEBcgACALLwG8ARAUCyAIIAYQEAJAAkACQCAAQcQAEEUiBgRAIARBATYCbCAEIAQoAmBBAmo2AmBB+MsAIQggAkE9Rg0BDAMLIAAoAhBBt39HDQEgAUUEQCAAQfSPAUEAEBMMBwsgAkE9Rw0CQYI/IQggBUGxf0cNACALLQBuQQFxRSADQX9zcQ0CCyAEIAg2AgAgAEH4LiAEEBMMBQsgAEGTPUEAEBMMBAsgABAPDQMCQCAGBEAgABBTRQ0BDAULIAAQiwENBAsgACAAKAJAKAK8ASAOEKECIABB/wBBgH8gARtB/gAgBhtB/wFxEA0gAEHsACAHEBgaIABBKRAoDQMgACgCQCICQYACaiIFIAIoAoQCIgggDCAKayIDahC8ARogBSACKAKAAiAKaiADEHIaIAIoAoACIApqQbMBIAMQLBogACgCQCIFIAIoAoQCQQVrNgKYAiAHIAUoAqwCIgIgAiAHSBshCyAIIAprIQggByEDA0AgAyALRwRAIAUoAqQCIANBFGxqIg8oAgQiAiAKSCACIAxOckUEQCAPIAIgCGo2AgQLIANBAWohAwwBCwsgACAQEBogABCkAg0DIAAgACgCQCgCvAEgDhChAiAAIAcQGgJ/IAYEQCABRQRAIABBFBANIABBDhANIABBJBANIABBABAUIABBjAEQDSAAQYQBEA1BhQEMAgsgAEGCARANIABBABBYQYUBDAELIABBgQEQDUEOCyEDIABB6gAgEhAYGiAAQQ4QDSAAIBEQGiAAIAMQDSAAKAJAIgEgASgCsAIoAgA2ArACCyAAENoBDAMLIAFBBHENACAAQcMSQQAQEwwBCyAAEA8NAEEAIQMgAEEBIAJBABDMAw0AIAAQrwFFDQILQX8hAwwBC0EAIQMLIA0gCRAQIAMhAQsgBEGQAWokACABCzYBAX8jAEHQAGsiASQAIAEgACgCACABQRBqIAAoAiAQgQE2AgAgAEGnMyABEBMgAUHQAGokAAvKFgEMfyMAQRBrIhAkACAAKAJAIQcgACgCACELAkACQAJAIAFBAksNAAJAIAINAEEAIQIgAEGGARBFRQ0AIABBARBzQQpGDQBBfyEIIAAQDw0DQQIhAgtBfyEIIAAQDw0CIAAoAhAiDUEqRgRAIAAQDw0DIAAoAhAhDSACQQFyIQILAkACQAJAAkACQCANQSlqDgIBAgALIA1Bg39HDQMCQCAAKAIoDQAgAUECRyIJIAJBAXFFckUgACgCICIMQS1GcQ0AIAkgAkECcUUgDEEuR3JyDQMLIAAQ3AEMBgsgAUECRw0CIActAG5BAXFFDQEMAgsgAUECRw0BIAAoAkQNAQsgCyAAKAIgEBYhDCAAEA9FDQEMAgsgAUECRiAFQQJGcg0AIABByuYAQQAQEwwCCwJAAkACQCAHKAIgIghFIAFBAUtyDQAgBygCJEEBRw0AIAcgDBCgAiINRQ0AIA0oAgggBygCvAFHDQAgAEGl3QBBABATDAELQX8hDQJAIAFBAUcEQAwBCwJAIAINACAHLQBuQQFxDQAgByAMIAcoAsABQQAQyQNBAE4NACAHIAwQ9wFBgICAgHpxQYCAgIACRg0AIAxBzgBGBEAgBygCSA0BC0EBIQ8LAkAgCEUNACAHKAIkQQFLDQAgBygCvAEiCCAHKALwAUcNACAHIAwQoAIiCkUNASAKKAIIIAhHDQEgAEHeMkEAEBMMAgtBfyEIIAAgByAMQQRBAyACGxCdASINQQBIDQMLIAsgB0EAIAFBAUsgACgCDCAEEOoDIgcNAQsgCyAMEBBBfyEIDAILIAYEQCAGIAc2AgALIAAgBzYCQCAHIAw2AnAgByACRSABQQNJcTYCNEEAIQggAUEEayIEQQVNBEAgBEECdEH49AFqKAIAIQgLIAcgCDYCMCAHIAFBCUYiBDYCYCAHIAFBA0ciCiABQQdHIglxIg42AkwgByAONgJIAkAgCkUEQCAHIAcoAgQiBCgCUDYCUCAHIAQoAlQ2AlQgByAEKAJYNgJYIAcgBCgCXDYCXAwBCyAHQQE2AlAgCUUEQCAHQQA2AlwgB0KAgICAEDcCVAwBCyAHQQE2AlwgByAINgJYIAcgBDYCVAsgByACQf8BcSABQQh0cjsBbCABQX5xQQhGBEAgAEErEA0LAkACQAJAAkACQAJAIAFBCEYEQCAAEOsEIAdCATcCOCAHQTxqIQQgB0E4aiEKDAELIAdCATcCOCAHQTxqIQQgB0E4aiEKIAFBA0YEQCAAKAIQQYN/Rw0BIAAoAigNBSALIAcgACgCIBDIA0EASA0GIAdBATYCjAEMAgsgAUEHRg0CCwJAIAAoAhBBKEYEQCAAIBBBDGpBABCcARogEC0ADEEEcQRAIARBATYCAAsgABAPRQ0BDAYLIABBKBAoDQULIAQoAgAEQEF/IQggB0F/NgK8ASAAEHRBAEgNBwtBACEJAkADQCAAKAIQIghBKUYNASAIQaV/RyIORQRAIApBADYCACAAEA8NByAAKAIQIQgLAkACQAJAAkAgCEGDf0cEQCAIQfsARyAIQdsAR3ENBCAKQQA2AgACQCAORQRAIABBDRANIAcoAogBIQgMAQsgCyAHQQAQyAMhCCAAQdsAEA0LIAAgCEH//wNxEBQgAEFRQbF/IAQoAgAbQQFBAUF/QQEQwgEiCEEASA0LIAggCXIhEkEBIQkgEkUEQCAHIAcoAowBQQFqNgKMAUEAIQkLIA5FDQEMAwsgACgCKA0JIAAoAiAiCEEtRgRAIActAGxBAUYNCgsgBCgCAARAIAAgByAIQQEQnQFBAEgNCwsgCyAHIAgQyAMiEUEASA0KIAAQDw0KIA4NASAAQQ0QDSAAIBFB//8DcSIJEBQgBCgCAARAIABBERANIABBvQEQDSAAIAgQFyAAIAcvAbwBEBQLIABB3AAQDSAAIAkQFCAKQQA2AgALIAAoAhBBKUYNBCAAQSkQKBoMCQsCQCAAKAIQQT1GBEAgCkEANgIAIAAQDw0KIAAQLSEJIABB2wAQDSAAIBFB//8DcSIOEBQgAEEREA0gAEEGEA0gAEGsARANIABB6gAgCRAYGiAAQQ4QDSAAEFMNCiAAIAgQngEgAEEREA0gAEHcABANIAAgDhAUIAAgCRAaQQEhCQwBCyAJRQRAIAcgBygCjAFBAWo2AowBCyAEKAIARQ0BIABB2wAQDSAAIBFB//8DcRAUCyAAQb0BEA0gACAIEBcgACAHLwG8ARAUCyAAKAIQQSlGDQIgAEEsEChFDQEMBwsLIABB1DBBABATDAULAkACQCABQQRrDgIBAAILIAcoAogBQQFGDQEMAwsgBygCiAENAgsgBCgCAEUNACAHKALMASAHKAK8AUEDdGpBBGohCANAAkAgCCgCACIEQQBIDQAgBygCdCIIIARBBHQiBGoiCigCBCAHKAK8AUcNACAHIAooAgAiChD3AUEASARAIAsgByAKEExBAEgNBiAHKAJ0IQggAEG4ARANIAAgBCAIaiIKKAIAEBcgACAHLwG8ARAUIABBuQEQDSAAIAooAgAQFyAAQQAQFAsgBCAIakEIaiEIDAELCyAAQbUBEA0gACAHLwG8ARAUIAdBADYCvAEgByAHKALMASgCBDYCwAELIAAQDw0CIAJBfXFBAUYEQCAAQYgBEA0LIAdBATYCZCAAEHQaIAcgBygCvAE2AvABAkACQCAAKAIQQaR/Rw0AIAAQDw0EIAAoAhBB+wBGDQAgACAHIAwQ6gQNBCAAEFMNBCAAQS5BKCACGxANIActAG5BAnENASAHIAAoAjQgA2siAjYCkAMgByALIAMgAhCXAyICNgKMAyACDQEMBAsgAUEHRwRAIABB+wAQKA0ECyAAEKUFDQMgACAHIAwQ6gQNAwNAIAAoAhBB/QBHBEAgABCkBUUNAQwFCwsgBy0AbkECcUUEQCAHIAAoAjggA2siAjYCkAMgByALIAMgAhCXAyICNgKMAyACRQ0ECyAAEA8NAyAAEOgCRQ0AIABBABCwAgsgACAHKAIENgJAIAAoAhAiAkGDf0cgAkHVAGpBLUtxRQRAIABBADYCKCAAQYN/NgIQIAAQ7wQLIAcoAnAhAiAHIABCgICAgCAQxwMiAzYCCCABQQJPBEBBACEIIAFBCmtBfUsNBSAAQQMQDSAAIAMQOCACDQUgAEHNABANIABBABA4DAULIAFBAUYEQCAAQQMQDSAAIAMQOCAPBEACQCAAKAJAIgEoAigEQCALIAEgAhDmAiIBRQ0GIAFBADYCCCABIAEtAARB/gFxIAAoAkAtAG5BAXFyOgAEDAELIAEgAhD3AUEATg0AIAsgASACEExBAEgNBQsgAEEREA0gAEG5ARANIAAgAhAXIABBABAUC0EAIQggDUEATgRAIAAoAkAoAnQgDUEEdGoiASABLQAMIANBCHRyNgIMIABBDhANDAYLIABBvQEQDSAAIAIQFyAAIAAoAkAvAbwBEBQMBQsCQAJAIAAoAkAiASgCKEUEQCAAIAEgAkEGEJ0BIgFBAEgNBSADQQh0IQIgACgCQCEAIAFBgICAgAJxBEAgACgCgAEgAUEEdGoiAEEMaiAALQAMIAJyNgIADAILIAAoAnQgAUEEdGoiACAALQAMIAJyNgIMDAELIAsgASACQf0AIAIbIgEQ5gIiAkUNBCACIAM2AgAgBQ0BC0EAIQgMBQtBACEIIAAgACgCQCgClAMgAUEWIAEgBUEBRxtBABD5AQ0EDAILIABB/i9BABATDAELIAAQ3AELIAAgBygCBDYCQCALIAcQ+wJBfyEIIAZFDQEgBkEANgIADAELIAsgDBAQCyAQQRBqJAAgCAuoAgIBfgJ/IwBBEGsiAiQAAkAgAUL/////b1gEQCAAECJCgICAgOAAIQUMAQsCQCAEDQAgAykDACIFQoCAgIBwVA0AIAWnIgYvAQZBLUcNACAGKAIgRQ0AIAAgBUE9IAVBABARIgVCgICAgHCDQoCAgIDgAFENASAAIAUgARBNIQcgACAFEAwgB0UNACADKQMAIgVCIIinQXVJDQEgBaciACAAKAIAQQFqNgIADAELIAAgAiABEIICIgFCgICAgHCDQoCAgIDgAFIEQCAAIAIgBEEDdGopAwBCgICAgDBBASADEBwhBSAAIAIpAwAQDCAAIAIpAwgQDCAFQoCAgIBwg0KAgICA4ABRBEAgACABEAwMAgsgACAFEAwLIAEhBQsgAkEQaiQAIAULDQAgACABIAJBABCaAwstAQF/QQEhAQJAAkACQCAAQQ1rDgQCAQECAAsgAEEwRg0BCyAAQTRGIQELIAELnQMDAn4BfAJ/AkACfgJAAkACQAJAIAFBCGsiBikDACIEQiCIp0EHa0FuSQ0AQX8hAUKAgICAMCEDIAAgBBBlIgRCgICAgHCDQoCAgIDgAFENBSAEQiCIpyIHQXZHBEAgBw0BIATEIQMCQAJAAkAgAkGNAWsOBAACAQEFCyAEQiCGUARAQQAhAUKAgICAwP7/AyEDDAkLQgAgA30hAwwBCyADIAJBAXRBnwJrrHwhAwsgA0L/////D4MgA0KAgICACHxC/////w9YDQUaQoCAgIDAfiADub0iA0KAgICAwIGA/P8AfSADQv///////////wCDQoCAgICAgID4/wBWGwwFCyAAIAYgAiAEIAAoAhAoAqgCER8ADQVBAA8LIARCgICAgMCBgPz/AHy/IQUCQCACQY0Baw4EAAMCAgELIAWaIQUMAgsQAQALIAJBAXRBnwJrtyAFoCEFC0KAgICAwH4gBb0iA0KAgICAwIGA/P8AfSADQv///////////wCDQoCAgICAgID4/wBWGwshA0EAIQELIAYgAzcDACABCzgBAX8gAEEYECQiAUUEQEKAgICA4AAPCyABQQE2AgAgACgC2AEgAUEEahC7ASABrUKAgICA4H6ECykBAX8gAkIgiKdBdU8EQCACpyIDIAMoAgBBAWo2AgALIAAgASACENIFCyYBAX8gAUIgiKdBdU8EQCABpyICIAIoAgBBAWo2AgALIAAgARAnC7UBAQJ/AkACQCABRQ0AIAEoAgAiAkEATA0BIAEgAkEBayICNgIAIAINAAJAIAEtAAVBAXEEQCAAIAEpAxgQIQwBCyABKAIYIgIgASgCHCIDNgIEIAMgAjYCACABQgA3AhggASgCICICRQ0AIAAgAhDOAQsgASgCCCICIAEoAgwiAzYCBCADIAI2AgAgAUIANwIIIABBEGogASAAKAIEEQAACw8LQfeEAUGo7ABB/ShBvcwAEAAACyEAIAEgAkYEQCABEBkPCyAAIAFBBGutQoCAgIDgfoQQDAtFAQF/AkAgAUGAgAFxRQRAIAFBgIACcUUNASAAKAIQKAKMASIBRQ0BIAEtAChBAXFFDQELIAAgAkGqDBC1AUF/IQMLIAML/gICA38CfiMAQRBrIgMkAAJAAkAgAUKAgICAcFoEQCABpyICLwEGQSxGBEACQCAAIANBCGogAUHgABB+IgJFDQAgAykDCCIBQoCAgIBwg0KAgICAMFEEQCAAIAIpAwAQ6AEhAQwFCyAAIAEgAikDCEEBIAIQNiIFQoCAgIBwg0KAgICA4ABRDQMCQAJAIAVCIIinQQFqDgQAAQEAAQsgACACKQMAEJcBIgRBAEgEQCAAIAUQDAwCCyAEDQRCgICAgOAAIQEgACACKQMAEOgBIgZCgICAgHCDQoCAgIDgAFEEQCAAIAUQDAwGCyAAIAYQDCAGpyAFp0YNBAsgACAFEAwgAEGr0gBBABASC0KAgICA4AAhAQwDCyACKAIQKAIsIgBFBEBCgICAgCAhAQwDCyAAIAAoAgBBAWo2AgAgAK1CgICAgHCEIQEMAgsgACABEIsEIgFCIIinQXVJDQEgAaciACAAKAIAQQFqNgIADAELIAUhAQsgA0EQaiQAIAELGgAgACgCECABIAIQ6AUiAUUEQCAAEHALIAELnwMCBH8CfiMAQSBrIgQkACABIAJqIQUgASEDA0ACQCADIAVPDQAgAywAAEEASA0AIANBAWohAwwBCwsCfgJAIAMgAWsiBkGAgICABE8EQCAAQeTIAEEAEDoMAQsgAyAFRgRAIAAgASACEJwDDAILIAAgBEEEaiIAIAIQPkUEQCAAIAEgBhCLAhoDQCADIAVJBEAgAywAACIAQQBOBEAgBEEEaiAAQf8BcRA8GiADQQFqIQMMAgUCQCADIAUgA2sgBEEcahBRIgFB//8DTQRAIAQoAhwhAwwBCyABQf//wwBNBEAgBCgCHCEDIARBBGogAUGAgARrQQp2QYCwA2oQhwEaIAFB/wdxQYC4A3IhAQwBCwNAQf3/AyEBIAMgBU8NASADLAAAQUBIBEAgA0EBaiEDDAELCwNAIAUgA0EBaiIDTQRAIAUhAwwCCyADLAAAQUBIDQALCyAEQQRqIAEQhwEaDAILAAsLIARBBGoQNwwCCyAEKAIEKAIQIgBBEGogBCgCCCAAKAIEEQAAC0KAgICA4AALIQggBEEgaiQAIAgL2wECAX8CfkEBIQQCQCAAQgBSIAFC////////////AIMiBUKAgICAgIDA//8AViAFQoCAgICAgMD//wBRGw0AIAJCAFIgA0L///////////8AgyIGQoCAgICAgMD//wBWIAZCgICAgICAwP//AFEbDQAgACAChCAFIAaEhFAEQEEADwsgASADg0IAWQRAQX8hBCAAIAJUIAEgA1MgASADURsNASAAIAKFIAEgA4WEQgBSDwtBfyEEIAAgAlYgASADVSABIANRGw0AIAAgAoUgASADhYRCAFIhBAsgBAuhAgEFfwNAAkACQAJAAkACfyACIAdMIgkgBCAGTHJFBEAgASAHQQJ0aigCACIIIAMgBkECdGooAgAiCUkEQCAIDAILIAggCUcNAyAGQQFqIQYgB0EBaiEHIAghCQwECyAJDQEgASAHQQJ0aigCAAshCSAHQQFqIQcMAgsgBCAGTA0CIAMgBkECdGooAgAhCQsgBkEBaiEGCwJ/AkACQAJAAkAgBQ4DAwABAgsgBiAHcUEBcQwDCyAGIAdzQQFxDAILEAEACyAGIAdyQQFxCyAAKAIAIghBAXFGDQEgACgCBCAITARAIAAgCEEBahDRAgRAQX8PCyAAKAIAIQgLIAAgCEEBajYCACAAKAIIIAhBAnRqIAk2AgAMAQsLIAAQmAZBAAvmAQAgAAJ/IAEoAggiAEH+////B04EQEEAIAJBAXENARpB/////wcgAEH+////B0cNARogASgCBEH/////B2oMAQtBACAAQQBMDQAaIABBH00EQEEAIAEoAhAgASgCDEECdGpBBGsoAgBBICAAa3YiAGsgACABKAIEGwwBCyACQQFxRQRAQf////8HIAEoAgRFDQEaQYCAgIB4IABBIEcNARogASgCECABKAIMQQJ0akEEaygCABpBgICAgHgMAQtBACABKAIQIAEoAgwiAiACQQV0IABrEHEiAGsgACABKAIEGws2AgALEgAgACABIAIgAyAEQZMDELEDCw4AIABBACABQRByELoBC58BAgR/An4gAzUCACEJA0AgAiAFRkUEQCAAIAVBAnQiB2ogBq0gASAHajUCACAJfnwiCj4CACAFQQFqIQUgCkIgiKchBgwBCwsgACACQQJ0IgdqIAY2AgBBASAEIARBAU0bIQRBASEFA0AgBCAFRkUEQCAAIAVBAnQiBmoiCCAHaiAIIAEgAiADIAZqKAIAEL0ENgIAIAVBAWohBQwBCwsLWgEFfyADQQAgA0EAShshBgNAIAUgBkcEQCAAIAVBAnQiA2ogASAD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gICAgOAAUg0AIAEhAgwBCwJAIAKnIgUpAgQiCkL/////B4NQDQAgAaciAykCBCELAkAgAygCAEEBRyAKIAuFQoCAgIAIg0IAUnINACADIAAoAhAoAgwRBQAgBSkCBCIKpyIEQf////8HcSIHIAMpAgQiC6ciBkH/////B3EiCGogBEEfdnQgBkEfdiIJQRFzakkNACAFQRBqIQYgA0EQaiEEIAkEQCAEIAhBAXRqIAYgB0EBdBAeGiADIAMpAgQiCiAFKQIEfEL/////B4MgCkKAgICAeIOENwIEDAILIAQgCGogBiAHEB4aIAMgAykCBCIKIAUpAgR8Qv////8HgyILIApCgICAgHiDhDcCBCAEIAunakEAOgAADAELAn4CQAJAIAunQf////8HcSAKp0H/////B3FqIgdBgICAgARPBEAgAEHkyABBABA6DAELIAAgByAKIAuEpyIGQR92EOkBIggNAQtCgICAgOAADAELIAhBEGohBAJAIAZBAE4EQCAEIANBEGogAygCBEH/////B3EQHiIEIAMoAgRB/////wdxaiAFQRBqIAUoAgRB/////wdxEB4aIAQgB2pBADoAAAwBCyAEIAMgAygCBEH/////B3EQkwUgBCADKAIEQQF0aiAFIAUoAgRB/////wdxEJMFCyAIrUKAgICAkH+ECyEKIAAgARAMDAELIAEhCgsgACACEAwgCgsPACAAIAFCgICAgDAQggILCwAgAEGfCUEAEBILjgIBA38jAEEQayIFJAAgBSAAOQMIIAUgAUEBayIHNgIAIAZBgAFB9t8AIAUQSBogAyAGLQAAQS1GNgIAIAQgBi0AAToAACABQQJOBEAgBEEBaiAGQQNqIAcQHhoLIAEgBGpBADoAACACIQkgASAGaiABQQFKakECaiEBA0AgASICQQFqIQEgAiwAACIDEI8GDQALQQEhBAJAAkACQCADQf8BcUEraw4DAQIAAgtBACEECyABLAAAIQMgASECC0EAIQEgA0EwayIDQQlNBEADQCABQQpsIANrIQEgAiwAASEIIAJBAWohAiAIQTBrIgNBCkkNAAsLIAlBACABayABIAQbQQFqNgIAIAVBEGokAAuADAIHfwV+IwBBoANrIgUkAAJAIAG9IgxCgICAgICAgPj/AINCgICAgICAgPj/AFEEQCAMQv//////////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QCAHqwwBC0EACyIDNgIcIAcgA7hhDQMMAQsgAwRAQX8hAyAAIAIQlgEiAkKAgICAcINCgICAgOAAUQ0EIAAgBEEcaiACQQEQvgJFDQMMBAsgACAEQRxqIAIQdQRAIAAgAhAMDAILQX8hAyAAIAIQlgEiAkKAgICAcINCgICAgOAAUQ0DIAAgBEEEaiACQQAQvgINAyAEKAIEIAQoAhxGDQILIABBiscAQQAQRAtBfyEDDAELIAEgBCgCHDYCAEEAIQMLIARBIGokACADC0ABAX4gABDiASICQoCAgIBwg0KAgICA4ABSBEAgAqdBBGogARCcAkUEQCACDwsgACACEAwgABBwC0KAgICA4AALMgEBfyMAQdAAayICJAAgAiAAIAJBEGogARCBATYCACAAQbzpACACEMMCIAJB0ABqJAALoAECAX8BfiMAQRBrIgUkACAFIAQ2AgxBfyEEIAAgASAFQQxqENMBRQRAIAMoAgAiAEF8cSABIAIgAygCBCAAQQNxQQJ0QYS3AWooAgARGwAhBiADEOAFIAUoAgwiACAAKAIAQf////8DcTYCACADQoCAgIAwIAYgBkKAgICAcINCgICAgOAAUSIAGzcDAEF/QQAgABshBAsgBUEQaiQAIAQLFQECfiAAIAEQ6AEhAyAAIAEQDCADCw0AIAAgASACQQIQyAIL1QEBA38jAEEQayIFJABBfyEDAkAgACgCFA0AAkACQCABQYCAgIAETgRAIAAoAgBB5MgAQQAQOgwBCyABIAAoAgxBA2xBAm0iBCABIARKGyEBIAAoAhAiBCACQYACSHJFBEAgACABEOADIQMMAwsgACgCACAAKAIEIAEgBHQgBGtBEWogBUEMahCnASICDQELIAAQ9wIMAQsgBSgCDCEDIAAgAjYCBCAAQf////8DIAMgACgCEHYgAWoiACAAQf////8DThs2AgxBACEDCyAFQRBqJAAgAwsqAQF/IAAoAhAiA0EQaiABIAIgAygCCBEBACIBIAJFckUEQCAAEHALIAELgQECAn8BfgJAIAEpAgQiBEL//////////79/VgRAIAEoAgwhAAwBCyAAKAI0IARCIIinIAAoAiRBAWtxQQJ0aiECIAAoAjghAwNAIAMgAigCACIAQQJ0aigCACICIAFGDQEgAkEMaiECIAANAAtBxocBQajsAEH/FEH4DhAAAAsgAAupBwIJfwF+AkACQAJAAn8gAkECTARAIAIgASkCBCIMQj6Ip0YEQCAAIAEQxgIiBEHXAUoNBSABIAEoAgBBAWs2AgAgBA8LIAAoAjQgACgCJEEBayABIAIQ6wVB/////wNxIgdxIgpBAnRqIQMgDKdB/////wdxIQUDQCACIAMoAgAiBEUNAhoCQCAAKAI4IARBAnRqKAIAIgMpAgQiDKdB/////wdxIAVHIAxCPoinIAJHciAMQiCIp0H/////A3EgB0dyDQAgAyABIAUQ6gUNACAEQdgBSA0EIAMgAygCAEEBajYCAAwECyADQQxqIQMMAAsACyACQQNHIQdBAwshBQJAIAAoAjwNAEEAIQQgAEEQaiILIAAoAjhB0wEgACgCLEEDbEECbSICIAJB0wFMGyICQQJ0IAAoAggRAQAiCEUNASAAKAIsIgkhAyAJRQRAIAtBECAAKAIAEQMAIgZFBEAgCyAIIAAoAgQRAAAMAwsgBkKAgICAgICAgEA3AgQgBkEBNgIAIAZBADYADCAIIAY2AgAgACAAKAIoQQFqNgIoQQEhAwsgACADNgI8IAAgCDYCOCAAIAI2AiwgCSACIAIgCUkbIQQgAkEBayEGA0AgAyAERg0BIAAoAjggA0ECdGpBASADQQFqIgJBAXRBAXIgAyAGRhs2AgAgAiEDDAALAAsCQCABBEAgASkCBCIMQv//////////P1gEQCABIAwgBa1CPoaENwIEDAILIABBEGogDKciAkEfdSACQf////8HcSACQR92dGpBEWogACgCABEDACICRQRAQQAhBAwECyACQQE2AgAgAiACKQIEQv////93gyABKQIEQoCAgIAIg4QiDDcCBCACIAxCgICAgHiDIAEpAgRC/////weDhDcCBCACQRBqIAFBEGogASgCBCIDQf////8HcSADQR92dCADQX9zQR92ahAeGiAAIAEQkAQgAiEBDAELIABBEGpBECAAKAIAEQMAIgFFBEBBAA8LIAFCgYCAgICAgICAfzcCAAsgACAAKAI4IAAoAjwiBEECdGoiAigCAEEBdjYCPCACIAE2AgAgASAENgIMIAEgATUCBCAHrUIghoQgBa1CPoaENwIEIAAgACgCKEEBajYCKCAFQQNGDQIgASAAKAI0IApBAnRqIgEoAgA2AgwgASAENgIAIAAo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QQAQEgwBCwJAIARFIAJBAExyRQRAIAAgBSAHQgAgAUF/EPMCDQIMAQsgASEICyACQQAgAkEAShutIQlCACEBA0AgASAJUgRAIAMgAadBA3RqKQMAIgdCIIinQXVPBEAgB6ciAiACKAIAQQFqNgIACyABIAh8IQogAUIBfCEBIAAgBSAKIAcQe0EATg0BDAILCyAAIAVBMCAGQoCAgIAIfCIIQv////8PWAR+IAZC/////w+DBUKAgICAwH4gBrm9IgFCgICAgMCBgPz/AH0gAUL///////////8Ag0KAgICAgICA+P8AVhsLEDlBAEgNACAAIAUQDCAGQv////8PgyAIQv////8PWA0BGkKAgICAwH4gBrm9IgFCgICAgMCBgPz/AH0gAUL///////////8Ag0KAgICAgICA+P8AVhsMAQsgACAFEAxCgICAgOAACyELIAxBEGokACALCxUBAn4gACABEIcFIQMgACABEAwgAwv5DgIKfgR/IwBBEGsiECQAIBAgAjcDCAJAAkACfgJAAkACQAJAAkACQAJAAkACQAJAQQcgAkIgiKciDiAOQQdrQW5JGyIOQQdqDg8EAwMDAwMABQUFAwMDAwECCwJAAkACQAJAIAKnIg4vAQYiD0EEaw4DAQACAwtCgICAgDAhByAAIAIQNCICQoCAgIBwg0KAgICA4ABRDQsgACACEO4DIgJCgICAgHCDQoCAgIDgAFENCyABKAIoIAIQhAEhDgwOC0KAgICAMCEHIAAgAhCWASICQoCAgIBwg0KAgICA4ABRDQogASgCKCACEIQBIQ4MDQsgASgCKCAOKQMgEI0BIQ4gACACEAwMDAsgD0EhRg0HQoCAgIAwIQYgACABKQMIQQEgEEEIahDxAyIEQoCAgIDwAINCgICAgOAAUQ0GIAAgBBAnBEAgAEHJ3wBBABASDAcLIANCIIinQXVPBEAgA6ciDiAOKAIAQQFqNgIACyABKQMYIgRCIIinQXVPBEAgBKciDiAOKAIAQQFqNgIACwJAAkACQAJAIAAgAyAEELYCIghCgICAgHCDQoCAgIDgAFEEQEKAgICAMCEHDAELIAEpAxgiBEKAgICAcINCgICAgJB/UQRAIASnKAIEQf////8HcUUNAwsgCEIgiKdBdU8EQCAIpyIOIA4oAgBBAWo2AgALIABB65YBIAhB7JYB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BEAgAiAANgIAC0EBC4cDAgN+BH8CQCABKAIIIgZB/v///wdOBEBBASEHIAJBAXENAUL///////////8AIQMgBkH+////B0cNASABNAIEQv///////////wB8IQMMAQsgBkEATARADAELIAZBP00EQCABKAIQIAEoAgwiCEECdGoiCUEEaygCACECQgAgBkEgTQR+IAJBICAGa3atBSAIQQJPBH4gCUEIazUCAAVCAAsgAq1CIIaEQcAAIAZrrYgLIgN9IAMgASgCBBshAwwBCyACQQFxRQRAIAEoAgRFBEBC////////////ACEDQQEhBwwCC0KAgICAgICAgIB/IQNBASEHIAZBwABHDQEgASgCECABKAIMIgFBAnRqIgJBBGs1AgBCIIYhBCABQQJPBH4gAkEIazUCAAVCAAsgBIRCgICAgICAgICAf1IhBwwBC0IAIAEoAhAiCCABKAIMIgIgAkEFdCAGayIGEHGtIAggAiAGQSBqEHGtQiCGhCIDfSADIAEoAgQbIQMLIAAgAzcDACAHC60CAgJ/An4jAEEgayICJAACQCAAKAIIQf////8HRgRAQoCAgICAgID8/wAhBAwBCyAAKAIAIQMgAkIANwIYIAJCgICAgICAgICAfzcCECACIAM2AgwgAkEMaiIDIAAQSRoCfiACKAIUIgBB/f///wdMBEAgA0E1QcgEELoBGiACKAIUIQALQoCAgICAgID4/wAgAEH+////B0YNABpCACAAQYCAgIB4Rg0AGiACKAIcIQMCfiACKAIYQQJGBEAgAykCAAwBCyADNQIAQiCGCyEEIABBgnhMBEAgBEGOeCAAa62IIQRCAAwBCyAEQguIQv////////8HgyEEIABB/gdqrUI0hgshBSAEIAWEIAI1AhBCP4aEIQQgAkEMahAZCyABIAQ3AwAgAkEgaiQACw0AIAAgASACQQIQsAMLIwACQAJAAkAgAg4CAAECCyAAIAFyDwsgACABcw8LIAAgAXEL4QgBEX8gAigCBCAFcyIFIAEoAgQiBnMhDQJAIAEgAhDyASIIIA1Fcg0AIAEoAghB/f///wdKDQAgACAEQQdxQQJGEIABQQAPCyAFIAYgCEEASCIGGyEFIAEgAiAGGyEKAkACQAJAIAIgASAGGyIIKAIMIgcEQCAKKAIMIgsNAQsgCCgCCCIBQf7///8H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CyAAIAsQDCAAIAgQDCAAIAoQDCAAIAEQDAwBCyABIQ0LIAVBIGokACANC0sBAn8gACABRwRAIAAoAhAiAgRAIAAoAgAiAygCACACQQAgAygCBBEBABoLIAAgASkCADcCACAAIAEoAhA2AhAgACABKQIINwIICwv0AQIDfgF/AkAgAykDACIEQoCAgIBwWgRAIAMpAwgiBUL/////b1YNAQsgABAiQoCAgIDgAA8LQoCAgIDgACEGIABCgICAgCBBLBBHIgFCgICAgHCDQoCAgIDgAFIEfiAAQRgQJCICRQRAIAAgARAMQoCAgIDgAA8LIASnIgMgAygCAEEBajYCACACIAQ3AwAgBaciByAHKAIAQQFqNgIAIAIgBTcDCCAAIAQQNSEAIAJBADoAESACIAA6ABAgAUKAgICAcFoEQCABpyIAIAI2AiAgACAALQAFQe8BcSADLQAFQRBxcjoABQsgAQVCgICAgOAACwsbACAAEBkgAEIANwIQIABCADcCCCAAQgA3AgALCQAgASACEPgFCxMAIABBEGogASACIAAoAggRAQALqAECAX8CfiAAvSIEQv///////////wCDQoGAgICAgID4/wBaBEAgAb1C////////////AINCgYCAgICAgPj/AFQPC0F/IQICQCAAIAFjDQAgAb0iA0L///////////8Ag0KAgICAgICA+P8AVg0AQQEhAiAAIAFkDQBBACECIABEAAAAAAAAAABiDQAgBEIAUwRAIANCP4enQX9zDwsgA0I/iKchAgsgAgvKBQIFfwN+IwBBMGsiAiQAIAIgATcDECACQQA2AgwgAiAANgIIIAIgAykDACIKNwMYAkACQCAKQoCAgIBwgyILQoCAgIAwUgRAQoCAgIDgACEJIAAgChBVDQELQoCAgIDgACEJIAAgARCKASIFQQBIDQACQCAFQQJJDQAgAaciAy8BBkEVayIEQf//A3FBC08NAiACIARBAnRB/P8PcSIEQZz1AWooAgA2AiBBASADLwEGQcqeAWotAAAiBnQhCCADKAIkIQcgC0KAgICAMFIEQCAAIAVBAnQQJCIERQ0CQQAhAwNAIAMgBUZFBEAgBCADQQJ0aiADNgIAIANBAWohAwwBCwsgAiAINgIoIAIgBzYCJCAEIAVBBEHLACACQQhqENcBAkACQAJAAkAgAigCDA4CAAEDCyAAIAUgBnQiAxAkIgYNAQsgACgCECIAQRBqIAQgACgCBBEAAAwECyAGIAcgAxAeIQZBACEDAkACQAJAAkACQCAIQQFrDggAAQkCCQkJAwkLA0AgAyAFRg0EIAMgB2ogBiAEIANBAnRqKAIAai0AADoAACADQQFqIQMMAAsACwNAIAMgBUYNAyAHIANBAXRqIAYgBCADQQJ0aigCAEEBdGovAQA7AQAgA0EBaiEDDAALAAsDQCADIAVGDQIgByADQQJ0IghqIAYgBCAIaigCAEECdGooAgA2AgAgA0EBaiEDDAALAAsDQCADIAVGDQEgByADQQN0aiAGIAQgA0ECdGooAgBBA3RqKQMANwMAIANBAWohAwwACwALIAAoAhAiA0EQaiAGIAMoAgQRAAALIAAoAhAiAEEQaiAEIAAoAgQRAAAMAQsgByAFIAggBEHI9QFqKAIAIAJBCGoQ1wEgAigCDA0BCyABQiCIp0F1TwRAIAGnIgAgACgCAEEBajYCAAsgASEJCyACQTBqJAAgCQ8LEAEAC+cCAQF+IAAgARCKASICQQBIBEBCgICAgOAADwsCQCACRQ0AAkACQAJAAkACQCABpyIALwEGQcqeAWotAAAOBAABAgMECyAAKAIkIgAgAmohAgNAIAAgAkEBayICTw0FIAAtAAAhAyAAIAItAAA6AAAgAiADOgAAIABBAWohAAwACwALIAAoAiQiACACQQF0aiECA0AgACACQQJrIgJPDQQgAC8BACEDIAAgAi8BADsBACACIAM7AQAgAEECaiEADAALAAsgACgCJCIAIAJBAnRqIQIDQCAAIAJBBGsiAk8NAyAAKAIAIQMgACACKAIANgIAIAIgAzYCACAAQQRqIQAMAAsACyAAKAIkIgAgAkEDdGohAgNAIAAgAkEIayICTw0CIAApAwAhBCAAIAIpAwA3AwAgAiAENwMAIABBCGohAAwACwALEAEACyABQiCIp0F1TwRAIAGnIgAgACgCAEEBajYCAAsgAQtRAgF/AX5CgICAgOAAIQQgACABIAIQayIDBH4gAygCICIDKAIMKAIgLQAEBEAgAkUEQEIADwsgABBfQoCAgIDgAA8LIAM1AhAFQoCAgIDgAAsLNwAgACABIAIQayIARQRAQoCAgIDgAA8LIAAoAiAoAgwiACAAKAIAQQFqNgIAIACtQoCAgIBwhAsMACAAKAIQIAEQ5wML2gEBAn4CQAJAIAJFBEAgAUKAgICAcIMhBSAAQS8QKSEEDAELAn4gAUKAgICAcIMiBUKAgICAMFIgAykDACIEQoCAgIBwg0KAgICAgH9SckUEQCAAQbmMASAAIAAoAhAgBKcQxgIQKUGrjAEQsgEMAQsgACAEECULIgRCgICAgHCDQoCAgIDgAFENAQsgBUKAgICAMFENACAAIAFBBRBeIgFCgICAgHCDQoCAgIDgAFIEQCAAIAEgBBC9ASAAIAFBMCAEpykCBEL/////B4NBABAVGgsgASEECyAEC0YBAX8CQCAAKAIIIAJqIgMgACgCDEoEQCAAIAMgARDEAg0BCwNAIAJBAEwEQEEADwsgAkEBayECIAAgARCHAUUNAAsLQX8LlQECBX8BfiABKQIEIginQf////8HcSIDRQRAIAIPCyAAKAIEQf////8HcSEHAn8gCEKAgICACINQRQRAIAEvARAMAQsgAS0AEAshBSADQQFrIQYgByADayEEAkADQCACIARKDQEgACAFIAIQoAEiA0EASCADIARKcg0BIAAgASADQQFqIgJBASAGELwDDQALIAMPC0F/C6cBAgN/AX4CQAJAIAAgARD2AyIDQQBIDQAgA0UNAUGbHiECIAAgACABQe4AIAFBABARIgVCgICAgHCDIgFCgICAgCBRIAFCgICAgDBRcgR/QZseBSABQoCAgIDgAFENASAAIAUQNCIBQoCAgIBwg0KAgICA4ABRDQFBACECIAGnQecAQQAQoAEhBCAAIAEQDCAEQQBODQJB2ssAC0EAEBILQX8hAgsgAguhAQIDfwF+AkACQCAAKQIEIgRCgICAgAiDUA0AIABBEGohAiAEp0H/////B3EhA0EAIQADQCAAIANODQECQCACIABBAXRqLwEAIgFBgPADcUGAsANHBEAgACEBDAELIAFB/7cDSw0DIABBAWoiASADTg0DIAIgAUEBdGovAQBBgEBrQf//A3FBgPgDSQ0DCyABQQFqIQAMAAsAC0F/IQALIAALVQEBfwJAAkACQCABQiCIp0EBag4DAAECAQsgAaciAi8BBkEGRw0AIAIpAyAiAUKAgICAcINCgICAgBBRDQELIABBlMAAQQAQEkKAgICA4AAhAQsgAQsQAEHOkQEgAEELEJICQQBHC4kB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ADcCECAAQRBqIAIgACgCBBEAAAwBCyACQoCAgIAwNwMoIAJCgICAgDA3AyAgAkEBNgIECyABIAEoAgxBAWs2AgwLC4YBACAAIAEgBEEiahBaIgJFBEBCgICAgOAADwsgACACIAMpAwAiAUIAIAFCIIinQQdrQW5PGyABIAFCgICAgMCBgPz/AHxC////////////AINQGxDyAiIARQRAQoCAgIAwDwsgACkDKCIBQiCIp0F1TwRAIAGnIgAgACgCAEEBajYCAAsgAQu7BQIDfgd/IwBBEGsiCyQAQoCAgIDgACEHAkAgACABIARBImoQWiICRQ0AIAIoAgBFIAMpAwAiBUIAIAVCIIinQQdrQW5PGyAFIAVCgICAgMCBgPz/AHxC////////////AINQGyIFQv////9vVnJFBEAgABAiDAELQoCAgIAwIQYgBEEBcUUEQCADKQMIIQYLAkAgACACIAUQ8gIiAwRAIAAgAykDKBAMDAELIABBMBAkIgNFDQEgAyACNgIIIANCATcDAAJAIAIoAgAEQCADIAWnIgQoAhg2AgwgBCADNgIYDAELIAVCIIinQXVJDQAgBaciBCAEKAIAQQFqNgIACyADIAU3AyAgAigCECIJIAIoAhQiBEEBayAFENkDcUEDdGoiCCgCACIKIANBGGoiDDYCBCADIAg2AhwgAyAKNgIYIAggDDYCACACKAIEIgggA0EQaiIKNgIEIAMgAkEEaiIMNgIUIAMgCDYCECACIAo2AgQgAiACKAIMQQFqIgg2AgwgCCACKAIYSQ0AIAAgCUEEIARBAXQgBEEBRhsiAEEDdCALQQxqEKcBIghFDQAgCygCDEEDdiAAaiEEQQAhAANAIAAgBEZFBEAgCCAAQQN0aiIJIAk2AgQgCSAJNgIAIABBAWohAAwBCwsgBEEBayEKIAJBCGohAANAIAwgACgCACIARwRAIABBDGsoAgBFBEAgCCAAKQMQENkDIApxQQN0aiIJKAIAIg0gAEEIaiIONgIEIAAgCTYCDCAAIA02AgggCSAONgIACyAAQQRqIQAMAQsLIAIgBDYCFCACIAg2AhAgAiAEQQF0NgIYCyAGQiCIp0F1TwRAIAanIgAgACgCAEEBajYCAAsgAyAGNwMoIAFCIIinQXVPBEAgAaciACAAKAIAQQFqNgIACyABIQcLIAtBEGokACAHCz8BAX8gAUEAIAFBAEobIQEDQAJAIAEgA0YEQEF/IQMMAQsgACADQQN0aigCBCACRg0AIANBAWohAwwBCwsgAwv/BAICfwR+AkAgAkL/////b1gEQCAAECIMAQsCQCAAIAJBPhBuBH9CgICAgDAhBUKAgICAMCEGQoCAgIAwIQggACACQT4gAkEAEBEiB0KAgICAcINCgICAgOAAUQ0BQYECQYACIAAgBxAnGwVBAAshAyAAIAJBPxBuBEBCgICAgDAhBUKAgICAMCEGQoCAgIAwIQggACACQT8gAkEAEBEiB0KAgICAcINCgICAgOAAUQ0BQYIEQYAEIAAgBxAnGyADciEDCyAAIAJBwAAQbgRAQoCAgIAwIQVCgICAgDAhBkKAgICAMCEIIAAgAkHAACACQQAQESIHQoCAgIBwg0KAgICA4ABRDQFBhAhBgAggACAHECcbIANyIQMLQoCAgIAwIQYCQCAAIAJBwQAQbkUEQEKAgICAMCEIDAELQoCAgIAwIQUgACACQcEAIAJBABARIghCgICAgHCDQoCAgIDgAFEEQAwCCyADQYDAAHIhAwsCQAJAIAAgAkHCABBuRQ0AQoCAgIAwIQUgA0GAEHIhAyAAIAJBwgAgAkEAEBEiBkKAgICAcIMiB0KAgICAMFENAEG+MCEEIAdCgICAgOAAUQ0BIAAgBhA1RQ0BCwJAIAAgAkHDABBuRQRAQoCAgIAwIQUMAQsgA0GAIHIhAyAAIAJBwwAgAkEAEBEiBUKAgICAcIMiAkKAgICAMFENAEGvMCEEIAJCgICAgOAAUQ0BIAAgBRA1RQ0BCyADQYAwcQRAQb/YACEEIANBgMQAcQ0BCyABIAU3AxggASAGNwMQIAEgCDcDCCABIAM2AgBBAA8LIAAgBEEAEBILIAAgCBAMIAAgBhAMIAAgBRAMC0F/C7kDAgl/A34jAEEgayIEJAAgBEEANgIMIARBADYCCAJAIAAgASACIAFBABARIg1CgICAgHCDQoCAgIDgAFEEQCANIQEMAQsCQAJAIA1CgICAgHBUDQAgACANEMwBIglBAEgNAQJAIAkEQCAAIARBDGogDRDKAUUNAQwDCyAAIARBCGogBEEMaiANp0EREH0hCyAEKAIIIQYgC0EASA0CCyAEKAIMIQgDQCAHIAhGDQECQCAJBEAgACAHEOwFIgVFDQQMAQsgACAGIAdBA3RqKAIEEBYhBQsCfwJAIAAgDSAFIAMQhQUiDkKAgICAcIMiD0KAgICAMFIEQCAPQoCAgIDgAFINASAAIAUQEAwFCyAAIA0gBUEAEM0BDAELIAAgDSAFIA5BBxAVCyEMIAAgBRAQIAdBAWohByAMQQBODQALDAELIAAgBiAIEFtBACEGIAAgAhBSIg5CgICAgHCDQoCAgIDgAFENACAEIA03AxggBCAONwMQIAAgAyABQQIgBEEQahAcIQEgACAOEAwgACANEAwMAQsgACAGIAQoAgwQWyAAIA0QDEKAgICA4AAhAQsgBEEgaiQAIAELMAEBfyAAKAI4IAFBAnRqKAIAIgEgASgCACICQQFrNgIAIAJBAUwEQCAAIAEQmwMLC44DAQR/IwBBQGoiAyQAAkAgACABEEoiAUKAgICAcINCgICAgOAAUQ0AAkAgACADQSRqIgIgAaciBCgCBEH/////B3FBAmoQPg0AIAJBIhA8DQBBACECIANBADYCPANAIAQoAgRB/////wdxIAJKBEACQAJAAkACQAJAAkACQAJAAkACQCAEIANBPGoQxgEiAkEIaw4GBQIEAQYDAAsgAkEiRiACQdwARnINBgsgAkGA8P8AcUGAsANHIAJBIE9xDQYgAyACNgIAIANBEGoiAkEQQf4PIAMQSBogA0EkaiACEIMBDQoMBwtB9AAhAgwEC0HyACECDAMLQe4AIQIMAgtB4gAhAgwBC0HmACECCyADQSRqIgVB3AAQPA0EIAUgAhA8RQ0BDAQLIANBJGogAhCxAQ0DCyADKAI8IQIMAQsLIANBJGoiAkEiEDwNACAAIAEQDCACEDchAQwBCyAAIAEQDCADKAIkKAIQIgBBEGogAygCKCAAKAIEEQAAQoCAgIDgACEBCyADQUBrJAAgAQvdBgIMfwd+IwBBMGsiAiQAAn4CQAJAIAEpAygiDkKAgICAcINCgICAgJB/UQRAIAEpAwgiEEKAgICAcINCgICAgJB/UQ0BCyAAQcbJAEEAEBIMAQsgASkDICESIAEpAxghDyABKQMAIRMgACACQQxqQQAQPhogAkEANgIkAkAgD0KAgICAcINCgICAgDBSBEAgACACQSRqIA8QygENAQsgACACQShqIBMQygENACAAIAJBLGogASkDEBB1QQBIDQAgEKchCCASQoCAgIBwgyEQIAIoAiwiDCACKAIoaiENIA6nIgRBEGohByAEKAIEQf////8HcSEKIAIoAiQhC0EAIQEDQAJAAkACQCAEQSQgARCgASIGQQBIDQAgBkEBaiIDIApPDQAgAkEMaiAEIAEgBhBLGiAGQQJqIQECQAJAAkACQAJ/IAQpAgRCgICAgAiDUCIJRQRAIAcgA0EBdGovAQAMAQsgAyAHai0AAAsiA0Ekaw4EAAMFAQILIAJBDGpBJBA8GgwGCyACQQxqIAggDSAIKAIEQf////8HcRBLGgwFCyADQeAARg0DCwJAIANBMGsiBUEJTQRAAkAgASAKTw0AAn8gCUUEQCAHIAFBAXRqLwEADAELIAEgB2otAAALIgNBMGtBCUsNACAGQQNqIAEgAyAFQQpsaiIBQTBLIAFBMGsiAyALSXEiCRshASADIAUgCRshBQsgBUUgBSALT3INASAAIA8gBa0QbCIOQoCAgIBwgyIRQoCAgIAwUQ0FIBFCgICAgOAAUQ0GIAJBDGogDhCEAUUNBQwGCyADQTxHIBBCgICAgDBRcg0AIARBPiABEKABIgNBAEgNACAAIAQgASADEI4BIg5CgICAgHCDQoCAgIDgAFENBSAAIBIgDhBOIg5CgICAgHCDIhFCgICAgDBSBEAgEUKAgICA4ABRDQYgAkEMaiAOEIQBDQYLIANBAWohAQwECyACQQxqIAQgBiABEEsaDAMLIAJBDGoiACAEIAEgBCgCBEH/////B3EQSxogABA3DAULIAJBDGogExCNAUUNAQwCCyACQQxqIAhBACAMEEsaDAALAAsgAigCDCgCECIAQRBqIAIoAhAgACgCBBEAAAtCgICAgOAACyEUIAJBMGokACAUC28BA38DQCAAKAIoIgFBAExFBEAgACABQQFrIgE2AiggACgCACAAKAIEIAFBA3RqKQMAEAwMAQsLIAAoAgQiASAAQQhqIgJHBEAgACgCACgCECIDQRBqIAEgAygCBBEAAAsgAEEENgIsIAAgAjYCBAu8CwIHfg1/IwBBEGsiECQAAkAgACABEPUCIgJFBEBCgICAgOAAIQQMAQtCgICAgOAAIQQgACADKQMAECUiCEKAgICAcINCgICAgOAAUQ0AQQAhA0KAgICAICEFQoCAgIAwIQcCQAJAIAAgAUHWACABQQAQESIEQoCAgIBwg0KAgICA4ABRDQAgACAQQQhqIAQQoQENACACKAIEQRBqIgstAAAiDkEhcSIRRQRAIBBCADcDCAsCQCALLQABIgxBAE0NACAAIAxBA3QQJCIDDQBBACEDDAELAkACQCAQKQMIIgkgCKciFCkCBCIEQv////8Hg1UNACADIAsgFEEQaiISIAmnIASnIgJB/////wdxIAJBH3YiEyAAEKQGIgJBAUcEQCACQQBOBEAgESACQQJGcg0CQoCAgIAgIQRCgICAgDAhBgwDCyAAQbg4QQAQOgwDCyARBEAgACABQdYAIAMoAgQgEmsgE3WtEDlBAEgNAwsgABA7IgRCgICAgHCDQoCAgIDgAFEEQEKAgICAMCEGQoCAgIAwIQFCgICAgOAAIQVCgICAgOAAIQQMBAtCgICAgDAhAQJAAkAgCywAAEEASAR/IAsgCygAA2pBB2oFQQALIg1FDQBCgICAgDAhBiAAQoCAgIAgEEEiAUKAgICAcINCgICAgOAAUg0AQoCAgIDgACEBDAELQoCAgIAwIQYCQCAOQcAAcUUNACAAEDsiBkKAgICAcINCgICAgOAAUQRAQoCAgIDgACEGDAILIA1FDQAgAEKAgICAIBBBIgdCgICAgHCDQoCAgIDgAFINAEKAgICA4AAhBwwBCyAMIREgB0KAgICAcIMhCSAGQoCAgIBwgyEKAkADQCAPIBFHBEBBACELIA9FIA1FckUEQCANQQAgDS0AABshCyANED0gDWpBAWohDQtBfyEMAn9BfyADIA9BA3RqIgIoAgAiDkUNABpBfyACKAIEIgJFDQAaIA4gEmsgE3UhDCACIBJrIBN1CyEOIApCgICAgDBSBEACQCAMQX9GBEBCgICAgDAhBQwBCyAAEDsiBUKAgICAcINCgICAgOAAUQ0FIAAgBUIAIAytQYeAARCUAUEASA0EIAAgBUIBIA6tQYeAARCUAUEASA0ECyALRSAJQoCAgIAwUXJFBEAgBUIgiKdBdU8EQCAFpyICIAIoAgBBAWo2AgALIAAgByALIAVBh4ABEL4BQQBIDQQLIAAgBiAPIAVBh4ABEJMBQQBIDQQLAkAgDEF/RgRAQoCAgIAwIQUMAQsgACAUIAwgDhCOASIFQoCAgIBwg0KAgICA4ABRDQQLAkAgC0UNACAFQiCIp0F1TwRAIAWnIgIgAigCAEEBajYCAAsgACABIAsgBUGHgAEQvgFBAE4NACAAIAUQDAwECyAAIAQgDyAFQYeAARCTASEVIA9BAWohDyAVQQBODQEMAwsLIAAgBEGIASABQYeAARAVIRZCgICAgDAhASAWQQBIDQEgACAEQdgAIAMoAgAgEmsgE3WtQYeAARAVQQBIDQECQCAAIARB2QAgCEGHgAEQFUEASA0AQoCAgIAwIQggCkKAgICAMFENBCAAIAZBiAEgB0GHgAEQFUEASARAQoCAgIAwIQcMAQsgACAEQYkBIAZBh4ABEBUhF0KAgICAMCEHQoCAgIAwIQYgF0EATg0ECyAEIQVCgICAgDAhCEKAgICA4AAhBAwFCyAAIAUQDAsgBCEFQoCAgIDgACEEDAMLQoCAgIAgIQRCgICAgDAhBiAAIAFB1gBCABA5QQBODQBCgICAgDAhAUKAgICA4AAhBAwCC0KAgICAMCEBQoCAgIAwIQUMAQtCgICAgDAhBkKAgICAMCEBQoCAgIDgACEECyAAIAcQDCAAIAYQDCAAIAgQDCAAIAEQDCAAIAUQDCAAKAIQIgBBEGogAyAAKAIEEQAACyAQQRBqJAAgBAu3BwEGfwJAAkACQAJAAkACQAJAAkAgAS0ABEEPcQ4FAAEFBQYFCyABIAEtAAVBAnI6AAUgASgCECIEQTBqIQMDQCABKAIUIQUgAiAEKAIgTkUEQCAAIAUgAkEDdGogAygCAEEadhDUBSACQQFqIQIgA0EIaiEDDAELCyAAQRBqIgYgBSAAKAIEEQAAIAAgBBCMAiABQgA3AxAgASgCGCICBEAgAiEDA0AgAwRAIAMoAggoAgBFDQUgAygCBA0EIAMoAhgiBCADKAIcIgU2AgQgBSAENgIAIANCADcCGCADKAIQIgQgAygCFCIFNgIEIAUgBDYCACADQgA3AhAgAygCDCEDDAELCwNAIAIEQCACKAIMIQcgACACKQMoECEgBiACIAAoAgQRAAAgByECDAELCyABQQA2AhgLIAAoAkQgAS8BBkEYbGooAggiAgRAIAAgAa1CgICAgHCEIAIRDAALIAFBADYCKCABQgA3AyAgAUEAOwEGIAEoAggiAiABKAIMIgM2AgQgAyACNgIAIAFCADcCCCAALQBoQQJHDQMgASgCAEUNAwwGCyAAIAEoAhQgASgCGEEBEJkFAkAgASgCIEUNAANAIAIgAS8BKiABLwEoak8NASAAIAEoAiAgAkEEdGooAgAQxwEgAkEBaiECDAALAAtBACECA0AgASgCOCACTARAQQAhAgNAIAIgASgCPE5FBEAgACABKAIkIAJBA3RqKAIEEMcBIAJBAWohAgwBCwsgASgCMCICBEAgAhCeAwsgACABKAIcEMcBIAEtABJBBHEEQCAAIAEoAkAQxwEgAEEQaiICIAEoAlAgACgCBBEAACACIAEoAlQgACgCBBEAAAsgASgCCCICIAEoAgwiAzYCBCADIAI2AgAgAUIANwIIAkAgAC0AaEECRw0AIAEoAgBFDQAMCAsgAEEQaiABIAAoAgQRAAAPBSAAIAEoAjQgAkEDdGopAwAQISACQQFqIQIMAQsACwALQb0LQajsAEHm7wJB6cwAEAAAC0GKxgBBqOwAQeXvAkHpzAAQAAALIAYgASAAKAIEEQAADwsQAQALIAEoAiBFBEAgACABEJgFCyAAIAEpAygQISAAIAEpAzAQISABKAIIIgIgASgCDCIDNgIEIAMgAjYCACABQgA3AggCQCAALQBoQQJHDQAgASgCAEUNAAwBCyAAQRBqIAEgACgCBBEAAA8LIAAoAlgiAiABQQhqIgM2AgQgASAAQdgAajYCDCABIAI2AgggACADNgJYC00BAX5BsNQEKAIABEBBuNQEKQMAIgBQRQRAQbTUBCgCACAAEAwLQbTUBCgCABCeA0G01ARBADYCAEGw1AQoAgAQwAVBsNQEQQA2AgALC+ACAQh/IAJBCGohBwJAAkACQAJAA0AgASgCaCAFTARAQQAhAwwFC0EAIQMgAigCBCIGQQAgBkEAShshCCABKAJkIAVBAnRqKAIAIQQCQAJAA0AgAyAIRwRAIANBAnQhCiADQQFqIQMgCiACKAIAaigCACAERw0BDAILCyAEKAKAAS0AoAENACAELQBXQRh0QYCAgCBHDQEgBC0AoAENAyAEKAJ0RQ0EIAQoAnAiA0EATA0FIAQgA0EBayIDNgJwIAMNAEF/IQMgACACQQQgByAGQQFqEGQNBiACIAIoAgQiBkEBajYCBCACKAIAIAZBAnRqIAQ2AgAgBC0AVA0AIAAgBCACEI0FDQYLIAVBAWohBQwBCwtB5v4AQajsAEGj3wFBqiMQAAALQeY4QajsAEGk3wFBqiMQAAALQfk6QajsAEGl3wFBqiMQAAALQZWFAUGo7ABBpt8BQaojEAAACyADC3YBAX8jAEEQayICJAAgAUEFOgBXAkAgATUCjAFCIIZCgICAgDBSBEAgASgCgAEgAUcNASACQoCAgIAwNwMIIAAgACABKQOQAUKAgICAMEEBIAJBCGoQHBAMCyACQRBqJAAPC0H5wABBqOwAQf3eAUGp5wAQAAALtQICAn4BfwJAAkACQCABKAJQIgUEQCAAIAEgBREDAEEASA0BDAMLIAAgASkDSEKAgICAMEEAQQAgARCfBCIDQoCAgIBwg0KAgICA4ABRDQBBfyEBAkAgA0KAgICAcFQNACADpyIFLwEGQS1HDQAgBSgCICIFRQ0AIAUoAgAhAQsCQAJAIAFBAWsOAgMAAQtCgICAgDAhBAJAIANCgICAgHBUDQAgA6ciAS8BBkEtRw0AIAEoAiAiAUUNACABKQMYIgRCIIinQXVJDQAgBKciASABKAIAQQFqNgIACyACIAQ3AwAgACADEAxBfw8LIAAgAxAMIABBw8sAQQAQEgsgACgCECIAKQOAASEDIABCgICAgCA3A4ABIAIgAzcDAEF/DwsgACADEAwLIAJCgICAgDA3AwBBAAu3AQIBfwR+IwBBIGsiAiQAIAAgASkDSEKAgICAMEEAQQAgABCfBCIDQoCAgIBwg0KAgICA4ABSBEAgASABKAIAQQFqNgIAIAIgAa1CgICAgFCEIgQ3AxggAiAAQT9BAEEAQQEgAkEYaiIBEIUBIgU3AwAgAiAAQcAAQQBBAEEBIAEQhQEiBjcDCCAAIAAgAyAAIAIQ+AMQDCAAIAQQDCAAIAUQDCAAIAYQDCAAIAMQDAsgAkEgaiQAC8sBAgJ/AX4jAEEQayIGJAACQAJAIAJCgICAgHBUDQAgAqciBy8BBkEMRw0AIActAClBDEcNACAAIAEgAyADBH8gBAUgBkKAgICAMDcDCCAGQQhqCyAFIAcuASogBygCJBERACEIDAELQoCAgIDgACEIAkAgACACIAEgAyAEEBwiAUKAgICAcINCgICAgOAAUgRAIAFC/////29WDQEgACABEAwgAEH6HkEAEBILIAVBADYCAAwBCyAFQQI2AgAgASEICyAGQRBqJAAgCAsNACAAIAEgAkEAELQBC18BAX8gAUEQaiEDAkAgAS0AB0GAAXEEQCAAIAMgAkEBdBAeGgwBC0EAIQEgAkEAIAJBAEobIQIDQCABIAJGDQEgACABQQF0aiABIANqLQAAOwEAIAFBAWohAQwACwALC6gBAQV/IACnIgMoAhAiAUEwaiEEIAEgASgCGEF/c0ECdEGgfnJqKAIAIQEDQCABRQRAQQAPCyAEIAFBAWsiBUEDdGoiASgCACECIAEoAgRBN0cEQCACQf///x9xIQEMAQsLQQEhAQJAIAJB/////wNLDQAgAygCFCAFQQN0aikDACIAQoCAgIBwg0KAgICAkH9SDQAgAKcoAgRB/////wdxQQBHIQELIAEL1wMBBn8jAEEQayIHJAAgBUEEaiEJAkACQANAQQAhBiABQQA2AgAgAkEANgIAIAUoAggiCEEAIAhBAEobIQoDQCAGIApHBEACQCAFKAIAIAZBA3RqIgsoAgAgA0cNACALKAIEIARHDQBBAiEGDAULIAZBAWohBgwBCwsgACAFQQggCSAIQQFqEGQEQEF/IQYMAwsgBSAFKAIIIgZBAWo2AgggBSgCACAGQQN0aiIGIAM2AgAgBiAAIAQQFiIINgIEIAMgCBC6BSIGBEAgBigCCEUNAiAGKAIMIgRB/gBGDQIgAygCECAGKAIAQQN0aigCBCEDDAELCyAIQRZHBEBBACEEA0AgAygCLCAESgRAAkACQCAAIAdBDGogB0EIaiADKAIQIAMoAiggBEECdGooAgBBA3RqKAIEIAggBRCVBSIGQQFqDgUGAAEBBgELIAIoAgAiBgRAIAEoAgAgBygCDEYEQCAHKAIIKAIMIAYoAgxGDQILIAFBADYCACACQQA2AgBBAyEGDAYLIAEgBygCDDYCACACIAcoAgg2AgALIARBAWohBAwBCwtBACEGIAIoAgANAgtBASEGDAELIAEgAzYCACACIAY2AgBBACEGCyAHQRBqJAAgBguwAwELfyABKAIIIgVBACAFQQBKGyEGAkACQANAIAQgBkcEQCAEQQJ0IQ4gBEEBaiEEIA4gASgCAGooAgAgAkcNAQwCCwtBfyEHIAAgAUEEIAFBBGogBUEBahBkDQEgASABKAIIIgRBAWo2AgggASgCACAEQQJ0aiACNgIAIAFBEGohCiABQQxqIQhBACEFA0AgAigCICAFTARAQQAhBANAIAQgAigCLE4NAyAEQQJ0IQMgBEEBaiEEIAAgASACKAIQIAMgAigCKGooAgBBA3RqKAIEQQEQlgVFDQALDAMLAkAgA0EAIAIoAhwgBUEUbGoiBigCECILQRZGGw0AQQAhBCABKAIUIglBACAJQQBKGyEMAkADQCAEIAxHBEAgCCgCACAEQQxsaiINKAIAIAtGDQIgBEEBaiEEDAELCyAAIAhBDCAKIAlBAWoQZA0EIAEgASgCFCIEQQFqNgIUIAEoAgwgBEEMbGoiBCAGKAIQNgIAAkAgA0UEQCAGKAIIRQ0BCyAEQQA2AggMAgsgBCAGNgIIDAELIA1BADYCCAsgBUEBaiEFDAALAAtBACEHCyAHC6sCAQR/IwBBEGsiAyQAAkACQAJAAkACQAJAAkACQCABQiCIpyICQQpqDgoCBAMABAQEBQEBBAsgAaciAikCBEKAgICAgICAgMAAVA0FIAAgAhCbAwwGCyAALQBoQQJGDQUgAaciAigCCCIEIAIoAgwiBTYCBCAFIAQ2AgAgAkEANgIMIAAoAlwhBCAAIAJBCGoiBTYCXCACIAQ2AgwgAiAAQdgAajYCCCAEIAU2AgAgAC0AaA0FIAAQ5gUMBQsgAaciAkEEahAZIABBEGogAiAAKAIEEQAADAQLIAAgAacQmwMMAwsgAyACNgIAIwBBEGsiACQAIAAgAzYCDEGQ0wRBv5MBIAMQkwQgAEEQaiQACxABAAsgAEEQaiACIAAoAgQRAAALIANBEGokAAt/AQJ/AkAgASgCSCICBEAgASgCZCIDRQ0BA0AgAiADT0UEQCAAIAIpAwAQISACQQhqIQIgASgCZCEDDAELCyAAQRBqIAEoAkggACgCBBEAACABQQA2AkgLIAAgASkDQBAhIAAgASkDEBAhDwtB5PUAQajsAEHwkgFBhtQAEAAAC2UBBH8DQCACIAVKBEAgASAFaiIGLQAAIgRBE2ogBCAEQbMBSxsgBCADG0ECdCIEQeCuAWotAAAhByAEQeOuAWotAABBF2tB/wFxQQRNBEAgACAGKAABEMcBCyAFIAdqIQUMAQsLC0gBA38gAkEAIAJBAEobIQIDQCACIANGBEBBAA8LIAEgA2ohBCADQQF0IQUgA0EBaiEDIAAgBWovAQAgBC0AAGsiBEUNAAsgBAtYAQJ/IAEEQAJAIAAoAgggACgCBCIDIAFqSQ0AIAEQjwIiAUUNACAAIANBCGo2AgQgACAAKAIAQQFqNgIAIAEhAgsgAg8LQc2HAUGo7ABBtQ1B9OsAEAAAC0wBA38gACgCIEEYaiEBAkADQCABIgMoAgAiAkUNASACQQxqIQEgACACRw0ACyADIAAoAgw2AgAPC0GC9gBBqOwAQbPvAkH4zAAQAAAL4wQBCX8gACAAQeAAaiIENgJkIAAgBDYCYCAAQdQAaiECIABB0ABqIQcgAEHkAGohBSAAKAJUIQMDQCAHIAMiAUYEQAJAAkADQAJAIAcgAigCACIBRgRAIAUhAQNAIAEoAgAiASAERg0CIAAgAUEIa0ENEN0DIAFBBGohAQwACwALIAFBCGsiAygCAEEATA0CIAFBBGsiAiACLQAAQQ9xOgAAIAAgA0EOEN0DIAFBBGohAgwBCwsgAEECOgBoIABB2ABqIQMDQCAEIAUoAgAiAUcEQCABQQRrLQAAQQ9xIgJBBEtBASACdEETcUVyBEAgASgCACICIAEoAgQiBzYCBCAHIAI2AgAgAUEANgIAIAMoAgAiAiABNgIEIAEgAzYCBCABIAI2AgAgAyABNgIADAIFIAAgAUEIaxCLBQwCCwALCyAAQQA6AGggAEEQaiEEIAAoAlwhAQNAIAEgA0cEQCABQQRrLQAAQQ9xIgVBBEtBASAFdEETcUVyDQMgASgCBCEJIAQgAUEIayAAKAIEEQAAIAkhAQwBCwsgACADNgJcIAAgAEHYAGo2AlgPC0HmhAFBqOwAQY0tQarAABAAAAtBzvMAQajsAEHFLUHjJxAAAAsgAUEEayIGLQAAQRBJBEAgASgCBCEDIAAgAUEIayIIQQ8Q3QMgBiAGLQAAQQ9xQRByOgAAIAgoAgANASABKAIAIgYgASgCBCIINgIEIAggBjYCACABQQA2AgAgBCgCACIGIAE2AgQgASAENgIEIAEgBjYCACAEIAE2AgAMAQsLQeuGAUGo7ABB6ixBs8wAEAAACxgBAX8gAacoAiAiAwRAIAAgAyACEQAACwsyACAAIAEQqgIiAUKAgICAcINCgICAgMB+UQR+IABB2cMAQQAQigJCgICAgOAABSABCwsMACAAIAEQtQNBAEwLSAEBfyMAQRBrIgIkAAJAIAFBIHEEQCAAEHAMAQsgAkH+N0GmO0H5ECABQQFxGyABQQJxGzYCACAAQZArIAIQRAsgAkEQaiQAC8oIAhN/AX4jAEEgayILJABCgICAgOAAIRYCQCAAIAtBDGogARCuAiIHRQ0AIAcoAgQhECAHKAIIQYCAgIB4RgRAIAdBADYCBAsjAEGAAWsiAyQAIANB6ABqIgYgBygCACIMQZUDEJ0CAn8CQAJAIAcoAggiBUH/////B0YEQCAGQbvzABDeAgwBCyAHKAIEBEAgA0HoAGpBLRAOIAcoAgghBQsgBUH+////B0YEQCADQegAakHRCxDeAgwBCyADQgA3AlggA0KAgICAgICAgIB/NwJQIAMgDDYCTCACIAJBAWsiBnFFBEBBICAGZ2tBACACQQJPGyEECwJAAkAgBARAIANBzABqIgUgBxBJDQEgBUEAQREQugFBIHENASAEQQFrQQAgAygCVCIFQQBOGyAFaiAEbSEEIAVBgICAgHhGBEAgA0HoAGpB1YcBEN4CDAMLQQAhBSAEQQBMBEAgA0HoAGpB6ocBEN4CQQAgBGshBgNAIAUgBkcEQCADQegAakEwEA4gBUEBaiEFDAELCyAEQQBMDQMgA0HoAGogA0HMAGogAiAEIAQQrAMMAwsgA0HoAGogA0HMAGogAiAEIAQQrAMMAgsgAyAHKAIQNgJIIAMgBygCDDYCRCADQQA2AjwgAyAFNgJAIAMgBUEAIAVBAEobIAJBARCtBEEBaiIFNgJgIANBzABqIhEhBCMAQSBrIhIkAAJAIANBOGoiCCgCDEUEQCADQQA2AmAgBCAIEEkhCQwBCyADKAJgIQ0gBSACQQAQrQQhE0EBQcEAIAUgDWsiDiAOQR91IgZzIAZrIg9BAWtnQQF0ayAPQQFNGyEUQRAhBgNAQSAhCSAEIAIgDyAGIBNqIhUgFGoiCkHgDxDXAgJ/IA5BAE4EQCAEIAQgCCAKQeAPEEAMAQsgBCAIIAQgCkHgDxCIAQtyIgpBIHENAQJAIApBEHFFDQAgBCAEKAIIQQEgFRC2Aw0AIAZBAm0gBmohBgwBCwsgBEEBEO8BQSBxDQAgAyANNgJgQQAhCQsgEkEgaiQAIAkNACADKAJsIQQgA0HoAGogESACIAUgBRCsAyADKAJsIgkgBEEBaiICIAIgCUkbQQFrIQYgAygCaCEIIAQhBQNAAkAgCSAFIgJBAWoiBU0EQCAGIQIMAQsgAiAIai0AAEEwRw0AIAUgCGotAABBLkcNAQsLIAIgBE0NASAEIAhqIAIgCGogCSACaxCrASADIAMoAmwgBCACa2o2AmwMAQsgA0HMAGoQGQwCCyADQcwAahAZCyADQegAakEAEA4gAygCdA0AIAMoAmgMAQsgAygCaCICBEAgDCgCACACQQAgDCgCBBEBABoLQQALIQIgA0GAAWokACAHIBA2AgQgACAHIAtBDGoQ5gEgAkUEQCAAEHAMAQsgACACEGAhFiAAKALYASIAKAIAIAJBACAAKAIEEQEAGgsgC0EgaiQAIBYLiXgCF38CfiMAQeAGayIDJAAgASgCyAEiBEEAIARBAEobIQYDQCACIAZHBEAgASgCzAEgAkEDdGpBfzYCBCACQQFqIQIMAQsLIAEoAjwEQCABKALMAUF+NgIMC0EAIQIgASgCfCIGQQAgBkEAShshBgJ+AkACQANAIAIgBkYEQAJAQQIhAkECIAQgBEECTBshBQNAAkAgAiAFRgRAQQAhAgNAIAIgBkYNAgJAIAEoAnQgAkEEdGoiBCgCCEEATg0AIAQoAgQiBUECSA0AIAQgASgCzAEiBCAEIAVBA3RqKAIAQQN0aigCBDYCCAsgAkEBaiECDAALAAsgASgCzAEiByACQQN0aiIEKAIEQQBIBEAgBCAHIAQoAgBBA3RqKAIENgIECyACQQFqIQIMAQsLAkAgASgCREUNAAJAIAEoAiANACABLQBuQQFxDQAgASAAIAFB0wAQTDYCkAEgASgCPEUNACABIAAgAUHUABBMNgKUAQsCQCABKAJMIgpFDQAgASgCqAFBAEgEQCABIAAgARDTAzYCqAELIAEoAqwBQQBIBEAgASAAIAFB8gAQTDYCrAELAkAgASgCYEUNACABKAKwAUEATg0AIAEgACABQfMAEEw2ArABCyABKAIwRQ0AIAEoArQBQQBODQAgASAAIAFB9AAQTDYCtAELAkAgASgCSCIIRQ0AIAAgARDwAhogASgCPEUNACABLQBuQQFxDQAgASgCnAFBAE4NACABKALMAUEMaiEFA0ACQCAFKAIAIgJBAEgNACABKAJ0IAJBBHRqIgIoAgRBAUcNACACQQhqIQUgAigCAEHOAEcNAQwCCwsgACABQc4AEEwiAkEASA0AIAEoAnQgAkEEdGoiBCABKALMASIGKAIMNgIIIAYgAjYCDCAEQQE2AgQgBCAEKAIMQQJyNgIMIAEgAjYCnAELAkAgASgCLEUNACABKAJwIgJFDQAgACABIAIQ7wIaCwJAIAEoAiAEQCABIQUMAQsgASEFIAEoAsACDQILA0AgBSgCBCICRQ0BIAUoAgwhBAJAIAoNACACKAJMRQRAQQAhCgwBCyACKAKoAUEASARAIAIgACACENMDNgKoAQsgAigCrAFBAEgEQCACIAAgAkHyABBMNgKsAQsCQCACKAJgRQ0AIAIoArABQQBODQAgAiAAIAJB8wAQTDYCsAELQQEhCiACKAIwRQ0AIAIoArQBQQBODQAgAiAAIAJB9AAQTDYCtAELAkAgCA0AIAIoAkhFBEBBACEIDAELIAAgAhDwAhpBASEICwJAIAIoAixFDQAgAigCcCIGRQ0AIAAgAiAGEO8CGgsgAigCzAEgBEEDdGpBBGohBQNAIAUoAgAiBEEATgRAIAIoAnQgBEEEdGoiBiAGKAIMIgVBBHI2AgwgACABIAJBACAEIAYoAgAgBUEBcSAFQQF2QQFxIAVBBHZBD3EQnwEaIAZBCGohBQwBCwsCQCAEQX5HBEBBACEFA0AgAigCiAEgBUwEQEEAIQUDQCAFIAIoAnxODQQCQCACKAJ0IAVBBHRqIgQoAgQNACAEKAIAIgZFIAZB0gBGcg0AIAAgASACQQAgBSAGQQAgBCgCDEEBdkEBcUEAEJ8BGgsgBUEBaiEFDAALAAsgAigCgAEgBUEEdGoiBCgCACIGBEAgACABIAJBASAFIAZBACAEKAIMQQF2QQFxQQAQnwEaCyAFQQFqIQUMAAsAC0EAIQUDQCAFIAIoAnxODQECQCACKAJ0IAVBBHRqIgQoAgQNACAEEKYFRQ0AIAAgASACQQAgBSAEKAIAQQAgBCgCDEEBdkEBcUEAEJ8BGgsgBUEBaiEFDAALAAsgAiIFKAIgRQ0AQQAhBQNAIAIoAsACIAVMBEAgAiEFDAIFIAAgASACQQAgAigCyAIgBUEDdGoiBi0AACIEQQF2QQFxIAUgBigCBCAEQQJ2QQFxIARBA3ZBAXEgBEEEdhD7ARogBUEBaiEFDAELAAsACwALIAEoApQDIgRFDQNBACECA0AgASgC9AEgAkwEQEEAIQcDQCAHIAQoAiBODQYgBCgCHCAHQRRsaiIGKAIIRQRAQQAhAiABKALAAiIFQQAgBUEAShshCiAGKAIMIQUCQANAIAIgCkcEQCABKALIAiACQQN0aigCBCAFRg0CIAJBAWohAgwBCwsgACAFQZAVEIEDDAkLIAYgAjYCAAsgB0EBaiEHDAALAAsgACABQQFBACACIAEoAvwBIAJBBHRqIgYoAgwgBi0ABCIGQQJ2QQFxIAZBAXZBAXFBABDSAyEUIAJBAWohAiAUQQBODQALDAQLBSABKAJ0IAJBBHRqIgUgASgCzAEgBSgCBEEDdGoiBSgCBDYCCCAFIAI2AgQgAkEBaiECDAELC0H8hQFBqOwAQYfxAUHyJxAAAAsgAUEQaiEFIAEoAhQhAgJAA0AgAiAFRwRAIAIoAgQhFSACQRBrKAIAIQYgACACQRhrEKMFIhlCgICAgHCDQoCAgIDgAFENAyAGQQBIDQIgASgCtAIgBkEDdGogGTcDACAVIQIMAQsLIAMgASgCgAIiDDYCnAYgAyABKAKEAiIPNgKgBiAAIANBwAZqEIMCIAFBgAJqIQ1BACEIA38gASgC9AEgCEwEf0EAIQpBAAVBACECIAEoAsACIgRBACAEQQBKGyEGIAEoAvwBIAhBBHRqIQQCQCADQcAGagJ/A0AgAiAGRwRAIAEoAsgCIAJBA3RqIgUoAgQiByAEKAIMRgRAIAEoAiRBAkcNBCAFLQAAQQhxRQ0EIANBwAZqIgJBMBAOIAIgACAEKAIMEBYQG0EBDAMLIAJBAWohAiAHQdMAa0ECTw0BDAMLCyADQcAGaiICQT8QDiACIAAgBCgCDBAWEBsgBC0ABEEGdCICQYB/cSACQcAAciAEKAIAQQBIGwtB/wFxEA4LIAhBAWohCAwBCwshBgNAAkACQAJAAkACQAJAAkACQAJAAkACQCAPIAoiAkoEQCACIAIgDGoiCS0AACIEQQJ0QeCuAWotAAAiEGohCgJAAkACQAJAAkACQAJAAkACQAJAIARBswFrDhQWBQ8EAQEBAQIBAQEDAwMDDQwWCwALIARBEWsiAkEfSw0QQQEgAnRBgIDQjHxxDREgAkUNDSACQQVHDRAgA0F/NgIYIANCyfqAgOABNwMQIANBnAZqIAogA0EQahAjRQ0TIANBwAZqIgQgAy0ArAYQDiADKAKkBiEKIAMoAqgGIgJBf0YgAiAGRnINFSABIAEoAtwCQQFqNgLcAiAEQcYBEA4gBCACEBsgAiEGDBULIAAgASAJKAABIgIgCS8ABSAEIANBwAZqQQBBACAKEPUEIQogACACEBAMFAsgCS8ACSEFIAkoAAEhAiABKAKkAiAJKAAFQRRsaiIEIAQoAgBBAWs2AgAgACABIAIgBUG7ASADQcAGaiAMIAQgChD1BCEKIAAgAhAQDBMLIAAgA0HYBmogA0HcBmogASAJKAABIgcgCS8ABSIJEPQEIgVBAEgNBSADKALcBiIIRQ0EAkACQAJAAkACQAJAIARBvwFrDgQAAAECAwsCQAJAAkAgCEEFaw4FAAECBgIFCyAEQcABRgRAIANBwAZqQREQDgsgA0HABmoiAiADKALYBiAFEPoBIAJBxAAQDgwGCyADQcAGaiICIAMoAtgGIAUQ+gEgAkEsEA4gBEHAAUYNBSACQQ8QDgwFCyAEQcABRgRAIANBwAZqQREQDgsgA0HABmoiAiADKALYBiAFEPoBIAJBLBAOIAJBJBAOIAJBABAmDAQLAkACQAJAIAhBBWsOBQABAQICBAsgA0HABmoiAiADKALYBiAFEPoBIAJBxQAQDgwFCyADQcAGaiICQTAQDiACIAAgBxAWEBsgAkEAEA4MBAsgACAHEPMEIgRFDQkgACADQdgGaiADQdwGaiABIAQgCRD0BCEFIAAgBBAQIAVBAEgNCSADKALcBkEIRw0HIANBwAZqIgIgAygC2AYgBRD6ASACQRsQDiACQR4QDiACQSwQDiACQR0QDiACQSQQDiACQQEQJiACQQ4QDgwDCyADQcAGaiICIAMoAtgGIAUQ+gEgAkGyARAODAILEAEACyADQcAGaiICQTAQDiACIAAgBxAWEBsgAkEAEA4LIAAgBxAQDBILIAkoAAEiAkEASA0BIAIgASgCrAJODQEgASgCpAIgAkEUbGogAygCxAYgEGo2AggMDwtBACEFQQAhAiAJLwABIhAgASgC8AFHDQoDQCABKAKIASACSgRAIAEoAoABIAJBBHRqIgcoAgxBAE4EQCADQcAGaiIEQQMQDiAEIAcoAgxBCHUQGyAEQdwAEA4gBCACQf//A3EQJgsgAkEBaiECDAELCwNAIAEoAnwgBUoEQAJAIAEoAnQgBUEEdGoiBCgCBA0AIAQoAgxBAEgNACADQcAGaiICQQMQDiACIAQoAgxBCHUQGyACQdkAEA4gAiAFQf//A3EQJgsgBUEBaiEFDAELCwJAIAEoApQDRQRAQX8hCQwBCyABQX8Q0QMhCSADQcAGaiICQQgQDiACQeoAEA4gAiAJEBsgASAJQQEQYxogASABKALQAkEBajYC0AILQQAhCANAAkACQCABKAL0ASAISgRAQQAhAiABKALAAiIEQQAgBEEAShshByABKAL8ASAIQQR0aiIELQAEIgVBAXEhCwJ/A0AgAiAHRwRAIAEoAsgCIAJBA3RqKAIEIg4gBCgCDEYEQEEAIQsgAiEHQQIMAwsgDkHTAGtBAU0EQCADQcAGaiIFQd4AEA4gBSACQf//A3EQJkEBIQsgAiEHQQEMAwUgAkEBaiECDAILAAsLIAEoAiRBAEchDiAFQQJxIhFFIAQoAgBBAE5xDQIgA0HABmoiAkE+EA4gAiAAIAQoAgwQFhAbIAJBgH9Bgn8gBUEEcRtBACARGyAOckGDAXEQDkEACyEFIAtFIAQoAgAiAkEASHENAgJAIAJBAE4EQCADQcAGaiICQQMQDiACIAQoAgAQGyAEKAIMQf0ARw0BIAJBzQAQDiACQRYQGwwBCyADQcAGakEGEA4LAkACQAJAIAVBAWsOAgEAAgsgA0HABmoiAkHfABAOIAIgB0H//wNxECYMBAsgA0HABmoiAkHMABAOIAIgACAEKAIMEBYQGyACQQ4QDgwDCyADQcAGaiICQTkQDiACIAAgBCgCDBAWEBsMAgsgASgClAMEQCADQcAGaiICQSkQDiACQbYBEA4gAiAJEBsgASgCpAIgCUEUbGogAygCxAY2AggLIAAoAhAiAkEQaiABKAL8ASACKAIEEQAAIAFCADcC9AEgAUEANgL8AQwNCyADQcAGaiICQQMQDiACIAQoAgAQGyACQcAAEA4gAiAAIAQoAgwQFhAbIAIgDhAOCyAAIAQoAgwQECAIQQFqIQgMAAsAC0HcF0Go7ABB4fYBQYUoEAAAC0HU8gBBqOwAQaXwAUHd4wAQAAALQY72AEGo7ABB6O8BQd3jABAAAAsDQCACIA9IBEAgA0HABmogAiAMaiIEIAQtAABBAnRB4K4Bai0AACIEEHIaIAIgBGohAgwBCwsgDRCJASANIAMpAtAGNwIQIA0gAykCyAY3AgggDSADKQLABjcCAAwOCyANEIkBIA0gAykC0AY3AhAgDSADKQLIBjcCCCANIAMpAsAGNwIAIAEoAowCBEAgABBwDA4LIAEoAqQCIQsgAyABKALwAjYC2AYgAyABKAKAAiIKNgKcBiADIAEoAoQCIgg2AqAGIAAgA0HABmoQgwIgASgC0AIiAgRAIAEgASgCACACQQR0EFwiAjYCzAIgAkUNDgsCQCABKALcAiICRQ0AIAEtAG5BAnENACABIAEoAgAgAkEDdBBcIgI2AtgCIAJFDQ4gAUEANgLoAiABIAEoAvACNgLkAgsgASgCtAFBAE4EQCADQcAGaiICQQwQDiACQQQQDiACQdkAIAEoArQBEFkLIAEoArABQQBOBEAgA0HABmoiAkEMEA4gAkECEA4gAkHZACABKAKwARBZCyABKAKsAUEATgRAIANBwAZqIgJBDBAOIAJBAxAOIAJB2QAgASgCrAEQWQsCQCABKAKoAUEASA0AIAEoAmAEQCADQcAGaiICQeEAEA4gAiABLwGoARAmDAELIANBwAZqIgJBCBAOIAJB2QAgASgCqAEQWQsgASgCmAFBAE4EQEEAIQIgAS0AbkEBcUUEQCABKAI4QQBHIQILIANBwAZqIgRBDBAOIAQgAhAOIAEoApwBIgJBAE4EQCAEQdoAIAIQWQsgA0HABmpB2QAgASgCmAEQWQsgASgCoAFBAE4EQCADQcAGaiICQQwQDiACQQIQDiACQdkAIAEoAqABEFkLIAEoApABQQBOBEAgA0HABmoiAkEMEA4gAkEFEA4gAkHZACABKAKQARBZCyABKAKUAUEATgRAIANBwAZqIgJBDBAOIAJBBRAOIAJB2QAgASgClAEQWQtBACECAkADQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCACIAhOBEBBACECIAEoAqwCIgRBACAEQQBKGyEEA0AgAiAERg0CIAJBFGwhFiACQQFqIQIgFiALaigCEEUNAAtBtfUAQajsAEHd/wFBniYQAAALIAIgAiAKaiIGLQAAIgVBAnRB4K4Bai0AACIMaiEEAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAFQdgAaw4iEBIaERIaERIaGhoaGhoaGhoaBAQBAwIaGhoMDAUFBQUFBQALAkAgBUEBaw4VCQoKCxoNBxoICBoaGgYaGg8aGhoOAAsgBUEiayIHQR9LDRhBASAHdCIJQcDhAXENEiAJQQVxRQRAIAdBH0cNGSAGKAABQTBHDRogASADKALEBiADKALYBhAuIANBwAZqQekBEA4gBCECDCMLIAYvAAEhAiADQqiAgIBwNwNQIANBnAZqIAQgA0HQAGoQIwRAAkAgAygCqAYiBEEASARAIAMoAtgGIQQMAQsgAyAENgLYBgsgASADKALEBiAEEC4gA0HABmogBUEBaiACEFkgASAKIAggAygCpAYgA0HYBmoQpQIhAgwjCyABIAMoAsQGIAMoAtgGEC4gA0HABmogBSACEFkgBCECDCILIAYoAAEhBSAEIQYMFgsgBigAASEHQe4AIQUMFAsgBigAASEHQe0AIQUMEwsgA0GcBmogBCABIAYoAAEgA0HcBmpBABDQAyIHEM8DBEAgASAHQX8QYxogA0HABmpBDhAOIAQhAgwfCyADQuyAgIBwNwNgIANBnAZqIgYgBCADQeAAahAjRQ0SIAMoAqgGIQkgBiADKAKkBiIGIAcQzwNFDRIgCUEATgRAIAMgCTYC2AYLIAEgB0F/EGMaIAVBAXMhBSADKAK0BiEHDBwLIAYtAAkhByAGKAABIQkgASAGKAAFIANB3AZqQQAQ0AMiAkEASA0PIAIgASgCrAJODQ8gASADKALEBiADKALYBhAuIAEgASgC1AIiBkEBajYC1AIgASgCzAIgBkEEdGoiBkEENgIEIAYgBTYCACADKALEBiEMIAYgAjYCDCAGIAxBBWo2AgggA0HABmoiBiAFEA4gBiAJEBsgBiALIAJBFGxqIgIoAgwgAygCxAZrEBsgAigCDEF/RgRAIAAgAiADKALEBkEEa0EEEO4CRQ0dCyADQcAGaiAHEA4gBCECDB0LIANCqYCAgHA3A3AgA0GcBmogBCADQfAAahAjRQ0TIAQhAiADKAKoBiIEQQBIDRwgAyAENgLYBgwcCyADQqyBgIBwNwOgASADQZwGaiAEIANBoAFqECMEQAJAIAMoAqgGIgJBAEgEQCADKALYBiECDAELIAMgAjYC2AYLIAEgAygCxAYgAhAuIANBwAZqQfMBEA4MGAsgA0F/NgKYASADQq2BgICg7Ro3A5ABIANBnAZqIAQgA0GQAWoQI0UNAAJAIAMoAqgGIgVBAEgEQCADKALYBiEFDAELIAMgBTYC2AYLIAEgAygCxAYgBRAuIANBwAZqQfMBEA4gAygCrAZBAXMhBQwYCyADQurWgYBwNwOAASADQZwGaiAEIANBgAFqECNFDREgBUEKRiEJDA0LAkAgBigAASIGQYCAgIB4ckGAgICAeEYNACADQo2BgIBwNwPgASADQZwGaiAEIANB4AFqECNFDQAgAygCqAYiAkEATgRAIAMgAjYC2AYLIANCjoCAgHA3A9ABIANBnAZqIAMoAqQGIANB0AFqECMEQCADKAKoBiICQQBIDRcgAyACNgLYBgwXCyABIAMoAsQGIAMoAtgGEC4gA0HABmpBACAGaxDOAwwWCyADQo6AgIBwNwPAASADQZwGaiAEIANBwAFqECMEQCADKAKoBiICQQBIDRYgAyACNgLYBgwWCyADQurWgYBwNwOwASADQZwGaiAEIANBsAFqECMEQCAGQQBHIQkMDQsgASADKALEBiADKALYBhAuIANBwAZqIAYQzgMgBCECDBkLIAYoAAEiAkH/AUoNDyABIAMoAsQGIAMoAtgGEC4gA0HABmoiBiAFQcMAa0H/AXEQDiAGIAJB/wFxEA4gBCECDBgLIAYoAAEhAiADQo6AgIBwNwPwASADQZwGaiAEIANB8AFqECMEQCAAIAIQECADKAKoBiICQQBIDRQgAyACNgLYBgwUCyACQS9HDQ4gASADKALEBiADKALYBhAuIANBwAZqQcEBEA4gBCECDBcLIANCyYCAgHA3A6gCIANC2Lb5gnA3A6ACIANBnAZqIgUgBCICIANBoAJqECMNFiADQX82ApgCIANCgYSQgJAJNwOQAiAFIAIgA0GQAmoQIw0WIANBfzYCiAIgA0KGjqjIkAk3A4ACIAUgAiADQYACahAjDRYMDQsgA0KOgICAcDcD8AIgA0GcBmogBCADQfACahAjBEAgAygCqAYiAkEASA0SIAMgAjYC2AYMEgsgA0KogICAcDcD4AIgA0GcBmogBCADQeACahAjBEACQCADKAKoBiICQQBIBEAgAygC2AYhAgwBCyADIAI2AtgGCyABIAMoAsQGIAIQLiADQcAGakEpEA4MEgsgA0Lq1oGAcDcD0AJBACEJIANBnAZqIgUgBCADQdACahAjDQggA0KsgYCAcDcDwAIgBSAEIANBwAJqECMEQAJAIAMoAqgGIgJBAEgEQCADKALYBiECDAELIAMgAjYC2AYLIAEgAygCxAYgAhAuIANBwAZqQfIBEA4MEgsgA0F/NgK4AiADQq2BgICg7Ro3A7ACIANBnAZqIAQgA0GwAmoQI0UNDAJAIAMoAqgGIgVBAEgEQCADKALYBiEFDAELIAMgBTYC2AYLIAEgAygCxAYgBRAuIANBwAZqQfIBEA4gAygCrAZBAXMhBQwSCyADQX82AogDIANCw/aAgOABNwOAAyADQZwGaiAEIANBgANqECNFDQsCQCADKAKoBiICQQBIBEAgAygC2AYhAgwBCyADIAI2AtgGCyABIAMoAsQGIAIQLiADQcAGaiICIAMtAKwGEA4gAiADKAK8BhAbDBALIANBfzYCuAMgA0LZuP2CcDcDsAMgA0GcBmogBCADQbADahAjRQ0KIAMoAqgGIgJBAE4EQCADIAI2AtgGCyADQo6AgIBwNwOgAyADKAKsBiIFQQFqIQYCQCADQZwGaiADKAKkBiICIANBoANqECMEfyADKAKoBiICQQBOBEAgAyACNgLYBgsgAyADKAKwBjYClANBfyEEIANBfzYCmAMgAyAFQQFrNgKQAyADQZwGaiADKAKkBiICIANBkANqECNFDQEgAygCpAYhAiADKAKoBgVBfwshBCAGIQULIAEgAygCxAYgAygC2AYQLiADQcAGaiAFIAMoArAGEFkgBEEASA0TIAMgBDYC2AYMEwsgBi8AASICQf8BSw0JIANCjoCAgHA3AswEIAMgAjYCyAQgA0KRpYKAkAs3A8AEAkAgA0GcBmoiBiAEIANBwARqECNFBEAgA0KOgICAcDcDsAQgAyACNgKsBCADQdkANgKoBCADQo+hgoCQAjcDoAQgBiAEIANBoARqECNFDQELAkAgAygCqAYiBUEASARAIAMoAtgGIQUMAQsgAyAFNgLYBgsgASADKALEBiAFEC4gA0HABmoiBEGUAUGTASADKAKsBkF9cUGQAUYbEA4gBCACQf8BcRAODA8LIANCjoCAgHA3ApQEIAMgAjYCkAQgA0KRgICAkAs3A4gEIANChICAgOATNwOABCADQZwGaiAEIANBgARqECMEQAJAIAMoAqgGIgVBAEgEQCADKALYBiEFDAELIAMgBTYC2AYLIAEgAygCxAYgBRAuAkAgAygCvAZBL0YEQCADQcAGakHBARAODAELIANBwAZqIgRBBBAOIAQgAygCvAYQGwsgA0HABmoiBEGVARAOIAQgAkH/AXEQDgwPCyADQo6AgIBwNwL0AyADIAI2AvADIANCkYCAgJALNwPoAyADQoGAgIDgEzcD4AMgA0GcBmogBCADQeADahAjBEACQCADKAKoBiIFQQBIBEAgAygC2AYhBQwBCyADIAU2AtgGCyABIAMoAsQGIAUQLiADQcAGaiIEIAMoArQGEM4DIARBlQEQDiAEIAJB/wFxEA4MDwsgA0KOgICAcDcD2AMgAyACNgLUAyADQdkANgLQAyADQp6BgICQAjcDyAMgA0LYtvmCcDcDwAMgA0GcBmogBCADQcADahAjBEACQCADKAKoBiIFQQBIBEAgAygC2AYhBQwBCyADIAU2AtgGCyABIAMoAsQGIAUQLiADQcAGaiIEIAMoAqwGIAMoArAGEFkgBEGVARAOIAQgAkH/AXEQDgwPCyABIAMoAsQGIAMoAtgGEC4gA0HABmpB2AAgAhBZIAQhAgwSCyAGLwABIQIgASADKALEBiADKALYBhAuIANBwAZqIAUgAhBZIAQhAgwRCyADIAYvAAEiAjYC5AQgA0F/NgLoBCADIAVBAWs2AuAEIANBnAZqIAQgA0HgBGoQIwRAAkAgAygCqAYiBEEASARAIAMoAtgGIQQMAQsgAyAENgLYBgsgASADKALEBiAEEC4gA0HABmogBUEBaiACEFkMDQsgASADKALEBiADKALYBhAuIANBwAZqIAUgAhBZIAQhAgwQCyABIAogCCAEIANB2AZqEKUCIQQMBgsgASgC1AIhCyABKALMAiEGQQAhCUEAIQgDQAJAIAkgC0gEQEEDIQogBigCACICQeoAa0EDTwRAIAJB7QFHDQJBASEKCwJAIAEoAqQCIAYoAgxBFGxqKAIMIAYoAggiBWsiBEGAf0ggBCAKQf8AakpyRQRAIAZBATYCBCACQe0BRgRAQewBIQIgBkHsATYCAAwCCyAGIAJBgAFqIgI2AgAMAQsgAkHsAEcgBEGAgAJqQf//A0tyDQIgBkLtgYCAIDcCAEECIQpB7QEhAgsgAygCwAYgBWpBAWsgAjoAACAGKAIEIgIgAygCwAYgBWpqIgQgBCAKaiADKALEBiAFIApqIAJqaxCrASADIAMoAsQGIAprNgLEBkEAIQQgASgCrAIiAkEAIAJBAEobIQcgASgCpAIhAgNAIAQgB0YEQCABKALUAiELIAYhByAJIQQDQAJAIAsgBEEBaiIETARAQQAhAiABKALgAiIEQQAgBEEAShshBANAIAIgBEYNAiAFIAEoAtgCIAJBA3RqIgcoAgAiDEkEQCAHIAwgCms2AgALIAJBAWohAgwACwALIAciAkEQaiEHIAIoAhgiDCAFTA0BIAIgDCAKazYCGAwBCwsgCEEBaiEIDAMLIAUgAigCDCILSARAIAIgCyAKazYCDAsgAkEUaiECIARBAWohBAwACwALIAEoAswCIQIgCARAQQAhBQNAIAUgC0gEQCABKAKkAiACKAIMQRRsaigCDCACKAIIIgRrIQYCQAJAAkACQCACKAIEQQFrDgQAAQMCAwsgAygCwAYgBGogBjoAACABKALUAiELDAILIAMoAsAGIARqIAY7AAAMAQsgAygCwAYgBGogBjYAAAsgAkEQaiECIAVBAWohBQwBCwsgASgCzAIhAgsgACgCECIEQRBqIAIgBCgCBBEAACABQQA2AswCIAAoAhAiAkEQaiABKAKkAiACKAIEEQAAIAFBADYCpAIgASgC2AIhAgJAIAEtAG5BAnEEQCACIQUMAQtBACEFIAJFDQAgASgC8AIhByABKAIAIAFB9AJqIggQgwJBACECQQAhCgNAIAEoAtgCIQUgAiABKALgAk4NAQJAIAUgAkEDdGoiBigCBCIEQQBIIAQgB0ZyDQAgBigCACIGIAprIgVBAEgNAAJAIAQgB2siB0EBaiIKQQRLIAVBMktyRQRAIAggCiAFQQVsakEBakH/AXEQDgwBCyAIQQAQDiAIIAUQsQUgCCAHQQF0IAdBH3VzELEFCyAGIQogBCEHCyACQQFqIQIMAAsACyAAKAIQIgJBEGogBSACKAIEEQAAIAFBADYC2AIgDRCJASANIAMpAtAGNwIQIA0gAykCyAY3AgggDSADKQLABjcCACABQQE2AqACIAEoAowCBEAgABBwDCALIAEoAoACIQcgAyABKAKEAiIENgKcBiADIAAgBEEBdBAkIgY2AqQGIAZFDR9BACECIARBACAEQQBKGyEFA0AgAiAFRwRAIAYgAkEBdGpB//8DOwEAIAJBAWohAgwBCwsgA0EANgKsBiADIAAgBEECdBAkIgI2AqgGAkAgAkUNACADQgA3ArAGIANBADYCoAYgACADQZwGakEAQQBBAEF/ELABDQADQCADKAKsBiECAkACQAJAIAMoArAGIgRBAEoEQCADIARBAWsiBDYCsAYgByACIARBAnRq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IAIgCTYCSCACIAo2AkQgAkHBAEHQACAHQQxIGzYCTCACIAdBC2pBDG9BAWo2AkAgAkGQAmogA2pBwAAgA2tB5vQAIAJBQGsQSCADaiEDCyAAIAJBkAJqIAMQ6gEhEQsgAkHQAmokACARCzcCA38BfiMAQRBrIgAkACAAEI0GIAApAwAhAyAAKAIIIQIgAEEQaiQAIAJB6AdtrCADQugHfnwLhwEBAXwgACADKQMAEKgBIgJFBEBCgICAgOAADwsgAhAHIQQgACACEDEgBL0iAQJ/IASZRAAAAAAAAOBBYwRAIASqDAELQYCAgIB4CyIAt71RBEAgAK0PC0KAgICAwH4gAUKAgICAwIGA/P8AfSABQv///////////wCDQoCAgICAgID4/wBWGwvxAQIGfwF+IABBCBAkIgRFBEBBfw8LIARCATcCACACpyEGIAJCIIinQXVJIQgDQAJAAkAgA0ECRg0AIAAgACkDMCADQS5yEEciCUKAgICAcINCgICAgOAAUgRAIABBEBAkIgUNAiAAIAkQDAtBfyEHIANFDQAgACABKQMAEAwLIAAoAhAgBBD/BCAHDwsgBCAEKAIAQQFqNgIAIAUgBDYCCCAIRQRAIAYgBigCAEEBajYCAAsgBSACNwMAIAlCgICAgHBaBEAgCacgBTYCIAsgACAJQS9BARCYAyABIANBA3RqIAk3AwAgA0EBaiEDDAALAAt/AQV/IABBEGohBCABQQxqIQUgASgCECECA0AgAiAFRkUEQCACKAIEIQYgACACKQMQECEgACACKQMYECEgACACKQMgECEgACACKQMoECEgBCACIAAoAgQRAAAgBiECDAELCyABKAIIIgMEQCAAIAMQzgELIAQgASAAKAIEEQAAC+EDAgR/An4jAEFAaiICJAAgAiAAIAEQsQIiBjcDOAJAAkAgASgCIARAIAZCgICAgHCDQoCAgIDgAFENASAAIAEpAyhCgICAgDBBASACQThqEBwhBiAAIAIpAzgQDCAAIAYQDAwCCyACIAEoAmRBCGsiAykDADcDKCADQoCAgIAwNwMAIAAgBhAMQQAhAyAAIAApA1AgACACQShqQQAQ3gEhBiAAIAIpAygQDCAGQoCAgIBwg0KAgICA4ABRDQADQAJAIANBAkcEQCACQRBqIANBA3RqIAAgACkDMCADQTFqEEciBzcD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aiIGKAIEIAJBABARIgNCgICAgHCDQoCAgIDgAFENAyAGKAIEIQYCfyAEBEAgACABIAYgAxA5DAELIAAgASAGIANBBxAVC0EASA0DCyAFQQFqIQUMAQsLIAAgCCALEFtBACEJDAELIAAgCCALEFsLIAdBMGokACAJC6YBAQF+AkACQAJ+IARBBHEEQEEpIQIgACABEEoMAQtBKCECIAAgARAgCyIBQoCAgIBwg0KAgICA4ABRDQAgACACEIYBIgVCgICAgHCDQoCAgIDgAFENACAAQRAQJCICBEAgAkEANgIMIAIgBEEDcTYCCCACIAE3AwAgBUKAgICAcFQNAiAFpyACNgIgDAILIAAgBRAMCyAAIAEQDEKAgICA4AAPCyAFC8QBAQR/IAGnIgUgAjYCICAFQgA3AiQCQCACKAI8IgZFDQACQCAAIAZBAnQQXCIIRQ0AIAUgCDYCJEEAIQUDQCAFIAIoAjxODQIgAigCJCAFQQN0aiIHLwECIQYCQCAHLQAAIgdBAXEEQCAAIAQgBiAHQQF2QQFxEP8DIgYNAQwDCyADIAZBAnRqKAIAIgYgBigCAEEBajYCAAsgCCAFQQJ0aiAGNgIAIAVBAWohBQwACwALIAAgARAMQoCAgIDgACEBCyABC4IBAQJ+IAAgARApIQICQCABQQBIDQAgACgCECgCOCABQQJ0aigCACkCBCIDp0GAgICAeEYgA0KAgICA8P///z+DUCADQv//////////v39WcSADQoCAgICAgICAQINCgICAgICAgICAf1FyRXINACAAQa/wACACQa3wABCyASECCyACC2QBAn8CQAJAIAFCgICAgHBUDQAgARCUBQ0AQX8hAyAAIAIQMCIERQ0BIAAgBBDEBSECIAAgBBAQIAJCgICAgHCDQoCAgIDgAFENASAAIAFBNyACQQEQFUEASA0BC0EAIQMLIAMLNQACQCACRSABQoCAgIBwVHINACABEJQFDQAgACABQTcgACACEClBARAVQQBODQBBfw8LQQALDAAgACABQbYVELUBC2gCAX8BfgJAIAAgAUHqACABQQAQESIEQoCAgIBwg0KAgICA4ABSBEAgACAEECchAyAAIAFBwQAgAUEAEBEiAUKAgICAcINCgICAgOAAUg0BC0EAIQNCgICAgOAAIQELIAIgAzYCACABCxQBAn4gACABECAhAyAAIAEQDCADC/sBAgR/AX4gACgCyAEiBSgCECIEQTBqIQYgBCAEKAIYIAFxQX9zQQJ0aigCACEEAkADQCAERQ0BIAEgBiAEQQFrIgdBA3RqIgQoAgRHBEAgBCgCAEH///8fcSEEDAELCyAFKAIUIAdBA3RqIQUCQCADQQFGDQAgBTUCBEIghkKAgICAwABRBEAgACACEAwgACAEKAIEENEBQX8PCyAELQADQQhxDQAgACACEAwgAEGAgAEgARDnAQ8LIAAgBSACEB1BAA8LIAAgACkDwAEiCCABIAIgCAJ/IAAoAhAoAowBIgMEQEGAgAYgAygCKEEBcQ0BGgtBgIACCxDQAQuKAQEBfwJAIAJCgICAgHCDQoCAgICQf1EgA0KAgICAcINCgICAgJB/UXFFBEAgAEGl5gBBABASDAELIAAgAUESEF4iAUKAgICAcINCgICAgOAAUQ0AIAGnIgQgAz4CJCAEIAI+AiAgACABQdYAQgBBAhAVGiABDwsgACADEAwgACACEAxCgICAgOAACw0AIAAgAUHMjQEQgQMLZwEBfwJAIAFBAE4EQCAAKAIQIgIoAiwgAU0NASACKAI4IAFBAnRqKAIAIgEgASgCAEEBajYCACAAIAFBBBDmAw8LQYaJAUGo7ABB1RdBycAAEAAAC0GQzgBBqOwAQdYXQcnAABAAAAuxAgEEfwJAAkACQAJAIAJCgICAgHBUDQAgAqciAy8BBhDgAUUNACADKAIoIgRFDQAgBCgCECIDQTBqIQUgAyADKAIYQX9zQQJ0QdR5cmooAgAhAwNAIANFDQMgBSADQQFrIgNBA3RqIgYoAgRBygFHBEAgBigCAEH///8fcSEDDAELCyABQoCAgIBwVA0AIAQoAhQgA0EDdGopAwAiAkKAgICAcINCgICAgIB/UQ0BCyAAECIMAgsgACACEIgCIQMgAacoAhAiAEEwaiEEIAAgAyAAKAIYcUF/c0ECdGooAgAhAANAIABFBEBBAA8LIAQgAEEDdGoiBUEIayEAIAMgBUEEaygCAEYEQCAAQQBHDwUgACgCAEH///8fcSEADAELAAsACyAAQZ3kAEEAEBILQX8LRAEBfyAAQeQBaiECIABB4AFqIQADfyAAIAIoAgAiAkYEQEEADwsgASACQQRrKAIARgR/IAJBCGsFIAJBBGohAgwBCwsLiQECA38BfgJAIAAoAhAoAowBIgJFDQADQCABQQBMBEADQCACKQMIIgRCgICAgHBUDQMgBKciAS8BBhDgAUUNAyABKAIgIgEvABEiA0GAwABxRQRAIANBgAhxRQ0EIAAgASgCQBAWDwsgAigCACICDQAMAwsACyABQQFrIQEgAigCACICDQALC0EACykBAX8gAkIgiKdBdU8EQCACpyIDIAMoAgBBAWo2AgALIAAgASACEIUEC/QBAwF+An8BfANAAkBBfyEEAkACQAJAQQcgAkIgiKciBSAFQQdrQW5JGw4IAAAAAAICAwECCyACxCEDQQAhBAwCC0EAIQQgAkKAgICAwIGA/P8AfCICQv///////////wCDQoCAgICAgID4/wBWDQFCgICAgICAgICAfyEDIAK/IgZEAAAAAAAA4MNjDQFC////////////ACEDIAZEAAAAAAAA4ENkDQEgBplEAAAAAAAA4ENjBEAgBrAhAwwCC0KAgICAgICAgIB/IQMMAQsgACACEJYBIgJCgICAgHCDQoCAgIDgAFINAQsLIAEgAzcDACAEC+YBAgN/AXwDQAJAQX8hBAJAAkACQEEHIAJCIIinIgUgBUEHa0FuSRsOCAAAAAACAgMBAgsgAqchA0EAIQQMAgtBACEEIAJCgICAgMCBgPz/AHwiAkL///////////8Ag0KAgICAgICA+P8AVgRADAILQYCAgIB4IQMgAr8iBkQAAAAAAADgwWMNAUH/////ByEDIAZEAADA////30FkDQEgBplEAAAAAAAA4EFjBEAgBqohAwwCC0GAgICAeCEDDAELIAAgAhCWASICQoCAgIBwg0KAgICA4ABSDQELCyABIAM2AgAgBAttAAJAAkACQAJAAkAgAkEEdkEDcUEBaw4DAAECAwsgASgCACICBEAgACACrUKAgICAcIQQIQsgASgCBCIBRQ0DIAAgAa1CgICAgHCEECEPCyAAIAEoAgAQ5QEPCyABEOAFDwsgACABKQMAECELC/UBAQl/QX8hAiABIAFBAWtxRQRAIABBEGoiCCABQQJ0IgMgACgCABEDACIFBH8gBUEAIAMQLCEGIAFB/////wNqQf////8DcSEJIAAoAjQhBwNAIAQgACgCJE9FBEAgByAEQQJ0aigCACECA0AgAgRAIAAoAjggAkECdGooAgAiAygC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QQpPBH8gAEHBAGsgA00EQCAAQTdrDwsgAiAAQdcAayAAQeEAayABTxsFIAQLC6IDAQJ/IAAgASgCBBAQA0AgASgCECEDIAIgASgCFE5FBEAgACADIAJBA3RqKAIAEBAgAkEBaiECDAELCyAAKAIQIgJBEGogAyACKAIEEQAAQQAhAgNAAkAgASgCHCEDIAIgASgCIE4NACADIAJBFGxqIgMoAghFBEAgACgCECADKAIEEOUBCyAAIAMoAhAQECAAIAMoAgwQECACQQFqIQIMAQsLIAAoAhAiAkEQaiADIAIoAgQRAAAgACgCECICQRBqIAEoAiggAigCBBEAAEEAIQIDQCABKAI0IQMgAiABKAI4TkUEQCAAIAMgAkEMbGooAgQQECACQQFqIQIMAQsLIAAoAhAiAkEQaiADIAIoAgQRAAAgACgCECICQRBqIAEoAmQgAigCBBEAACAAIAEpA0AQDCAAIAEpA0gQDCAAIAEpA6gBEAwgACABKQOwARAMIAAgASkDiAEQDCAAIAEpA5ABEAwgACABKQOYARAMIAEoAggiAiABKAIMIgM2AgQgAyACNgIAIAFCADcCCCAAKAIQIgBBEGogASAAKAIEEQAAC9IDAgJ+An8jAEEgayIEJAACQCABQv///////////wCDIgNCgICAgICAwIA8fSADQoCAgICAgMD/wwB9VARAIAFCBIYgAEI8iIQhAyAAQv//////////D4MiAEKBgICAgICAgAhaBEAgA0KBgICAgICAgMAAfCECDAILIANCgICAgICAgIBAfSECIABCgICAgICAgIAIUg0BIAIgA0IBg3whAgwBCyAAUCADQoCAgICAgMD//wBUIANCgICAgICAwP//AFEbRQRAIAFCBIYgAEI8iIRC/////////wODQoCAgICAgID8/wCEIQIMAQtCgICAgICAgPj/ACECIANC////////v//DAFYNAEIAIQIgA0IwiKciBUGR9wBJDQAgBEEQaiAAIAFC////////P4NCgICAgICAwACEIgIgBUGB9wBrEGIgBCAAIAJBgfgAIAVrEI0CIAQpAwhCBIYgBCkDACIAQjyIhCECIAQpAxAgBCkDGIRCAFKtIABC//////////8Pg4QiAEKBgICAgICAgAhaBEAgAkIBfCECDAELIABCgICAgICAgIAIUg0AIAJCAYMgAnwhAgsgBEEgaiQAIAIgAUKAgICAgICAgIB/g4S/C6oPAgV/D34jAEHQAmsiBSQAIARC////////P4MhCiACQv///////z+DIQsgAiAEhUKAgICAgICAgIB/gyEMIARCMIinQf//AXEhCAJAAkAgAkIwiKdB//8BcSIJQf//AWtBgoB+TwRAIAhB//8Ba0GBgH5LDQELIAFQIAJC////////////AIMiDUKAgICAgIDA//8AVCANQoCAgICAgMD//wBRG0UEQCACQoCAgICAgCCEIQwMAgsgA1AgBEL///////////8AgyICQoCAgICAgMD//wBUIAJCgICAgICAwP//AFEbRQRAIARCgICAgICAIIQhDCADIQEMAgsgASANQoCAgICAgMD//wCFhFAEQCADIAJCgICAgICAwP//AIWEUARAQgAhAUKAgICAgIDg//8AIQwMAwsgDEKAgICAgIDA//8AhCEMQgAhAQwCCyADIAJCgICAgICAwP//AIWEUARAQgAhAQwCCyABIA2EUARAQoCAgICAgOD//wAgDCACIAOEUBshDEIAIQEMAgsgAiADhFAEQCAMQoCAgICAgMD//wCEIQxCACEBDAILIA1C////////P1gEQCAFQcACaiABIAsgASALIAtQIgYbeSAGQQZ0rXynIgZBD2sQYkEQIAZrIQYgBSkDyAIhCyAFKQPAAiEBCyACQv///////z9WDQAgBUGwAmogAyAKIAMgCiAKUCIHG3kgB0EGdK18pyIHQQ9rEGIgBiAHakEQayEGIAUpA7gCIQogBSkDsAIhAwsgBUGgAmogCkKAgICAgIDAAIQiEkIPhiADQjGIhCICQgBCgICAgLDmvIL1ACACfSIEQgAQYSAFQZACakIAIAUpA6gCfUIAIARCABBhIAVBgAJqIAUpA5gCQgGGIAUpA5ACQj+IhCIEQgAgAkIAEGEgBUHwAWogBEIAQgAgBSkDiAJ9QgAQYSAFQeABaiAFKQP4AUIBhiAFKQPwAUI/iIQiBEIAIAJCABBhIAVB0AFqIARCAEIAIAUpA+gBfUIAEGEgBUHAAWogBSkD2AFCAYYgBSkD0AFCP4iEIgRCACACQgAQYSAFQbABaiAEQgBCACAFKQPIAX1CABBhIAVBoAFqIAJCACAFKQO4AUIBhiAFKQOwAUI/iIRCAX0iAkIA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IAUgA0E5ShshAwJAIBdCB1cEQCADIAdBBHRqIQcMAQsgF0IcWARAIAJBMGogAxB4IAJBIGogHCAYQgBCgICAgICAwP0/ECsgAkEQaiACKQMwIAIpAzggAikDICIcIAIpAygiGBArIAIgAikDECACKQMYIBkgGxBvIAIpAwghGyACKQMAIRkMAQsgA0UgCHINACACQdAAaiAcIBhCAEKAgICAgICA/z8QKyACQUBrIAIpA1AgAikDWCAZIBsQbyACKQNIIRtBASEIIAIpA0AhGQsgF0IBfCEXQQEhCQsgACgCBCIDIAAoAmhHBH8gACADQQFqNgIEIAMtAAAFIAAQTwshAwwBCwsCfiAJRQRAIAApA3BCAFkEQAJAIAAgACgCBCIDQQFrNgIEIAAgA0ECazYCBCABRQ0AIAAgA0EDazYCBAsLIAJB4ABqIAS3RAAAAAAAAAAAohCpASACKQNgIRkgAikDaAwBCyAXQgdXBEAgFyEYA0AgB0EEdCEHIBhCAXwiGEIIUg0ACwsCQAJAAkAgA0FfcUHQAEYEQCAAEIEGIhhCgICAgICAgICAf1INAyAAKQNwQgBZDQEMAgtCACEYIAApA3BCAFMNAgsgACAAKAIEQQFrNgIEC0IAIRgLIAdFBEAgAkHwAGogBLdEAAAAAAAAAACiEKkBIAIpA3AhGSACKQN4DAELIBogFyABG0IChiAYfEIgfSIXQQAgD2utVQRAQcTUBEHEADYCACACQaABaiAEEHggAkGQAWogAikDoAEgAikDqAFCf0L///////+///8AECsgAkGAAWogAikDkAEgAikDmAFCf0L///////+///8AECsgAikDgAEhGSACKQOIAQwBCyAPQeIBa6wgF1cEQCAHQQBOBEADQCACQaADaiAZIBtCAEKAgICAgIDA/79/EG8gGSAbQoCAgICAgID/PxD1BSEAIAJBkANqIBkgGyACKQOgAyAZIABBAE4iABsgAikDqAMgGyAAGxBvIBdCAX0hFyACKQOYAyEbIAIpA5ADIRkgB0EBdCAAciIHQQBODQALCwJ+IBcgD6x9QiB8IhinIgBBACAAQQBKGyANIBggDa1TGyIAQfEATgRAIAJBgANqIAQQeCACKQOIAyEaIAIpA4ADIRxCAAwBCyACQeACakQAAAAAAADwP0GQASAAaxDVARCpASACQdAC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QQFqNgIEIAEtAAAMAQsgABBPC0Ewa0EKSQ0ACwsgACkDcEIAWQRAIAAgACgCBEEBazYCBAtCACAFfSAFIAMbIQUMAQtCgICAgICAgICAfyEFIAApA3BCAFMNACAAIAAoAgRBAWs2AgRCgICAgICAgICAfw8LIAULygYCBX8EfiMAQYABayIFJAACQAJAAkAgAyAEQgBCABDrAUUNAAJ/IARC////////P4MhCwJ/IARCMIinQf//AXEiBkH//wFHBEBBBCAGDQEaQQJBAyADIAuEUBsMAgsgAyALhFALCyEJIAJCMIinIghB//8BcSIHQf//AUYNACAJDQELIAVBEGogASACIAMgBBArIAUgBSkDECICIAUpAxgiASACIAEQ9AUgBSkDCCECIAUpAwAhBAwBCyABIAJC////////////AIMiCyADIARC////////////AIMiChDrAUEATARAIAEgCyADIAoQ6wEEQCABIQQMAgsgBUHwAGogASACQgBCABArIAUpA3ghAiAFKQNwIQQMAQsgBEIwiKdB//8BcSEGIAcEfiABBSAFQeAAaiABIAtCAEKAgICAgIDAu8AAECsgBSkDaCILQjCIp0H4AGshByAFKQNgCyEEIAZFBEAgBUHQAGogAyAKQgBCgICAgICAwLvAABArIAUpA1giCkIwiKdB+ABrIQYgBSkDUCEDCyAKQv///////z+DQoCAgICAgMAAhCEMIAtC////////P4NCgICAgICAwACEIQsgBiAHSARAA0ACfiALIAx9IAMgBFatfSIKQgBZBEAgCiAEIAN9IgSEUARAIAVBIGogASACQgBCABArIAUpAyghAiAFKQMgIQQMBQsgCkIBhiAEQj+IhAwBCyALQgGGIARCP4iECyELIARCAYYhBCAHQQFrIgcgBkoNAAsgBiEHCwJAIAsgDH0gAyAEVq19IgpCAFMEQCALIQoMAQsgCiAEIAN9IgSEQgBSDQAgBUEwaiABIAJCAEIAECsgBSkDOCECIAUpAzAhBAwBCyAKQv///////z9YBEADQCAEQj+IIQ0gB0EBayEHIARCAYYhBCANIApCAYaEIgpCgICAgICAwABUDQALCyAIQYCAAnEhBiAHQQBMBEAgBUFAayAEIApC////////P4MgB0H4AGogBnKtQjCGhEIAQoCAgICAgMDDPxAr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6e0AQdjsAEHUIUGzxAAQAAALCyAFQQxqEBkgBUEgaiQAQRALrwEBAn8jAEEgayIEJAAgACgCACEFIARBCGogA0EAEO0BIAAgASAEKAIIIgEgAUEfdSIBcyABayIBIAJBwAAgAUEBa2dBAXRrQQAgAUECTxtqQQhqIgJB4A8QrwMhASADKAIEBEAgBEIANwIYIARCgICAgICAgICAfzcCECAEIAU2AgwgBEEMaiIDQgEQMhogACADIAAgAkHgDxCIASABciEBIAMQGQsgBEEgaiQAIAELkAYCCH8BfiMAQfAAayIDJAAgACABRwRAIAAoAgAhBCADQgA3AmggA0KAgICAgICAgIB/NwJgIAMgBDYCXCADQdwAaiIFIAEQSRogA0IANwJUIANCgICAgICAgICAfzcCTCADIAQ2AkggAygCZCEGIANBADYCZCADQcgAaiIBQqrVqtUKEDIaIANBADYCUCAFIAEQrAIEQCADIAMoAmRBAWo2AmQgBkEBayEGCyADQcgAahAZIAJBAWpBAXYQuQQhBSADQgA3AlQgA0KAgICAgICAgIB/NwJMIAMgBDYCSCADQgA3AkAgA0KAgICAgICAgIB/NwI4IAMgBDYCNCADQdwAaiIBIAFCf0H/////A0EAEHoaIAVBACAFQQBKGyEJIAIgBWogAiAFQQF0bkEBaiIKQQF0akEgaiECQQAhAQNAIAEgCUZFBEAgA0HIAGoiByADQdwAaiIIQgEgAkEAEHoaIANBNGoiCyAHIAJBBhC1BCAHIAtCASACQQAQehogCCAIIAcgAkEAEIgBGiABQQFqIQEMAQsLIANCADcCLCADQoCAgICAgICAgH83AiQgAyAENgIgIANCADcCGCADQoCAgICAgICAgH83AhAgAyAENgIMIANBIGoiASADQdwAaiIEQgIgAkEAEHoaIAEgBCABIAJBABCIARogA0EMaiABIAEgAkEAEEAaIABCABAyGiAKrSEMA0AgDKdBAExFBEAgA0HIAGoiAUIBEDIaIANBNGoiBCAMQgGGQv7///8Pg0IBhBAyGiABIAEgBCACQQAQiAEaIAAgACABIAJBABC4ARogACAAIANBDGogAkEAEEAaIAxCAX0hDAwBCwsgACAAQgEgAkEAEHoaIAAgACADQSBqIgEgAkEAEEAaIAEQGSADQQxqEBkgA0E0ahAZ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C0KAgICAMCEFIAZCgICAgHCDQoCAgIAwUQ0DIAAgBkEBEJABGgwDC0KAgICAMCEJQoCAgIAwIQZCgICAgDAhBSAAIAQQICIHQoCAgIBwg0KAgICA4ABRDQILIAAgDUEIaiAHEC9BAEgNACANAn4gDSkDCCIEQoCAgIAIfEL/////D1gEQCAEQv////8PgwwBC0KAgICAwH4gBLm9IgVCgICAgMCBgPz/AH0gBUL///////////8Ag0KAgICAgICA+P8AVhsLIgg3AyAgACABQQEgDUEgahC/AyEFIAAgCBAMAkAgBUKAgICAcINCgICAgOAAUQ0AQgAhCCAEQgAgBEIAVRshDANAIAggDFENBCAAIAcgCBBsIgRCgICAgHCDQoCAgIDgAFENAQJAIA4EQCAEIQEMAQsgDSAENwMgIA0gCEL/////D4M3AyggACAKIAtBAiANQSBqEBwhASAAIAQQDCABQoCAgIBwg0KAgICA4ABRDQILIAAgBSAIIAEQeyERIAhCAXwhCCARQQBODQALCwwBC0KAgICAMCEFCyAAIAUQDEKAgICA4AAhBQsgACAHEAwgACAGEAwgACAJEAwgDUEwaiQAIAULDwAgACsDACABKwMAEMQECzkBAX5CgICAgMB+IAEpAwAiAkKAgICAwIGA/P8AfSACQv///////////wCDQoCAgICAgID4/wBWGwsRACAAKgIAuyABKgIAuxDEBAs7AQF+QoCAgIDAfiABKgIAu70iAkKAgICAwIGA/P8AfSACQv///////////wCDQoCAgICAgID4/wBWGwsZAQJ+IAEpAwAiAyAAKQMAIgRUIAMgBFZrCwwAIAAgASkDABCIBAsZAQJ+IAEpAwAiAyAAKQMAIgRTIAMgBFVrCwwAIAAgASkDABC/AgsXACABKAIAIgEgACgCACIASSAAIAFJaws9AQF+IAEoAgAiAEEATgRAIACtDwtCgICAgMB+IAC4vSICQoCAgIDAgYD8/wB9IAJCgICAgICAgPj/AFYbC9sFAwV/A34BfCMAQUBqIgUkAAJAAnwCQAJAAkACQAJAIAJBACABQoCAgIBwgyILQoCAgIAwUhsiAg4CAgABCwJAIAMpAwAiCUKAgICAcFQNACAJpyIELwEGQQpHDQAgBCkDICIKQiCIpyIEQQAgBEELakESSRsNACAAIAUg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IQULIAJBEGokACAFCx0AIAAgAUEAEGsiAEUEQEKAgICA4AAPCyAANQIoCz0BAX5CgICAgBAhASADKQMAIgRCgICAgHBaBH4gBKcvAQZBFWtB//8DcUEMSa1CgICAgBCEBUKAgICAEAsL7gMCBX4CfyMAQSBrIgokAEKAgICA4AAhBQJAIAAgASAEEFoiC0UNACALLQAEBEAgABBfDAELIAAgCkEYaiADKQMAQgAgCzQCACIGIAYQZg0AIAogBjcDECADKQMIIgdCgICAgHCDQoCAgIAwUgRAIAAgCkEQaiAHQgAgBiAGEGYNASAKKQMQIQYLIAopAxghCSAAIAFCgICAgDAQ/QEiB0KAgICAcIMiBUKAgICA4ABRBEAgByEFDAELIAYgCX0iBkIAIAZCAFUbIQgCQCAFQoCAgIAwUQRAIABCgICAgDAgCCAEEOUDIQUMAQsgCiAGQv////8HVwR+IAhC/////w+DBUKAgICAwH4gCLm9IgVCgICAgMCBgPz/AH0gBUL///////////8Ag0KAgICAgICA+P8AVhsLNwMIIAAgB0EBIApBCGoQowEhBSAAIAcQDCAAIAopAwgQDAsgBUKAgICAcINCgICAgOAAUQ0AAkAgACAFIAQQWiICRQ0AIAAgBSABEE0EQCAAQco0QQAQEgwBCwJAIAItAAQNACACNAIAIAhTBEAgAEHOwgBBABASDAILIAstAAQNACACKAIIIAsoAgggCadqIAinEB4aDAILIAAQXwsgACAFEAxCgICAgOAAIQULIApBIGokACAFC1EAIAAgASACEFoiAEUEQEKAgICA4AAPCyAAKAIAIgBBAE4EQCAArQ8LQoCAgIDAfiAAuL0iAUKAgICAwIGA/P8AfSABQoCAgICAgID4/wBWGwv/AwICfwF+AkACQAJAAkACQAJAIAFCgICAgHBaBEAgAaciAi8BBkErRg0BCyAEQQE2AgAMAQsgAigCICEGIARBATYCACAGDQELIABBsS1BABASDAELIAYoAgQhAgJAAkACQAJ/AkACQAJAAkAgBigCACIHQQFrDgQCAgcBAAsgBUUNAiAAIAYQyQQLQoCAgIAwIQEgBUEBaw4CAwQHCyADKQMAIgFCIIinQXVPBEAgAaciAyADKAIAQQFqNgIACwJAIAVBAkcNAEEBIQMgB0EBRw0AIAAgARCYASAGKAIEDAILIAIoAmQiAyAFrTcDACADQQhrIAE3AwAgAiADQQhqNgJkC0EAIQMgAgsiBSADNgIcIAZBAzYCACAAIAUQsQIhASAGQQE2AgAgBigCBCgCIARAIAAgBhDJBCABDwsgAUKAgICAEFoNBSACKAJkQQhrIgApAwAhCCAAQoCAgIAwNwMAIAFCAlEEQCAGQQI2AgAgBEECNgIAIAgPCyAEQQA2AgAgCA8LIAMpAwAiAUIgiKdBdUkNAyABpyIAIAAoAgBBAWo2AgAgAQ8LIAMpAwAiAUIgiKdBdU8EQCABpyICIAIoAgBBAWo2AgALIAAgARCYAQwBCyAAQY8tQQAQEgtCgICAgOAAIQELIAEPC0HW8QBBqOwAQaCUAUHEFBAAAAt3AQF+IAMpAwAiAUKAgICAcINCgICAgIB/UgRAIABBkcEAQQAQEkKAgICA4AAPC0KAgICAMCEEIAGnIgApAgRCgICAgICAgIBAg0KAgICAgICAgIB/UQR+IAAgACgCAEEBajYCACABQoCAgICQf4QFQoCAgIAwCws8AQF+QoCAgIDgACEBIAAgAykDABAlIgRCgICAgHCDQoCAgIDgAFIEfiAAIASnQQIQ5gMFQoCAgIDgAAsLVgIBfgF/IAAgARC7AyIBQoCAgIBwg0KAgICA4ABRBEAgAQ8LQoCAgIAwIQIgAaciAygCBEGAgICAeEcEQCAAIAAoAhAgAxDGAhApIQILIAAgARAMIAILCQAgACABELsDC1sBAX4jAEEQayICJAAgAiAAIAEQuwMiATcDCAJAIAFCgICAgHCDQoCAgIDgAFEEQCABIQQMAQsgAEKAgICAMEEBIAJBCGoQygQhBCAAIAEQDAsgAkEQaiQAIAQLLQBCgICAgOAAIAAgAykDACADKQMIQQAQiQIiAEEAR61CgICAgBCEIABBAEgbC6ABAQN+IAMpAwAiBSEEIAJBBE4EQCADKQMYIQQLIAVC/////29YBEAgABAiQoCAgIDgAA8LIAMpAxAhAUKAgICA4AAhBgJAIAAgAykDCBAwIgJFDQAgAUIgiKdBdU8EQCABpyIDIAMoAgBBAWo2AgALIAAgBSACIAEgBEEAENABIQMgACACEBAgA0EASA0AIANBAEetQoCAgIAQhCEGCyAGCyoAIAMpAwAiAUL/////b1gEQCAAECJCgICAgOAADwsgACABQQNBABCyAgtjAQF+IAMpAwAiBEL/////b1gEQCAAECJCgICAgOAADwtCgICAgOAAIQECQCAAIAMpAwgQMCICRQ0AIAAgBCACEG4hAyAAIAIQECADQQBIDQAgA0EAR61CgICAgBCEIQELIAELYwEDfgJAAkAgAykDACIBQv////9vWARAIAAQIgwBCyADKQMIIQUgASEEIAJBA04EQCADKQMQIQQLIAAgBRAwIgINAQtCgICAgOAADwsgACABIAIgBEEAEBEhBiAAIAIQECAGC2YBAX4gAykDACIEQv////9vWARAIAAQIkKAgICA4AAPC0KAgICA4AAhAQJAIAAgAykDCBAwIgJFDQAgACAEIAJBABDNASEDIAAgAhAQIANBAEgNACADQQBHrUKAgICAEIQhAQsgAQuaAQIBfwJ+IwBBEGsiBCQAIAMpAwghBSADKQMAIgYhAQJAAkACQAJAIAJBA0gNACADKQMQIgFCgICAgHBaBEAgAactAAVBEHENAQsgAEGdLEEAEBIMAQsgACAEQQxqIAUQ/QMiAg0BC0KAgICA4AAhAQwBCyAAIAYgASAEKAIMIgMgAhD+AiEBIAAgAiADEIYDCyAEQRBqJAAgAQt5AQF/IAFCgICAgHCDQoCAgIAwUgRAIABBnSxBABASQoCAgIDgAA8LAn4CQCACRQ0AIAMpAwAiAUKAgICAcINCgICAgDBRDQBCgICAgOAAIAAgARAlIgFCgICAgHCDQoCAgIDgAFENARogAachBAsgACAEQQMQ5gMLCxUAIAAgAykDACADIANBCGpBAhCIAws3ACMAQRBrIgIkACAAIAJBDGogAykDABB1IQAgAigCDCEDIAJBEGokAEKAgICA4AAgA2etIAAbC04AIwBBEGsiAiQAQoCAgIDgACEBAkAgACACQQxqIAMpAwAQdQ0AIAAgAkEIaiADKQMIEHUNACACKAIIIAIoAgxsrSEBCyACQRBqJAAgAQsGACAAtrsLfwAgACAAKQPQASIBQgyIIAGFIgFCGYYgAYUiAUIbiCABhSIBNwPQAUKAgICAwH4gAUKdurP7lJL9oiV+QgyIQoCAgICAgID4P4S/RAAAAAAAAPC/oL0iAUKAgICAwIGA/P8AfSABQv///////////wCDQoCAgICAgID4/wBWGwuIBAMFfAV/AX4jAEEQayIKJAAgCkIANwMIAkACQCACQQBMDQBCgICAgOAAIQEgACAKQQhqIAMpAwAQQg0BQQEhCyAKKwMIIQQgAkEBRwRAA0AgAiALRg0CIAAgCiADIAtBA3RqKQMAEEINAyALQQFqIQsgCisDACEFIwBBIGsiCSQAAkAgBJkiByAFmSIGIAe9IAa9VCIMGyIEvSIOQjSIpyINQf8PRg0AIAYgByAMGyEFAkAgDlANACAFvUI0iKciDEH/D0YNACAMIA1rQcEATgRAIAcgBqAhBAwCCwJ8IAxB/gtPBEAgBEQAAAAAAAAwFKIhBCAFRAAAAAAAADAUoiEFRAAAAAAAALBrDAELRAAAAAAAAPA/IA1BvARLDQAaIAREAAAAAAAAsGuiIQQgBUQAAAAAAACwa6IhBUQAAAAAAAAwFAshCCAJQRhqIAlBEGogBRCIBiAJQQhqIAkgBBCIBiAIIAkrAwAgCSsDEKAgCSsDCKAgCSsDGKCfoiEEDAELIAUhBAsgCUEgaiQADAALAAsgBJkhBAsgBL0iAQJ/IASZRAAAAAAAAOBBYwRAIASqDAELQYCAgIB4CyIAt71RBEAgAK0hAQwBC0KAgICAwH4gAUKAgICAwIGA/P8AfSABQv///////////wCDQoCAgICAgID4/wBWGyEBCyAKQRBqJAAgAQtOACAAIABEAAAAAAAA8L9EAAAAAAAA8D8gAEQAAAAAAAAAAGMbIAC9Qv///////////wCDQoCAgICAgID4/wBWGyAARAAAAAAAAAAAYRsLgwECAn4BfyAAvSIBQjSIp0H/D3EiA0H+B00EQCABQoCAgICAgICAgH+DIQIgA0H+B0cgAUKAgICAgICA8L9/UXJFBEAgAkKAgICAgICA+D+Evw8LIAK/DwsgA0GyCE0EfCABQj+HIAF8QgFBswggA2uthiIBQgGIfEIAIAF9g78FIAALC4IFAwJ8BX8CfiMAQRBrIgkkAAJ+QoCAgIDA/v/7/wBCgICAgMD+/3sgBBsgAkUNABoCfCADKQMAIgFC/////w9YBEBBASACIAJBAUwbIQogAachCEEBIQcDQCAHIApHBEAgCLcgAyAHQQN0aikDACIBQoCAgIAQWg0DGiAIIAGnIgsgCCALShsgCCALIAggC0gbIAQbIQggB0EBaiEHDAELCyAIrQwCC0KAgICA4AAgACAJQQhqIAEQQg0BGkEBIQcgCSsDCAshBSAHIAIgAiAHSBshAgNAIAIgB0cEQEKAgICA4AAgACAJIAMgB0EDdGopAwAQQg0CGgJAIAW9IgxC////////////AINCgICAgICAgPj/AFYNACAJKwMAIga9IgFC////////////AINCgICAgICAgPj/AFYEQCAGIQUMAQsgBUQAAAAAAAAAAGEgBkQAAAAAAAAAAGFxIQogBARAIAoEQCABIAyDvyEFDAILIAUgBSAGpSAGvUL///////////8Ag0KAgICAgICA+P8AVhsgBiAFvUL///////////8Ag0KAgICAgICA+P8AWBshBQwBCyAKBEAgASAMhL8hBQwBCyAFIAUgBqQgBr1C////////////AINCgICAgICAgPj/AFYbIAYgBb1C////////////AINCgICAgICAgPj/AFgbIQULIAdBAWohBwwBCwsgBb0iAQJ/IAWZRAAAAAAAAOBBYwRAIAWqDAELQYCAgIB4CyIAt71RBEAgAK0MAQtCgICAgMB+IAFCgICAgMCBgPz/AH0gAUL///////////8Ag0KAgICAgICA+P8AVhsLIQ0gCUEQaiQAIA0L4wECAX4CfyMAQRBrIgIkAAJAIAAgAUEpEFoiA0UEQCAEQQA2AgBCgICAgOAAIQEMAQtCgICAgDAhAQJAIAMpAwAiBkKAgICAcINCgICAgDBSBEAgAiADKAIMIgU2AgwgBSAGpyIHKAIEQf////8HcUkNASAAIAYQDCADQoCAgIAwNwMACyAEQQE2AgAMAQsgByACQQxqEMYBIQggAyACKAIMNgIMIARBADYCACAIQf//A00EQCAAIAhB//8DcRCUAyEBDAELIAAgByAFQQF0akEQakECEJIDIQELIAJBEGokACABC5EDAgN/An4jAEEgayICJABCgICAgOAAIQgCQCAAIAEQSiIBQoCAgIBwg0KAgICA4ABRDQAgACACQQhqIgVBBxA+GiAFQTwQPBogBSAEQQN0IgRB0OEBaigCACIGEIMBGiAEQdThAWooAgAiBARAIAVBIBA8GiAFIAQQgwEaIAVB2pEBEIMBGiAAIAMpAwAQSiIJQoCAgIBwg0KAgICA4ABRBEAgACABEAwgAigCCCgCECIAQRBqIAIoAgwgACgCBBEAAAwCCyAJpyIH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QdAAaiQAIAYLuAICA38DfiMAQSBrIgIkAEKAgICA4AAhBwJAAkACQCAAIAEQSiIBQoCAgIBwg0KAgICA4ABRDQAgACACIAMpAwAQ4wENACACKQMAIghCgICAgAhaBEAgAEHTGEEAEEQMAQsgCKciA0EBRg0BIAGnIgQpAgQiCaciBkH/////B3EiBUUNASAJQv////8HgyAIfkKAgICABFoEQCAAQeTIAEEAEDoMAQsgACACQQhqIAMgBWwgBkEfdhCZAw0AAkAgBUEBRwRAA0AgA0EATA0CIAJBCGogBEEAIAUQSxogA0EBayEDDAALAAsgAkEIagJ/IAQtAAdBgAFxBEAgBC8BEAwBCyAELQAQCyADEMsEGgsgACABEAwgAkEIahA3IQcMAgsgACABEAwMAQsgASEHCyACQSBqJAAgBwtYAQF+IAAgAykDABDkAUEAR61CgICAgBCEIQQgAUKAgICAcINCgICAgDBRBEAgBA8LIAAgAUEGEF4iAUKAgICAcINCgICAgOAAUgRAIAAgASAEEL0BCyABC8EBAgJ/An4jAEEQayIEJABCgICAgOAAIQYCQCAAIAEQSiIBQoCAgIBwg0KAgICA4ABRBEAgASEGDAELAkAgACAEQQxqIAMpAwAgAaciBSgCBEH/////B3EiAiACEFYNACAEIAI2AgggAykDCCIHQoCAgIBwg0KAgICAMFIEQCAAIARBCGogByACIAIQVg0BIAQoAgghAgsgACAFIAQoAgwiAyACIAMgAiADShsQjgEhBgsgACABEAwLIARBEGokACAGC8ABAgN/An4jAEEQayICJABCgICAgOAAIQcCQCAAIAEQSiIBQoCAgIBwg0KAgICA4ABRBEAgASEHDAELAkAgACACQQxqIAMpAwAgAaciBigCBEH/////B3EiBCAEEFYNACACIAQgAigCDCIFayIENgIIIAAgBiAFIAMpAwgiCEKAgICAcINCgICAgDBSBH8gACACQQhqIAggBEEAEFYNASACKAIIBSAECyAFahCOASEHCyAAIAEQDAsgAkEQaiQAIAcL0wECAn8CfiMAQRBrIgIkAEKAgICA4AAhBgJAIAAgARBKIgFCgICAgHCDQoCAgIDgAFEEQCABIQYMAQsCQCAAIAJBDGogAykDACABpyIFKAIEQf////8HcUEAEFYNACACIAUo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ABogAy0AAEEBcUUEQCADKAI4IQAgAygCNCIBBEAgASAANgI4CyAABEAgACABNgI0CyADQZjVBCgCAEYEQEGY1QQgADYCAAsgAygCYBDUASADENQBCyAMEAogDBDUAQsgB0HQAWokAAsJACAAIAEQzwQLLAAgACABEM8EIgFCgICAgHCDQoCAgIDgAFIEfiAAQQNBAiABpxsQKQUgAQsLkAECAXwBfiMAQRBrIgIkAAJ+IAMpAwAiAUIgiKciAwRAQoCAgIAQIANBC2pBEkkNARoLQoCAgIDgACAAIAJBCGogARBCDQAaIAIrAwgiBJlE////////P0NlIAS9QoCAgICAgID4/wCDQoCAgICAgID4/wBSIAScIARhcXGtQoCAgIAQhAshBSACQRBqJAAgBQsmAEKAgICA4AAgACADKQMAENkFIgBBAEetQoCAgIAQhCAAQQBIGwsvAQF+An4gAygCBCICBEBCgICAgBAiBCACQQtqQRJJDQEaCyAAIAQgAyADENIECwsvAQF+An4gAygCBCICBEBCgICAgBAiBCACQQtqQRJJDQEaCyAAIAQgAyADENMECwsJACAAIAEQngILowECAn4BfyMAQRBrIgIkAAJ+IAAgARCeAiIFQoCAgIBwg0KAgICA4ABRBEAgBQwBC0EKIQcCQAJAIAQNACADKQMAIgFCgICAgHCDQoCAgIAwUQ0AIAAgARDbBCIHQQBIDQELQoCAgIDgACAAIAJBCGogBRBtDQEaIAAgAisDCCAHQQBBABC6AgwBCyAAIAUQDEKAgICA4AALIQYgAkEQaiQAIAYLkAICAX4BfCMAQRBrIgIkAEKAgICA4AAhBAJAIAAgARCeAiIBQoCAgIBwg0KAgICA4ABRBEAgASEEDAELIAAgAiABEG0NAAJAAkAgAykDACIBQoCAgIBwg0KAgICAMFEEQCACKwMAIgW9IQEMAQsgACACQQxqIAEQswENAiACKwMAIgW9IgFCgICAgICAgPj/AINCgICAgICAgPj/AFINAQsgAEKAgICAwH4gAUKAgICAwIGA/P8AfSAFvUL///////////8Ag0KAgICAgICA+P8AVhsQNCEEDAELIAIoAgwiA0HlAGtBm39NBEAgAEHrIUEAEEQMAQsgACAFQQogA0EBELoCIQQLIAJBEGokACAEC80BAgF+AnwjAEEQ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IAQgCjcDACAEQQhqIQQgCEIBfCEIDAELCyAJIAx8IQgDQCAIIAtZDQIgBiAIp0EDdGopAwAiCUIgiKdBdU8EQCAJpyIDIAMoAgBBAWo2AgALIAQgCTcDACAEQQhqIQQgCEIBfCEIDAALAAsDQAJAIAkgClEEQCADQRBqIQNCACEJA0AgCSAPUQ0CIAMgCadBA3RqKQMAIhBCIIinQXVPBEAgEKciBiAGKAIAQQFqNgIACyAEIBA3AwAgBEEIaiEEIAlCAXwhCQwACwALIAAgDiAJIAQQVEF/Rg0DIARBCGohBCAJQgF8IQkMAQsLIAogDHwhCQNAIAkgC1kNASAAIA4gCSAEEFRBf0YNAiAEQQhqIQQgCUIBfCEJDAALAAsgAiAERgRAIAFCgICAgDAgACABQTAgDUKAgICACFoEfkKAgICAwH4gDbm9IghCgICAgMCBgPz/AH0gCEL///////////8Ag0KAgICAgICA+P8AVhsFIA0LEDlBAEgiAxshC0KAgICA4AAgASADGyEIIAIhBAwDC0GJFkGo7ABB4rkCQfHqABAAAAsgASELDAELQQAhAkKAgICAMCELCwNAIAIgBEZFBEAgBEKAgICAMDcDACAEQQhqIQQMAQsLIAAgCxAMIAAgDhAMIAVBMGokACAIC8gIAgl+A38jAEEwayIOJABCgICAgDAhBQJAAkAgACAOQSBqIAAgARAgIgoQLw0AIAAgDkEYaiADKQMAQgAgDikDICIGIAYQZg0AAkAgBARAAkACQAJAIAIOAgIAAQsgBiAOKQMYfSEHQQAhAgwBCyAAIA5BEGogAykDCEIAIAYgDikDGH1CABBmDQMgAkECayECIA4pAxAhBwsgBiACrXwgB31CgICAgICAgBBTDQEgAEH0yABBABASDAILIA4gBjcDECAGIQEgAykDCCINQoCAgIBwg0KAgICAMFIEfiAAIA5BEGogDUIAIAEgARBmDQIgDikDEAUgAQsgDikDGH0iAUIAIAFCAFUbIQdBACECCyAAIAogB0KAgICACHxC/////w9YBH4gB0L/////D4MFQoCAgIDAfiAHub0iAUKAgICAwIGA/P8AfSABQv///////////wCDQoCAgICAgID4/wBWGwsiBRCfAiEBIAAgBRAMAkAgAUKAgICAcINCgICAgOAAUQ0AIA4pAxgiDSAH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AiAAIAFBNyABQQAQESIBQoCAgIBwg0KAgICAMFEEfiAAQS8QKQUgAQtBnggQsgELC+EFAwN+B38DfAJAIAAgARBVDQAgACAAKQMwQQ4QRyIFQoCAgIBwg0KAgICA4ABRDQAgBaciCSABQoCAgIBwWgR/IAGnLQAFQRBxBUEACyAJLQAFQe8BcXI6AAUCQCAAQQEgAiACQQFMGyIKQQFrIghBA3RBGGoQJCIHRQ0AIAFCIIinQXVPBEAgAaciAiACKAIAQQFqNgIACyAHIAE3AwAgAykDACIEQiCIp0F1TwRAIASnIgIgAigCAEEBajYCAAsgByAINgIQIAcgBDcDCCAHQRhqIQtBACECA0AgAiAIRwRAIAMgAkEBaiIMQQN0aikDACIEQiCIp0F1TwRAIASnIg0gDSgCAEEBajYCAAsgCyACQQN0aiAENwMAIAwhAgwBCwsgCSAHNgIgAn8gAUL/////b1gEQCAAECJBfwwBCyAAQQAgAadBMBBDCyICQQBIDQACQCACRQ0AIAAgAUEwIAFBABARIgRCgICAgHCDQoCAgIDgAFENASAEQv////8PWARAIASnIgIgCGtBACACIApOG60hBgwBCyAEQiCIp0EHa0FtTQRAAkAgBEKAgICAwIGA/P8AfCIEQv///////////wCDQoCAgICAgID4/wBWDQAgBL+dIg8gCLciEGUNACAPIBChIQ4LIA69IgQCfyAOmUQAAAAAAADgQWMEQCAOqgwBC0GAgICAeAsiAre9UQRAIAKtIQYMAgtCgICAgMB+IARCgICAgMCBgPz/AH0gBEL///////////8Ag0KAgICAgICA+P8AVhshBgwBCyAAIAQQDAsgACAFQTAgBkEBEBUaIABBgJIBIAAgAUE3IAFBABARIgFCgICAgHCDIgRCgICAgJB/UgR+IARCgICAgOAAUQ0BIAAgARAMIABBLxApBSABC0HslgEQsgEiAUKAgICAcINCgICAgOAAUQ0AIAAgBUE3IAFBARAVGiAFDwsgACAFEAwLQoCAgIDgAAswACACQQBMBEAgACABQoCAgIAwQQBBABAcDwsgACABIAMpAwAgAkEBayADQQhqEBwLgwICAX4BfyMAQSBrIgIkAEKAgICA4AAhBQJAAkAgACABECAiAUKAgICAcINCgICAgOAAUQ0AIAAgAykDABAw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ARAgCzYAIAMpAwAiAUIgiKciAkF/RiAERSACQX5xQQJHcXJFBEAgABAiQoCAgIDgAA8LIAAgARDoAQuJAQEBfiADKQMAIgFC/////29WIAFCgICAgHCDQoCAgIAgUXJFBEAgAEHe0gBBABASQoCAgIDgAA8LAkAgACABEEEiAUKAgICAcINCgICAgOAAUgRAIAMpAwgiBEKAgICAcINCgICAgDBRDQEgACABIAQQ2gRFDQEgACABEAwLQoCAgIDgAA8LIAELnwIBA34gAUL/////b1gEQCAAECJCgICAgOAADwtCgICAgOAAIQUCfiAAIAFBNyABQQAQESIEQoCAgIBwg0KAgICAMFEEQCAAQZQBECkMAQsgACAEEDQLIgRCgICAgHCDIgZCgICAgOAAUgR+An4gACABQTMgAUEAEBEiAUKAgICAcINCgICAgDBRBEAgAEEvECkMAQsgACABEDQLIgFCgICAgHCDIgVCgICAgOAAUQRAIAAgBBAMQoCAgIDgAA8LAkAgBkKAgICAkH9RBEAgBKcoAgRB/////wdxRQ0BCyAFQoCAgICQf1EEQCABpygCBEH/////B3FFDQELIABB7JYBIARBpJIBELIBIQQLIAAgBCABELYCBUKAgICA4AALC5UCAgF+An8jAEEwayICJABCgICAgOAAIQECQCAAIAJBKGogAykDABCkAQ0AIAAQ4gEiBUKAgICAcINCgICAgOAAUQ0AIAAgAkEUaiADKQMIEK4CIgZFBEAgACAFEAwMAQsgACgC2AEgAhC7ASACQgEQMhogAiACKQMoIgGnIgdBARC5ARogAiACQn9B/////wNBARB6GiAFp0EEaiIDIAYgAhCyBBoCQCAERSABUHINACACQgEQMhogAiAHQQFrQQEQuQEaIAMgAhDyAUEASA0AIAJCARAyGiACIAdBARC5ARogAyADIAJB/////wNBARDuARoLIAIQGSAAIAYgAkEUahDmASAFEK8CIQELIAJBMGokACABCwkAIAAgARDcBAt0AgJ+AX8gACABENwEIgFCgICAgHCDQoCAgIDgAFEEQCABDwtBCiEGAn4CQCACRQ0AIAMpAwAiBEKAgICAcINCgICAgDBRDQAgACAEENsEIgZBAE4NAEKAgICA4AAMAQsgACABIAYQogULIQUgACABEAwgBQvOAQIBfwJ+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/////////wCDQoCAgICAgID4/wBWGwvvAQEDfiMAQRBrIgIkAEKAgICA4AAhBAJAIAAgACABECAiAUEBELsCIgVCgICAgHCDQoCAgIDgAFENACAFQiCIpyIDQQAgA0ELakESSRtFBEAgACACQQhqIAUQQkEASA0BQoCAgIAgIQQgAikDCEKAgICAgICA+P8Ag0KAgICAgICA+P8AUQ0BC0KAgICA4AAhBCAAIAFB48oAEIcCIgZCgICAgHCDQoCAgIDgAFENACAAIAYQNUUEQCAAQergAEEAEBIgACAGEAwMAQsgACAGIAFBAEEAEDYhBAsgACABEAwgACAFEAwgAkEQaiQAIAQLjAIDAXwBfgF/IwBBEGsiAiQAQoCAgIDgACEFAkAgACACQQhqIgYgARCmAg0AIAAgBiADKQMAEEINACACAn4gAisDCCIEvUKAgICAgICA+P8Ag0KAgICAgICA+P8AUgRAIASdIgREAAAAAACwnUCgIAQgBEQAAAAAAABZQGMbIAQgBEQAAAAAAAAAAGYbIQQLIAS9IgUCfyAEmUQAAAAAAADgQWMEQCAEqgwBC0GAgICAeAsiA7e9UQRAIAOtDAELQoCAgIDAfiAFQoCAgIDAgYD8/wB9IAVC////////////AINCgICAgICAgPj/AFYbCzcDACAAIAFBASACQREQ+AQhBQsgAkEQaiQAIAULigEDAX4BfAF/IwBBEGsiAiQAQoCAgIDgACEEAkAgACACQQhqIgYgARCmAg0AIAAgBiADKQMAEEINACAAIAEgAisDCCIFnUQAAAAAAAAAAKBEAAAAAAAA+H8gBUQAANzCCLI+Q2UbRAAAAAAAAPh/IAVEAADcwgiyPsNmGxD5BCEECyACQRBqJAAgBAvZAQIBfAF+IwBB0ABrIgIkAAJ+QoCAgIDgACAAIAEgAiAEQQ9xQQAQ1QMiAEEASA0AGkKAgICAwH4gAEUNABogBEGAAnEEQCACIAIrAwBEAAAAAACwncCgOQMACyACIARBBHZBD3FBA3RqKwMAIgW9IgECfyAFmUQAAAAAAADgQWMEQCAFqgwBC0GAgICAeAsiBLe9UQRAIAStDAELQoCAgIDAfiABQoCAgIDAgYD8/wB9IAFC////////////AINCgICAgICAgPj/AFYbCyEGIAJB0ABqJAAgBguHAQIBfAF+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IAMpAxAiBUIgiKdBdU8EQCAFpyIJIAkoAgBBAWo2AgALIAggBTcDCAJAIAQNACADKQMYIgVCIIinQXVJDQAgBaciCSAJKAIAQQFqNgIACyAIIAE3AxAgCCAFNwMAIAAgByAGQQMgCBAcIQUgACAIKQMAEAwgBEUEQCAAIAgpAwgQDAsgAygCBCEDIAAoAhAgAhDiAyAFQoCAgIBwg0KAgICA4ABRDQIgACAFEAwLDAALAAsgCEEgaiQAIAULVAAgACABIAJBImoQWiIARQRAQoCAgIDgAA8LIAAoAgwiAEEATgRAIACtDwtCgICAgMB+IAC4vSIBQoCAgIDAgYD8/wB9IAFCgICAgICAgPj/AFYbC1kBAX8gACABIARBImoQWiICRQRAQoCAgIDgAA8LIAJBBGohAyACKAIIIQQDfiADIARGBH5CgICAgDAFIARBEGshBSAEKAIEIQQgACgCECACIAUQgAUMAQsLC3UAIAAgASAEQSJqEFoiAkUEQEKAgICA4AAPCyAAIAIgAykDACIBQgAgAUIgiKdBB2tBbk8bIAEgAUKAgICAwIGA/P8AfEL///////////8Ag1AbEPICIgNFBEBCgICAgBAPCyAAKAIQIAIgAxCABUKBgICAEAthACAAIAEgBEEiahBaIgJFBEBCgICAgOAADwsgACACIAMpAwAiAUIAIAFCIIinQQdrQW5PGyABIAFCgICAgMCBgPz/AHxC////////////AINQGxDyAkEAR61CgICAgBCECwgAQoCAgIAwC0oAAkAgBSkDACIBQoCAgIBwVA0AIAGnIgIvAQZBLEcNACACKAIgIgJFDQAgAkEBOgARIAAgARAMIAVCgICAgCA3AwALQoCAgIAwC88BAQN+IwBBEGsiAiQAQoCAgIDgACEFAkACQAJ+QoCAgIAwIABCgICAgDAgACADEMAEIgRCgICAgHCDQoCAgIDgAFENABogAiAENwMIQoCAgIDgACAAQcMAQQBBAEEBIAJBCGoQhQEiBkKAgICAcINCgICAgOAAUQ0AGiAAEDMiAUKAgICAcINCgICAgOAAUg0BIAYLIQEgACAEEAwgACABEAwMAQsgACABQYQBIARBBxAVGiAAIAFBhQEgBkEHEBUaIAEhBQsgAkEQaiQAIAULswMCA38CfiMAQdAAayIGJABBfyEHAkAgACAG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QoCAgIBwg0KAgICA4ABRcg0AIAAgAykDCCIGEDVFDQBCgICAgOAAIQQCQCAAEDMiBUKAgICAcINCgICAgOAAUQRAIAEhBQwBCyAAIAVBLyABQQcQFUEASA0AIAAgBUEvIAYQhQUhBAsgACAFEAwMAQsgASEECyAHQRBqJAAgBAsQACAAIAMpAwBBESAEELICC6UDAQR+IwBBEGsiAyQAIAQCfwJAAkACQAJAIAAgAUEqEFoiAkUEQEKAgICAMCEBDAELIAIoAhgEQEKAgICAMCEBQQEMBQsgACACKQMAIgggAikDCCIGEMUBIgFCgICAgHCDIgdCgICAgOAAUg0BC0KAgICAMCEHDAELIAdCgICAgCBRBEAgAkEBNgIYQoCAgIAwIQFBAQwDCyACKAIQBEAgACAAIAFCABBOEDQiB0KAgICAcIMiCUKAgICA4ABRDQECQCAJQoCAgICQf1INACAHpygCBEH/////B3ENACAAIANBCGogACAIQdYAIAhBABAREKEBQQBIDQIgACAIQdYAAn4gBqcgAykDCCACKAIUEPQCIgZCgICAgAh8Qv////8PWARAIAZC/////w+DDAELQoCAgIDAfiAGub0iBkKAgICAwIGA/P8AfSAGQv///////////wCDQoCAgICAgID4/wBWGwsQOUEASA0CCyAAIAcQDAwCCyACQQE2AhgMAQsgACABEAwgACAHEAxCgICAgOAAIQELQQALNgIAIANBEGokACABCyAAIAFCIIinQXVPBEAgAaciACAAKAIAQQFqNgIACyABC/EHAgR/C34jAEEwayIEJAACQCABQv////9vWARAIAAQIkKAgICA4AAhAQwBC0KAgICAMCEIAkACQCAAIAMpAwAQJSIPQoCAgIBwg0KAgICA4ABRBEBCgICAgDAhDEKAgICAMCEBQoCAgIAwIQ1CgICAgDAhEAwBCyAAIAEgACkDSBD9ASIQQoCAgIBwg0KAgICA4ABRBEBCgICAgDAhDEKAgICAMCEBQoCAgIAwIQ0MAQsCQAJAIAAgACABQe4AIAFBABAREDQiDUKAgICAcINCgICAgOAAUQ0AIA2nIgJB9QBBABCgASEGIAJB+QBBABCgAUEASARAIABB7JYBIA1B0A4QsgEiDUKAgICAcINCgICAgOAAUQ0BCyAEIA03AyggBCABNwMgIAAgEEECIARBIGoQowEiDEKAgICAcINCgICAgOAAUQ0BIAAQOyIBQoCAgIBwg0KAgICA4ABRBEBCgICAgOAAIQEMAwtBfyECAkAgAykDCCIJQoCAgIBwg0KAgICAMFENACAAIARBHGogCRB1QQBIDQMgBCgCHCICDQAMBAsCQAJAIA+nIgcpAgQiCKdB/////wdxIgUEQCAGQX9zQR92IQYgCEL/////B4MhESACrSESQgAhCUKAgICAMCEIQQAhAgNAIAKtIQogAiEDA0AgAyAFTw0DIAAgDEHWACADrSIOEDlBAEgNByAAIAgQDAJAIAAgDCAPEMUBIghCgICAgHCDIgtCgICAgCBRDQAgC0KAgICA4ABRDQggACAEQRBqIAAgDEHWACAMQQAQERChAQ0IIAQgBCkDECILIBEgCyARUxsiCzcDECAKIAtRDQAgACAHIAIgAxCOASIKQoCAgIBwg0KAgICA4ABRDQggACABIAkgChBnQQBIDQggCUIBfCIKIBJRDQkgACAEQQhqIAgQLw0IIAunIQJCASELIAlCASAEKQMIIg4gDkIBVxt8IQkDQCAJIApRDQMgACAAIAggCxBsEDQiDkKAgICAcINCgICAgOAAUQ0JIAAgASAKIA4QZ0EASA0JIAtCAXwhCyAKQgF8IgogElINAAsMCQsgByAOIAYQ9AKnIQMMAAsACwALIAAgDCAPEMUBIghCgICAgHCDIglCgICAgCBSDQFCACEJQQAhAgsgACAHIAIgBSACIAVJGyAFEI4BIgpCgICAgHCDQoCAgIDgAFENAyAAIAEgCSAKEGdBAEgNAwwECyAJQoCAgIDgAFINAwwCC0KAgICAMCEMC0KAgICAMCEBCyAAIAEQDEKAgICA4AAhAQsgACAPEAwgACAQEAwgACAMEAwgACANEAwgACAIEAwLIARBMGokACABC+ACAQd+IAFC/////29YBEAgABAiQoCAgIDgAA8LQoCAgIDgACEIQoCAgIAwIQYCQAJAAkAgACADKQMAECUiB0KAgICAcINCgICAgOAAUQRAQoCAgIAwIQQMAQsgACABQdYAIAFBABARIgRCgICAgHCDQoCAgIDgAFENACAAIARCABBNRQRAIAAgAUHWAEIAEDlBAEgNAQsgACABIAcQxQEiBUKAgICAcIMiCUKAgICA4ABRDQEgACABQdYAIAFBABARIgZCgICAgHCDQoCAgIDgAFENAQJAIAAgBiAEEE0EQCAAIAQQDAwBCyAAIAFB1gAgBBA5QQBODQBCgICAgDAhBAwCCyAAIAcQDCAAIAYQDEL/////DyEIIAlCgICAgCBRDQIgACAFQdgAIAVBABARIQogACAFEAwgCg8LQoCAgIAwIQULIAAgBRAMIAAgBxAMIAAgBhAMIAAgBBAMCyAIC84EAgZ+AX8jAEEgayICJAACQCABQv////9vWARAIAAQIkKAgICA4AAhBwwBC0KAgICA4AAhB0KAgICAMCEIAkAgACADKQMAECUiCUKAgICAcINCgICAgOAAUQRAQoCAgIAwIQRCgICAgDAhBUKAgICAMCEGDAELAkACQCAAIAEgACkDSBD9ASIGQoCAgIBwg0KAgICA4ABRBEBCgICAgDAhBAwBCyAAIAAgAUHuACABQQAQERA0IgRCgICAgHCDQoCAgIDgAFINAQtCgICAgDAhBQwBCyACIAQ3AxggAiABNwMQIAAgBkECIAJBEGoQowEiBUKAgICAcINCgICAgOAAUQ0AIAAgAkEIaiAAIAFB1gAgAUEAEBEQoQENACAAIAVB1gACfiACKQMIIgFCgICAgAh8Qv////8PWARAIAFC/////w+DDAELQoCAgIDAfiABub0iAUKAgICAwIGA/P8AfSABQv///////////wCDQoCAgICAgID4/wBWGwsQOUEASA0AQoCAgIDgACEIIABBKhCGASIBQoCAgIBwg0KAgICA4ABRDQAgAEEgECQiA0UEQCABIQgMAQsgAyAJNwMIIAMgBTcDACADIASnIgpB5wBBABCgAUF/c0EfdjYCECAKQfUAQQAQoAEhCiADQQA2AhggAyAKQX9zQR92NgIUIAFCgICAgHBaBEAgAacgAzYCIAsgACAGEAwgACAEEAwgASEHDAELIAAgCRAMIAAgBhAMIAAgBBAMIAAgBRAMIAAgCBAMCyACQSBqJAAgBwv+BAIIfgJ/IwBBEGsiAiQAAkAgAUL/////b1gEQCAAECJCgICAgOAAIQcMAQtCgICAgOAAIQdCgICAgDAhBQJAAkACQCAAIAMpAwAQJSIJQoCAgIBwg0KAgICA4ABRBEBCgICAgDAhCAwBCyAAIAFB7gAgAUEAEBEiCEKAgICAcINCgICAgOAAUQ0AIAAgCBA0IghCgICAgHCDQoCAgIDgAFENACAIp0HnAEEAEKABQX9GBEAgACABIAkQxQEhBwwDCyAAIAAgAUHwACABQQAQERAnIgxBAEgNACAAIAFB1gBCABA5QQBIDQAgABA7IgZCgICAgHCDQoCAgIDgAFEEQEKAgICA4AAhBgwCCyAJpyENA0ACQCAAIAUQDCAAIAEgCRDFASIFQoCAgIBwgyIEQoCAgIAgUQ0AIARCgICAgOAAUQ0DAkAgACAAIAVCABBOEDQiBEKAgICAcIMiC0KAgICAkH9SBEBBACEDIAtCgICAgOAAUQ0FDAELIASnKAIEQf////8HcUUhAwsgACAGIAogBBB7QQBIDQMgCkIBfCEKIANFDQEgACACQQhqIAAgAUHWACABQQAQERChAUEASA0DIAAgAUHWAAJ+IA0gAikDCCAMEPQCIgRCgICAgAh8Qv////8PWARAIARC/////w+DDAELQoCAgIDAfiAEub0iBEKAgICAwIGA/P8AfSAEQv///////////wCDQoCAgICAgID4/wBWGwsQOUEATg0BDAMLCyAKpwRAIAYhBwwDCyAAIAYQDEKAgICAICEHDAILQoCAgIAwIQYLIAAgBhAMCyAAIAUQDCAAIAgQDCAAIAkQDAsgAkEQaiQAIAcLjgEBAn8gASgCACICQQBKBEAgASACQQFrIgI2AgACQCACDQAgAS0ABEHwAXFBEEcNACABKAIIIgIgASgCDCIDNgIEIAMgAjYCACABQQA2AgggACgCYCICIAFBCGoiAzYCBCABIABB4ABqNgIMIAEgAjYCCCAAIAM2AmALDwtB5oQBQajsAEHWLEHN4wAQAAAL8BQCDn8OfiMAQZABayIEJAACQCABQv////9vWARAIAAQIkKAgICA4AAhFQwBCyADKQMIIR4gACAEQThqQQAQPhogBEEANgIwIARCgICAgMAANwMoIAQgADYCACAEIARBCGoiBzYCBEKAgICA4AAhFUKAgICAMCEWQoCAgIAwIRdCgICAgDAhE0KAgICAMCEUQoCAgIAwIR1CgICAgDAhHAJAAkAgACADKQMAECUiGEKAgICAcINCgICAgOAAUQ0AIAAgHhA1IglFBEAgACAeECUiHUKAgICAcINCgICAgOAAUQ0BIB2nIQULAkACQCAAIAFB7gAgAUEAEBEiHEKAgICA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QQRqIQMMAAsACwuaAQEHfyABpygCICIDBEAgAEEQaiEEIANBBGohBiADKAIIIQIDQCACIAZHBEAgAigCBCEIIAJBEGshBSACQQxrKAIARQRAAkAgAygCAARAIAUQnAUMAQsgACACKQMQECELIAAgAikDGBAhCyAEIAUgACgCBBEAACAIIQIMAQsLIAQgAygCECAAKAIEEQAAIAQgAyAAKAIEEQAACwsbAQF/IAGnKAIgIgMEQCAAIAMoAgwgAhEAAAsLUgEDfyABpygCICICBEAgAigCBCIDBEAgAigCACIEIAM2AgQgAyAENgIAIAJCADcCAAsgACACNQIMQoCAgIBwhBAhIABBEGogAiAAKAIEEQAACwupAQEGfyABpygCICIDBEAgA0EMaiEFIAMoAhAhAgNAIAIgBUcEQCACKAIEIQcgAkIANwIAIAIoAgghBCAHIQIgBC8BBkEgRg0BIARCADcCJAwBCwsCQAJAIAMtAAVFDQAgACgCyAEiAkUNACAAKALQASADKAIIIAIRAAAMAQsgAygCGCICRQ0AIAAgAygCFCADKAIIIAIRBgALIABBEGogAyAAKAIEEQAACwspAQF/IAAgAaciAjUCJEKAgICAkH+EECEgACACNQIgQoCAgICQf4QQIQshACABpygCICkDACIBQoCAgIBgWgRAIAAgAacgAhEAAAsLaQEDfyAAIAGnKAIgIgIpAwAQISACLQARRQRAA0AgAigCFCEEIAMgAigCDE9FBEAgACAEIANBA3RqKAIEEMcBIANBAWohAwwBCwsgAEEQaiAEIAAoAgQRAAALIABBEGogAiAAKAIEEQAAC2wBA38CQCABQoCAgIBwVA0AIAGnIgMvAQZBD0cNACADKAIgIgRFDQAgBEEIaiEFQQAhAwNAIAMgBC0ABU8NASAFIANBA3RqKQMAIgFCgICAgGBaBEAgACABpyACEQAACyADQQFqIQMMAAsACwtqAQN/AkAgAUKAgICAcFQNACABpyICLwEGQQ9HDQAgAigCICIDRQ0AIANBCGohBEEAIQIDQCACIAMtAAVPRQRAIAAgBCACQQN0aikDABAhIAJBAWohAgwBCwsgAEEQaiADIAAoAgQRAAALC38BA38gAacoAiAiBCkDACIBQoCAgIBgWgRAIAAgAacgAhEAAAsgBCkDCCIBQoCAgIBgWgRAIAAgAacgAhEAAAsgBEEYaiEFA0AgBCgCECADSgRAIAUgA0EDdGopAwAiAUKAgICAYFoEQCAAIAGnIAIRAAALIANBAWohAwwBCwsLWQEDfyAAIAGnKAIgIgIpAwAQISAAIAIpAwgQISACQRhqIQQDQCADIAIoAhBORQRAIAAgBCADQQN0aikDABAhIANBAWohAwwBCwsgAEEQaiACIAAoAgQRAAALcgEEfyABpyIDKAIgIQQgAygCJCEFIAMoAigiAwRAIAAgAyACEQAACyAEBEACQCAFRQ0AQQAhAwNAIAMgBCgCPE4NASAFIANBAnRqKAIAIgYEQCAAIAYgAhEAAAsgA0EBaiEDDAALAAsgACAEIAIRAAALC3wBA38gAaciAigCKCIDBEAgACADrUKAgICAcIQQIQsgAigCICIDBEAgAigCJCIEBEBBACECA0AgAiADKAI8TkUEQCAAIAQgAkECdGooAgAQ5QEgAkEBaiECDAELCyAAQRBqIAQgACgCBBEAAAsgACADrUKAgICAYIQQIQsLEgAgAacoAiAiAARAIAAQngMLCx4AIAGnKQMgIgFCgICAgGBaBEAgACABpyACEQAACwsZACAAIAGnIgApAyAQISAAQoCAgIAwNwMgC0QBAn8gAachBANAIAQoAiggA0sEQCAEKAIkIANBA3RqKQMAIgFCgICAgGBaBEAgACABpyACEQAACyADQQFqIQMMAQsLC0YBA38gAachAwNAIAMoAiQhBCACIAMoAihPRQRAIAAgBCACQQN0aikDABAhIAJBAWohAgwBCwsgAEEQaiAEIAAoAgQRAAALEQAgAEEQaiACIAAoAgQRAAAL2wECAX8CfiMAQSBrIgMkACABQQNGBEAgAikDECEEIAIpAwghBQJAIAAgA0EQaiACKQMAEKoFQQBIBEBCgICAgOAAIQQMAQsgACAEIAVBAiADQRBqEBwiBEKAgICAcINCgICAgOAAUQRAIAAoAhAiASkDgAEhBCABQoCAgIAgNwOAASADIAQ3AwggACADKQMYQoCAgIAwQQEgA0EIahAcIQQgACADKQMIEAwLIAAgAykDEBAMIAAgAykDGBAMCyADQSBqJAAgBA8LQZuCAUGo7ABBy/UCQaDtABAAAAuIAQIBfgF/QQAhAkKAgICAMCEBA0ACQCACQQJHBH4gBSACQQN0IgRq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Y29kZV0KfQBjYW5ub3QgbWl4ID8/IHdpdGggJiYgb3IgfHwAcHJveHk6IHByb3BlcnR5IG5vdCBwcmVzZW50IGluIHRhcmdldCB3ZXJlIHJldHVybmVkIGJ5IG5vbiBleHRlbnNpYmxlIHByb3h5AHJldm9rZWQgcHJveHkAUHJveHkAYWRkX3Byb3BlcnR5AHByb3h5OiBjYW5ub3Qgc2V0IHByb3BlcnR5AG5vIHNldHRlciBmb3IgcHJvcGVydHkAdmFsdWUgaGFzIG5vIHByb3BlcnR5AGNvdWxkIG5vdCBkZWxldGUgcHJvcGVydHkAcHJveHk6IGR1cGxpY2F0ZSBwcm9wZXJ0eQBKU19EZWZpbmVBdXRvSW5pdFByb3BlcnR5AGhhc093blByb3BlcnR5AHByb3h5OiBpbmNvbnNpc3RlbnQgZGVsZXRlUHJvcGVydHkAcHJveHk6IGluY29uc2lzdGVudCBkZWZpbmVQcm9wZXJ0eQBKU19EZWZpbmVQcm9wZXJ0eQAhbXItPmVtcHR5AGluZmluaXR5AEluZmluaXR5AG91dCBvZiBtZW1vcnkAdW5rbm93biB1bmljb2RlIGdlbmVyYWwgY2F0ZWdvcnkAR2VuZXJhbF9DYXRlZ29yeQBldmVyeQBhbnkAYXBwbHkAJyVzJyBpcyByZWFkLW9ubHkAZXhwZWN0aW5nIGNhdGNoIG9yIGZpbmFsbHkAc3RpY2t5AGJpZ2ludCBhcmUgZm9yYmlkZGVuIGluIEpTT04uc3RyaW5naWZ5AHN1YmFycmF5AGVtcHR5IGFycmF5AG5vbiBpbnRlZ2VyIGluZGV4IGluIHR5cGVkIGFycmF5AG5lZ2F0aXZlIGluZGV4IGluIHR5cGVkIGFycmF5AG91dC1vZi1ib3VuZCBpbmRleCBpbiB0eXBlZCBhcnJheQBjYW5ub3QgY3JlYXRlIG51bWVyaWMgaW5kZXggaW4gdHlwZWQgYXJyYXkAaXNBcnJheQBUeXBlZEFycmF5AGdldERheQBnZXRVVENEYXkAZ3JvdXBCeQBtLT5kZnNfYW5jZXN0b3JfaW5kZXggPD0gbS0+ZGZzX2luZGV4AGpzX2dldF9hdG9tX2luZGV4AGludmFsaWQgYXJyYXkgaW5kZXgASlNfQXRvbUlzQXJyYXlJbmRleABmaW5kTGFzdEluZGV4AGZpbmRJbmRleABpbnZhbGlkIGV4cG9ydCBzeW50YXgAaW52YWxpZCBhc3NpZ25tZW50IHN5bnRheABtYXgAXHUlMDR4AGludmFsaWQgb3Bjb2RlOiBwYz0ldSBvcGNvZGU9MHglMDJ4AC0rICAgMFgweAAtMFgrMFggMFgtMHgrMHggMHgAbGluZSB0ZXJtaW5hdG9yIG5vdCBhbGxvd2VkIGFmdGVyIHRocm93AGJmX3BvdwBub3cAaW50ZWdlciBvdmVyZmxvdwBzdGFjayBvdmVyZmxvdwBtdXN0IGJlIGNhbGxlZCB3aXRoIG5ldwBpc1ZpZXcARGF0YVZpZXcAcmF3ACV1AGNsYXNzIGRlY2xhcmF0aW9ucyBjYW4ndCBhcHBlYXIgaW4gc2luZ2xlLXN0YXRlbWVudCBjb250ZXh0AGZ1bmN0aW9uIGRlY2xhcmF0aW9ucyBjYW4ndCBhcHBlYXIgaW4gc2luZ2xlLXN0YXRlbWVudCBjb250ZXh0AGxleGljYWwgZGVjbGFyYXRpb25zIGNhbid0IGFwcGVhciBpbiBzaW5nbGUtc3RhdGVtZW50IGNvbnRleHQAZHVwbGljYXRlIGFyZ3VtZW50IG5hbWVzIG5vdCBhbGxvd2VkIGluIHRoaXMgY29udGV4dABkdXBsaWNhdGUgcGFyYW1ldGVyIG5hbWVzIG5vdCBhbGxvd2VkIGluIHRoaXMgY29udGV4dABpbXBvcnQubWV0YSBub3Qgc3VwcG9ydGVkIGluIHRoaXMgY29udGV4dABKU19GcmVlQ29udGV4dABKU0NvbnRleHQAanNfbWFwX2l0ZXJhdG9yX25leHQAanNfZ2VuZXJhdG9yX25leHQAanNfYXN5bmNfZ2VuZXJhdG9yX3Jlc3VtZV9uZXh0AHVuZXhwZWN0ZWQgZW5kIG9mIGlucHV0AHR0AGV4cG9ydGVkIHZhcmlhYmxlICclcycgZG9lcyBub3QgZXhpc3QAcHJpdmF0ZSBjbGFzcyBmaWVsZCAnJXMnIGRvZXMgbm90IGV4aXN0AHRlc3QAYXNzaWdubWVudCByZXN0IHByb3BlcnR5IG11c3QgYmUgbGFzdABwdmFsID09IGxhc3QAZmluZExhc3QAYmZfc3FydABzb3J0AGNicnQAdHJpbVN0YXJ0AHBhZFN0YXJ0AHVua25vd24gdW5pY29kZSBzY3JpcHQAU2NyaXB0AGh5cG90AGZyZWVfemVyb19yZWZjb3VudABzdHJfaW5kZXggPT0gbnVtX2tleXNfY291bnQgKyBzdHJfa2V5c19jb3VudABudW1faW5kZXggPT0gbnVtX2tleXNfY291bnQAc3ltX2luZGV4ID09IGF0b21fY291bnQAbGFiZWwgPj0gMCAmJiBsYWJlbCA8IHMtPmxhYmVsX2NvdW50AGxhYjEgPj0gMCAmJiBsYWIxIDwgcy0+bGFiZWxfY291bnQAdmFsIDwgcy0+Y2FwdHVyZV9jb3VudAB2YWwyIDwgcy0+Y2FwdHVyZV9jb3VudABpbnZhbGlkIHJlcGVhdCBjb3VudABpbnZhbGlkIHJlcGV0aXRpb24gY291bnQAZm9udABpbnZhbGlkIGNvZGUgcG9pbnQAZnJvbUNvZGVQb2ludABpbnZhbGlkIGhpbnQAY2Fubm90IGNvbnZlcnQgTmFOIG9yIEluZmluaXR5IHRvIGJpZ2ludABjYW5ub3QgY29udmVydCB0byBiaWdpbnQAYm90aCBvcGVyYW5kcyBtdXN0IGJlIGJpZ2ludABub3QgYSBiaWdpbnQAcHJpdmF0ZSBtZXRob2QgaXMgYWxyZWFkeSBwcmVzZW50AGVuY29kZVVSSUNvbXBvbmVudABkZWNvZGVVUklDb21wb25lbnQAdW5leHBlY3RlZCBlbmQgb2YgY29tbWVudABpbnZhbGlkIHN3aXRjaCBzdGF0ZW1lbnQAQmlnSW50AHBhcnNlSW50AGR1cGxpY2F0ZSBkZWZhdWx0AHNwbGl0AGV4cGVjdGluZyBoZXggZGlnaXQAdHJpbVJpZ2h0AHJlZHVjZVJpZ2h0AHVuc2hpZnQAdHJpbUxlZnQAaW52YWxpZCBvZmZzZXQAaW52YWxpZCBieXRlT2Zmc2V0AGdldFRpbWV6b25lT2Zmc2V0AHJlc29sdmluZyBmdW5jdGlvbiBhbHJlYWR5IHNldABwcm94eTogaW5jb25zaXN0ZW50IHNldABmaW5kX2p1bXBfdGFyZ2V0AGV4cGVjdGluZyB0YXJnZXQAaW52YWxpZCBkZXN0cnVjdHVyaW5nIHRhcmdldABwcm94eTogaW5jb25zaXN0ZW50IGdldABXZWFrU2V0AGNvbnN0cnVjdABKU19GcmVlQXRvbVN0cnVjdAB1c2Ugc3RyaWN0AFJlZmxlY3QAcmVqZWN0AG5vdCBhbiBBc3luY0dlbmVyYXRvciBvYmplY3QAY2Fubm90IGNvbnZlcnQgdG8gb2JqZWN0AGludmFsaWQgYnJhbmQgb24gb2JqZWN0AG9wZXJhbmQgJ3Byb3RvdHlwZScgcHJvcGVydHkgaXMgbm90IGFuIG9iamVjdABpdGVyYXRvciBtdXN0IHJldHVybiBhbiBvYmplY3QAbm90IGEgRGF0ZSBvYmplY3QAbm90IGEgb2JqZWN0AEpTT2JqZWN0AHBhcnNlRmxvYXQAZmxhdABub3RoaW5nIHRvIHJlcGVhdABjb25jYXQAY29kZVBvaW50QXQAY2hhckF0AGNoYXJDb2RlQXQAa2V5cwBwcm94eTogdGFyZ2V0IHByb3BlcnR5IG11c3QgYmUgcHJlc2VudCBpbiBwcm94eSBvd25LZXlzACAgZmFzdCBhcnJheXMAZXhwb3J0ICclcycgaW4gbW9kdWxlICclcycgaXMgYW1iaWd1b3VzAHByaXZhdGUgY2xhc3MgZmllbGQgJyVzJyBhbHJlYWR5IGV4aXN0cwB0b28gbWFueSBhcmd1bWVudHMAVG9vIG1hbnkgY2FsbCBhcmd1bWVudHMAdG9vIG1hbnkgZWxlbWVudHMAICBlbGVtZW50cwBpbnZhbGlkIG51bWJlciBvZiBkaWdpdHMAYmluYXJ5IG9iamVjdHMAaW52YWxpZCBwcm9wZXJ0eSBhY2Nlc3MAanNfb3BfZGVmaW5lX2NsYXNzAGZkLT5ieXRlX2NvZGUuYnVmW2RlZmluZV9jbGFzc19wb3NdID09IE9QX2RlZmluZV9jbGFzcwBfX2dldENsYXNzAHNldEhvdXJzAGdldEhvdXJzAHNldFVUQ0hvdXJzAGdldFVUQ0hvdXJzAGdhdGhlcl9hdmFpbGFibGVfYW5jZXN0b3JzAGdldE93blByb3BlcnR5RGVzY3JpcHRvcnMAd2l0aFJlc29sdmVycwB0b28gbWFueSBpbWJyaWNhdGVkIHF1YW50aWZpZXJzAHVuaWNvZGVfcHJvcF9vcHMAYWNvcwBmb3IgYXdhaXQgaXMgb25seSB2YWxpZCBpbiBhc3luY2hyb25vdXMgZnVuY3Rpb25zAG5ldy50YXJnZXQgb25seSBhbGxvd2VkIHdpdGhpbiBmdW5jdGlvbnMAYnl0ZWNvZGUgZnVuY3Rpb25zAEMgZnVuY3Rpb25zAHByb3h5OiBpbmNvbnNpc3RlbnQgcHJldmVudEV4dGVuc2lvbnMAU2NyaXB0X0V4dGVuc2lvbnMAYXRvbXMAcHJveHk6IHByb3BlcnRpZXMgbXVzdCBiZSBzdHJpbmdzIG9yIHN5bWJvbHMAZ2V0T3duUHJvcGVydHlTeW1ib2xzAHJlc29sdmVfbGFiZWxzAEpTX0V2YWxUaGlzAHN0cmluZ3MAaW52YWxpZCBkZXNjcmlwdG9yIGZsYWdzAGludmFsaWQgcmVndWxhciBleHByZXNzaW9uIGZsYWdzAHZhbHVlcwBzZXRNaW51dGVzAGdldE1pbnV0ZXMAc2V0VVRDTWludXRlcwBnZXRVVENNaW51dGVzAHRvbyBtYW55IGNhcHR1cmVzACAgc2hhcGVzAGdldE93blByb3BlcnR5TmFtZXMAZ2NfZnJlZV9jeWNsZXMAYWRkX2V2YWxfdmFyaWFibGVzAHJlc29sdmVfdmFyaWFibGVzAHRvbyBtYW55IGxvY2FsIHZhcmlhYmxlcwB0b28gbWFueSBjbG9zdXJlIHZhcmlhYmxlcwBjb21wYWN0X3Byb3BlcnRpZXMAICBwcm9wZXJ0aWVzAGRlZmluZVByb3BlcnRpZXMAZW50cmllcwBmcm9tRW50cmllcwB0b28gbWFueSByYW5nZXMAaW5jbHVkZXMAaGFzSW5kaWNlcwBzZXRNaWxsaXNlY29uZHMAZ2V0TWlsbGlzZWNvbmRzAHNldFVUQ01pbGxpc2Vjb25kcwBnZXRVVENNaWxsaXNlY29uZHMAc2V0U2Vjb25kcwBnZXRTZWNvbmRzAHNldFVUQ1NlY29uZHMAZ2V0VVRDU2Vjb25kcwBpdGFsaWNzAGFicwBwcm94eTogaW5jb25zaXN0ZW50IGhhcwAlLipzACAoJXMAc2V0ICVzAGdldCAlcwAgICAgYXQgJXMAbm90IGEgJXMAdW5zdXBwb3J0ZWQga2V5d29yZDogJXMAc3Vic3RyAHByb3h5OiBpbmNvbnNpc3RlbnQgZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yAHN1cGVyKCkgaXMgb25seSB2YWxpZCBpbiBhIGRlcml2ZWQgY2xhc3MgY29uc3RydWN0b3IAcGFyZW50IGNsYXNzIG11c3QgYmUgY29uc3RydWN0b3IAbm90IGEgY29uc3RydWN0b3IAQXJyYXkgSXRlcmF0b3IAU2V0IEl0ZXJhdG9yAE1hcCBJdGVyYXRvcgBSZWdFeHAgU3RyaW5nIEl0ZXJhdG9yAG5vdCBhbiBBc3luYy1mcm9tLVN5bmMgSXRlcmF0b3IAY2Fubm90IGludm9rZSBhIHJ1bm5pbmcgZ2VuZXJhdG9yAG5vdCBhIGdlbmVyYXRvcgBBc3luY0dlbmVyYXRvcgBzeW50YXggZXJyb3IAU3ludGF4RXJyb3IARXZhbEVycm9yAEludGVybmFsRXJyb3IAQWdncmVnYXRlRXJyb3IAVHlwZUVycm9yAFJhbmdlRXJyb3IAUmVmZXJlbmNlRXJyb3IAVVJJRXJyb3IAZmxvb3IAZm9udGNvbG9yAGFuY2hvcgBmb3IAa2V5Rm9yAGV4cGVjdGluZyBzdXJyb2dhdGUgcGFpcgBhIGRlY2xhcmF0aW9uIGluIHRoZSBoZWFkIG9mIGEgZm9yLSVzIGxvb3AgY2FuJ3QgaGF2ZSBhbiBpbml0aWFsaXplcgAnYXJndW1lbnRzJyBpZGVudGlmaWVyIGlzIG5vdCBhbGxvd2VkIGluIGNsYXNzIGZpZWxkIGluaXRpYWxpemVyAGludmFsaWQgbnVtYmVyIG9mIGFyZ3VtZW50cyBmb3IgZ2V0dGVyIG9yIHNldHRlcgBpbnZhbGlkIHNldHRlcgBpbnZhbGlkIGdldHRlcgBmaWx0ZXIAbWlzc2luZyBmb3JtYWwgcGFyYW1ldGVyACJ1c2Ugc3RyaWN0IiBub3QgYWxsb3dlZCBpbiBmdW5jdGlvbiB3aXRoIGRlZmF1bHQgb3IgZGVzdHJ1Y3R1cmluZyBwYXJhbWV0ZXIAaW52YWxpZCBjaGFyYWN0ZXIAdW5leHBlY3RlZCBjaGFyYWN0ZXIAcHJpdmF0ZSBjbGFzcyBmaWVsZCBmb3JiaWRkZW4gYWZ0ZXIgc3VwZXIAaW52YWxpZCByZWRlZmluaXRpb24gb2YgbGV4aWNhbCBpZGVudGlmaWVyACdsZXQnIGlzIG5vdCBhIHZhbGlkIGxleGljYWwgaWRlbnRpZmllcgBpbnZhbGlkIHJlZGVmaW5pdGlvbiBvZiBnbG9iYWwgaWRlbnRpZmllcgB5aWVsZCBpcyBhIHJlc2VydmVkIGlkZW50aWZpZXIAJyVzJyBpcyBhIHJlc2VydmVkIGlkZW50aWZpZXIAb3RoZXIAYXRvbTFfaXNfaW50ZWdlciAmJiBhdG9tMl9pc19pbnRlZ2VyAGNhbm5vdCBjb252ZXJ0IHRvIGJpZ2ludDogbm90IGFuIGludGVnZXIAaXNJbnRlZ2VyAGlzU2FmZUludGVnZXIAYnVmZmVyAFNoYXJlZEFycmF5QnVmZmVyAGNhbm5vdCB1c2UgaWRlbnRpY2FsIEFycmF5QnVmZmVyAGNhbm5vdCBjb252ZXJ0IGJpZ2ludCB0byBudW1iZXIAY2Fubm90IGNvbnZlcnQgc3ltYm9sIHRvIG51bWJlcgBub3QgYSBudW1iZXIAbGluZU51bWJlcgBtYWxmb3JtZWQgdW5pY29kZSBjaGFyAGNsZWFyAHNldFllYXIAZ2V0WWVhcgBzZXRGdWxsWWVhcgBnZXRGdWxsWWVhcgBzZXRVVENGdWxsWWVhcgBnZXRVVENGdWxsWWVhcgBxICE9IHIAdW5leHBlY3RlZCBsaW5lIHRlcm1pbmF0b3IgaW4gcmVnZXhwAHVuZXhwZWN0ZWQgZW5kIG9mIHJlZ2V4cABSZWdFeHAAc3VwAGludmFsaWQgZ3JvdXAAcG9wAGNvbnRpbnVlIG11c3QgYmUgaW5zaWRlIGxvb3AAYmZfbG9naWNfb3AAZHVtcABudW1fa2V5c19jbXAAdXNlIHN0cmlwAG1hcABmbGF0TWFwAFdlYWtNYXAAZXhwZWN0aW5nICd7JyBhZnRlciBccABsb2cxcABkaXZpc2lvbiBieSB6ZXJvADBvAGhhc093bgByZXR1cm4AcHJvbWlzZSBzZWxmIHJlc29sdXRpb24Ab3V0IG9mIG1lbW9yeSBpbiByZWdleHAgZXhlY3V0aW9uAGRlc2NyaXB0aW9uACFtLT5ldmFsX2hhc19leGNlcHRpb24AIW1vZHVsZS0+ZXZhbF9oYXNfZXhjZXB0aW9uAHByb3h5OiBkZWZpbmVQcm9wZXJ0eSBleGNlcHRpb24AanNfYXN5bmNfZ2VuZXJhdG9yX3Jlc29sdmVfZnVuY3Rpb24AanNfY3JlYXRlX2Z1bmN0aW9uAHNldC9hZGQgaXMgbm90IGEgZnVuY3Rpb24AcmV0dXJuIG5vdCBpbiBhIGZ1bmN0aW9uAEFzeW5jR2VuZXJhdG9yRnVuY3Rpb24AY2FsbEV4dGVybmFsRnVuY3Rpb24AQXN5bmNGdW5jdGlvbgBqc19pbm5lcl9tb2R1bGVfZXZhbHVhdGlvbgAhbS0+YXN5bmNfZXZhbHVhdGlvbgBtb2R1bGUtPmFzeW5jX2V2YWx1YXRpb24AaW52YWxpZCBvcGVyYXRpb24AdW5zdXBwb3J0ZWQgb3BlcmF0aW9uAGF3YWl0IGluIGRlZmF1bHQgZXhwcmVzc2lvbgB5aWVsZCBpbiBkZWZhdWx0IGV4cHJlc3Npb24AaW52YWxpZCBkZWNpbWFsIGVzY2FwZSBpbiByZWd1bGFyIGV4cHJlc3Npb24AYmFjayByZWZlcmVuY2Ugb3V0IG9mIHJhbmdlIGluIHJlZ3VsYXIgZXhwcmVzc2lvbgBpbnZhbGlkIGVzY2FwZSBzZXF1ZW5jZSBpbiByZWd1bGFyIGV4cHJlc3Npb24AZXhwZWN0ZWQgJ29mJyBvciAnaW4nIGluIGZvciBjb250cm9sIGV4cHJlc3Npb24AdG9vIGNvbXBsaWNhdGVkIGRlc3RydWN0dXJpbmcgZXhwcmVzc2lvbgBleHBlY3RlZCAnfScgYWZ0ZXIgdGVtcGxhdGUgZXhwcmVzc2lvbgB0b1ByZWNpc2lvbgBhc2luAGpvaW4AbWluAGNvcHlXaXRoaW4AdGVtcGxhdGUgbGl0ZXJhbCBjYW5ub3QgYXBwZWFyIGluIGFuIG9wdGlvbmFsIGNoYWluAGNpcmN1bGFyIHByb3RvdHlwZSBjaGFpbgBhc3NpZ24AIXktPnNpZ24AaXNGcm96ZW4AKHBvcyArIGxlbikgPD0gYmNfYnVmX2xlbgB1bmV4cGVjdGVkIGVsbGlwc2lzIHRva2VuAHRoZW4Ac2V0dGVyIGlzIGZvcmJpZGRlbgBudWxsIG9yIHVuZGVmaW5lZCBhcmUgZm9yYmlkZGVuAGF0YW4AbmFuAG5vdCBhIGJvb2xlYW4AQm9vbGVhbgBnY19zY2FuAGJhZCBub3JtYWxpemF0aW9uIGZvcm0ASlNfTmV3U3ltYm9sRnJvbUF0b20AZnJvbQByYW5kb20AdHJpbQBiZl9kaXZyZW0AbS0+Y3ljbGVfcm9vdCA9PSBtAGltdWwAbm90IGEgc3ltYm9sAFN5bWJvbABSZWdFeHAgZXhlYyBtZXRob2QgbXVzdCByZXR1cm4gYW4gb2JqZWN0IG9yIG51bGwAcGFyZW50IHByb3RvdHlwZSBtdXN0IGJlIGFuIG9iamVjdCBvciBudWxsAGNhbm5vdCBzZXQgcHJvcGVydHkgJyVzJyBvZiBudWxsAGNhbm5vdCByZWFkIHByb3BlcnR5ICclcycgb2YgbnVsbABOdWxsAGZpbGwAbmV3IEFycmF5QnVmZmVyIGlzIHRvbyBzbWFsbABUeXBlZEFycmF5IGxlbmd0aCBpcyB0b28gc21hbGwAY2FsbABkb3RBbGwAbWF0Y2hBbGwAcmVwbGFjZUFsbABjZWlsAHVwZGF0ZV9sYWJlbABiY19idWZbcG9zXSA9PSBPUF9sYWJlbABldmFsAGludmFsaWQgYmlnaW50IGxpdGVyYWwAaW52YWxpZCBudW1iZXIgbGl0ZXJhbABtYWxmb3JtZWQgZXNjYXBlIHNlcXVlbmNlIGluIHN0cmluZyBsaXRlcmFsAGJmX2V4cF9pbnRlcm5hbABiZl9sb2dfaW50ZXJuYWwAYmZfZnRvYV9pbnRlcm5hbABKU19TZXRQcm9wZXJ0eUludGVybmFsAEpTX0dldE93blByb3BlcnR5TmFtZXNJbnRlcm5hbABfX0pTX0V2YWxJbnRlcm5hbAB0b0V4cG9uZW50aWFsAHNlYWwAZ2xvYmFsAGJsaW5rAHJldHVybiBpbiBhIHN0YXRpYyBpbml0aWFsaXplciBibG9jawBzdGFjawBscmVfZXhlY19iYWNrdHJhY2sAcy0+aXNfd2VhawBiZl9wb3dfdWkAc2V0TW9udGgAZ2V0TW9udGgAc2V0VVRDTW9udGgAZ2V0VVRDTW9udGgAaW52YWxpZCBrZXl3b3JkOiB3aXRoAHN0YXJ0c1dpdGgAZW5kc1dpdGgAcHJvcCA9PSBKU19BVE9NX2xlbmd0aABpbnZhbGlkIGFycmF5IGxlbmd0aABpbnZhbGlkIGFycmF5IGJ1ZmZlciBsZW5ndGgAaW52YWxpZCBsZW5ndGgAaW52YWxpZCBieXRlTGVuZ3RoAE1hdGgAcHVzaABhY29zaABKU19SZXNpemVBdG9tSGFzaABhc2luaABhdGFuaABicmVhayBtdXN0IGJlIGluc2lkZSBsb29wIG9yIHN3aXRjaABtYXRjaABuaXBfY2F0Y2gAc2VhcmNoAGZvckVhY2gAYmZfbG9nAEFycmF5IHRvbyBsb25nAHN0cmluZyB0b28gbG9uZwBBcnJheSBsb28gbG9uZwBzdWJzdHJpbmcAY2Fubm90IGNvbnZlcnQgc3ltYm9sIHRvIHN0cmluZwB1bmV4cGVjdGVkIGVuZCBvZiBzdHJpbmcAbm90IGEgc3RyaW5nAGludmFsaWQgY2hhcmFjdGVyIGluIGEgSlNPTiBzdHJpbmcAdG9TdHJpbmcAdG9EYXRlU3RyaW5nAHRvTG9jYWxlRGF0ZVN0cmluZwB0b1RpbWVTdHJpbmcAdG9Mb2NhbGVUaW1lU3RyaW5nAHRvTG9jYWxlU3RyaW5nAHRvR01UU3RyaW5nAEpTU3RyaW5nAHRvSVNPU3RyaW5nAHRvVVRDU3RyaW5nAGpzX2lubmVyX21vZHVsZV9saW5raW5nAGR1cGxpY2F0ZSBpbXBvcnQgYmluZGluZwBpbnZhbGlkIGltcG9ydCBiaW5kaW5nAHByb21pc2UgaXMgcGVuZGluZwBiaWcAcmVnZXhwIG11c3QgaGF2ZSB0aGUgJ2cnIGZsYWcAb2YAaW5mAEluZgBkaWZmID09IChpbnQ4X3QpZGlmZgBkaWZmID09IChpbnQxNl90KWRpZmYAaHJlZgBnY19kZWNyZWYAZnJlZV92YXJfcmVmAG9wdGltaXplX3Njb3BlX21ha2VfZ2xvYmFsX3JlZgByZXNldF93ZWFrX3JlZgBkZWxldGVfd2Vha19yZWYAb3B0aW1pemVfc2NvcGVfbWFrZV9yZWYAaW5kZXhPZgBsYXN0SW5kZXhPZgB2YWx1ZU9mAHNldFByb3RvdHlwZU9mAGdldFByb3RvdHlwZU9mAGlzUHJvdG90eXBlT2YAJS4qZgBmb250c2l6ZQBuZXdfc2l6ZSA8PSBzaC0+cHJvcF9zaXplAGRlc2NyIDwgcnQtPmF0b21fc2l6ZQBhdG9tIDwgcnQtPmF0b21fc2l6ZQBjb21wdXRlX3N0YWNrX3NpemUAbiA8IGJ1Zl9zaXplAG5vcm1hbGl6ZQBjcl9yZWdleHBfY2Fub25pY2FsaXplAGZyZWV6ZQByZXNvbHZlAHRvUHJpbWl0aXZlAHB1dF9sdmFsdWUAdW5rbm93biB1bmljb2RlIHByb3BlcnR5IHZhbHVlAHJlc3QgZWxlbWVudCBjYW5ub3QgaGF2ZSBhIGRlZmF1bHQgdmFsdWUAaW52YWxpZCByZXQgdmFsdWUAX19KU19BdG9tVG9WYWx1ZQBfX3F1b3RlAGlzRmluaXRlAGRlbGV0ZQBjcmVhdGUAc2V0RGF0ZQBnZXREYXRlAHNldFVUQ0RhdGUAZ2V0VVRDRGF0ZQBJbnZhbGlkIERhdGUAcmV2ZXJzZQBwYXJzZQBwcm94eSBwcmV2ZW50RXh0ZW5zaW9ucyBoYW5kbGVyIHJldHVybmVkIGZhbHNlAG1vZHVsZSBuYW1lc3BhY2UgcHJvcGVydGllcyBoYXZlIHdyaXRhYmxlID0gZmFsc2UAUHJvbWlzZQB0b0xvd2VyQ2FzZQB0b0xvY2FsZUxvd2VyQ2FzZQB0b1VwcGVyQ2FzZQB0b0xvY2FsZVVwcGVyQ2FzZQBpZ25vcmVDYXNlAGxvY2FsZUNvbXBhcmUAcHJveHk6IGluY29uc2lzdGVudCBwcm90b3R5cGUAcHJveHk6IGJhZCBwcm90b3R5cGUAbm90IGEgcHJvdG90eXBlAGludmFsaWQgb2JqZWN0IHR5cGUAdW5lc2NhcGUAbm9uZQByZXN0IGVsZW1lbnQgbXVzdCBiZSB0aGUgbGFzdCBvbmUAbXVsdGlsaW5lACAgcGMybGluZQBhc3luY19mdW5jX3Jlc3VtZQBzb21lAEpTX0ZyZWVSdW50aW1lAEpTUnVudGltZQBzZXRUaW1lAGdldFRpbWUAYXN5bmNfZnVuY19mcmVlX2ZyYW1lAHNldF9vYmplY3RfbmFtZQBleHBlY3RpbmcgcHJvcGVydHkgbmFtZQB1bmtub3duIHVuaWNvZGUgcHJvcGVydHkgbmFtZQBpbnZhbGlkIHByb3BlcnR5IG5hbWUAZHVwbGljYXRlIF9fcHJvdG9fXyBwcm9wZXJ0eSBuYW1lAGludmFsaWQgcmVkZWZpbml0aW9uIG9mIHBhcmFtZXRlciBuYW1lAGV4cGVjdGluZyBncm91cCBuYW1lAGR1cGxpY2F0ZSBncm91cCBuYW1lAGludmFsaWQgZ3JvdXAgbmFtZQBkdXBsaWNhdGUgbGFiZWwgbmFtZQBpbnZhbGlkIGZpcnN0IGNoYXJhY3RlciBvZiBwcml2YXRlIG5hbWUAaW52YWxpZCBsZXhpY2FsIHZhcmlhYmxlIG5hbWUAaW52YWxpZCBtZXRob2QgbmFtZQBleHBlY3RpbmcgZmllbGQgbmFtZQBpbnZhbGlkIGZpZWxkIG5hbWUAY2xhc3Mgc3RhdGVtZW50IHJlcXVpcmVzIGEgbmFtZQBmaWxlTmFtZQBqc19saW5rX21vZHVsZQBqc19ldmFsdWF0ZV9tb2R1bGUAbW9kdWxlLT5jeWNsZV9yb290ID09IG1vZHVsZQBjb21waWxlAG9iamVjdCBpcyBub3QgZXh0ZW5zaWJsZQBwcm94eTogaW5jb25zaXN0ZW50IGlzRXh0ZW5zaWJsZQBjYW5ub3QgaGF2ZSBzZXR0ZXIvZ2V0dGVyIGFuZCB2YWx1ZSBvciB3cml0YWJsZQBwcm9wZXJ0eSBpcyBub3QgY29uZmlndXJhYmxlAHZhbHVlIGlzIG5vdCBpdGVyYWJsZQBwcm9wZXJ0eUlzRW51bWVyYWJsZQBtaXNzaW5nIGluaXRpYWxpemVyIGZvciBjb25zdCB2YXJpYWJsZQBsZXhpY2FsIHZhcmlhYmxlAGludmFsaWQgcmVkZWZpbml0aW9uIG9mIGEgdmFyaWFibGUAcmV2b2NhYmxlAHN0cmlrZQBtcF9kaXZub3JtX2xhcmdlAGludmFsaWQgY2xhc3MgcmFuZ2UAbWVzc2FnZQBpbnZhbGlkIGx2YWx1ZSBpbiBzdHJpY3QgbW9kZQBpbnZhbGlkIHZhcmlhYmxlIG5hbWUgaW4gc3RyaWN0IG1vZGUAY2Fubm90IGRlbGV0ZSBhIGRpcmVjdCByZWZlcmVuY2UgaW4gc3RyaWN0IG1vZGUAb2N0YWwgZXNjYXBlIHNlcXVlbmNlcyBhcmUgbm90IGFsbG93ZWQgaW4gc3RyaWN0IG1vZGUAb2N0YWwgbGl0ZXJhbHMgYXJlIGRlcHJlY2F0ZWQgaW4gc3RyaWN0IG1vZGUAdW5pY29kZQAgIGJ5dGVjb2RlAEpTRnVuY3Rpb25CeXRlY29kZQBza2lwX2RlYWRfY29kZQBpbnZhbGlkIGFyZ3VtZW50IG5hbWUgaW4gc3RyaWN0IGNvZGUAaW52YWxpZCBmdW5jdGlvbiBuYW1lIGluIHN0cmljdCBjb2RlAGludmFsaWQgcmVkZWZpbml0aW9uIG9mIGdsb2JhbCBpZGVudGlmaWVyIGluIG1vZHVsZSBjb2RlAGltcG9ydC5tZXRhIG9ubHkgdmFsaWQgaW4gbW9kdWxlIGNvZGUAZnJvbUNoYXJDb2RlAGludmFsaWQgZm9yIGluL29mIGxlZnQgaGFuZC1zaWRlAGludmFsaWQgYXNzaWdubWVudCBsZWZ0LWhhbmQgc2lkZQByZWR1Y2UAc291cmNlACd0aGlzJyBjYW4gYmUgaW5pdGlhbGl6ZWQgb25seSBvbmNlAHByb3BlcnR5IGNvbnN0cnVjdG9yIGFwcGVhcnMgbW9yZSB0aGFuIG9uY2UAaW52YWxpZCBVVEYtOCBzZXF1ZW5jZQBjaXJjdWxhciByZWZlcmVuY2UAc2xpY2UAc3BsaWNlAHJhY2UAcmVwbGFjZQAlKy4qZQB1bmV4cGVjdGVkICdhd2FpdCcga2V5d29yZAB1bmV4cGVjdGVkICd5aWVsZCcga2V5d29yZABtYXBfZGVjcmVmX3JlY29yZABpdGVyYXRvciBkb2VzIG5vdCBoYXZlIGEgdGhyb3cgbWV0aG9kAG9iamVjdCBuZWVkcyB0b0lTT1N0cmluZyBtZXRob2QAJ3N1cGVyJyBpcyBvbmx5IHZhbGlkIGluIGEgbWV0aG9kAGZyb3VuZABfX2JmX3JvdW5kAGJyZWFrL2NvbnRpbnVlIGxhYmVsIG5vdCBmb3VuZABvdXQgb2YgYm91bmQAZmluZABiaW5kAGludmFsaWQgaW5kZXggZm9yIGFwcGVuZABleHRyYW5lb3VzIGNoYXJhY3RlcnMgYXQgdGhlIGVuZAB1bmV4cGVjdGVkIGRhdGEgYXQgdGhlIGVuZAB1bmV4cGVjdGVkIGVuZABpbnZhbGlkIGluY3JlbWVudC9kZWNyZW1lbnQgb3BlcmFuZABpbnZhbGlkICdpbnN0YW5jZW9mJyByaWdodCBvcGVyYW5kAGludmFsaWQgJ2luJyBvcGVyYW5kAHRyaW1FbmQAcGFkRW5kAGJvbGQAJWxsZABnY19kZWNyZWZfY2hpbGQAcmVzb2x2ZV9zY29wZV9wcml2YXRlX2ZpZWxkAGNhbm5vdCBkZWxldGUgYSBwcml2YXRlIGNsYXNzIGZpZWxkAGV4cGVjdGluZyA8YnJhbmQ+IHByaXZhdGUgZmllbGQAJXMgaXMgbm90IGluaXRpYWxpemVkAGZpeGVkAHRvRml4ZWQAc2V0X29iamVjdF9uYW1lX2NvbXB1dGVkAHJlZ2V4IG5vdCBzdXBwb3J0ZWQAZXZhbCBpcyBub3Qgc3VwcG9ydGVkAFJlZ0V4cCBhcmUgbm90IHN1cHBvcnRlZAB0b1NvcnRlZABpbnRlcnJ1cHRlZAAhcy0+aXNfY29tcGxldGVkACVzIG9iamVjdCBleHBlY3RlZABpZGVudGlmaWVyIGV4cGVjdGVkAGJ5dGVjb2RlIGZ1bmN0aW9uIGV4cGVjdGVkAHN0cmluZyBleHBlY3RlZABmcm9tIGNsYXVzZSBleHBlY3RlZABmdW5jdGlvbiBuYW1lIGV4cGVjdGVkAHZhcmlhYmxlIG5hbWUgZXhwZWN0ZWQAbWV0YSBleHBlY3RlZABqc19hc3luY19tb2R1bGVfZXhlY3V0aW9uX3JlamVjdGVkAGpzX3NldF9tb2R1bGVfZXZhbHVhdGVkAG1lbW9yeSBhbGxvY2F0ZWQAbWVtb3J5IHVzZWQAdG9SZXZlcnNlZABkZXJpdmVkIGNsYXNzIGNvbnN0cnVjdG9yIG11c3QgcmV0dXJuIGFuIG9iamVjdCBvciB1bmRlZmluZWQAY2Fubm90IHNldCBwcm9wZXJ0eSAnJXMnIG9mIHVuZGVmaW5lZABjYW5ub3QgcmVhZCBwcm9wZXJ0eSAnJXMnIG9mIHVuZGVmaW5lZABmbGFncyBtdXN0IGJlIHVuZGVmaW5lZABVbmRlZmluZWQAcHJpdmF0ZSBjbGFzcyBmaWVsZCBpcyBhbHJlYWR5IGRlZmluZWQAJyVzJyBpcyBub3QgZGVmaW5lZABncm91cCBuYW1lIG5vdCBkZWZpbmVkAGlzV2VsbEZvcm1lZAB0b1dlbGxGb3JtZWQAYWxsU2V0dGxlZABqc19hc3luY19tb2R1bGVfZXhlY3V0aW9uX2Z1bGZpbGxlZABjYW5ub3QgYmUgY2FsbGVkAGlzU2VhbGVkACFzaC0+aXNfaGFzaGVkAEFycmF5QnVmZmVyIGlzIGRldGFjaGVkAGpzX2FycmF5X3RvU3BsaWNlZABhZGQAJSswN2QAJTA0ZAAlMDJkJTAyZABwJStkACVjJStkACUwMmQvJTAyZC8lMCpkACUuM3MgJS4zcyAlMDJkICUwKmQAcCVkACVjJWQAOiVkAGludmFsaWQgdGhyb3cgdmFyIHR5cGUgJWQAc2MAanNfZGVmX21hbGxvYwB0cnVuYwBnYwBleGVjAGJmX2ludGVnZXJfdG9fcmFkaXhfcmVjAC90bXAvcXVpY2tqcy9xdWlja2pzLmMAL3RtcC9xdWlja2pzL2xpYnJlZ2V4cC5jAC90bXAvcXVpY2tqcy9saWJiZi5jAC90bXAvcXVpY2tqcy9saWJ1bmljb2RlLmMAc3ViAHByb21pc2VfcmVhY3Rpb25fam9iAGpzX3Byb21pc2VfcmVzb2x2ZV90aGVuYWJsZV9qb2IAMGIAciAhPSBhICYmIHIgIT0gYgBxICE9IGEgJiYgcSAhPSBiAHJ3YQByICE9IGEAX19sb29rdXBTZXR0ZXJfXwBfX2RlZmluZVNldHRlcl9fAF9fbG9va3VwR2V0dGVyX18AX19kZWZpbmVHZXR0ZXJfXwBfX3Byb3RvX18AW1N5bWJvbC5zcGxpdF0AW1N5bWJvbC5zcGVjaWVzXQBbU3ltYm9sLml0ZXJhdG9yXQBbU3ltYm9sLmFzeW5jSXRlcmF0b3JdAFtTeW1ib2wubWF0Y2hBbGxdAFtTeW1ib2wubWF0Y2hdAFtTeW1ib2wuc2VhcmNoXQBbU3ltYm9sLnRvU3RyaW5nVGFnXQBbU3ltYm9sLnRvUHJpbWl0aXZlXQBbdW5zdXBwb3J0ZWQgdHlwZV0AW2Z1bmN0aW9uIGJ5dGVjb2RlXQBbU3ltYm9sLmhhc0luc3RhbmNlXQBbU3ltYm9sLnJlcGxhY2VdAFsAJTAyZDolMDJkOiUwMmQuJTAzZFoAUE9TSVRJVkVfSU5GSU5JVFkATkVHQVRJVkVfSU5GSU5JVFkAcC0+Y2xhc3NfaWQgPT0gSlNfQ0xBU1NfQVJSQVkAc3RhY2tfbGVuIDwgUE9QX1NUQUNLX0xFTl9NQVgALSUwMmQtJTAyZFQASlNfQXRvbUdldFN0clJUAG9wY29kZSA8IFJFT1BfQ09VTlQASlNfVkFMVUVfR0VUX1RBRyhmdW5jX3JldCkgPT0gSlNfVEFHX0lOVABCWVRFU19QRVJfRUxFTUVOVAAlMDJkOiUwMmQ6JTAyZCBHTVQASlNfVkFMVUVfR0VUX1RBRyhzZi0+Y3VyX2Z1bmMpID09IEpTX1RBR19PQkpFQ1QAdmFyX2tpbmQgPT0gSlNfVkFSX1BSSVZBVEVfU0VUVEVSAE1BWF9TQUZFX0lOVEVHRVIATUlOX1NBRkVfSU5URUdFUgBhc1VpbnROAGFzSW50TgBpc05hTgBEYXRlIHZhbHVlIGlzIE5hTgB0b0pTT04ARVBTSUxPTgBwLT5nY19vYmpfdHlwZSA9PSBKU19HQ19PQkpfVFlQRV9KU19PQkpFQ1QgfHwgcC0+Z2Nfb2JqX3R5cGUgPT0gSlNfR0NfT0JKX1RZUEVfRlVOQ1RJT05fQllURUNPREUgfHwgcC0+Z2Nfb2JqX3R5cGUgPT0gSlNfR0NfT0JKX1RZUEVfQVNZTkNfRlVOQ1RJT04ATkFOACUwMmQ6JTAyZDolMDJkICVjTQBzdGFja190b3AgPT0gTlVMTABzLT5sYWJlbF9zbG90c1tsYWJlbF0uZmlyc3RfcmVsb2MgPT0gTlVMTABsYWJlbF9zbG90c1tpXS5maXJzdF9yZWxvYyA9PSBOVUxMAHBycyAhPSBOVUxMAHNmLT5jdXJfc3AgIT0gTlVMTABzZiAhPSBOVUxMAG1yMSAhPSBOVUxMAHZhcl9raW5kICE9IEpTX1ZBUl9OT1JNQUwAYi0+ZnVuY19raW5kID09IEpTX0ZVTkNfTk9STUFMAGVuY29kZVVSSQBkZWNvZGVVUkkAUEkAc3BlY2lhbCA9PSBQVVRfTFZBTFVFX05PS0VFUCB8fCBzcGVjaWFsID09IFBVVF9MVkFMVUVfTk9LRUVQX0RFUFRIAHMtPnN0YXRlID09IEpTX0FTWU5DX0dFTkVSQVRPUl9TVEFURV9FWEVDVVRJTkcAbTEtPnN0YXR1cyA9PSBKU19NT0RVTEVfU1RBVFVTX0VWQUxVQVRJTkcAbTEtPnN0YXR1cyA9PSBKU19NT0RVTEVfU1RBVFVTX0xJTktJTkcAcHJlYyAhPSBCRl9QUkVDX0lORgBwcmVjMSAhPSBCRl9QUkVDX0lORgAwMTIzNDU2Nzg5QUJDREVGAFNJWkUATUFYX1ZBTFVFAE1JTl9WQUxVRQBOQU1FAGV2YWxfdHlwZSA9PSBKU19FVkFMX1RZUEVfR0xPQkFMIHx8IGV2YWxfdHlwZSA9PSBKU19FVkFMX1RZUEVfTU9EVUxFAExPRzJFAExPRzEwRQBzLT5zdGF0ZSA9PSBKU19BU1lOQ19HRU5FUkFUT1JfU1RBVEVfQVdBSVRJTkdfUkVUVVJOIHx8IHMtPnN0YXRlID09IEpTX0FTWU5DX0dFTkVSQVRPUl9TVEFURV9DT01QTEVURUQAbS0+c3RhdHVzID09IEpTX01PRFVMRV9TVEFUVVNfVU5MSU5LRUQgfHwgbS0+c3RhdHVzID09IEpTX01PRFVMRV9TVEFUVVNfTElOS0VEIHx8IG0tPnN0YXR1cyA9PSBKU19NT0RVTEVfU1RBVFVTX0VWQUxVQVRJTkdfQVNZTkMgfHwgbS0+c3RhdHVzID09IEpTX01PRFVMRV9TVEFUVVNfRVZBTFVBVEVEAG0xLT5zdGF0dXMgPT0gSlNfTU9EVUxFX1NUQVRVU19FVkFMVUFUSU5HIHx8IG0xLT5zdGF0dXMgPT0gSlNfTU9EVUxFX1NUQVRVU19FVkFMVUFUSU5HX0FTWU5DIHx8IG0xLT5zdGF0dXMgPT0gSlNfTU9EVUxFX1NUQVRVU19FVkFMVUFURUQAbTEtPnN0YXR1cyA9PSBKU19NT0RVTEVfU1RBVFVTX0xJTktJTkcgfHwgbTEtPnN0YXR1cyA9PSBKU19NT0RVTEVfU1RBVFVTX0xJTktFRCB8fCBtMS0+c3RhdHVzID09IEpTX01PRFVMRV9TVEFUVVNfRVZBTFVBVElOR19BU1lOQyB8fCBtMS0+c3RhdHVzID09IEpTX01PRFVMRV9TVEFUVVNfRVZBTFVBVEVEAG0tPnN0YXR1cyA9PSBKU19NT0RVTEVfU1RBVFVTX0xJTktFRABtLT5zdGF0dXMgPT0gSlNfTU9EVUxFX1NUQVRVU19VTkxJTktFRABVVEMAbS0+c3RhdHVzID09IEpTX01PRFVMRV9TVEFUVVNfRVZBTFVBVElOR19BU1lOQwBtb2R1bGUtPnN0YXR1cyA9PSBKU19NT0RVTEVfU1RBVFVTX0VWQUxVQVRJTkdfQVNZTkMAPGlucHV0PgA8aW5pdFNjcmlwdD4APGV2YWxTY3JpcHQ+ADxzZXQ+ADxhbm9ueW1vdXM+ADxjb21tRnVuPgA8Y2FsbEV4dGVybmFsRnVuY3Rpb24+ADxudWxsPgBiaWdpbnQgb3BlcmFuZHMgYXJlIGZvcmJpZGRlbiBmb3IgPj4+ACZxdW90OwBzZXRVaW50OABnZXRVaW50OABzZXRJbnQ4AGdldEludDgAbWFsZm9ybWVkIFVURi04AHJhZGl4IG11c3QgYmUgYmV0d2VlbiAyIGFuZCAzNgBzZXRVaW50MTYAZ2V0VWludDE2AHNldEludDE2AGdldEludDE2AGFyZ2MgPT0gNQBzZXRCaWdVaW50NjQAZ2V0QmlnVWludDY0AHNldEJpZ0ludDY0AGdldEJpZ0ludDY0AHNldEZsb2F0NjQAZ2V0RmxvYXQ2NABhcmdjID09IDMAYXRhbjIAbG9nMgBTUVJUMV8yAFNRUlQyAExOMgBjbHozMgBzZXRVaW50MzIAZ2V0VWludDMyAHNldEludDMyAGdldEludDMyAHNldEZsb2F0MzIAZ2V0RmxvYXQzMgBzdGFja19sZW4gPj0gMgBKU19BdG9tSXNOdW1lcmljSW5kZXgxAGpzX2ZjdnQxAEpTX0NvbXBhY3RCaWdJbnQxAGV4cG0xAHIgIT0gYTEgJiYgciAhPSBiMQBscy0+YWRkciA9PSAtMQBucSA+PSAxAHN0YWNrX2xlbiA+PSAxAHAtPmhlYWRlci5yZWZfY291bnQgPT0gMQBwLT5zaGFwZS0+aGVhZGVyLnJlZl9jb3VudCA9PSAxAHN0YWNrX2xlbiA9PSAxAGpzX2ZyZWVfc2hhcGUwAGxvZzEwAExOMTAAcC0+cmVmX2NvdW50ID4gMAB2YXJfcmVmLT5oZWFkZXIucmVmX2NvdW50ID4gMABtLT5wZW5kaW5nX2FzeW5jX2RlcGVuZGVuY2llcyA+IDAAc3RhY2tfc2l6ZSA+IDAAY3Bvb2xfaWR4ID49IDAAcnQtPmF0b21fY291bnQgPj0gMABscy0+cmVmX2NvdW50ID49IDAAcy0+aXNfZXZhbCB8fCBzLT5jbG9zdXJlX3Zhcl9jb3VudCA9PSAwAHAtPnJlZl9jb3VudCA9PSAwAGN0eC0+aGVhZGVyLnJlZl9jb3VudCA9PSAwAHNoLT5oZWFkZXIucmVmX2NvdW50ID09IDAAcC0+bWFyayA9PSAwAChwci0+dS5pbml0LnJlYWxtX2FuZF9pZCAmIDMpID09IDAAKG5ld19oYXNoX3NpemUgJiAobmV3X2hhc2hfc2l6ZSAtIDEpKSA9PSAwAGkgIT0gMABzaXplICE9IDAAXiRcLiorPygpW117fXwvADwvADAuAG1pc3NpbmcgYmluZGluZyBwYXR0ZXJuLi4uAGJpZ2ludCBhcmd1bWVudCB3aXRoIHVuYXJ5ICsAYXN5bmMgZnVuY3Rpb24gKgAKfSkAbGlzdF9lbXB0eSgmcnQtPmdjX29ial9saXN0KQBqID09IChzaC0+cHJvcF9jb3VudCAtIHNoLT5kZWxldGVkX3Byb3BfY291bnQpACFfX0pTX0F0b21Jc1RhZ2dlZEludChkZXNjcikAIWF0b21faXNfZnJlZShwKQAobnVsbCkAIChuYXRpdmUpAGpzX2NsYXNzX2hhc19ieXRlY29kZShwLT5jbGFzc19pZCkAbmlwX2NhdGNoOiBubyBjYXRjaCBvcCAocGM9JWQpAGluY29uc2lzdGVudCBjYXRjaCBwb3NpdGlvbjogJWQgJWQgKHBjPSVkKQBpbmNvbnNpc3RlbnQgc3RhY2sgc2l6ZTogJWQgJWQgKHBjPSVkKQBieXRlY29kZSBidWZmZXIgb3ZlcmZsb3cgKG9wPSVkLCBwYz0lZCkAc3RhY2sgb3ZlcmZsb3cgKG9wPSVkLCBwYz0lZCkAc3RhY2sgdW5kZXJmbG93IChvcD0lZCwgcGM9JWQpAGludmFsaWQgb3Bjb2RlIChvcD0lZCwgcGM9JWQpACg/OikAaWR4IDwgY291bnRvZihjYXNlX2NvbnZfdGFibGUxKQBubyBmdW5jdGlvbiBmaWxlbmFtZSBmb3IgaW1wb3J0KCkALV8uIX4qJygpACBhbm9ueW1vdXMoAFN5bWJvbCgAZXhwZWN0aW5nICd9JwBjbGFzcyBjb25zdHJ1Y3RvcnMgbXVzdCBiZSBpbnZva2VkIHdpdGggJ25ldycAZXhwZWN0aW5nICdhcycAdW5leHBlY3RlZCB0b2tlbiBpbiBleHByZXNzaW9uOiAnJS4qcycAdW5leHBlY3RlZCB0b2tlbjogJyUuKnMnAHJlZGVjbGFyYXRpb24gb2YgJyVzJwBkdXBsaWNhdGUgZXhwb3J0ZWQgbmFtZSAnJXMnAGNpcmN1bGFyIHJlZmVyZW5jZSB3aGVuIGxvb2tpbmcgZm9yIGV4cG9ydCAnJXMnIGluIG1vZHVsZSAnJXMnAENvdWxkIG5vdCBmaW5kIGV4cG9ydCAnJXMnIGluIG1vZHVsZSAnJXMnAGNvdWxkIG5vdCBsb2FkIG1vZHVsZSAnJXMnAGNhbm5vdCBkZWZpbmUgdmFyaWFibGUgJyVzJwB1bmRlZmluZWQgcHJpdmF0ZSBmaWVsZCAnJXMnAHVuc3VwcG9ydGVkIHJlZmVyZW5jZSB0byAnc3VwZXInAGludmFsaWQgdXNlIG9mICdzdXBlcicAJ2ZvciBhd2FpdCcgbG9vcCBzaG91bGQgYmUgdXNlZCB3aXRoICdvZicAJ2ZvciBvZicgZXhwcmVzc2lvbiBjYW5ub3Qgc3RhcnQgd2l0aCAnYXN5bmMnAGV4cGVjdGluZyAnJWMnAHVucGFyZW50aGVzaXplZCB1bmFyeSBleHByZXNzaW9uIGNhbid0IGFwcGVhciBvbiB0aGUgbGVmdC1oYW5kIHNpZGUgb2YgJyoqJwBpbnZhbGlkIHVzZSBvZiAnaW1wb3J0KCknAGV4cGVjdGluZyAlJQA7Lz86QCY9KyQsIwA9IgBzZXQgAGdldCAAW29iamVjdCAAYXN5bmMgZnVuY3Rpb24gAGJvdW5kIAAlLjNzLCAlMDJkICUuM3MgJTAqZCAAYXN5bmMgADogACAgICAgICAgICAACikgewoACkpTT2JqZWN0IGNsYXNzZXMKACUtMjBzICU4cyAlOHMKACAgJTVkICAlMi4wZCAlcwoAICAlM3UgKyAlLTJ1ICAlcwoAICBtYWxsb2NfdXNhYmxlX3NpemUgdW5hdmFpbGFibGUKACUtMjBzICU4bGxkCgAlLTIwcyAlOGxsZCAlOGxsZAoAX19KU19GcmVlVmFsdWU6IHVua25vd24gdGFnPSVkCgAlLTIwcyAlOGxsZCAlOGxsZCAgKCUwLjFmIHBlciBmYXN0IGFycmF5KQoAJS0yMHMgJThsbGQgJThsbGQgICglMC4xZiBwZXIgb2JqZWN0KQoAJS0yMHMgJThsbGQgJThsbGQgICglMC4xZiBwZXIgZnVuY3Rpb24pCgAlLTIwcyAlOGxsZCAlOGxsZCAgKCUwLjFmIHBlciBhdG9tKQoAJS0yMHMgJThsbGQgJThsbGQgICglMC4xZiBwZXIgYmxvY2spCgAlLTIwcyAlOGxsZCAlOGxsZCAgKCVkIG92ZXJoZWFkLCAlMC4xZiBhdmVyYWdlIHNsYWNrKQoAJS0yMHMgJThsbGQgJThsbGQgICglMC4xZiBwZXIgc3RyaW5nKQoAJS0yMHMgJThsbGQgJThsbGQgICglMC4xZiBwZXIgc2hhcGUpCgBRdWlja0pTIG1lbW9yeSB1c2FnZSAtLSAxLjAuMCB2ZXJzaW9uLCAlZC1iaXQsIG1hbGxvYyBsaW1pdDogJWxsZAoKAAAAAJIAQfyWAQsNkwAAAEwAAABNAAAAlABBlJcBCz2VAAAATgAAAE8AAACWAAAATgAAAE8AAACXAAAATgAAAE8AAACYAAAATgAAAE8AAACZAAAATAAAAE0AAACZAEHclwELDZwAAABOAAAATwAAAJIAQfSXAQv9Ap0AAABQAAAAUQAAAJ0AAABSAAAAUwAAAJ0AAABUAAAAVQAAAJ0AAABWAAAAVwAAAJ4AAABSAAAAUwAAAJ8AAABYAAAAWQAAAKAAAABaAAAAAAAAAKEAAABbAAAAAAAAAKIAAABbAAAAAAAAAKMAAABcAAAAXQAAAKQAAABcAAAAXQAAAKUAAABcAAAAXQAAAKYAAABcAAAAXQAAAKcAAABcAAAAXQAAAKgAAABcAAAAXQAAAKkAAABcAAAAXQAAAKoAAABcAAAAXQAAAKsAAABcAAAAXQAAAKwAAABcAAAAXQAAAK0AAABcAAAAXQAAAK4AAABcAAAAXQAAAK8AAABOAAAATwAAALAAAABeAAAAXwAAALEAAABeAAAAXwAAALIAAABeAAAAXwAAALMAAABeAAAAXwAAALQAAABgAAAAYQAAALUAAABgAAAAYQAAALYAAABiAAAAYwAAALcAAABiAAAAYwAAALgAAABkAAAAZQAAALkAAABmAAAAZwBBgJsBCwFoAEGQmwELDWkAAAAAAAAAagAAAGsAQbybAQsBbABByJsBCw1tAAAAbgAAAG8AAABwAEHgmwELtxvsKQAAQAEAACUKAAD4AAAAuA8AADAAAABaJQAAEAAAADkuAABYAAAAkgAAAHEAAAByAAAAcwAAAHQAAAB1AAAAdgAAAHcAAAB4AAAAeQAAAFBdAAAQXgAAwF4AABBfAABQXwAAcF8AAAwLBQQCAgAAuwAAAHoAAAB7AAAAvAAAAHwAAAB9AAAAvQAAAHwAAAB9AAAAvgAAAFIAAABTAAAAvwAAAH4AAAB/AAAAwAAAAH4AAAB/AAAALwAAAIAAAACBAAAAwQAAAFIAAABTAAAAwgAAAIIAAACDAAAAAAAAAOkWAAAaFwAAJRcAAN0WAAAQFwAANBcAAPMWAAABFwAAY29weVdpdGhpbgBlbnRyaWVzAGZpbGwAZmluZABmaW5kSW5kZXgAZmluZExhc3QAZmluZExhc3RJbmRleABmbGF0AGZsYXRNYXAAaW5jbHVkZXMAa2V5cwB0b1JldmVyc2VkAHRvU29ydGVkAHRvU3BsaWNlZAB2YWx1ZXMAAAAAAAEBAgIDAwIDAAAAAAAAbnVsbABmYWxzZQB0cnVlAGlmAGVsc2UAcmV0dXJuAHZhcgB0aGlzAGRlbGV0ZQB2b2lkAHR5cGVvZgBuZXcAaW4AaW5zdGFuY2VvZgBkbwB3aGlsZQBmb3IAYnJlYWsAY29udGludWUAc3dpdGNoAGNhc2UAZGVmYXVsdAB0aHJvdwB0cnkAY2F0Y2gAZmluYWxseQBmdW5jdGlvbgBkZWJ1Z2dlcgB3aXRoAGNsYXNzAGNvbnN0AGVudW0AZXhwb3J0AGV4dGVuZHMAaW1wb3J0AHN1cGVyAGltcGxlbWVudHMAaW50ZXJmYWNlAGxldABwYWNrYWdlAHByaXZhdGUAcHJvdGVjdGVkAHB1YmxpYwBzdGF0aWMAeWllbGQAYXdhaXQAAGxlbmd0aABmaWxlTmFtZQBsaW5lTnVtYmVyAG1lc3NhZ2UAY2F1c2UAZXJyb3JzAHN0YWNrAG5hbWUAdG9TdHJpbmcAdG9Mb2NhbGVTdHJpbmcAdmFsdWVPZgBldmFsAHByb3RvdHlwZQBjb25zdHJ1Y3RvcgBjb25maWd1cmFibGUAd3JpdGFibGUAZW51bWVyYWJsZQB2YWx1ZQBnZXQAc2V0AG9mAF9fcHJvdG9fXwB1bmRlZmluZWQAbnVtYmVyAGJvb2xlYW4Ac3RyaW5nAG9iamVjdABzeW1ib2wAaW50ZWdlcgB1bmtub3duAGFyZ3VtZW50cwBjYWxsZWUAY2FsbGVyADxldmFsPgA8cmV0PgA8dmFyPgA8YXJnX3Zhcj4APHdpdGg+AGxhc3RJbmRleAB0YXJnZXQAaW5kZXgAaW5wdXQAZGVmaW5lUHJvcGVydGllcwBhcHBseQBqb2luAGNvbmNhdABzcGxpdABjb25zdHJ1Y3QAZ2V0UHJvdG90eXBlT2YAc2V0UHJvdG90eXBlT2YAaXNFeHRlbnNpYmxlAHByZXZlbnRFeHRlbnNpb25zAGhhcwBkZWxldGVQcm9wZXJ0eQBkZWZpbmVQcm9wZXJ0eQBnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IAb3duS2V5cwBhZGQAZG9uZQBuZXh0AHZhbHVlcwBzb3VyY2UAZmxhZ3MAZ2xvYmFsAHVuaWNvZGUAcmF3AG5ldy50YXJnZXQAdGhpcy5hY3RpdmVfZnVuYwA8aG9tZV9vYmplY3Q+ADxjb21wdXRlZF9maWVsZD4APHN0YXRpY19jb21wdXRlZF9maWVsZD4APGNsYXNzX2ZpZWxkc19pbml0PgA8YnJhbmQ+ACNjb25zdHJ1Y3RvcgBhcwBmcm9tAG1ldGEAKmRlZmF1bHQqACoATW9kdWxlAHRoZW4AcmVzb2x2ZQByZWplY3QAcHJvbWlzZQBwcm94eQByZXZva2UAYXN5bmMAZXhlYwBncm91cHMAaW5kaWNlcwBzdGF0dXMAcmVhc29uAGdsb2JhbFRoaXMAYmlnaW50AG5vdC1lcXVhbAB0aW1lZC1vdXQAb2sAdG9KU09OAE9iamVjdABBcnJheQBFcnJvcgBOdW1iZXIAU3RyaW5nAEJvb2xlYW4AU3ltYm9sAEFyZ3VtZW50cwBNYXRoAEpTT04ARGF0ZQBGdW5jdGlvbgBHZW5lcmF0b3JGdW5jdGlvbgBGb3JJbkl0ZXJhdG9yAFJlZ0V4cABBcnJheUJ1ZmZlcgBTaGFyZWRBcnJheUJ1ZmZlcgBVaW50OENsYW1wZWRBcnJheQBJbnQ4QXJyYXkAVWludDhBcnJheQBJbnQxNkFycmF5AFVpbnQxNkFycmF5AEludDMyQXJyYXkAVWludDMyQXJyYXkAQmlnSW50NjRBcnJheQBCaWdVaW50NjRBcnJheQBGbG9hdDMyQXJyYXkARmxvYXQ2NEFycmF5AERhdGFWaWV3AEJpZ0ludABNYXAAU2V0AFdlYWtNYXAAV2Vha1NldABNYXAgSXRlcmF0b3IAU2V0IEl0ZXJhdG9yAEFycmF5IEl0ZXJhdG9yAFN0cmluZyBJdGVyYXRvcgBSZWdFeHAgU3RyaW5nIEl0ZXJhdG9yAEdlbmVyYXRvcgBQcm94eQBQcm9taXNlAFByb21pc2VSZXNvbHZlRnVuY3Rpb24AUHJvbWlzZVJlamVjdEZ1bmN0aW9uAEFzeW5jRnVuY3Rpb24AQXN5bmNGdW5jdGlvblJlc29sdmUAQXN5bmNGdW5jdGlvblJlamVjdABBc3luY0dlbmVyYXRvckZ1bmN0aW9uAEFzeW5jR2VuZXJhdG9yAEV2YWxFcnJvcgBSYW5nZUVycm9yAFJlZmVyZW5jZUVycm9yAFN5bnRheEVycm9yAFR5cGVFcnJvcgBVUklFcnJvcgBJbnRlcm5hbEVycm9yADxicmFuZD4AU3ltYm9sLnRvUHJpbWl0aXZlAFN5bWJvbC5pdGVyYXRvcgBTeW1ib2wubWF0Y2gAU3ltYm9sLm1hdGNoQWxsAFN5bWJvbC5yZXBsYWNlAFN5bWJvbC5zZWFyY2gAU3ltYm9sLnNwbGl0AFN5bWJvbC50b1N0cmluZ1RhZwBTeW1ib2wuaXNDb25jYXRTcHJlYWRhYmxlAFN5bWJvbC5oYXNJbnN0YW5jZQBTeW1ib2wuc3BlY2llcwBTeW1ib2wudW5zY29wYWJsZXMAU3ltYm9sLmFzeW5jSXRlcmF0b3IAAAAAAAEAAAAFAAEUBQABFQUAARUFAAEXBQABFwEAAQABAAEAAQABAAEAAQABAAEAAQABAAIAAQUDAAEKAQEAAAECAQABAwIAAQECAAECAwABAgQAAQMGAAECAwABAwQAAQQFAAEDAwABBAQAAQUFAAECAgABBAQAAQMDAAEDAwABBAQAAQUFAAMCAQ0DAQENAwEADQMCAQ0DAgANAwABDQMDAQoBAQAAAQAAAAEBAgABAAAAAQICAAECAAABAQAAAQEAAAYAABgFAQEPAwIBCgECAQABAQEAAQEBAAUAARcFAAEXBQABFwUBABcFAQAXBQIAFwECAwABAwAABgAAGAYAABgGAQAYBQEBFwUBAhcFAgAXAQIBAAEDAAABAwEAAQIBAAECAgABAwAAAQMBAAEEAAAFAgEXBQEBFwECAgABAgEAAQICAAEDAgABAwIAAgMDBQYCARgCAwEFBgICGAYDAxgDAAEQAwEAEAMBARAD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BOOAQJ+GiIlBY4C8jUHxjUPVhuw0iVKViWwFBUDvAEGAowILhBP6BgBwCQDwCkBXDADwDWDHDyDqF0AFGwBBIAAMqIA3qiBQ/iA6DSF0EQFaFCFEGYFaHaH1aiFF0kGv4iHwAQ4AQWRsYW0sQWRsbQBBaG9tLEFob20AQW5hdG9saWFuX0hpZXJvZ2x5cGhzLEhsdXcAQXJhYmljLEFyYWIAQXJtZW5pYW4sQXJtbgBBdmVzdGFuLEF2c3QAQmFsaW5lc2UsQmFsaQBCYW11bSxCYW11AEJhc3NhX1ZhaCxCYXNzAEJhdGFrLEJhdGsAQmVuZ2FsaSxCZW5nAEJoYWlrc3VraSxCaGtzAEJvcG9tb2ZvLEJvcG8AQnJhaG1pLEJyYWgAQnJhaWxsZSxCcmFpAEJ1Z2luZXNlLEJ1Z2kAQnVoaWQsQnVoZABDYW5hZGlhbl9BYm9yaWdpbmFsLENhbnMAQ2FyaWFuLENhcmkAQ2F1Y2FzaWFuX0FsYmFuaWFuLEFnaGIAQ2hha21hLENha20AQ2hhbSxDaGFtAENoZXJva2VlLENoZXIAQ2hvcmFzbWlhbixDaHJzAENvbW1vbixaeXl5AENvcHRpYyxDb3B0LFFhYWMAQ3VuZWlmb3JtLFhzdXgAQ3lwcmlvdCxDcHJ0AEN5cmlsbGljLEN5cmwAQ3lwcm9fTWlub2FuLENwbW4ARGVzZXJldCxEc3J0AERldmFuYWdhcmksRGV2YQBEaXZlc19Ba3VydSxEaWFrAERvZ3JhLERvZ3IARHVwbG95YW4sRHVwbABFZ3lwdGlhbl9IaWVyb2dseXBocyxFZ3lwAEVsYmFzYW4sRWxiYQBFbHltYWljLEVseW0ARXRoaW9waWMsRXRoaQBHZW9yZ2lhbixHZW9yAEdsYWdvbGl0aWMsR2xhZwBHb3RoaWMsR290aABHcmFudGhhLEdyYW4AR3JlZWssR3JlawBHdWphcmF0aSxHdWpyAEd1bmphbGFfR29uZGksR29uZwBHdXJtdWtoaSxHdXJ1AEhhbixIYW5pAEhhbmd1bCxIYW5nAEhhbmlmaV9Sb2hpbmd5YSxSb2hnAEhhbnVub28sSGFubwBIYXRyYW4sSGF0cgBIZWJyZXcsSGVicgBIaXJhZ2FuYSxIaXJhAEltcGVyaWFsX0FyYW1haWMsQXJtaQBJbmhlcml0ZWQsWmluaCxRYWFpAEluc2NyaXB0aW9uYWxfUGFobGF2aSxQaGxpAEluc2NyaXB0aW9uYWxfUGFydGhpYW4sUHJ0aQBKYXZhbmVzZSxKYXZhAEthaXRoaSxLdGhpAEthbm5hZGEsS25kYQBLYXRha2FuYSxLYW5hAEthd2ksS2F3aQBLYXlhaF9MaSxLYWxpAEtoYXJvc2h0aGksS2hhcgBLaG1lcixLaG1yAEtob2praSxLaG9qAEtoaXRhbl9TbWFsbF9TY3JpcHQsS2l0cwBLaHVkYXdhZGksU2luZABMYW8sTGFvbwBMYXRpbixMYXRuAExlcGNoYSxMZXBjAExpbWJ1LExpbWIATGluZWFyX0EsTGluYQBMaW5lYXJfQixMaW5iAExpc3UsTGlzdQBMeWNpYW4sTHljaQBMeWRpYW4sTHlkaQBNYWthc2FyLE1ha2EATWFoYWphbmksTWFoagBNYWxheWFsYW0sTWx5bQBNYW5kYWljLE1hbmQATWFuaWNoYWVhbixNYW5pAE1hcmNoZW4sTWFyYwBNYXNhcmFtX0dvbmRpLEdvbm0ATWVkZWZhaWRyaW4sTWVkZgBNZWV0ZWlfTWF5ZWssTXRlaQBNZW5kZV9LaWtha3VpLE1lbmQATWVyb2l0aWNfQ3Vyc2l2ZSxNZXJjAE1lcm9pdGljX0hpZXJvZ2x5cGhzLE1lcm8ATWlhbyxQbHJkAE1vZGksTW9kaQBNb25nb2xpYW4sTW9uZwBNcm8sTXJvbwBNdWx0YW5pLE11bHQATXlhbm1hcixNeW1yAE5hYmF0YWVhbixOYmF0AE5hZ19NdW5kYXJpLE5hZ20ATmFuZGluYWdhcmksTmFuZABOZXdfVGFpX0x1ZSxUYWx1AE5ld2EsTmV3YQBOa28sTmtvbwBOdXNodSxOc2h1AE55aWFrZW5nX1B1YWNodWVfSG1vbmcsSG1ucABPZ2hhbSxPZ2FtAE9sX0NoaWtpLE9sY2sAT2xkX0h1bmdhcmlhbixIdW5nAE9sZF9JdGFsaWMsSXRhbABPbGRfTm9ydGhfQXJhYmlhbixOYXJiAE9sZF9QZXJtaWMsUGVybQBPbGRfUGVyc2lhbixYcGVvAE9sZF9Tb2dkaWFuLFNvZ28AT2xkX1NvdXRoX0FyYWJpYW4sU2FyYgBPbGRfVHVya2ljLE9ya2gAT2xkX1V5Z2h1cixPdWdyAE9yaXlhLE9yeWEAT3NhZ2UsT3NnZQBPc21hbnlhLE9zbWEAUGFoYXdoX0htb25nLEhtbmcAUGFsbXlyZW5lLFBhbG0AUGF1X0Npbl9IYXUsUGF1YwBQaGFnc19QYSxQaGFnAFBob2VuaWNpYW4sUGhueABQc2FsdGVyX1BhaGxhdmksUGhscABSZWphbmcsUmpuZwBSdW5pYyxSdW5yAFNhbWFyaXRhbixTYW1yAFNhdXJhc2h0cmEsU2F1cgBTaGFyYWRhLFNocmQAU2hhdmlhbixTaGF3AFNpZGRoYW0sU2lkZABTaWduV3JpdGluZyxTZ253AFNpbmhhbGEsU2luaABTb2dkaWFuLFNvZ2QAU29yYV9Tb21wZW5nLFNvcmEAU295b21ibyxTb3lvAFN1bmRhbmVzZSxTdW5kAFN5bG90aV9OYWdyaSxTeWxvAFN5cmlhYyxTeXJjAFRhZ2Fsb2csVGdsZwBUYWdiYW53YSxUYWdiAFRhaV9MZSxUYWxlAFRhaV9UaGFtLExhbmEAVGFpX1ZpZXQsVGF2dABUYWtyaSxUYWtyAFRhbWlsLFRhbWwAVGFuZ3V0LFRhbmcAVGVsdWd1LFRlbHUAVGhhYW5hLFRoYWEAVGhhaSxUaGFpAFRpYmV0YW4sVGlidABUaWZpbmFnaCxUZm5nAFRpcmh1dGEsVGlyaABUYW5nc2EsVG5zYQBUb3RvLFRvdG8AVWdhcml0aWMsVWdhcgBWYWksVmFpaQBWaXRoa3VxaSxWaXRoAFdhbmNobyxXY2hvAFdhcmFuZ19DaXRpLFdhcmEAWWV6aWRpLFllemkAWWksWWlpaQBaYW5hYmF6YXJfU3F1YXJlLFphbmIAQZC2AgvyIMAZmUeFGZlHrhmAR44ZgEeEGZZHgBmeR4AZ4WBHphmER4QZgQ2TGeAPOIMsgBmCLAGDLIAZgCwDgCyAGYAsgBmCLACALACTLAC+LI0ajyzgJB2BOOBIHQClBQGxBQGCBQC2NQeaNQOFNQqEBIAZhQSAGY0EgBmCBIAZnwSAGYkEijiZBIA44AsEgBmhBI2LALuLAYKLrwSxlQ26ZgGCZq1/AY5/AJtSAYBSAIqLBJ4EAIEEBckEgBmcBNAggziOIIEZmSCDCwCHCwGBCwGVCwCGCwCACwKDCwGI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eW1ib2wAU28sT3RoZXJfU3ltYm9sAFBjLENvbm5lY3Rvcl9QdW5jdHVhdGlvbgBQZCxEYXNoX1B1bmN0dWF0aW9uAFBzLE9wZW5fUHVuY3R1YXRpb24AUGUsQ2xvc2VfUHVuY3R1YXRpb24AUGksSW5pdGlhbF9QdW5jdHVhdGlvbgBQZixGaW5hbF9QdW5jdHVhdGlvbgBQbyxPdGhlcl9QdW5jdHVhdGlvbgBacyxTcGFjZV9TZXBhcmF0b3IAWmwsTGluZV9TZXBhcmF0b3IAWnAsUGFyYWdyYXBoX1NlcGFyYXRvcgBDYyxDb250cm9sLGNudHJsAENmLEZvcm1hdABDcyxTdXJyb2dhdGUAQ28sUHJpdmF0ZV9Vc2UATEMsQ2FzZWRfTGV0dGVyAEwsTGV0dGVyAE0sTWFyayxDb21iaW5pbmdfTWFyawBOLE51bWJlcgBTLFN5bWJvbABQLFB1bmN0dWF0aW9uLHB1bmN0AFosU2VwYXJhdG9yAEMsT3RoZXIAQZDXAguwCA4AAAA+AAAAwAEAAAAOAAAA8AAAAAB/AAAAgAMBAAA8QVNDSUlfSGV4X0RpZ2l0LEFIZXgAQmlkaV9Db250cm9sLEJpZGlfQwBEYXNoAERlcHJlY2F0ZWQsRGVwAERpYWNyaXRpYyxEaWEARXh0ZW5kZXIsRXh0AEhleF9EaWdpdCxIZXgASURTX0JpbmFyeV9PcGVyYXRvcixJRFNCAElEU19UcmluYXJ5X09wZXJhdG9yLElEU1QASWRlb2dyYXBoaWMsSWRlbwBKb2luX0NvbnRyb2wsSm9pbl9DAExvZ2ljYWxfT3JkZXJfRXhjZXB0aW9uLExPRQBOb25jaGFyYWN0ZXJfQ29kZV9Qb2ludCxOQ2hhcgBQYXR0ZXJuX1N5bnRheCxQYXRfU3luAFBhdHRlcm5fV2hpdGVfU3BhY2UsUGF0X1dTAFF1b3RhdGlvbl9NYXJrLFFNYXJrAFJhZGljYWwAUmVnaW9uYWxfSW5kaWNhdG9yLFJJAFNlbnRlbmNlX1Rlcm1pbmFsLFNUZXJtAFNvZnRfRG90dGVkLFNEAFRlcm1pbmFsX1B1bmN0dWF0aW9uLFRlcm0AVW5pZmllZF9JZGVvZ3JhcGgsVUlkZW8AVmFyaWF0aW9uX1NlbGVjdG9yLFZTAFdoaXRlX1NwYWNlLHNwYWNlAEJpZGlfTWlycm9yZWQsQmlkaV9NAEVtb2ppAEVtb2ppX0NvbXBvbmVudCxFQ29tcABFbW9qaV9Nb2RpZmllcixFTW9kAEVtb2ppX01vZGlmaWVyX0Jhc2UsRUJhc2UARW1vamlfUHJlc2VudGF0aW9uLEVQcmVzAEV4dGVuZGVkX1BpY3RvZ3JhcGhpYyxFeHRQaWN0AERlZmF1bHRfSWdub3JhYmxlX0NvZGVfUG9pbnQsREkASURfU3RhcnQsSURTAENhc2VfSWdub3JhYmxlLENJAEFTQ0lJAEFscGhhYmV0aWMsQWxwaGEAQW55AEFzc2lnbmVkAENhc2VkAENoYW5nZXNfV2hlbl9DYXNlZm9sZGVkLENXQ0YAQ2hhbmdlc19XaGVuX0Nhc2VtYXBwZWQsQ1dDTQBDaGFuZ2VzX1doZW5fTG93ZXJjYXNlZCxDV0wAQ2hhbmdlc19XaGVuX05GS0NfQ2FzZWZvbGRlZCxDV0tDRgBDaGFuZ2VzX1doZW5fVGl0bGVjYXNlZCxDV1QAQ2hhbmdlc19XaGVuX1VwcGVyY2FzZWQsQ1dVAEdyYXBoZW1lX0Jhc2UsR3JfQmFzZQBHcmFwaGVtZV9FeHRlbmQsR3JfRXh0AElEX0NvbnRpbnVlLElEQwBMb3dlcmNhc2UsTG93ZXIATWF0aABVcHBlcmNhc2UsVXBwZXIAWElEX0NvbnRpbnVlLFhJREMAWElEX1N0YXJ0LFhJRFMAQdDfAgvyAgEAnAYHTQMEEACPCwAAEQAIAFNKUQBSAFMAOlRVAFdZP11cAEZhY0JkAGYAaABqAGwAbgAAQAAAAAAaAJMAACA1ACcAIQAkIioAE2ttACYkJxQWGBscPh4/Hzk9IiFBHkAlJSYoICpILEMuSzBMMkRCmQAAlY99foOEEoCCdncSe6N8eHmKkpimoIUAmqGTdTOVAI4AdJmYl5YAAJ4AnAChoBUuLzC0tU+qqRIUHiEiIio0NaanNh9JAACXAVraHTYFAMTDxsXIx8rJzMvE1UXWQtdG2M7Q0tTa2e72/g4HD4CfACGAo+0AwEDGYOfb5pnAAAAGYNwp/RUSBhb43QYVEoQIxhb/3wPAQABGYN7gbTc4ORUUFxYAGhkcGwBft2VERwBPYk5QAABIAAAAo6SlAAAAAAC2AABaAEcAW1ZY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n4PLdYFAzXWfjM11gcDTdS0D1HWfiNV1gcDZdZ8D2nWBwNt1nwzcdYFA4nUtg+J1nwjkdYFA6HWfg+h1gUDqdZ+M6nWBwPB1LQTxdR+F83UfBfZ1H4X4dR8F+3Ufhf11nwQMeJ9BDnifBQ94A8IReK3QEngDARt4LQKAe61NgXsDQoh7gcCJey1FinsDBI17gYCQewPckXstBaB7rciie4NEqHutyKp7lwBAfCFFQHwlDUR8h4BKfBXBSnwXQUt8Hw1MfBeCUnyZgFN8l8BTfJeBWnyXAGR8LwGAfIGAgHwDFoR8wQSQfAMBlHwfBfx+rAEAvhDRAL6sRwm+EDkNviyHKb4sAi2+kDcuvpD/Sb4QvGm+AAAAACAAAABhAAIABAAGALwDCAAKAAwAFQCVAKUAuQDBAMMAxwDLANEA1wDdAOAA5gD4AAgBCgFzABABEgEUASABLAFEAU0BUwFiAWgBagF2AZIBlAGpAbsBxwHRAdUBuQLXATsA2QHbAbcA4QH8AQwCGAIdAiMCJwKjAzMCPwJCAksCTgJRAl0CYAJpAmwCbwJ1AngCgQKKApwCnwKjAq8CuQLFAskCzQLRAtUC5wLtAvEC9QL5Av0CBQMJAw0DEwMXAxsDIwMnAysDLwM1Az0DQQNJA00DUQMLD1cDWwNfA2MDZwNrA28DcwN5A30DgQOFA4kDjQORA5UDmQOdA6ED3BClA8kDzQPZA90D4QPvA/EDPQRPBJkE8AQCBUoFZAVsBXAFcwWaBfoF/gUHBgsGFAYYBh4GIgYoBo4GlAaYBp4GogarBqwD8watA/YGrgP5Bq8D/AbMA/8GzQMCB84DBQcJBw0HEQeGAzIHNQe5AzcHOweIA1MHiQNWB5ADaweKA3cHsAOJB44DmQefB6MHjAO4B48Duwe0AL4HwAfCBxAgywcuAM0HzwcgANIH1gfbB98H5AfqB/AHIAD2BxIiAQgFCAcIHQglCCcIQwAtCDAIkAE2CDkITgBFCEcITAhOCFEIWgCpA1oAUwhXCGAIaQBiCGUIbwh0CHoIfgiiCEkApAimCKkIVgCrCK0IsAi0CFgAtgi4CLsIwAjCCMUIdgDHCMkIzAjQCHgA0gjUCNcI2wjeCOQI5wjwCPMI9gj5CAIJBgkLCQ8JFAkXCRoJIwksCTsJPglBCUQJRwlKCVYJXAlgCWIJZAloCWoJ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";if(!K(L)){var M=L;L=d.locateFile?d.locateFile(M,q):q+M;}function ca(){var a=L;return Promise.resolve().then(()=>{if(a==L&&v)var b=new Uint8Array(v);else{if(K(a)){b=atob(a.slice(37));for(var c=new Uint8Array(b.length),e=0;eWebAssembly.instantiate(c,a)).then(c=>c).then(b,c=>{u(`failed to asynchronously prepare wasm: ${c}`);w(c);});}function ea(a,b){return da(a,b);}var N=a=>{for(;0{for(var c=b+NaN,e=b;a[e]&&!(e>=c);)++e;if(16f?c+=String.fromCharCode(f):(f-=65536,c+=String.fromCharCode(55296|f>>10,56320|f&1023));}}else c+=String.fromCharCode(f);}return c;},fa=[0,31,60,91,121,152,182,213,244,274,305,335],ha=[0,31,59,90,120,151,181,212,243,273,304,334],Q=a=>{for(var b=0,c=0;c=e?b++:2047>=e?b+=2:55296<=e&&57343>=e?(b+=4,++c):b+=3;}return b;},R=(a,b,c)=>{var e=A;if(0=g){var l=a.charCodeAt(++f);g=65536+((g&1023)<<10)|l&1023;}if(127>=g){if(b>=c)break;e[b++]=g;}else{if(2047>=g){if(b+1>=c)break;e[b++]=192|g>>6;}else{if(65535>=g){if(b+2>=c)break;e[b++]=224|g>>12;}else{if(b+3>=c)break;e[b++]=240|g>>18;e[b++]=128|g>>12&63;}e[b++]=128|g>>6&63;}e[b++]=128|g&63;}}e[b]=0;}},T=a=>{var b=Q(a)+1,c=S(b);c&&R(a,c,b);return c;};function U(){}var ia=[null,[],[]],ka=(a,b,c,e)=>{var f={string:h=>{var r=0;if(null!==h&&void 0!==h&&0!==h){r=Q(h)+1;var Y=V(r);R(h,Y,r);r=Y;}return r;},array:h=>{var r=V(h.length);z.set(h,r);return r;}};a=d["_"+a];var g=[],l=0;if(e)for(var m=0;m{a=a?P(A,a):"";b=null!==b?JSON.parse(b?P(A,b):""):[];try{const e=d.externalCall(a,b);return e?T(e):null;}catch(e){return d.HEAPU8[c]=1,T(e.message);}};var la={a:(a,b,c,e)=>{w(`Assertion failed: ${a?P(A,a):""}, at: `+[b?b?P(A,b):"":"unknown filename",c,e?e?P(A,e):"":"unknown function"]);},i:function(a,b,c){a=new Date(1E3*(b+2097152>>>0<4194305-!!a?(a>>>0)+4294967296*b:NaN));B[c>>2]=a.getSeconds();B[c+4>>2]=a.getMinutes();B[c+8>>2]=a.getHours();B[c+12>>2]=a.getDate();B[c+16>>2]=a.getMonth();B[c+20>>2]=a.getFullYear()-1900;B[c+24>>2]=a.getDay();b=a.getFullYear();B[c+28>>2]=(0!==b%4||0===b%100&&0!==b%400?ha:fa)[a.getMonth()]+a.getDate()-1|0;B[c+36>>2]=-(60*a.getTimezoneOffset());b=new Date(a.getFullYear(),6,1).getTimezoneOffset();var e=new Date(a.getFullYear(),0,1).getTimezoneOffset();B[c+32>>2]=(b!=e&&a.getTimezoneOffset()==Math.min(e,b))|0;},d:(a,b,c)=>{function e(t){return(t=t.toTimeString().match(/\(([A-Za-z ]+)\)$/))?t[1]:"GMT";}var f=new Date().getFullYear(),g=new Date(f,0,1),l=new Date(f,6,1);f=g.getTimezoneOffset();var m=l.getTimezoneOffset();C[a>>2]=60*Math.max(f,m);B[b>>2]=Number(f!=m);a=e(g);b=e(l);a=T(a);b=T(b);m>2]=a,C[c+4>>2]=b):(C[c>>2]=b,C[c+4>>2]=a);},b:()=>{w("");},g:U,f:function(a,b){a=a?P(A,a):"";let c;try{c=window.JSON.parse(a);}catch(e){c=a;}0!==b?window.alert(a):window.console.log("DUMP",c);},e:()=>Date.now(),j:a=>{var b=A.length;a>>>=0;if(2147483648=c;c*=2){var e=b*(1+.2/c);e=Math.min(e,a+100663296);var f=Math;e=Math.max(a,e);a:{f=(f.min.call(f,2147483648,e+(65536-e%65536)%65536)-x.buffer.byteLength+65535)/65536;try{x.grow(f);D();var g=1;break a;}catch(l){}g=void 0;}if(g)return!0;}return!1;},c:(a,b,c,e)=>{for(var f=0,g=0;g>2],m=C[b+4>>2];b+=8;for(var t=0;t>2]=f;return 0;},k:function(a){a=a?P(A,a):"";window.console.log(a);},h:function(a){a=a?P(A,a):"";return Date.parse(a);},l:function(a,b,c,e){a=a?P(A,a):"";b=b?P(A,b):"";c=c?P(A,c):"";c=`Quickjs -- ${a}: ${b}\n${c}`;0!==e?window.alert(c):window.console.error(c);}},X=function(){function a(c){X=c.exports;x=X.m;D();F.unshift(X.n);H--;d.monitorRunDependencies?.(H);0==H&&(null!==I&&(clearInterval(I),I=null),J&&(c=J,J=null,c()));return X;}var b={a:la};H++;d.monitorRunDependencies?.(H);if(d.instantiateWasm)try{return d.instantiateWasm(b,a);}catch(c){u(`Module.instantiateWasm callback failed with error: ${c}`),n(c);}ea(b,function(c){a(c.instance);}).catch(n);return{};}();d._evalInSandbox=(a,b)=>(d._evalInSandbox=X.o)(a,b);d._nukeSandbox=()=>(d._nukeSandbox=X.p)();d._init=(a,b)=>(d._init=X.q)(a,b);d._commFun=(a,b)=>(d._commFun=X.r)(a,b);d._dumpMemoryUse=()=>(d._dumpMemoryUse=X.s)();var S=a=>(S=X.t)(a);d._free=a=>(d._free=X.u)(a);var W=()=>(W=X.w)(),ja=a=>(ja=X.x)(a),V=a=>(V=X.y)(a);d.ccall=ka;d.cwrap=(a,b,c,e)=>{var f=!c||c.every(g=>"number"===g||"boolean"===g);return"string"!==b&&f&&!e?d["_"+a]:function(){return ka(a,b,c,arguments,e);};};d.stringToNewUTF8=T;var Z;J=function ma(){Z||na();Z||(J=ma);};function na(){function a(){if(!Z&&(Z=!0,d.calledRun=!0,!y)){N(F);k(d);if(d.onRuntimeInitialized)d.onRuntimeInitialized();if(d.postRun)for("function"==typeof d.postRun&&(d.postRun=[d.postRun]);d.postRun.length;){var b=d.postRun.shift();G.unshift(b);}N(G);}}if(!(0 { + if (typeof callbackId !== "number" || typeof nMilliseconds !== "number") { + return; + } + if (callbackId === 0) { + this.win.clearTimeout(this.timeoutIds.get(callbackId)); + } + const id = this.win.setTimeout(() => { + this.timeoutIds.delete(callbackId); + this.callSandboxFunction("timeoutCb", { + callbackId, + interval: false + }); + }, nMilliseconds); + this.timeoutIds.set(callbackId, id); + }, + clearTimeout: callbackId => { + this.win.clearTimeout(this.timeoutIds.get(callbackId)); + this.timeoutIds.delete(callbackId); + }, + setInterval: (callbackId, nMilliseconds) => { + if (typeof callbackId !== "number" || typeof nMilliseconds !== "number") { + return; + } + const id = this.win.setInterval(() => { + this.callSandboxFunction("timeoutCb", { + callbackId, + interval: true + }); + }, nMilliseconds); + this.timeoutIds.set(callbackId, id); + }, + clearInterval: callbackId => { + this.win.clearInterval(this.timeoutIds.get(callbackId)); + this.timeoutIds.delete(callbackId); + }, + alert: cMsg => { + if (typeof cMsg !== "string") { + return; + } + this.win.alert(cMsg); + }, + confirm: cMsg => { + if (typeof cMsg !== "string") { + return false; + } + return this.win.confirm(cMsg); + }, + prompt: (cQuestion, cDefault) => { + if (typeof cQuestion !== "string" || typeof cDefault !== "string") { + return null; + } + return this.win.prompt(cQuestion, cDefault); + }, + parseURL: cUrl => { + const url = new this.win.URL(cUrl); + const props = ["hash", "host", "hostname", "href", "origin", "password", "pathname", "port", "protocol", "search", "searchParams", "username"]; + return Object.fromEntries(props.map(name => [name, url[name].toString()])); + }, + send: data => { + if (!data) { + return; + } + const event = new this.win.CustomEvent("updatefromsandbox", { + detail: this.importValueFromSandbox(data) + }); + this.win.dispatchEvent(event); + } + }; + Object.setPrototypeOf(externals, null); + return (name, args) => { + try { + const result = externals[name](...args); + return this.exportValueToSandbox(result); + } catch (error) { + throw this.createErrorForSandbox(error?.toString() ?? ""); + } + }; + } +} +;// ./src/pdf.sandbox.js + + +const pdfjsVersion = "4.10.38"; +const pdfjsBuild = "f9bea397f"; +class SandboxSupport extends SandboxSupportBase { + exportValueToSandbox(val) { + return JSON.stringify(val); + } + importValueFromSandbox(val) { + return val; + } + createErrorForSandbox(errorMessage) { + return new Error(errorMessage); + } +} +class Sandbox { + constructor(win, module) { + this.support = new SandboxSupport(win, this); + module.externalCall = this.support.createSandboxExternals(); + this._module = module; + this._alertOnError = 0; + } + create(data) { + const code = ["var __webpack_exports__ = globalThis.pdfjsSandbox = {};\n\n;// ./src/scripting_api/constants.js\nconst Border = Object.freeze({\n s: \"solid\",\n d: \"dashed\",\n b: \"beveled\",\n i: \"inset\",\n u: \"underline\"\n});\nconst Cursor = Object.freeze({\n visible: 0,\n hidden: 1,\n delay: 2\n});\nconst Display = Object.freeze({\n visible: 0,\n hidden: 1,\n noPrint: 2,\n noView: 3\n});\nconst Font = Object.freeze({\n Times: \"Times-Roman\",\n TimesB: \"Times-Bold\",\n TimesI: \"Times-Italic\",\n TimesBI: \"Times-BoldItalic\",\n Helv: \"Helvetica\",\n HelvB: \"Helvetica-Bold\",\n HelvI: \"Helvetica-Oblique\",\n HelvBI: \"Helvetica-BoldOblique\",\n Cour: \"Courier\",\n CourB: \"Courier-Bold\",\n CourI: \"Courier-Oblique\",\n CourBI: \"Courier-BoldOblique\",\n Symbol: \"Symbol\",\n ZapfD: \"ZapfDingbats\",\n KaGo: \"HeiseiKakuGo-W5-UniJIS-UCS2-H\",\n KaMi: \"HeiseiMin-W3-UniJIS-UCS2-H\"\n});\nconst Highlight = Object.freeze({\n n: \"none\",\n i: \"invert\",\n p: \"push\",\n o: \"outline\"\n});\nconst Position = Object.freeze({\n textOnly: 0,\n iconOnly: 1,\n iconTextV: 2,\n textIconV: 3,\n iconTextH: 4,\n textIconH: 5,\n overlay: 6\n});\nconst ScaleHow = Object.freeze({\n proportional: 0,\n anamorphic: 1\n});\nconst ScaleWhen = Object.freeze({\n always: 0,\n never: 1,\n tooBig: 2,\n tooSmall: 3\n});\nconst Style = Object.freeze({\n ch: \"check\",\n cr: \"cross\",\n di: \"diamond\",\n ci: \"circle\",\n st: \"star\",\n sq: \"square\"\n});\nconst Trans = Object.freeze({\n blindsH: \"BlindsHorizontal\",\n blindsV: \"BlindsVertical\",\n boxI: \"BoxIn\",\n boxO: \"BoxOut\",\n dissolve: \"Dissolve\",\n glitterD: \"GlitterDown\",\n glitterR: \"GlitterRight\",\n glitterRD: \"GlitterRightDown\",\n random: \"Random\",\n replace: \"Replace\",\n splitHI: \"SplitHorizontalIn\",\n splitHO: \"SplitHorizontalOut\",\n splitVI: \"SplitVerticalIn\",\n splitVO: \"SplitVerticalOut\",\n wipeD: \"WipeDown\",\n wipeL: \"WipeLeft\",\n wipeR: \"WipeRight\",\n wipeU: \"WipeUp\"\n});\nconst ZoomType = Object.freeze({\n none: \"NoVary\",\n fitP: \"FitPage\",\n fitW: \"FitWidth\",\n fitH: \"FitHeight\",\n fitV: \"FitVisibleWidth\",\n pref: \"Preferred\",\n refW: \"ReflowWidth\"\n});\nconst GlobalConstants = Object.freeze({\n IDS_GREATER_THAN: \"Invalid value: must be greater than or equal to % s.\",\n IDS_GT_AND_LT: \"Invalid value: must be greater than or equal to % s \" + \"and less than or equal to % s.\",\n IDS_LESS_THAN: \"Invalid value: must be less than or equal to % s.\",\n IDS_INVALID_MONTH: \"** Invalid **\",\n IDS_INVALID_DATE: \"Invalid date / time: please ensure that the date / time exists. Field\",\n IDS_INVALID_DATE2: \" should match format \",\n IDS_INVALID_VALUE: \"The value entered does not match the format of the field\",\n IDS_AM: \"am\",\n IDS_PM: \"pm\",\n IDS_MONTH_INFO: \"January[1] February[2] March[3] April[4] May[5] \" + \"June[6] July[7] August[8] September[9] October[10] \" + \"November[11] December[12] Sept[9] Jan[1] Feb[2] Mar[3] \" + \"Apr[4] Jun[6] Jul[7] Aug[8] Sep[9] Oct[10] Nov[11] Dec[12]\",\n IDS_STARTUP_CONSOLE_MSG: \"** ^ _ ^ **\",\n RE_NUMBER_ENTRY_DOT_SEP: [\"[+-]?\\\\d*\\\\.?\\\\d*\"],\n RE_NUMBER_COMMIT_DOT_SEP: [\"[+-]?\\\\d+(\\\\.\\\\d+)?\", \"[+-]?\\\\.\\\\d+\", \"[+-]?\\\\d+\\\\.\"],\n RE_NUMBER_ENTRY_COMMA_SEP: [\"[+-]?\\\\d*,?\\\\d*\"],\n RE_NUMBER_COMMIT_COMMA_SEP: [\"[+-]?\\\\d+([.,]\\\\d+)?\", \"[+-]?[.,]\\\\d+\", \"[+-]?\\\\d+[.,]\"],\n RE_ZIP_ENTRY: [\"\\\\d{0,5}\"],\n RE_ZIP_COMMIT: [\"\\\\d{5}\"],\n RE_ZIP4_ENTRY: [\"\\\\d{0,5}(\\\\.|[- ])?\\\\d{0,4}\"],\n RE_ZIP4_COMMIT: [\"\\\\d{5}(\\\\.|[- ])?\\\\d{4}\"],\n RE_PHONE_ENTRY: [\"\\\\d{0,3}(\\\\.|[- ])?\\\\d{0,3}(\\\\.|[- ])?\\\\d{0,4}\", \"\\\\(\\\\d{0,3}\", \"\\\\(\\\\d{0,3}\\\\)(\\\\.|[- ])?\\\\d{0,3}(\\\\.|[- ])?\\\\d{0,4}\", \"\\\\(\\\\d{0,3}(\\\\.|[- ])?\\\\d{0,3}(\\\\.|[- ])?\\\\d{0,4}\", \"\\\\d{0,3}\\\\)(\\\\.|[- ])?\\\\d{0,3}(\\\\.|[- ])?\\\\d{0,4}\", \"011(\\\\.|[- \\\\d])*\"],\n RE_PHONE_COMMIT: [\"\\\\d{3}(\\\\.|[- ])?\\\\d{4}\", \"\\\\d{3}(\\\\.|[- ])?\\\\d{3}(\\\\.|[- ])?\\\\d{4}\", \"\\\\(\\\\d{3}\\\\)(\\\\.|[- ])?\\\\d{3}(\\\\.|[- ])?\\\\d{4}\", \"011(\\\\.|[- \\\\d])*\"],\n RE_SSN_ENTRY: [\"\\\\d{0,3}(\\\\.|[- ])?\\\\d{0,2}(\\\\.|[- ])?\\\\d{0,4}\"],\n RE_SSN_COMMIT: [\"\\\\d{3}(\\\\.|[- ])?\\\\d{2}(\\\\.|[- ])?\\\\d{4}\"]\n});\n\n;// ./src/scripting_api/common.js\nconst FieldType = {\n none: 0,\n number: 1,\n percent: 2,\n date: 3,\n time: 4\n};\nfunction createActionsMap(actions) {\n const actionsMap = new Map();\n if (actions) {\n for (const [eventType, actionsForEvent] of Object.entries(actions)) {\n actionsMap.set(eventType, actionsForEvent);\n }\n }\n return actionsMap;\n}\nfunction getFieldType(actions) {\n let format = actions.get(\"Format\");\n if (!format) {\n return FieldType.none;\n }\n format = format[0];\n format = format.trim();\n if (format.startsWith(\"AFNumber_\")) {\n return FieldType.number;\n }\n if (format.startsWith(\"AFPercent_\")) {\n return FieldType.percent;\n }\n if (format.startsWith(\"AFDate_\")) {\n return FieldType.date;\n }\n if (format.startsWith(\"AFTime_\")) {\n return FieldType.time;\n }\n return FieldType.none;\n}\n\n;// ./src/shared/scripting_utils.js\nfunction makeColorComp(n) {\n return Math.floor(Math.max(0, Math.min(1, n)) * 255).toString(16).padStart(2, \"0\");\n}\nfunction scaleAndClamp(x) {\n return Math.max(0, Math.min(255, 255 * x));\n}\nclass ColorConverters {\n static CMYK_G([c, y, m, k]) {\n return [\"G\", 1 - Math.min(1, 0.3 * c + 0.59 * m + 0.11 * y + k)];\n }\n static G_CMYK([g]) {\n return [\"CMYK\", 0, 0, 0, 1 - g];\n }\n static G_RGB([g]) {\n return [\"RGB\", g, g, g];\n }\n static G_rgb([g]) {\n g = scaleAndClamp(g);\n return [g, g, g];\n }\n static G_HTML([g]) {\n const G = makeColorComp(g);\n return `#${G}${G}${G}`;\n }\n static RGB_G([r, g, b]) {\n return [\"G\", 0.3 * r + 0.59 * g + 0.11 * b];\n }\n static RGB_rgb(color) {\n return color.map(scaleAndClamp);\n }\n static RGB_HTML(color) {\n return `#${color.map(makeColorComp).join(\"\")}`;\n }\n static T_HTML() {\n return \"#00000000\";\n }\n static T_rgb() {\n return [null];\n }\n static CMYK_RGB([c, y, m, k]) {\n return [\"RGB\", 1 - Math.min(1, c + k), 1 - Math.min(1, m + k), 1 - Math.min(1, y + k)];\n }\n static CMYK_rgb([c, y, m, k]) {\n return [scaleAndClamp(1 - Math.min(1, c + k)), scaleAndClamp(1 - Math.min(1, m + k)), scaleAndClamp(1 - Math.min(1, y + k))];\n }\n static CMYK_HTML(components) {\n const rgb = this.CMYK_RGB(components).slice(1);\n return this.RGB_HTML(rgb);\n }\n static RGB_CMYK([r, g, b]) {\n const c = 1 - r;\n const m = 1 - g;\n const y = 1 - b;\n const k = Math.min(c, m, y);\n return [\"CMYK\", c, m, y, k];\n }\n}\n\n;// ./src/scripting_api/pdf_object.js\nclass PDFObject {\n constructor(data) {\n this._expandos = Object.create(null);\n this._send = data.send || null;\n this._id = data.id || null;\n }\n}\n\n;// ./src/scripting_api/color.js\n\n\nclass Color extends PDFObject {\n constructor() {\n super({});\n this.transparent = [\"T\"];\n this.black = [\"G\", 0];\n this.white = [\"G\", 1];\n this.red = [\"RGB\", 1, 0, 0];\n this.green = [\"RGB\", 0, 1, 0];\n this.blue = [\"RGB\", 0, 0, 1];\n this.cyan = [\"CMYK\", 1, 0, 0, 0];\n this.magenta = [\"CMYK\", 0, 1, 0, 0];\n this.yellow = [\"CMYK\", 0, 0, 1, 0];\n this.dkGray = [\"G\", 0.25];\n this.gray = [\"G\", 0.5];\n this.ltGray = [\"G\", 0.75];\n }\n static _isValidSpace(cColorSpace) {\n return typeof cColorSpace === \"string\" && (cColorSpace === \"T\" || cColorSpace === \"G\" || cColorSpace === \"RGB\" || cColorSpace === \"CMYK\");\n }\n static _isValidColor(colorArray) {\n if (!Array.isArray(colorArray) || colorArray.length === 0) {\n return false;\n }\n const space = colorArray[0];\n if (!Color._isValidSpace(space)) {\n return false;\n }\n switch (space) {\n case \"T\":\n if (colorArray.length !== 1) {\n return false;\n }\n break;\n case \"G\":\n if (colorArray.length !== 2) {\n return false;\n }\n break;\n case \"RGB\":\n if (colorArray.length !== 4) {\n return false;\n }\n break;\n case \"CMYK\":\n if (colorArray.length !== 5) {\n return false;\n }\n break;\n default:\n return false;\n }\n return colorArray.slice(1).every(c => typeof c === \"number\" && c >= 0 && c <= 1);\n }\n static _getCorrectColor(colorArray) {\n return Color._isValidColor(colorArray) ? colorArray : [\"G\", 0];\n }\n convert(colorArray, cColorSpace) {\n if (!Color._isValidSpace(cColorSpace)) {\n return this.black;\n }\n if (cColorSpace === \"T\") {\n return [\"T\"];\n }\n colorArray = Color._getCorrectColor(colorArray);\n if (colorArray[0] === cColorSpace) {\n return colorArray;\n }\n if (colorArray[0] === \"T\") {\n return this.convert(this.black, cColorSpace);\n }\n return ColorConverters[`${colorArray[0]}_${cColorSpace}`](colorArray.slice(1));\n }\n equal(colorArray1, colorArray2) {\n colorArray1 = Color._getCorrectColor(colorArray1);\n colorArray2 = Color._getCorrectColor(colorArray2);\n if (colorArray1[0] === \"T\" || colorArray2[0] === \"T\") {\n return colorArray1[0] === \"T\" && colorArray2[0] === \"T\";\n }\n if (colorArray1[0] !== colorArray2[0]) {\n colorArray2 = this.convert(colorArray2, colorArray1[0]);\n }\n return colorArray1.slice(1).every((c, i) => c === colorArray2[i + 1]);\n }\n}\n\n;// ./src/scripting_api/field.js\n\n\n\nclass Field extends PDFObject {\n constructor(data) {\n super(data);\n this.alignment = data.alignment || \"left\";\n this.borderStyle = data.borderStyle || \"\";\n this.buttonAlignX = data.buttonAlignX || 50;\n this.buttonAlignY = data.buttonAlignY || 50;\n this.buttonFitBounds = data.buttonFitBounds;\n this.buttonPosition = data.buttonPosition;\n this.buttonScaleHow = data.buttonScaleHow;\n this.ButtonScaleWhen = data.buttonScaleWhen;\n this.calcOrderIndex = data.calcOrderIndex;\n this.comb = data.comb;\n this.commitOnSelChange = data.commitOnSelChange;\n this.currentValueIndices = data.currentValueIndices;\n this.defaultStyle = data.defaultStyle;\n this.defaultValue = data.defaultValue;\n this.doNotScroll = data.doNotScroll;\n this.doNotSpellCheck = data.doNotSpellCheck;\n this.delay = data.delay;\n this.display = data.display;\n this.doc = data.doc.wrapped;\n this.editable = data.editable;\n this.exportValues = data.exportValues;\n this.fileSelect = data.fileSelect;\n this.hidden = data.hidden;\n this.highlight = data.highlight;\n this.lineWidth = data.lineWidth;\n this.multiline = data.multiline;\n this.multipleSelection = !!data.multipleSelection;\n this.name = data.name;\n this.password = data.password;\n this.print = data.print;\n this.radiosInUnison = data.radiosInUnison;\n this.readonly = data.readonly;\n this.rect = data.rect;\n this.required = data.required;\n this.richText = data.richText;\n this.richValue = data.richValue;\n this.style = data.style;\n this.submitName = data.submitName;\n this.textFont = data.textFont;\n this.textSize = data.textSize;\n this.type = data.type;\n this.userName = data.userName;\n this._actions = createActionsMap(data.actions);\n this._browseForFileToSubmit = data.browseForFileToSubmit || null;\n this._buttonCaption = null;\n this._buttonIcon = null;\n this._charLimit = data.charLimit;\n this._children = null;\n this._currentValueIndices = data.currentValueIndices || 0;\n this._document = data.doc;\n this._fieldPath = data.fieldPath;\n this._fillColor = data.fillColor || [\"T\"];\n this._isChoice = Array.isArray(data.items);\n this._items = data.items || [];\n this._hasValue = data.hasOwnProperty(\"value\");\n this._page = data.page || 0;\n this._strokeColor = data.strokeColor || [\"G\", 0];\n this._textColor = data.textColor || [\"G\", 0];\n this._value = null;\n this._kidIds = data.kidIds || null;\n this._fieldType = getFieldType(this._actions);\n this._siblings = data.siblings || null;\n this._rotation = data.rotation || 0;\n this._globalEval = data.globalEval;\n this._appObjects = data.appObjects;\n this.value = data.value || \"\";\n }\n get currentValueIndices() {\n if (!this._isChoice) {\n return 0;\n }\n return this._currentValueIndices;\n }\n set currentValueIndices(indices) {\n if (!this._isChoice) {\n return;\n }\n if (!Array.isArray(indices)) {\n indices = [indices];\n }\n if (!indices.every(i => typeof i === \"number\" && Number.isInteger(i) && i >= 0 && i < this.numItems)) {\n return;\n }\n indices.sort();\n if (this.multipleSelection) {\n this._currentValueIndices = indices;\n this._value = [];\n indices.forEach(i => {\n this._value.push(this._items[i].displayValue);\n });\n } else if (indices.length > 0) {\n indices = indices.splice(1, indices.length - 1);\n this._currentValueIndices = indices[0];\n this._value = this._items[this._currentValueIndices];\n }\n this._send({\n id: this._id,\n indices\n });\n }\n get fillColor() {\n return this._fillColor;\n }\n set fillColor(color) {\n if (Color._isValidColor(color)) {\n this._fillColor = color;\n }\n }\n get bgColor() {\n return this.fillColor;\n }\n set bgColor(color) {\n this.fillColor = color;\n }\n get charLimit() {\n return this._charLimit;\n }\n set charLimit(limit) {\n if (typeof limit !== \"number\") {\n throw new Error(\"Invalid argument value\");\n }\n this._charLimit = Math.max(0, Math.floor(limit));\n }\n get numItems() {\n if (!this._isChoice) {\n throw new Error(\"Not a choice widget\");\n }\n return this._items.length;\n }\n set numItems(_) {\n throw new Error(\"field.numItems is read-only\");\n }\n get strokeColor() {\n return this._strokeColor;\n }\n set strokeColor(color) {\n if (Color._isValidColor(color)) {\n this._strokeColor = color;\n }\n }\n get borderColor() {\n return this.strokeColor;\n }\n set borderColor(color) {\n this.strokeColor = color;\n }\n get page() {\n return this._page;\n }\n set page(_) {\n throw new Error(\"field.page is read-only\");\n }\n get rotation() {\n return this._rotation;\n }\n set rotation(angle) {\n angle = Math.floor(angle);\n if (angle % 90 !== 0) {\n throw new Error(\"Invalid rotation: must be a multiple of 90\");\n }\n angle %= 360;\n if (angle < 0) {\n angle += 360;\n }\n this._rotation = angle;\n }\n get textColor() {\n return this._textColor;\n }\n set textColor(color) {\n if (Color._isValidColor(color)) {\n this._textColor = color;\n }\n }\n get fgColor() {\n return this.textColor;\n }\n set fgColor(color) {\n this.textColor = color;\n }\n get value() {\n return this._value;\n }\n set value(value) {\n if (this._isChoice) {\n this._setChoiceValue(value);\n return;\n }\n if (value === \"\" || typeof value !== \"string\" || this._fieldType >= FieldType.date) {\n this._originalValue = undefined;\n this._value = value;\n return;\n }\n this._originalValue = value;\n const _value = value.trim().replace(\",\", \".\");\n this._value = !isNaN(_value) ? parseFloat(_value) : value;\n }\n _getValue() {\n return this._originalValue ?? this.value;\n }\n _setChoiceValue(value) {\n if (this.multipleSelection) {\n if (!Array.isArray(value)) {\n value = [value];\n }\n const values = new Set(value);\n if (Array.isArray(this._currentValueIndices)) {\n this._currentValueIndices.length = 0;\n this._value.length = 0;\n } else {\n this._currentValueIndices = [];\n this._value = [];\n }\n this._items.forEach((item, i) => {\n if (values.has(item.exportValue)) {\n this._currentValueIndices.push(i);\n this._value.push(item.exportValue);\n }\n });\n } else {\n if (Array.isArray(value)) {\n value = value[0];\n }\n const index = this._items.findIndex(({\n exportValue\n }) => value === exportValue);\n if (index !== -1) {\n this._currentValueIndices = index;\n this._value = this._items[index].exportValue;\n }\n }\n }\n get valueAsString() {\n return (this._value ?? \"\").toString();\n }\n set valueAsString(_) {}\n browseForFileToSubmit() {\n if (this._browseForFileToSubmit) {\n this._browseForFileToSubmit();\n }\n }\n buttonGetCaption(nFace = 0) {\n if (this._buttonCaption) {\n return this._buttonCaption[nFace];\n }\n return \"\";\n }\n buttonGetIcon(nFace = 0) {\n if (this._buttonIcon) {\n return this._buttonIcon[nFace];\n }\n return null;\n }\n buttonImportIcon(cPath = null, nPave = 0) {}\n buttonSetCaption(cCaption, nFace = 0) {\n if (!this._buttonCaption) {\n this._buttonCaption = [\"\", \"\", \"\"];\n }\n this._buttonCaption[nFace] = cCaption;\n }\n buttonSetIcon(oIcon, nFace = 0) {\n if (!this._buttonIcon) {\n this._buttonIcon = [null, null, null];\n }\n this._buttonIcon[nFace] = oIcon;\n }\n checkThisBox(nWidget, bCheckIt = true) {}\n clearItems() {\n if (!this._isChoice) {\n throw new Error(\"Not a choice widget\");\n }\n this._items = [];\n this._send({\n id: this._id,\n clear: null\n });\n }\n deleteItemAt(nIdx = null) {\n if (!this._isChoice) {\n throw new Error(\"Not a choice widget\");\n }\n if (!this.numItems) {\n return;\n }\n if (nIdx === null) {\n nIdx = Array.isArray(this._currentValueIndices) ? this._currentValueIndices[0] : this._currentValueIndices;\n nIdx ||= 0;\n }\n if (nIdx < 0 || nIdx >= this.numItems) {\n nIdx = this.numItems - 1;\n }\n this._items.splice(nIdx, 1);\n if (Array.isArray(this._currentValueIndices)) {\n let index = this._currentValueIndices.findIndex(i => i >= nIdx);\n if (index !== -1) {\n if (this._currentValueIndices[index] === nIdx) {\n this._currentValueIndices.splice(index, 1);\n }\n for (const ii = this._currentValueIndices.length; index < ii; index++) {\n --this._currentValueIndices[index];\n }\n }\n } else if (this._currentValueIndices === nIdx) {\n this._currentValueIndices = this.numItems > 0 ? 0 : -1;\n } else if (this._currentValueIndices > nIdx) {\n --this._currentValueIndices;\n }\n this._send({\n id: this._id,\n remove: nIdx\n });\n }\n getItemAt(nIdx = -1, bExportValue = false) {\n if (!this._isChoice) {\n throw new Error(\"Not a choice widget\");\n }\n if (nIdx < 0 || nIdx >= this.numItems) {\n nIdx = this.numItems - 1;\n }\n const item = this._items[nIdx];\n return bExportValue ? item.exportValue : item.displayValue;\n }\n getArray() {\n if (this._kidIds) {\n const array = [];\n const fillArrayWithKids = kidIds => {\n for (const id of kidIds) {\n const obj = this._appObjects[id];\n if (!obj) {\n continue;\n }\n if (obj.obj._hasValue) {\n array.push(obj.wrapped);\n }\n if (obj.obj._kidIds) {\n fillArrayWithKids(obj.obj._kidIds);\n }\n }\n };\n fillArrayWithKids(this._kidIds);\n return array;\n }\n if (this._children === null) {\n this._children = this._document.obj._getTerminalChildren(this._fieldPath);\n }\n return this._children;\n }\n getLock() {\n return undefined;\n }\n isBoxChecked(nWidget) {\n return false;\n }\n isDefaultChecked(nWidget) {\n return false;\n }\n insertItemAt(cName, cExport = undefined, nIdx = 0) {\n if (!this._isChoice) {\n throw new Error(\"Not a choice widget\");\n }\n if (!cName) {\n return;\n }\n if (nIdx < 0 || nIdx > this.numItems) {\n nIdx = this.numItems;\n }\n if (this._items.some(({\n displayValue\n }) => displayValue === cName)) {\n return;\n }\n if (cExport === undefined) {\n cExport = cName;\n }\n const data = {\n displayValue: cName,\n exportValue: cExport\n };\n this._items.splice(nIdx, 0, data);\n if (Array.isArray(this._currentValueIndices)) {\n let index = this._currentValueIndices.findIndex(i => i >= nIdx);\n if (index !== -1) {\n for (const ii = this._currentValueIndices.length; index < ii; index++) {\n ++this._currentValueIndices[index];\n }\n }\n } else if (this._currentValueIndices >= nIdx) {\n ++this._currentValueIndices;\n }\n this._send({\n id: this._id,\n insert: {\n index: nIdx,\n ...data\n }\n });\n }\n setAction(cTrigger, cScript) {\n if (typeof cTrigger !== \"string\" || typeof cScript !== \"string\") {\n return;\n }\n if (!(cTrigger in this._actions)) {\n this._actions[cTrigger] = [];\n }\n this._actions[cTrigger].push(cScript);\n }\n setFocus() {\n this._send({\n id: this._id,\n focus: true\n });\n }\n setItems(oArray) {\n if (!this._isChoice) {\n throw new Error(\"Not a choice widget\");\n }\n this._items.length = 0;\n for (const element of oArray) {\n let displayValue, exportValue;\n if (Array.isArray(element)) {\n displayValue = element[0]?.toString() || \"\";\n exportValue = element[1]?.toString() || \"\";\n } else {\n displayValue = exportValue = element?.toString() || \"\";\n }\n this._items.push({\n displayValue,\n exportValue\n });\n }\n this._currentValueIndices = 0;\n this._send({\n id: this._id,\n items: this._items\n });\n }\n setLock() {}\n signatureGetModifications() {}\n signatureGetSeedValue() {}\n signatureInfo() {}\n signatureSetSeedValue() {}\n signatureSign() {}\n signatureValidate() {}\n _isButton() {\n return false;\n }\n _reset() {\n this.value = this.defaultValue;\n }\n _runActions(event) {\n const eventName = event.name;\n if (!this._actions.has(eventName)) {\n return false;\n }\n const actions = this._actions.get(eventName);\n try {\n for (const action of actions) {\n this._globalEval(action);\n }\n } catch (error) {\n event.rc = false;\n throw error;\n }\n return true;\n }\n}\nclass RadioButtonField extends Field {\n constructor(otherButtons, data) {\n super(data);\n this.exportValues = [this.exportValues];\n this._radioIds = [this._id];\n this._radioActions = [this._actions];\n for (const radioData of otherButtons) {\n this.exportValues.push(radioData.exportValues);\n this._radioIds.push(radioData.id);\n this._radioActions.push(createActionsMap(radioData.actions));\n if (this._value === radioData.exportValues) {\n this._id = radioData.id;\n }\n }\n this._hasBeenInitialized = true;\n this._value = data.value || \"\";\n }\n get _siblings() {\n return this._radioIds.filter(id => id !== this._id);\n }\n set _siblings(_) {}\n get value() {\n return this._value;\n }\n set value(value) {\n if (!this._hasBeenInitialized) {\n return;\n }\n if (value === null || value === undefined) {\n this._value = \"\";\n }\n const i = this.exportValues.indexOf(value);\n if (0 <= i && i < this._radioIds.length) {\n this._id = this._radioIds[i];\n this._value = value;\n } else if (value === \"Off\" && this._radioIds.length === 2) {\n const nextI = (1 + this._radioIds.indexOf(this._id)) % 2;\n this._id = this._radioIds[nextI];\n this._value = this.exportValues[nextI];\n }\n }\n checkThisBox(nWidget, bCheckIt = true) {\n if (nWidget < 0 || nWidget >= this._radioIds.length || !bCheckIt) {\n return;\n }\n this._id = this._radioIds[nWidget];\n this._value = this.exportValues[nWidget];\n this._send({\n id: this._id,\n value: this._value\n });\n }\n isBoxChecked(nWidget) {\n return nWidget >= 0 && nWidget < this._radioIds.length && this._id === this._radioIds[nWidget];\n }\n isDefaultChecked(nWidget) {\n return nWidget >= 0 && nWidget < this.exportValues.length && this.defaultValue === this.exportValues[nWidget];\n }\n _getExportValue(state) {\n const i = this._radioIds.indexOf(this._id);\n return this.exportValues[i];\n }\n _runActions(event) {\n const i = this._radioIds.indexOf(this._id);\n this._actions = this._radioActions[i];\n return super._runActions(event);\n }\n _isButton() {\n return true;\n }\n}\nclass CheckboxField extends RadioButtonField {\n get value() {\n return this._value;\n }\n set value(value) {\n if (!value || value === \"Off\") {\n this._value = \"Off\";\n } else {\n super.value = value;\n }\n }\n _getExportValue(state) {\n return state ? super._getExportValue(state) : \"Off\";\n }\n isBoxChecked(nWidget) {\n if (this._value === \"Off\") {\n return false;\n }\n return super.isBoxChecked(nWidget);\n }\n isDefaultChecked(nWidget) {\n if (this.defaultValue === \"Off\") {\n return this._value === \"Off\";\n }\n return super.isDefaultChecked(nWidget);\n }\n checkThisBox(nWidget, bCheckIt = true) {\n if (nWidget < 0 || nWidget >= this._radioIds.length) {\n return;\n }\n this._id = this._radioIds[nWidget];\n this._value = bCheckIt ? this.exportValues[nWidget] : \"Off\";\n this._send({\n id: this._id,\n value: this._value\n });\n }\n}\n\n;// ./src/scripting_api/aform.js\n\nclass AForm {\n constructor(document, app, util, color) {\n this._document = document;\n this._app = app;\n this._util = util;\n this._color = color;\n this._dateFormats = [\"m/d\", \"m/d/yy\", \"mm/dd/yy\", \"mm/yy\", \"d-mmm\", \"d-mmm-yy\", \"dd-mmm-yy\", \"yy-mm-dd\", \"mmm-yy\", \"mmmm-yy\", \"mmm d, yyyy\", \"mmmm d, yyyy\", \"m/d/yy h:MM tt\", \"m/d/yy HH:MM\"];\n this._timeFormats = [\"HH:MM\", \"h:MM tt\", \"HH:MM:ss\", \"h:MM:ss tt\"];\n this._emailRegex = new RegExp(\"^[a-zA-Z0-9.!#$%&'*+\\\\/=?^_`{|}~-]+\" + \"@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\" + \"(?:\\\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$\");\n }\n _mkTargetName(event) {\n return event.target ? `[ ${event.target.name} ]` : \"\";\n }\n _parseDate(cFormat, cDate, strict = false) {\n let date = null;\n try {\n date = this._util._scand(cFormat, cDate, strict);\n } catch {}\n if (date) {\n return date;\n }\n if (strict) {\n return null;\n }\n date = Date.parse(cDate);\n return isNaN(date) ? null : new Date(date);\n }\n AFMergeChange(event = globalThis.event) {\n if (event.willCommit) {\n return event.value.toString();\n }\n return this._app._eventDispatcher.mergeChange(event);\n }\n AFParseDateEx(cString, cOrder) {\n return this._parseDate(cOrder, cString);\n }\n AFExtractNums(str) {\n if (typeof str === \"number\") {\n return [str];\n }\n if (!str || typeof str !== \"string\") {\n return null;\n }\n const first = str.charAt(0);\n if (first === \".\" || first === \",\") {\n str = `0${str}`;\n }\n const numbers = str.match(/(\\d+)/g);\n if (numbers.length === 0) {\n return null;\n }\n return numbers;\n }\n AFMakeNumber(str) {\n if (typeof str === \"number\") {\n return str;\n }\n if (typeof str !== \"string\") {\n return null;\n }\n str = str.trim().replace(\",\", \".\");\n const number = parseFloat(str);\n if (isNaN(number) || !isFinite(number)) {\n return null;\n }\n return number;\n }\n AFMakeArrayFromList(string) {\n if (typeof string === \"string\") {\n return string.split(/, ?/g);\n }\n return string;\n }\n AFNumber_Format(nDec, sepStyle, negStyle, currStyle, strCurrency, bCurrencyPrepend) {\n const event = globalThis.event;\n let value = this.AFMakeNumber(event.value);\n if (value === null) {\n event.value = \"\";\n return;\n }\n const sign = Math.sign(value);\n const buf = [];\n let hasParen = false;\n if (sign === -1 && bCurrencyPrepend && negStyle === 0) {\n buf.push(\"-\");\n }\n if ((negStyle === 2 || negStyle === 3) && sign === -1) {\n buf.push(\"(\");\n hasParen = true;\n }\n if (bCurrencyPrepend) {\n buf.push(strCurrency);\n }\n sepStyle = Math.min(Math.max(0, Math.floor(sepStyle)), 4);\n buf.push(\"%,\", sepStyle, \".\", nDec.toString(), \"f\");\n if (!bCurrencyPrepend) {\n buf.push(strCurrency);\n }\n if (hasParen) {\n buf.push(\")\");\n }\n if (negStyle === 1 || negStyle === 3) {\n event.target.textColor = sign === 1 ? this._color.black : this._color.red;\n }\n if ((negStyle !== 0 || bCurrencyPrepend) && sign === -1) {\n value = -value;\n }\n const formatStr = buf.join(\"\");\n event.value = this._util.printf(formatStr, value);\n }\n AFNumber_Keystroke(nDec, sepStyle, negStyle, currStyle, strCurrency, bCurrencyPrepend) {\n const event = globalThis.event;\n let value = this.AFMergeChange(event);\n if (!value) {\n return;\n }\n value = value.trim();\n let pattern;\n if (sepStyle > 1) {\n pattern = event.willCommit ? /^[+-]?(\\d+(,\\d*)?|,\\d+)$/ : /^[+-]?\\d*,?\\d*$/;\n } else {\n pattern = event.willCommit ? /^[+-]?(\\d+(\\.\\d*)?|\\.\\d+)$/ : /^[+-]?\\d*\\.?\\d*$/;\n }\n if (!pattern.test(value)) {\n if (event.willCommit) {\n const err = `${GlobalConstants.IDS_INVALID_VALUE} ${this._mkTargetName(event)}`;\n this._app.alert(err);\n }\n event.rc = false;\n }\n if (event.willCommit && sepStyle > 1) {\n event.value = parseFloat(value.replace(\",\", \".\"));\n }\n }\n AFPercent_Format(nDec, sepStyle, percentPrepend = false) {\n if (typeof nDec !== \"number\") {\n return;\n }\n if (typeof sepStyle !== \"number\") {\n return;\n }\n if (nDec < 0) {\n throw new Error(\"Invalid nDec value in AFPercent_Format\");\n }\n const event = globalThis.event;\n if (nDec > 512) {\n event.value = \"%\";\n return;\n }\n nDec = Math.floor(nDec);\n sepStyle = Math.min(Math.max(0, Math.floor(sepStyle)), 4);\n let value = this.AFMakeNumber(event.value);\n if (value === null) {\n event.value = \"%\";\n return;\n }\n const formatStr = `%,${sepStyle}.${nDec}f`;\n value = this._util.printf(formatStr, value * 100);\n event.value = percentPrepend ? `%${value}` : `${value}%`;\n }\n AFPercent_Keystroke(nDec, sepStyle) {\n this.AFNumber_Keystroke(nDec, sepStyle, 0, 0, \"\", true);\n }\n AFDate_FormatEx(cFormat) {\n const event = globalThis.event;\n const value = event.value;\n if (!value) {\n return;\n }\n const date = this._parseDate(cFormat, value);\n if (date !== null) {\n event.value = this._util.printd(cFormat, date);\n }\n }\n AFDate_Format(pdf) {\n if (pdf >= 0 && pdf < this._dateFormats.length) {\n this.AFDate_FormatEx(this._dateFormats[pdf]);\n }\n }\n AFDate_KeystrokeEx(cFormat) {\n const event = globalThis.event;\n if (!event.willCommit) {\n return;\n }\n const value = this.AFMergeChange(event);\n if (!value) {\n return;\n }\n if (this._parseDate(cFormat, value, true) === null) {\n const invalid = GlobalConstants.IDS_INVALID_DATE;\n const invalid2 = GlobalConstants.IDS_INVALID_DATE2;\n const err = `${invalid} ${this._mkTargetName(event)}${invalid2}${cFormat}`;\n this._app.alert(err);\n event.rc = false;\n }\n }\n AFDate_Keystroke(pdf) {\n if (pdf >= 0 && pdf < this._dateFormats.length) {\n this.AFDate_KeystrokeEx(this._dateFormats[pdf]);\n }\n }\n AFRange_Validate(bGreaterThan, nGreaterThan, bLessThan, nLessThan) {\n const event = globalThis.event;\n if (!event.value) {\n return;\n }\n const value = this.AFMakeNumber(event.value);\n if (value === null) {\n return;\n }\n bGreaterThan = !!bGreaterThan;\n bLessThan = !!bLessThan;\n if (bGreaterThan) {\n nGreaterThan = this.AFMakeNumber(nGreaterThan);\n if (nGreaterThan === null) {\n return;\n }\n }\n if (bLessThan) {\n nLessThan = this.AFMakeNumber(nLessThan);\n if (nLessThan === null) {\n return;\n }\n }\n let err = \"\";\n if (bGreaterThan && bLessThan) {\n if (value < nGreaterThan || value > nLessThan) {\n err = this._util.printf(GlobalConstants.IDS_GT_AND_LT, nGreaterThan, nLessThan);\n }\n } else if (bGreaterThan) {\n if (value < nGreaterThan) {\n err = this._util.printf(GlobalConstants.IDS_GREATER_THAN, nGreaterThan);\n }\n } else if (value > nLessThan) {\n err = this._util.printf(GlobalConstants.IDS_LESS_THAN, nLessThan);\n }\n if (err) {\n this._app.alert(err);\n event.rc = false;\n }\n }\n AFSimple(cFunction, nValue1, nValue2) {\n const value1 = this.AFMakeNumber(nValue1);\n if (value1 === null) {\n throw new Error(\"Invalid nValue1 in AFSimple\");\n }\n const value2 = this.AFMakeNumber(nValue2);\n if (value2 === null) {\n throw new Error(\"Invalid nValue2 in AFSimple\");\n }\n switch (cFunction) {\n case \"AVG\":\n return (value1 + value2) / 2;\n case \"SUM\":\n return value1 + value2;\n case \"PRD\":\n return value1 * value2;\n case \"MIN\":\n return Math.min(value1, value2);\n case \"MAX\":\n return Math.max(value1, value2);\n }\n throw new Error(\"Invalid cFunction in AFSimple\");\n }\n AFSimple_Calculate(cFunction, cFields) {\n const actions = {\n AVG: args => args.reduce((acc, value) => acc + value, 0) / args.length,\n SUM: args => args.reduce((acc, value) => acc + value, 0),\n PRD: args => args.reduce((acc, value) => acc * value, 1),\n MIN: args => args.reduce((acc, value) => Math.min(acc, value), Number.MAX_VALUE),\n MAX: args => args.reduce((acc, value) => Math.max(acc, value), Number.MIN_VALUE)\n };\n if (!(cFunction in actions)) {\n throw new TypeError(\"Invalid function in AFSimple_Calculate\");\n }\n const event = globalThis.event;\n const values = [];\n cFields = this.AFMakeArrayFromList(cFields);\n for (const cField of cFields) {\n const field = this._document.getField(cField);\n if (!field) {\n continue;\n }\n for (const child of field.getArray()) {\n const number = this.AFMakeNumber(child.value);\n values.push(number ?? 0);\n }\n }\n if (values.length === 0) {\n event.value = 0;\n return;\n }\n const res = actions[cFunction](values);\n event.value = Math.round(1e6 * res) / 1e6;\n }\n AFSpecial_Format(psf) {\n const event = globalThis.event;\n if (!event.value) {\n return;\n }\n psf = this.AFMakeNumber(psf);\n let formatStr;\n switch (psf) {\n case 0:\n formatStr = \"99999\";\n break;\n case 1:\n formatStr = \"99999-9999\";\n break;\n case 2:\n formatStr = this._util.printx(\"9999999999\", event.value).length >= 10 ? \"(999) 999-9999\" : \"999-9999\";\n break;\n case 3:\n formatStr = \"999-99-9999\";\n break;\n default:\n throw new Error(\"Invalid psf in AFSpecial_Format\");\n }\n event.value = this._util.printx(formatStr, event.value);\n }\n AFSpecial_KeystrokeEx(cMask) {\n const event = globalThis.event;\n const simplifiedFormatStr = cMask.replaceAll(/[^9AOX]/g, \"\");\n this.#AFSpecial_KeystrokeEx_helper(simplifiedFormatStr, false);\n if (event.rc) {\n return;\n }\n event.rc = true;\n this.#AFSpecial_KeystrokeEx_helper(cMask, true);\n }\n #AFSpecial_KeystrokeEx_helper(cMask, warn) {\n if (!cMask) {\n return;\n }\n const event = globalThis.event;\n const value = this.AFMergeChange(event);\n if (!value) {\n return;\n }\n const checkers = new Map([[\"9\", char => char >= \"0\" && char <= \"9\"], [\"A\", char => \"a\" <= char && char <= \"z\" || \"A\" <= char && char <= \"Z\"], [\"O\", char => \"a\" <= char && char <= \"z\" || \"A\" <= char && char <= \"Z\" || \"0\" <= char && char <= \"9\"], [\"X\", char => true]]);\n function _checkValidity(_value, _cMask) {\n for (let i = 0, ii = _value.length; i < ii; i++) {\n const mask = _cMask.charAt(i);\n const char = _value.charAt(i);\n const checker = checkers.get(mask);\n if (checker) {\n if (!checker(char)) {\n return false;\n }\n } else if (mask !== char) {\n return false;\n }\n }\n return true;\n }\n const err = `${GlobalConstants.IDS_INVALID_VALUE} = \"${cMask}\"`;\n if (value.length > cMask.length) {\n if (warn) {\n this._app.alert(err);\n }\n event.rc = false;\n return;\n }\n if (event.willCommit) {\n if (value.length < cMask.length) {\n if (warn) {\n this._app.alert(err);\n }\n event.rc = false;\n return;\n }\n if (!_checkValidity(value, cMask)) {\n if (warn) {\n this._app.alert(err);\n }\n event.rc = false;\n return;\n }\n event.value += cMask.substring(value.length);\n return;\n }\n if (value.length < cMask.length) {\n cMask = cMask.substring(0, value.length);\n }\n if (!_checkValidity(value, cMask)) {\n if (warn) {\n this._app.alert(err);\n }\n event.rc = false;\n }\n }\n AFSpecial_Keystroke(psf) {\n const event = globalThis.event;\n psf = this.AFMakeNumber(psf);\n let formatStr;\n switch (psf) {\n case 0:\n formatStr = \"99999\";\n break;\n case 1:\n formatStr = \"99999-9999\";\n break;\n case 2:\n const value = this.AFMergeChange(event);\n formatStr = value.startsWith(\"(\") || value.length > 7 && /^\\p{N}+$/.test(value) ? \"(999) 999-9999\" : \"999-9999\";\n break;\n case 3:\n formatStr = \"999-99-9999\";\n break;\n default:\n throw new Error(\"Invalid psf in AFSpecial_Keystroke\");\n }\n this.AFSpecial_KeystrokeEx(formatStr);\n }\n AFTime_FormatEx(cFormat) {\n this.AFDate_FormatEx(cFormat);\n }\n AFTime_Format(pdf) {\n if (pdf >= 0 && pdf < this._timeFormats.length) {\n this.AFDate_FormatEx(this._timeFormats[pdf]);\n }\n }\n AFTime_KeystrokeEx(cFormat) {\n this.AFDate_KeystrokeEx(cFormat);\n }\n AFTime_Keystroke(pdf) {\n if (pdf >= 0 && pdf < this._timeFormats.length) {\n this.AFDate_KeystrokeEx(this._timeFormats[pdf]);\n }\n }\n eMailValidate(str) {\n return this._emailRegex.test(str);\n }\n AFExactMatch(rePatterns, str) {\n if (rePatterns instanceof RegExp) {\n return str.match(rePatterns)?.[0] === str || 0;\n }\n return rePatterns.findIndex(re => str.match(re)?.[0] === str) + 1;\n }\n}\n\n;// ./src/scripting_api/app_utils.js\nconst VIEWER_TYPE = \"PDF.js\";\nconst VIEWER_VARIATION = \"Full\";\nconst VIEWER_VERSION = 21.00720099;\nconst FORMS_VERSION = 21.00720099;\nconst USERACTIVATION_CALLBACKID = 0;\nconst USERACTIVATION_MAXTIME_VALIDITY = 5000;\nfunction serializeError(error) {\n const value = `${error.toString()}\\n${error.stack}`;\n return {\n command: \"error\",\n value\n };\n}\n\n;// ./src/scripting_api/event.js\n\nclass Event {\n constructor(data) {\n this.change = data.change || \"\";\n this.changeEx = data.changeEx || null;\n this.commitKey = data.commitKey || 0;\n this.fieldFull = data.fieldFull || false;\n this.keyDown = data.keyDown || false;\n this.modifier = data.modifier || false;\n this.name = data.name;\n this.rc = true;\n this.richChange = data.richChange || [];\n this.richChangeEx = data.richChangeEx || [];\n this.richValue = data.richValue || [];\n this.selEnd = data.selEnd ?? -1;\n this.selStart = data.selStart ?? -1;\n this.shift = data.shift || false;\n this.source = data.source || null;\n this.target = data.target || null;\n this.targetName = \"\";\n this.type = \"Field\";\n this.value = data.value || \"\";\n this.willCommit = data.willCommit || false;\n }\n}\nclass EventDispatcher {\n constructor(document, calculationOrder, objects, externalCall) {\n this._document = document;\n this._calculationOrder = calculationOrder;\n this._objects = objects;\n this._externalCall = externalCall;\n this._document.obj._eventDispatcher = this;\n this._isCalculating = false;\n }\n mergeChange(event) {\n let value = event.value;\n if (Array.isArray(value)) {\n return value;\n }\n if (typeof value !== \"string\") {\n value = value.toString();\n }\n const prefix = event.selStart >= 0 ? value.substring(0, event.selStart) : \"\";\n const postfix = event.selEnd >= 0 && event.selEnd <= value.length ? value.substring(event.selEnd) : \"\";\n return `${prefix}${event.change}${postfix}`;\n }\n userActivation() {\n this._document.obj._userActivation = true;\n this._externalCall(\"setTimeout\", [USERACTIVATION_CALLBACKID, USERACTIVATION_MAXTIME_VALIDITY]);\n }\n dispatch(baseEvent) {\n const id = baseEvent.id;\n if (!(id in this._objects)) {\n let event;\n if (id === \"doc\" || id === \"page\") {\n event = globalThis.event = new Event(baseEvent);\n event.source = event.target = this._document.wrapped;\n event.name = baseEvent.name;\n }\n if (id === \"doc\") {\n const eventName = event.name;\n if (eventName === \"Open\") {\n this.userActivation();\n this._document.obj._initActions();\n this.formatAll();\n }\n if (![\"DidPrint\", \"DidSave\", \"WillPrint\", \"WillSave\"].includes(eventName)) {\n this.userActivation();\n }\n this._document.obj._dispatchDocEvent(event.name);\n } else if (id === \"page\") {\n this.userActivation();\n this._document.obj._dispatchPageEvent(event.name, baseEvent.actions, baseEvent.pageNumber);\n } else if (id === \"app\" && baseEvent.name === \"ResetForm\") {\n this.userActivation();\n for (const fieldId of baseEvent.ids) {\n const obj = this._objects[fieldId];\n obj?.obj._reset();\n }\n }\n return;\n }\n const name = baseEvent.name;\n const source = this._objects[id];\n const event = globalThis.event = new Event(baseEvent);\n let savedChange;\n this.userActivation();\n if (source.obj._isButton()) {\n source.obj._id = id;\n event.value = source.obj._getExportValue(event.value);\n if (name === \"Action\") {\n source.obj._value = event.value;\n }\n }\n switch (name) {\n case \"Keystroke\":\n savedChange = {\n value: event.value,\n changeEx: event.changeEx,\n change: event.change,\n selStart: event.selStart,\n selEnd: event.selEnd\n };\n break;\n case \"Blur\":\n case \"Focus\":\n Object.defineProperty(event, \"value\", {\n configurable: false,\n writable: false,\n enumerable: true,\n value: event.value\n });\n break;\n case \"Validate\":\n this.runValidation(source, event);\n return;\n case \"Action\":\n this.runActions(source, source, event, name);\n this.runCalculate(source, event);\n return;\n }\n this.runActions(source, source, event, name);\n if (name !== \"Keystroke\") {\n return;\n }\n if (event.rc) {\n if (event.willCommit) {\n this.runValidation(source, event);\n } else {\n if (source.obj._isChoice) {\n source.obj.value = savedChange.changeEx;\n source.obj._send({\n id: source.obj._id,\n siblings: source.obj._siblings,\n value: source.obj.value\n });\n return;\n }\n const value = source.obj.value = this.mergeChange(event);\n let selStart, selEnd;\n if (event.selStart !== savedChange.selStart || event.selEnd !== savedChange.selEnd) {\n selStart = event.selStart;\n selEnd = event.selEnd;\n } else {\n selEnd = selStart = savedChange.selStart + event.change.length;\n }\n source.obj._send({\n id: source.obj._id,\n siblings: source.obj._siblings,\n value,\n selRange: [selStart, selEnd]\n });\n }\n } else if (!event.willCommit) {\n source.obj._send({\n id: source.obj._id,\n siblings: source.obj._siblings,\n value: savedChange.value,\n selRange: [savedChange.selStart, savedChange.selEnd]\n });\n } else {\n source.obj._send({\n id: source.obj._id,\n siblings: source.obj._siblings,\n value: \"\",\n formattedValue: null,\n selRange: [0, 0]\n });\n }\n }\n formatAll() {\n const event = globalThis.event = new Event({});\n for (const source of Object.values(this._objects)) {\n event.value = source.obj._getValue();\n this.runActions(source, source, event, \"Format\");\n }\n }\n runValidation(source, event) {\n const didValidateRun = this.runActions(source, source, event, \"Validate\");\n if (event.rc) {\n source.obj.value = event.value;\n this.runCalculate(source, event);\n const savedValue = event.value = source.obj._getValue();\n let formattedValue = null;\n if (this.runActions(source, source, event, \"Format\")) {\n formattedValue = event.value?.toString?.();\n }\n source.obj._send({\n id: source.obj._id,\n siblings: source.obj._siblings,\n value: savedValue,\n formattedValue\n });\n event.value = savedValue;\n } else if (didValidateRun) {\n source.obj._send({\n id: source.obj._id,\n siblings: source.obj._siblings,\n value: \"\",\n formattedValue: null,\n selRange: [0, 0],\n focus: true\n });\n }\n }\n runActions(source, target, event, eventName) {\n event.source = source.wrapped;\n event.target = target.wrapped;\n event.name = eventName;\n event.targetName = target.obj.name;\n event.rc = true;\n return target.obj._runActions(event);\n }\n calculateNow() {\n if (!this._calculationOrder || this._isCalculating || !this._document.obj.calculate) {\n return;\n }\n this._isCalculating = true;\n const first = this._calculationOrder[0];\n const source = this._objects[first];\n globalThis.event = new Event({});\n try {\n this.runCalculate(source, globalThis.event);\n } catch (error) {\n this._isCalculating = false;\n throw error;\n }\n this._isCalculating = false;\n }\n runCalculate(source, event) {\n if (!this._calculationOrder || !this._document.obj.calculate) {\n return;\n }\n for (const targetId of this._calculationOrder) {\n if (!(targetId in this._objects)) {\n continue;\n }\n if (!this._document.obj.calculate) {\n break;\n }\n event.value = null;\n const target = this._objects[targetId];\n let savedValue = target.obj._getValue();\n try {\n this.runActions(source, target, event, \"Calculate\");\n } catch (error) {\n const fieldId = target.obj._id;\n const serializedError = serializeError(error);\n serializedError.value = `Error when calculating value for field \"${fieldId}\"\\n${serializedError.value}`;\n this._externalCall(\"send\", [serializedError]);\n continue;\n }\n if (!event.rc) {\n continue;\n }\n if (event.value !== null) {\n target.obj.value = event.value;\n } else {\n event.value = target.obj._getValue();\n }\n this.runActions(target, target, event, \"Validate\");\n if (!event.rc) {\n if (target.obj._getValue() !== savedValue) {\n target.wrapped.value = savedValue;\n }\n continue;\n }\n if (event.value === null) {\n event.value = target.obj._getValue();\n }\n savedValue = target.obj._getValue();\n let formattedValue = null;\n if (this.runActions(target, target, event, \"Format\")) {\n formattedValue = event.value?.toString?.();\n }\n target.obj._send({\n id: target.obj._id,\n siblings: target.obj._siblings,\n value: savedValue,\n formattedValue\n });\n }\n }\n}\n\n;// ./src/scripting_api/fullscreen.js\n\n\nclass FullScreen extends PDFObject {\n constructor(data) {\n super(data);\n this._backgroundColor = [];\n this._clickAdvances = true;\n this._cursor = Cursor.hidden;\n this._defaultTransition = \"\";\n this._escapeExits = true;\n this._isFullScreen = true;\n this._loop = false;\n this._timeDelay = 3600;\n this._usePageTiming = false;\n this._useTimer = false;\n }\n get backgroundColor() {\n return this._backgroundColor;\n }\n set backgroundColor(_) {}\n get clickAdvances() {\n return this._clickAdvances;\n }\n set clickAdvances(_) {}\n get cursor() {\n return this._cursor;\n }\n set cursor(_) {}\n get defaultTransition() {\n return this._defaultTransition;\n }\n set defaultTransition(_) {}\n get escapeExits() {\n return this._escapeExits;\n }\n set escapeExits(_) {}\n get isFullScreen() {\n return this._isFullScreen;\n }\n set isFullScreen(_) {}\n get loop() {\n return this._loop;\n }\n set loop(_) {}\n get timeDelay() {\n return this._timeDelay;\n }\n set timeDelay(_) {}\n get transitions() {\n return [\"Replace\", \"WipeRight\", \"WipeLeft\", \"WipeDown\", \"WipeUp\", \"SplitHorizontalIn\", \"SplitHorizontalOut\", \"SplitVerticalIn\", \"SplitVerticalOut\", \"BlindsHorizontal\", \"BlindsVertical\", \"BoxIn\", \"BoxOut\", \"GlitterRight\", \"GlitterDown\", \"GlitterRightDown\", \"Dissolve\", \"Random\"];\n }\n set transitions(_) {\n throw new Error(\"fullscreen.transitions is read-only\");\n }\n get usePageTiming() {\n return this._usePageTiming;\n }\n set usePageTiming(_) {}\n get useTimer() {\n return this._useTimer;\n }\n set useTimer(_) {}\n}\n\n;// ./src/scripting_api/thermometer.js\n\nclass Thermometer extends PDFObject {\n constructor(data) {\n super(data);\n this._cancelled = false;\n this._duration = 100;\n this._text = \"\";\n this._value = 0;\n }\n get cancelled() {\n return this._cancelled;\n }\n set cancelled(_) {\n throw new Error(\"thermometer.cancelled is read-only\");\n }\n get duration() {\n return this._duration;\n }\n set duration(val) {\n this._duration = val;\n }\n get text() {\n return this._text;\n }\n set text(val) {\n this._text = val;\n }\n get value() {\n return this._value;\n }\n set value(val) {\n this._value = val;\n }\n begin() {}\n end() {}\n}\n\n;// ./src/scripting_api/app.js\n\n\n\n\n\n\nclass App extends PDFObject {\n constructor(data) {\n super(data);\n this._constants = null;\n this._focusRect = true;\n this._fs = null;\n this._language = App._getLanguage(data.language);\n this._openInPlace = false;\n this._platform = App._getPlatform(data.platform);\n this._runtimeHighlight = false;\n this._runtimeHighlightColor = [\"T\"];\n this._thermometer = null;\n this._toolbar = false;\n this._document = data._document;\n this._proxyHandler = data.proxyHandler;\n this._objects = Object.create(null);\n this._eventDispatcher = new EventDispatcher(this._document, data.calculationOrder, this._objects, data.externalCall);\n this._timeoutIds = new WeakMap();\n if (typeof FinalizationRegistry !== \"undefined\") {\n this._timeoutIdsRegistry = new FinalizationRegistry(this._cleanTimeout.bind(this));\n } else {\n this._timeoutIdsRegistry = null;\n }\n this._timeoutCallbackIds = new Map();\n this._timeoutCallbackId = USERACTIVATION_CALLBACKID + 1;\n this._globalEval = data.globalEval;\n this._externalCall = data.externalCall;\n }\n _dispatchEvent(pdfEvent) {\n this._eventDispatcher.dispatch(pdfEvent);\n }\n _registerTimeoutCallback(cExpr) {\n const id = this._timeoutCallbackId++;\n this._timeoutCallbackIds.set(id, cExpr);\n return id;\n }\n _unregisterTimeoutCallback(id) {\n this._timeoutCallbackIds.delete(id);\n }\n _evalCallback({\n callbackId,\n interval\n }) {\n if (callbackId === USERACTIVATION_CALLBACKID) {\n this._document.obj._userActivation = false;\n return;\n }\n const expr = this._timeoutCallbackIds.get(callbackId);\n if (!interval) {\n this._unregisterTimeoutCallback(callbackId);\n }\n if (expr) {\n this._globalEval(expr);\n }\n }\n _registerTimeout(callbackId, interval) {\n const timeout = Object.create(null);\n const id = {\n callbackId,\n interval\n };\n this._timeoutIds.set(timeout, id);\n this._timeoutIdsRegistry?.register(timeout, id);\n return timeout;\n }\n _unregisterTimeout(timeout) {\n this._timeoutIdsRegistry?.unregister(timeout);\n const data = this._timeoutIds.get(timeout);\n if (!data) {\n return;\n }\n this._timeoutIds.delete(timeout);\n this._cleanTimeout(data);\n }\n _cleanTimeout({\n callbackId,\n interval\n }) {\n this._unregisterTimeoutCallback(callbackId);\n if (interval) {\n this._externalCall(\"clearInterval\", [callbackId]);\n } else {\n this._externalCall(\"clearTimeout\", [callbackId]);\n }\n }\n static _getPlatform(platform) {\n if (typeof platform === \"string\") {\n platform = platform.toLowerCase();\n if (platform.includes(\"win\")) {\n return \"WIN\";\n } else if (platform.includes(\"mac\")) {\n return \"MAC\";\n }\n }\n return \"UNIX\";\n }\n static _getLanguage(language) {\n const [main, sub] = language.toLowerCase().split(/[-_]/);\n switch (main) {\n case \"zh\":\n if (sub === \"cn\" || sub === \"sg\") {\n return \"CHS\";\n }\n return \"CHT\";\n case \"da\":\n return \"DAN\";\n case \"de\":\n return \"DEU\";\n case \"es\":\n return \"ESP\";\n case \"fr\":\n return \"FRA\";\n case \"it\":\n return \"ITA\";\n case \"ko\":\n return \"KOR\";\n case \"ja\":\n return \"JPN\";\n case \"nl\":\n return \"NLD\";\n case \"no\":\n return \"NOR\";\n case \"pt\":\n if (sub === \"br\") {\n return \"PTB\";\n }\n return \"ENU\";\n case \"fi\":\n return \"SUO\";\n case \"SV\":\n return \"SVE\";\n default:\n return \"ENU\";\n }\n }\n get activeDocs() {\n return [this._document.wrapped];\n }\n set activeDocs(_) {\n throw new Error(\"app.activeDocs is read-only\");\n }\n get calculate() {\n return this._document.obj.calculate;\n }\n set calculate(calculate) {\n this._document.obj.calculate = calculate;\n }\n get constants() {\n if (!this._constants) {\n this._constants = Object.freeze({\n align: Object.freeze({\n left: 0,\n center: 1,\n right: 2,\n top: 3,\n bottom: 4\n })\n });\n }\n return this._constants;\n }\n set constants(_) {\n throw new Error(\"app.constants is read-only\");\n }\n get focusRect() {\n return this._focusRect;\n }\n set focusRect(val) {\n this._focusRect = val;\n }\n get formsVersion() {\n return FORMS_VERSION;\n }\n set formsVersion(_) {\n throw new Error(\"app.formsVersion is read-only\");\n }\n get fromPDFConverters() {\n return [];\n }\n set fromPDFConverters(_) {\n throw new Error(\"app.fromPDFConverters is read-only\");\n }\n get fs() {\n if (this._fs === null) {\n this._fs = new Proxy(new FullScreen({\n send: this._send\n }), this._proxyHandler);\n }\n return this._fs;\n }\n set fs(_) {\n throw new Error(\"app.fs is read-only\");\n }\n get language() {\n return this._language;\n }\n set language(_) {\n throw new Error(\"app.language is read-only\");\n }\n get media() {\n return undefined;\n }\n set media(_) {\n throw new Error(\"app.media is read-only\");\n }\n get monitors() {\n return [];\n }\n set monitors(_) {\n throw new Error(\"app.monitors is read-only\");\n }\n get numPlugins() {\n return 0;\n }\n set numPlugins(_) {\n throw new Error(\"app.numPlugins is read-only\");\n }\n get openInPlace() {\n return this._openInPlace;\n }\n set openInPlace(val) {\n this._openInPlace = val;\n }\n get platform() {\n return this._platform;\n }\n set platform(_) {\n throw new Error(\"app.platform is read-only\");\n }\n get plugins() {\n return [];\n }\n set plugins(_) {\n throw new Error(\"app.plugins is read-only\");\n }\n get printColorProfiles() {\n return [];\n }\n set printColorProfiles(_) {\n throw new Error(\"app.printColorProfiles is read-only\");\n }\n get printerNames() {\n return [];\n }\n set printerNames(_) {\n throw new Error(\"app.printerNames is read-only\");\n }\n get runtimeHighlight() {\n return this._runtimeHighlight;\n }\n set runtimeHighlight(val) {\n this._runtimeHighlight = val;\n }\n get runtimeHighlightColor() {\n return this._runtimeHighlightColor;\n }\n set runtimeHighlightColor(val) {\n if (Color._isValidColor(val)) {\n this._runtimeHighlightColor = val;\n }\n }\n get thermometer() {\n if (this._thermometer === null) {\n this._thermometer = new Proxy(new Thermometer({\n send: this._send\n }), this._proxyHandler);\n }\n return this._thermometer;\n }\n set thermometer(_) {\n throw new Error(\"app.thermometer is read-only\");\n }\n get toolbar() {\n return this._toolbar;\n }\n set toolbar(val) {\n this._toolbar = val;\n }\n get toolbarHorizontal() {\n return this.toolbar;\n }\n set toolbarHorizontal(value) {\n this.toolbar = value;\n }\n get toolbarVertical() {\n return this.toolbar;\n }\n set toolbarVertical(value) {\n this.toolbar = value;\n }\n get viewerType() {\n return VIEWER_TYPE;\n }\n set viewerType(_) {\n throw new Error(\"app.viewerType is read-only\");\n }\n get viewerVariation() {\n return VIEWER_VARIATION;\n }\n set viewerVariation(_) {\n throw new Error(\"app.viewerVariation is read-only\");\n }\n get viewerVersion() {\n return VIEWER_VERSION;\n }\n set viewerVersion(_) {\n throw new Error(\"app.viewerVersion is read-only\");\n }\n addMenuItem() {}\n addSubMenu() {}\n addToolButton() {}\n alert(cMsg, nIcon = 0, nType = 0, cTitle = \"PDF.js\", oDoc = null, oCheckbox = null) {\n if (!this._document.obj._userActivation) {\n return 0;\n }\n this._document.obj._userActivation = false;\n if (cMsg && typeof cMsg === \"object\") {\n nType = cMsg.nType;\n cMsg = cMsg.cMsg;\n }\n cMsg = (cMsg || \"\").toString();\n if (!cMsg) {\n return 0;\n }\n nType = typeof nType !== \"number\" || isNaN(nType) || nType < 0 || nType > 3 ? 0 : nType;\n if (nType >= 2) {\n return this._externalCall(\"confirm\", [cMsg]) ? 4 : 3;\n }\n this._externalCall(\"alert\", [cMsg]);\n return 1;\n }\n beep() {}\n beginPriv() {}\n browseForDoc() {}\n clearInterval(oInterval) {\n this._unregisterTimeout(oInterval);\n }\n clearTimeOut(oTime) {\n this._unregisterTimeout(oTime);\n }\n endPriv() {}\n execDialog() {}\n execMenuItem(item) {\n if (!this._document.obj._userActivation) {\n return;\n }\n this._document.obj._userActivation = false;\n switch (item) {\n case \"SaveAs\":\n if (this._document.obj._disableSaving) {\n return;\n }\n this._send({\n command: item\n });\n break;\n case \"FirstPage\":\n case \"LastPage\":\n case \"NextPage\":\n case \"PrevPage\":\n case \"ZoomViewIn\":\n case \"ZoomViewOut\":\n this._send({\n command: item\n });\n break;\n case \"FitPage\":\n this._send({\n command: \"zoom\",\n value: \"page-fit\"\n });\n break;\n case \"Print\":\n if (this._document.obj._disablePrinting) {\n return;\n }\n this._send({\n command: \"print\"\n });\n break;\n }\n }\n getNthPlugInName() {}\n getPath() {}\n goBack() {}\n goForward() {}\n hideMenuItem() {}\n hideToolbarButton() {}\n launchURL() {}\n listMenuItems() {}\n listToolbarButtons() {}\n loadPolicyFile() {}\n mailGetAddrs() {}\n mailMsg() {}\n newDoc() {}\n newCollection() {}\n newFDF() {}\n openDoc() {}\n openFDF() {}\n popUpMenu() {}\n popUpMenuEx() {}\n removeToolButton() {}\n response(cQuestion, cTitle = \"\", cDefault = \"\", bPassword = \"\", cLabel = \"\") {\n if (cQuestion && typeof cQuestion === \"object\") {\n cDefault = cQuestion.cDefault;\n cQuestion = cQuestion.cQuestion;\n }\n cQuestion = (cQuestion || \"\").toString();\n cDefault = (cDefault || \"\").toString();\n return this._externalCall(\"prompt\", [cQuestion, cDefault || \"\"]);\n }\n setInterval(cExpr, nMilliseconds = 0) {\n if (cExpr && typeof cExpr === \"object\") {\n nMilliseconds = cExpr.nMilliseconds || 0;\n cExpr = cExpr.cExpr;\n }\n if (typeof cExpr !== \"string\") {\n throw new TypeError(\"First argument of app.setInterval must be a string\");\n }\n if (typeof nMilliseconds !== \"number\") {\n throw new TypeError(\"Second argument of app.setInterval must be a number\");\n }\n const callbackId = this._registerTimeoutCallback(cExpr);\n this._externalCall(\"setInterval\", [callbackId, nMilliseconds]);\n return this._registerTimeout(callbackId, true);\n }\n setTimeOut(cExpr, nMilliseconds = 0) {\n if (cExpr && typeof cExpr === \"object\") {\n nMilliseconds = cExpr.nMilliseconds || 0;\n cExpr = cExpr.cExpr;\n }\n if (typeof cExpr !== \"string\") {\n throw new TypeError(\"First argument of app.setTimeOut must be a string\");\n }\n if (typeof nMilliseconds !== \"number\") {\n throw new TypeError(\"Second argument of app.setTimeOut must be a number\");\n }\n const callbackId = this._registerTimeoutCallback(cExpr);\n this._externalCall(\"setTimeout\", [callbackId, nMilliseconds]);\n return this._registerTimeout(callbackId, false);\n }\n trustedFunction() {}\n trustPropagatorFunction() {}\n}\n\n;// ./src/scripting_api/console.js\n\nclass Console extends PDFObject {\n clear() {\n this._send({\n id: \"clear\"\n });\n }\n hide() {}\n println(msg) {\n if (typeof msg === \"string\") {\n this._send({\n command: \"println\",\n value: \"PDF.js Console:: \" + msg\n });\n }\n }\n show() {}\n}\n\n;// ./src/scripting_api/print_params.js\nclass PrintParams {\n constructor(data) {\n this.binaryOk = true;\n this.bitmapDPI = 150;\n this.booklet = {\n binding: 0,\n duplexMode: 0,\n subsetFrom: 0,\n subsetTo: -1\n };\n this.colorOverride = 0;\n this.colorProfile = \"\";\n this.constants = Object.freeze({\n bookletBindings: Object.freeze({\n Left: 0,\n Right: 1,\n LeftTall: 2,\n RightTall: 3\n }),\n bookletDuplexMode: Object.freeze({\n BothSides: 0,\n FrontSideOnly: 1,\n BasicSideOnly: 2\n }),\n colorOverrides: Object.freeze({\n auto: 0,\n gray: 1,\n mono: 2\n }),\n fontPolicies: Object.freeze({\n everyPage: 0,\n jobStart: 1,\n pageRange: 2\n }),\n handling: Object.freeze({\n none: 0,\n fit: 1,\n shrink: 2,\n tileAll: 3,\n tileLarge: 4,\n nUp: 5,\n booklet: 6\n }),\n interactionLevel: Object.freeze({\n automatic: 0,\n full: 1,\n silent: 2\n }),\n nUpPageOrders: Object.freeze({\n Horizontal: 0,\n HorizontalReversed: 1,\n Vertical: 2\n }),\n printContents: Object.freeze({\n doc: 0,\n docAndComments: 1,\n formFieldsOnly: 2\n }),\n flagValues: Object.freeze({\n applyOverPrint: 1,\n applySoftProofSettings: 1 << 1,\n applyWorkingColorSpaces: 1 << 2,\n emitHalftones: 1 << 3,\n emitPostScriptXObjects: 1 << 4,\n emitFormsAsPSForms: 1 << 5,\n maxJP2KRes: 1 << 6,\n setPageSize: 1 << 7,\n suppressBG: 1 << 8,\n suppressCenter: 1 << 9,\n suppressCJKFontSubst: 1 << 10,\n suppressCropClip: 1 << 1,\n suppressRotate: 1 << 12,\n suppressTransfer: 1 << 13,\n suppressUCR: 1 << 14,\n useTrapAnnots: 1 << 15,\n usePrintersMarks: 1 << 16\n }),\n rasterFlagValues: Object.freeze({\n textToOutline: 1,\n strokesToOutline: 1 << 1,\n allowComplexClip: 1 << 2,\n preserveOverprint: 1 << 3\n }),\n subsets: Object.freeze({\n all: 0,\n even: 1,\n odd: 2\n }),\n tileMarks: Object.freeze({\n none: 0,\n west: 1,\n east: 2\n }),\n usages: Object.freeze({\n auto: 0,\n use: 1,\n noUse: 2\n })\n });\n this.downloadFarEastFonts = false;\n this.fileName = \"\";\n this.firstPage = 0;\n this.flags = 0;\n this.fontPolicy = 0;\n this.gradientDPI = 150;\n this.interactive = 1;\n this.lastPage = data.lastPage;\n this.npUpAutoRotate = false;\n this.npUpNumPagesH = 2;\n this.npUpNumPagesV = 2;\n this.npUpPageBorder = false;\n this.npUpPageOrder = 0;\n this.pageHandling = 0;\n this.pageSubset = 0;\n this.printAsImage = false;\n this.printContent = 0;\n this.printerName = \"\";\n this.psLevel = 0;\n this.rasterFlags = 0;\n this.reversePages = false;\n this.tileLabel = false;\n this.tileMark = 0;\n this.tileOverlap = 0;\n this.tileScale = 1.0;\n this.transparencyLevel = 75;\n this.usePrinterCRD = 0;\n this.useT1Conversion = 0;\n }\n}\n\n;// ./src/scripting_api/doc.js\n\n\n\n\n\nconst DOC_EXTERNAL = false;\nclass InfoProxyHandler {\n static get(obj, prop) {\n return obj[prop.toLowerCase()];\n }\n static set(obj, prop, value) {\n throw new Error(`doc.info.${prop} is read-only`);\n }\n}\nclass Doc extends PDFObject {\n constructor(data) {\n super(data);\n this._expandos = globalThis;\n this._baseURL = data.baseURL || \"\";\n this._calculate = true;\n this._delay = false;\n this._dirty = false;\n this._disclosed = false;\n this._media = undefined;\n this._metadata = data.metadata || \"\";\n this._noautocomplete = undefined;\n this._nocache = undefined;\n this._spellDictionaryOrder = [];\n this._spellLanguageOrder = [];\n this._printParams = null;\n this._fields = new Map();\n this._fieldNames = [];\n this._event = null;\n this._author = data.Author || \"\";\n this._creator = data.Creator || \"\";\n this._creationDate = this._getDate(data.CreationDate) || null;\n this._docID = data.docID || [\"\", \"\"];\n this._documentFileName = data.filename || \"\";\n this._filesize = data.filesize || 0;\n this._keywords = data.Keywords || \"\";\n this._layout = data.layout || \"\";\n this._modDate = this._getDate(data.ModDate) || null;\n this._numFields = 0;\n this._numPages = data.numPages || 1;\n this._pageNum = data.pageNum || 0;\n this._producer = data.Producer || \"\";\n this._securityHandler = data.EncryptFilterName || null;\n this._subject = data.Subject || \"\";\n this._title = data.Title || \"\";\n this._URL = data.URL || \"\";\n this._info = new Proxy({\n title: this._title,\n author: this._author,\n authors: data.authors || [this._author],\n subject: this._subject,\n keywords: this._keywords,\n creator: this._creator,\n producer: this._producer,\n creationdate: this._creationDate,\n moddate: this._modDate,\n trapped: data.Trapped || \"Unknown\"\n }, InfoProxyHandler);\n this._zoomType = ZoomType.none;\n this._zoom = data.zoom || 100;\n this._actions = createActionsMap(data.actions);\n this._globalEval = data.globalEval;\n this._pageActions = null;\n this._userActivation = false;\n this._disablePrinting = false;\n this._disableSaving = false;\n this._otherPageActions = null;\n }\n _initActions() {\n const dontRun = new Set([\"WillClose\", \"WillSave\", \"DidSave\", \"WillPrint\", \"DidPrint\", \"OpenAction\"]);\n this._disableSaving = true;\n for (const actionName of this._actions.keys()) {\n if (!dontRun.has(actionName)) {\n this._runActions(actionName);\n }\n }\n this._runActions(\"OpenAction\");\n this._disableSaving = false;\n }\n _dispatchDocEvent(name) {\n switch (name) {\n case \"Open\":\n this._disableSaving = true;\n this._runActions(\"OpenAction\");\n this._disableSaving = false;\n break;\n case \"WillPrint\":\n this._disablePrinting = true;\n try {\n this._runActions(name);\n } catch (error) {\n this._send(serializeError(error));\n }\n this._send({\n command: \"WillPrintFinished\"\n });\n this._disablePrinting = false;\n break;\n case \"WillSave\":\n this._disableSaving = true;\n this._runActions(name);\n this._disableSaving = false;\n break;\n default:\n this._runActions(name);\n }\n }\n _dispatchPageEvent(name, actions, pageNumber) {\n if (name === \"PageOpen\") {\n this._pageActions ||= new Map();\n if (!this._pageActions.has(pageNumber)) {\n this._pageActions.set(pageNumber, createActionsMap(actions));\n }\n this._pageNum = pageNumber - 1;\n }\n for (const acts of [this._pageActions, this._otherPageActions]) {\n actions = acts?.get(pageNumber)?.get(name);\n if (actions) {\n for (const action of actions) {\n this._globalEval(action);\n }\n }\n }\n }\n _runActions(name) {\n const actions = this._actions.get(name);\n if (actions) {\n for (const action of actions) {\n this._globalEval(action);\n }\n }\n }\n _addField(name, field) {\n this._fields.set(name, field);\n this._fieldNames.push(name);\n this._numFields++;\n const po = field.obj._actions.get(\"PageOpen\");\n const pc = field.obj._actions.get(\"PageClose\");\n if (po || pc) {\n this._otherPageActions ||= new Map();\n let actions = this._otherPageActions.get(field.obj._page + 1);\n if (!actions) {\n actions = new Map();\n this._otherPageActions.set(field.obj._page + 1, actions);\n }\n if (po) {\n let poActions = actions.get(\"PageOpen\");\n if (!poActions) {\n poActions = [];\n actions.set(\"PageOpen\", poActions);\n }\n poActions.push(...po);\n }\n if (pc) {\n let pcActions = actions.get(\"PageClose\");\n if (!pcActions) {\n pcActions = [];\n actions.set(\"PageClose\", pcActions);\n }\n pcActions.push(...pc);\n }\n }\n }\n _getDate(date) {\n if (!date || date.length < 15 || !date.startsWith(\"D:\")) {\n return date;\n }\n date = date.substring(2);\n const year = date.substring(0, 4);\n const month = date.substring(4, 6);\n const day = date.substring(6, 8);\n const hour = date.substring(8, 10);\n const minute = date.substring(10, 12);\n const o = date.charAt(12);\n let second, offsetPos;\n if (o === \"Z\" || o === \"+\" || o === \"-\") {\n second = \"00\";\n offsetPos = 12;\n } else {\n second = date.substring(12, 14);\n offsetPos = 14;\n }\n const offset = date.substring(offsetPos).replaceAll(\"'\", \"\");\n return new Date(`${year}-${month}-${day}T${hour}:${minute}:${second}${offset}`);\n }\n get author() {\n return this._author;\n }\n set author(_) {\n throw new Error(\"doc.author is read-only\");\n }\n get baseURL() {\n return this._baseURL;\n }\n set baseURL(baseURL) {\n this._baseURL = baseURL;\n }\n get bookmarkRoot() {\n return undefined;\n }\n set bookmarkRoot(_) {\n throw new Error(\"doc.bookmarkRoot is read-only\");\n }\n get calculate() {\n return this._calculate;\n }\n set calculate(calculate) {\n this._calculate = calculate;\n }\n get creator() {\n return this._creator;\n }\n set creator(_) {\n throw new Error(\"doc.creator is read-only\");\n }\n get dataObjects() {\n return [];\n }\n set dataObjects(_) {\n throw new Error(\"doc.dataObjects is read-only\");\n }\n get delay() {\n return this._delay;\n }\n set delay(delay) {\n this._delay = delay;\n }\n get dirty() {\n return this._dirty;\n }\n set dirty(dirty) {\n this._dirty = dirty;\n }\n get disclosed() {\n return this._disclosed;\n }\n set disclosed(disclosed) {\n this._disclosed = disclosed;\n }\n get docID() {\n return this._docID;\n }\n set docID(_) {\n throw new Error(\"doc.docID is read-only\");\n }\n get documentFileName() {\n return this._documentFileName;\n }\n set documentFileName(_) {\n throw new Error(\"doc.documentFileName is read-only\");\n }\n get dynamicXFAForm() {\n return false;\n }\n set dynamicXFAForm(_) {\n throw new Error(\"doc.dynamicXFAForm is read-only\");\n }\n get external() {\n return DOC_EXTERNAL;\n }\n set external(_) {\n throw new Error(\"doc.external is read-only\");\n }\n get filesize() {\n return this._filesize;\n }\n set filesize(_) {\n throw new Error(\"doc.filesize is read-only\");\n }\n get hidden() {\n return false;\n }\n set hidden(_) {\n throw new Error(\"doc.hidden is read-only\");\n }\n get hostContainer() {\n return undefined;\n }\n set hostContainer(_) {\n throw new Error(\"doc.hostContainer is read-only\");\n }\n get icons() {\n return undefined;\n }\n set icons(_) {\n throw new Error(\"doc.icons is read-only\");\n }\n get info() {\n return this._info;\n }\n set info(_) {\n throw new Error(\"doc.info is read-only\");\n }\n get innerAppWindowRect() {\n return [0, 0, 0, 0];\n }\n set innerAppWindowRect(_) {\n throw new Error(\"doc.innerAppWindowRect is read-only\");\n }\n get innerDocWindowRect() {\n return [0, 0, 0, 0];\n }\n set innerDocWindowRect(_) {\n throw new Error(\"doc.innerDocWindowRect is read-only\");\n }\n get isModal() {\n return false;\n }\n set isModal(_) {\n throw new Error(\"doc.isModal is read-only\");\n }\n get keywords() {\n return this._keywords;\n }\n set keywords(_) {\n throw new Error(\"doc.keywords is read-only\");\n }\n get layout() {\n return this._layout;\n }\n set layout(value) {\n if (!this._userActivation) {\n return;\n }\n this._userActivation = false;\n if (typeof value !== \"string\") {\n return;\n }\n if (value !== \"SinglePage\" && value !== \"OneColumn\" && value !== \"TwoColumnLeft\" && value !== \"TwoPageLeft\" && value !== \"TwoColumnRight\" && value !== \"TwoPageRight\") {\n value = \"SinglePage\";\n }\n this._send({\n command: \"layout\",\n value\n });\n this._layout = value;\n }\n get media() {\n return this._media;\n }\n set media(media) {\n this._media = media;\n }\n get metadata() {\n return this._metadata;\n }\n set metadata(metadata) {\n this._metadata = metadata;\n }\n get modDate() {\n return this._modDate;\n }\n set modDate(_) {\n throw new Error(\"doc.modDate is read-only\");\n }\n get mouseX() {\n return 0;\n }\n set mouseX(_) {\n throw new Error(\"doc.mouseX is read-only\");\n }\n get mouseY() {\n return 0;\n }\n set mouseY(_) {\n throw new Error(\"doc.mouseY is read-only\");\n }\n get noautocomplete() {\n return this._noautocomplete;\n }\n set noautocomplete(noautocomplete) {\n this._noautocomplete = noautocomplete;\n }\n get nocache() {\n return this._nocache;\n }\n set nocache(nocache) {\n this._nocache = nocache;\n }\n get numFields() {\n return this._numFields;\n }\n set numFields(_) {\n throw new Error(\"doc.numFields is read-only\");\n }\n get numPages() {\n return this._numPages;\n }\n set numPages(_) {\n throw new Error(\"doc.numPages is read-only\");\n }\n get numTemplates() {\n return 0;\n }\n set numTemplates(_) {\n throw new Error(\"doc.numTemplates is read-only\");\n }\n get outerAppWindowRect() {\n return [0, 0, 0, 0];\n }\n set outerAppWindowRect(_) {\n throw new Error(\"doc.outerAppWindowRect is read-only\");\n }\n get outerDocWindowRect() {\n return [0, 0, 0, 0];\n }\n set outerDocWindowRect(_) {\n throw new Error(\"doc.outerDocWindowRect is read-only\");\n }\n get pageNum() {\n return this._pageNum;\n }\n set pageNum(value) {\n if (!this._userActivation) {\n return;\n }\n this._userActivation = false;\n if (typeof value !== \"number\" || value < 0 || value >= this._numPages) {\n return;\n }\n this._send({\n command: \"page-num\",\n value\n });\n this._pageNum = value;\n }\n get pageWindowRect() {\n return [0, 0, 0, 0];\n }\n set pageWindowRect(_) {\n throw new Error(\"doc.pageWindowRect is read-only\");\n }\n get path() {\n return \"\";\n }\n set path(_) {\n throw new Error(\"doc.path is read-only\");\n }\n get permStatusReady() {\n return true;\n }\n set permStatusReady(_) {\n throw new Error(\"doc.permStatusReady is read-only\");\n }\n get producer() {\n return this._producer;\n }\n set producer(_) {\n throw new Error(\"doc.producer is read-only\");\n }\n get requiresFullSave() {\n return false;\n }\n set requiresFullSave(_) {\n throw new Error(\"doc.requiresFullSave is read-only\");\n }\n get securityHandler() {\n return this._securityHandler;\n }\n set securityHandler(_) {\n throw new Error(\"doc.securityHandler is read-only\");\n }\n get selectedAnnots() {\n return [];\n }\n set selectedAnnots(_) {\n throw new Error(\"doc.selectedAnnots is read-only\");\n }\n get sounds() {\n return [];\n }\n set sounds(_) {\n throw new Error(\"doc.sounds is read-only\");\n }\n get spellDictionaryOrder() {\n return this._spellDictionaryOrder;\n }\n set spellDictionaryOrder(spellDictionaryOrder) {\n this._spellDictionaryOrder = spellDictionaryOrder;\n }\n get spellLanguageOrder() {\n return this._spellLanguageOrder;\n }\n set spellLanguageOrder(spellLanguageOrder) {\n this._spellLanguageOrder = spellLanguageOrder;\n }\n get subject() {\n return this._subject;\n }\n set subject(_) {\n throw new Error(\"doc.subject is read-only\");\n }\n get templates() {\n return [];\n }\n set templates(_) {\n throw new Error(\"doc.templates is read-only\");\n }\n get title() {\n return this._title;\n }\n set title(_) {\n throw new Error(\"doc.title is read-only\");\n }\n get URL() {\n return this._URL;\n }\n set URL(_) {\n throw new Error(\"doc.URL is read-only\");\n }\n get viewState() {\n return undefined;\n }\n set viewState(_) {\n throw new Error(\"doc.viewState is read-only\");\n }\n get xfa() {\n return this._xfa;\n }\n set xfa(_) {\n throw new Error(\"doc.xfa is read-only\");\n }\n get XFAForeground() {\n return false;\n }\n set XFAForeground(_) {\n throw new Error(\"doc.XFAForeground is read-only\");\n }\n get zoomType() {\n return this._zoomType;\n }\n set zoomType(type) {\n if (!this._userActivation) {\n return;\n }\n this._userActivation = false;\n if (typeof type !== \"string\") {\n return;\n }\n switch (type) {\n case ZoomType.none:\n this._send({\n command: \"zoom\",\n value: 1\n });\n break;\n case ZoomType.fitP:\n this._send({\n command: \"zoom\",\n value: \"page-fit\"\n });\n break;\n case ZoomType.fitW:\n this._send({\n command: \"zoom\",\n value: \"page-width\"\n });\n break;\n case ZoomType.fitH:\n this._send({\n command: \"zoom\",\n value: \"page-height\"\n });\n break;\n case ZoomType.fitV:\n this._send({\n command: \"zoom\",\n value: \"auto\"\n });\n break;\n case ZoomType.pref:\n case ZoomType.refW:\n break;\n default:\n return;\n }\n this._zoomType = type;\n }\n get zoom() {\n return this._zoom;\n }\n set zoom(value) {\n if (!this._userActivation) {\n return;\n }\n this._userActivation = false;\n if (typeof value !== \"number\" || value < 8.33 || value > 6400) {\n return;\n }\n this._send({\n command: \"zoom\",\n value: value / 100\n });\n }\n addAnnot() {}\n addField() {}\n addIcon() {}\n addLink() {}\n addRecipientListCryptFilter() {}\n addRequirement() {}\n addScript() {}\n addThumbnails() {}\n addWatermarkFromFile() {}\n addWatermarkFromText() {}\n addWeblinks() {}\n bringToFront() {}\n calculateNow() {\n this._eventDispatcher.calculateNow();\n }\n closeDoc() {}\n colorConvertPage() {}\n createDataObject() {}\n createTemplate() {}\n deletePages() {}\n deleteSound() {}\n embedDocAsDataObject() {}\n embedOutputIntent() {}\n encryptForRecipients() {}\n encryptUsingPolicy() {}\n exportAsFDF() {}\n exportAsFDFStr() {}\n exportAsText() {}\n exportAsXFDF() {}\n exportAsXFDFStr() {}\n exportDataObject() {}\n exportXFAData() {}\n extractPages() {}\n flattenPages() {}\n getAnnot() {}\n getAnnots() {}\n getAnnot3D() {}\n getAnnots3D() {}\n getColorConvertAction() {}\n getDataObject() {}\n getDataObjectContents() {}\n _getField(cName) {\n if (cName && typeof cName === \"object\") {\n cName = cName.cName;\n }\n if (typeof cName !== \"string\") {\n throw new TypeError(\"Invalid field name: must be a string\");\n }\n const searchedField = this._fields.get(cName);\n if (searchedField) {\n return searchedField;\n }\n const parts = cName.split(\"#\");\n let childIndex = NaN;\n if (parts.length === 2) {\n childIndex = Math.floor(parseFloat(parts[1]));\n cName = parts[0];\n }\n for (const [name, field] of this._fields.entries()) {\n if (name.endsWith(cName)) {\n if (!isNaN(childIndex)) {\n const children = this._getChildren(name);\n if (childIndex < 0 || childIndex >= children.length) {\n childIndex = 0;\n }\n if (childIndex < children.length) {\n this._fields.set(cName, children[childIndex]);\n return children[childIndex];\n }\n }\n this._fields.set(cName, field);\n return field;\n }\n }\n return null;\n }\n getField(cName) {\n const field = this._getField(cName);\n if (!field) {\n return null;\n }\n return field.wrapped;\n }\n _getChildren(fieldName) {\n const len = fieldName.length;\n const children = [];\n const pattern = /^\\.[^.]+$/;\n for (const [name, field] of this._fields.entries()) {\n if (name.startsWith(fieldName)) {\n const finalPart = name.slice(len);\n if (pattern.test(finalPart)) {\n children.push(field);\n }\n }\n }\n return children;\n }\n _getTerminalChildren(fieldName) {\n const children = [];\n const len = fieldName.length;\n for (const [name, field] of this._fields.entries()) {\n if (name.startsWith(fieldName)) {\n const finalPart = name.slice(len);\n if (field.obj._hasValue && (finalPart === \"\" || finalPart.startsWith(\".\"))) {\n children.push(field.wrapped);\n }\n }\n }\n return children;\n }\n getIcon() {}\n getLegalWarnings() {}\n getLinks() {}\n getNthFieldName(nIndex) {\n if (nIndex && typeof nIndex === \"object\") {\n nIndex = nIndex.nIndex;\n }\n if (typeof nIndex !== \"number\") {\n throw new TypeError(\"Invalid field index: must be a number\");\n }\n if (0 <= nIndex && nIndex < this.numFields) {\n return this._fieldNames[Math.trunc(nIndex)];\n }\n return null;\n }\n getNthTemplate() {\n return null;\n }\n getOCGs() {}\n getOCGOrder() {}\n getPageBox() {}\n getPageLabel() {}\n getPageNthWord() {}\n getPageNthWordQuads() {}\n getPageNumWords() {}\n getPageRotation() {}\n getPageTransition() {}\n getPrintParams() {\n return this._printParams ||= new PrintParams({\n lastPage: this._numPages - 1\n });\n }\n getSound() {}\n getTemplate() {}\n getURL() {}\n gotoNamedDest() {}\n importAnFDF() {}\n importAnXFDF() {}\n importDataObject() {}\n importIcon() {}\n importSound() {}\n importTextData() {}\n importXFAData() {}\n insertPages() {}\n mailDoc() {}\n mailForm() {}\n movePage() {}\n newPage() {}\n openDataObject() {}\n print(bUI = true, nStart = 0, nEnd = -1, bSilent = false, bShrinkToFit = false, bPrintAsImage = false, bReverse = false, bAnnotations = true, printParams = null) {\n if (this._disablePrinting || !this._userActivation) {\n return;\n }\n this._userActivation = false;\n if (bUI && typeof bUI === \"object\") {\n nStart = bUI.nStart;\n nEnd = bUI.nEnd;\n bSilent = bUI.bSilent;\n bShrinkToFit = bUI.bShrinkToFit;\n bPrintAsImage = bUI.bPrintAsImage;\n bReverse = bUI.bReverse;\n bAnnotations = bUI.bAnnotations;\n printParams = bUI.printParams;\n bUI = bUI.bUI;\n }\n if (printParams) {\n nStart = printParams.firstPage;\n nEnd = printParams.lastPage;\n }\n nStart = typeof nStart === \"number\" ? Math.max(0, Math.trunc(nStart)) : 0;\n nEnd = typeof nEnd === \"number\" ? Math.max(0, Math.trunc(nEnd)) : -1;\n this._send({\n command: \"print\",\n start: nStart,\n end: nEnd\n });\n }\n removeDataObject() {}\n removeField() {}\n removeIcon() {}\n removeLinks() {}\n removeRequirement() {}\n removeScript() {}\n removeTemplate() {}\n removeThumbnails() {}\n removeWeblinks() {}\n replacePages() {}\n resetForm(aFields = null) {\n if (aFields && typeof aFields === \"object\" && !Array.isArray(aFields)) {\n aFields = aFields.aFields;\n }\n if (aFields && !Array.isArray(aFields)) {\n aFields = [aFields];\n }\n let mustCalculate = false;\n let fieldsToReset;\n if (aFields) {\n fieldsToReset = [];\n for (const fieldName of aFields) {\n if (!fieldName) {\n continue;\n }\n if (typeof fieldName !== \"string\") {\n fieldsToReset = null;\n break;\n }\n const field = this._getField(fieldName);\n if (!field) {\n continue;\n }\n fieldsToReset.push(field);\n mustCalculate = true;\n }\n }\n if (!fieldsToReset) {\n fieldsToReset = this._fields.values();\n mustCalculate = this._fields.size !== 0;\n }\n for (const field of fieldsToReset) {\n field.obj.value = field.obj.defaultValue;\n this._send({\n id: field.obj._id,\n siblings: field.obj._siblings,\n value: field.obj.defaultValue,\n formattedValue: null,\n selRange: [0, 0]\n });\n }\n if (mustCalculate) {\n this.calculateNow();\n }\n }\n saveAs() {}\n scroll() {}\n selectPageNthWord() {}\n setAction() {}\n setDataObjectContents() {}\n setOCGOrder() {}\n setPageAction() {}\n setPageBoxes() {}\n setPageLabels() {}\n setPageRotations() {}\n setPageTabOrder() {}\n setPageTransitions() {}\n spawnPageFromTemplate() {}\n submitForm() {}\n syncAnnotScan() {}\n}\n\n;// ./src/scripting_api/proxy.js\nclass ProxyHandler {\n constructor() {\n this.nosend = new Set([\"delay\"]);\n }\n get(obj, prop) {\n if (prop in obj._expandos) {\n const val = obj._expandos[prop];\n if (typeof val === \"function\") {\n return val.bind(obj);\n }\n return val;\n }\n if (typeof prop === \"string\" && !prop.startsWith(\"_\") && prop in obj) {\n const val = obj[prop];\n if (typeof val === \"function\") {\n return val.bind(obj);\n }\n return val;\n }\n return undefined;\n }\n set(obj, prop, value) {\n if (obj._kidIds) {\n obj._kidIds.forEach(id => {\n obj._appObjects[id].wrapped[prop] = value;\n });\n }\n if (typeof prop === \"string\" && !prop.startsWith(\"_\") && prop in obj) {\n const old = obj[prop];\n obj[prop] = value;\n if (!this.nosend.has(prop) && obj._send && obj._id !== null && typeof old !== \"function\") {\n const data = {\n id: obj._id\n };\n data[prop] = prop === \"value\" ? obj._getValue() : obj[prop];\n if (!obj._siblings) {\n obj._send(data);\n } else {\n data.siblings = obj._siblings;\n obj._send(data);\n }\n }\n } else {\n obj._expandos[prop] = value;\n }\n return true;\n }\n has(obj, prop) {\n return prop in obj._expandos || typeof prop === \"string\" && !prop.startsWith(\"_\") && prop in obj;\n }\n getPrototypeOf(obj) {\n return null;\n }\n setPrototypeOf(obj, proto) {\n return false;\n }\n isExtensible(obj) {\n return true;\n }\n preventExtensions(obj) {\n return false;\n }\n getOwnPropertyDescriptor(obj, prop) {\n if (prop in obj._expandos) {\n return {\n configurable: true,\n enumerable: true,\n value: obj._expandos[prop]\n };\n }\n if (typeof prop === \"string\" && !prop.startsWith(\"_\") && prop in obj) {\n return {\n configurable: true,\n enumerable: true,\n value: obj[prop]\n };\n }\n return undefined;\n }\n defineProperty(obj, key, descriptor) {\n Object.defineProperty(obj._expandos, key, descriptor);\n return true;\n }\n deleteProperty(obj, prop) {\n if (prop in obj._expandos) {\n delete obj._expandos[prop];\n }\n }\n ownKeys(obj) {\n const fromExpandos = Reflect.ownKeys(obj._expandos);\n const fromObj = Reflect.ownKeys(obj).filter(k => !k.startsWith(\"_\"));\n return fromExpandos.concat(fromObj);\n }\n}\n\n;// ./src/scripting_api/util.js\n\nclass Util extends PDFObject {\n #dateActionsCache = null;\n constructor(data) {\n super(data);\n this._scandCache = new Map();\n this._months = [\"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"];\n this._days = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\n this.MILLISECONDS_IN_DAY = 86400000;\n this.MILLISECONDS_IN_WEEK = 604800000;\n this._externalCall = data.externalCall;\n }\n printf(...args) {\n if (args.length === 0) {\n throw new Error(\"Invalid number of params in printf\");\n }\n if (typeof args[0] !== \"string\") {\n throw new TypeError(\"First argument of printf must be a string\");\n }\n const pattern = /%(,[0-4])?([+ 0#]+)?(\\d+)?(\\.\\d+)?(.)/g;\n const PLUS = 1;\n const SPACE = 2;\n const ZERO = 4;\n const HASH = 8;\n let i = 0;\n return args[0].replaceAll(pattern, function (match, nDecSep, cFlags, nWidth, nPrecision, cConvChar) {\n if (cConvChar !== \"d\" && cConvChar !== \"f\" && cConvChar !== \"s\" && cConvChar !== \"x\") {\n const buf = [\"%\"];\n for (const str of [nDecSep, cFlags, nWidth, nPrecision, cConvChar]) {\n if (str) {\n buf.push(str);\n }\n }\n return buf.join(\"\");\n }\n i++;\n if (i === args.length) {\n throw new Error(\"Not enough arguments in printf\");\n }\n const arg = args[i];\n if (cConvChar === \"s\") {\n return arg.toString();\n }\n let flags = 0;\n if (cFlags) {\n for (const flag of cFlags) {\n switch (flag) {\n case \"+\":\n flags |= PLUS;\n break;\n case \" \":\n flags |= SPACE;\n break;\n case \"0\":\n flags |= ZERO;\n break;\n case \"#\":\n flags |= HASH;\n break;\n }\n }\n }\n cFlags = flags;\n if (nWidth) {\n nWidth = parseInt(nWidth);\n }\n let intPart = Math.trunc(arg);\n if (cConvChar === \"x\") {\n let hex = Math.abs(intPart).toString(16).toUpperCase();\n if (nWidth !== undefined) {\n hex = hex.padStart(nWidth, cFlags & ZERO ? \"0\" : \" \");\n }\n if (cFlags & HASH) {\n hex = `0x${hex}`;\n }\n return hex;\n }\n if (nPrecision) {\n nPrecision = parseInt(nPrecision.substring(1));\n }\n nDecSep = nDecSep ? nDecSep.substring(1) : \"0\";\n const separators = {\n 0: [\",\", \".\"],\n 1: [\"\", \".\"],\n 2: [\".\", \",\"],\n 3: [\"\", \",\"],\n 4: [\"'\", \".\"]\n };\n const [thousandSep, decimalSep] = separators[nDecSep];\n let decPart = \"\";\n if (cConvChar === \"f\") {\n decPart = nPrecision !== undefined ? Math.abs(arg - intPart).toFixed(nPrecision) : Math.abs(arg - intPart).toString();\n if (decPart.length > 2) {\n if (/^1\\.0+$/.test(decPart)) {\n intPart += Math.sign(arg);\n decPart = `${decimalSep}${decPart.split(\".\")[1]}`;\n } else {\n decPart = `${decimalSep}${decPart.substring(2)}`;\n }\n } else {\n if (decPart === \"1\") {\n intPart += Math.sign(arg);\n }\n decPart = cFlags & HASH ? \".\" : \"\";\n }\n }\n let sign = \"\";\n if (intPart < 0) {\n sign = \"-\";\n intPart = -intPart;\n } else if (cFlags & PLUS) {\n sign = \"+\";\n } else if (cFlags & SPACE) {\n sign = \" \";\n }\n if (thousandSep && intPart >= 1000) {\n const buf = [];\n while (true) {\n buf.push((intPart % 1000).toString().padStart(3, \"0\"));\n intPart = Math.trunc(intPart / 1000);\n if (intPart < 1000) {\n buf.push(intPart.toString());\n break;\n }\n }\n intPart = buf.reverse().join(thousandSep);\n } else {\n intPart = intPart.toString();\n }\n let n = `${intPart}${decPart}`;\n if (nWidth !== undefined) {\n n = n.padStart(nWidth - sign.length, cFlags & ZERO ? \"0\" : \" \");\n }\n return `${sign}${n}`;\n });\n }\n iconStreamFromIcon() {}\n printd(cFormat, oDate) {\n switch (cFormat) {\n case 0:\n return this.printd(\"D:yyyymmddHHMMss\", oDate);\n case 1:\n return this.printd(\"yyyy.mm.dd HH:MM:ss\", oDate);\n case 2:\n return this.printd(\"m/d/yy h:MM:ss tt\", oDate);\n }\n const handlers = {\n mmmm: data => this._months[data.month],\n mmm: data => this._months[data.month].substring(0, 3),\n mm: data => (data.month + 1).toString().padStart(2, \"0\"),\n m: data => (data.month + 1).toString(),\n dddd: data => this._days[data.dayOfWeek],\n ddd: data => this._days[data.dayOfWeek].substring(0, 3),\n dd: data => data.day.toString().padStart(2, \"0\"),\n d: data => data.day.toString(),\n yyyy: data => data.year.toString(),\n yy: data => (data.year % 100).toString().padStart(2, \"0\"),\n HH: data => data.hours.toString().padStart(2, \"0\"),\n H: data => data.hours.toString(),\n hh: data => (1 + (data.hours + 11) % 12).toString().padStart(2, \"0\"),\n h: data => (1 + (data.hours + 11) % 12).toString(),\n MM: data => data.minutes.toString().padStart(2, \"0\"),\n M: data => data.minutes.toString(),\n ss: data => data.seconds.toString().padStart(2, \"0\"),\n s: data => data.seconds.toString(),\n tt: data => data.hours < 12 ? \"am\" : \"pm\",\n t: data => data.hours < 12 ? \"a\" : \"p\"\n };\n const data = {\n year: oDate.getFullYear(),\n month: oDate.getMonth(),\n day: oDate.getDate(),\n dayOfWeek: oDate.getDay(),\n hours: oDate.getHours(),\n minutes: oDate.getMinutes(),\n seconds: oDate.getSeconds()\n };\n const patterns = /(mmmm|mmm|mm|m|dddd|ddd|dd|d|yyyy|yy|HH|H|hh|h|MM|M|ss|s|tt|t|\\\\.)/g;\n return cFormat.replaceAll(patterns, function (match, pattern) {\n if (pattern in handlers) {\n return handlers[pattern](data);\n }\n return pattern.charCodeAt(1);\n });\n }\n printx(cFormat, cSource) {\n cSource = (cSource ?? \"\").toString();\n const handlers = [x => x, x => x.toUpperCase(), x => x.toLowerCase()];\n const buf = [];\n let i = 0;\n const ii = cSource.length;\n let currCase = handlers[0];\n let escaped = false;\n for (const command of cFormat) {\n if (escaped) {\n buf.push(command);\n escaped = false;\n continue;\n }\n if (i >= ii) {\n break;\n }\n switch (command) {\n case \"?\":\n buf.push(currCase(cSource.charAt(i++)));\n break;\n case \"X\":\n while (i < ii) {\n const char = cSource.charAt(i++);\n if (\"a\" <= char && char <= \"z\" || \"A\" <= char && char <= \"Z\" || \"0\" <= char && char <= \"9\") {\n buf.push(currCase(char));\n break;\n }\n }\n break;\n case \"A\":\n while (i < ii) {\n const char = cSource.charAt(i++);\n if (\"a\" <= char && char <= \"z\" || \"A\" <= char && char <= \"Z\") {\n buf.push(currCase(char));\n break;\n }\n }\n break;\n case \"9\":\n while (i < ii) {\n const char = cSource.charAt(i++);\n if (\"0\" <= char && char <= \"9\") {\n buf.push(char);\n break;\n }\n }\n break;\n case \"*\":\n while (i < ii) {\n buf.push(currCase(cSource.charAt(i++)));\n }\n break;\n case \"\\\\\":\n escaped = true;\n break;\n case \">\":\n currCase = handlers[1];\n break;\n case \"<\":\n currCase = handlers[2];\n break;\n case \"=\":\n currCase = handlers[0];\n break;\n default:\n buf.push(command);\n }\n }\n return buf.join(\"\");\n }\n #tryToGuessDate(cFormat, cDate) {\n let actions = (this.#dateActionsCache ||= new Map()).get(cFormat);\n if (!actions) {\n actions = [];\n this.#dateActionsCache.set(cFormat, actions);\n cFormat.replaceAll(/(d+)|(m+)|(y+)|(H+)|(M+)|(s+)/g, function (_match, d, m, y, H, M, s) {\n if (d) {\n actions.push((n, data) => {\n if (n >= 1 && n <= 31) {\n data.day = n;\n return true;\n }\n return false;\n });\n } else if (m) {\n actions.push((n, data) => {\n if (n >= 1 && n <= 12) {\n data.month = n - 1;\n return true;\n }\n return false;\n });\n } else if (y) {\n actions.push((n, data) => {\n if (n < 50) {\n n += 2000;\n } else if (n < 100) {\n n += 1900;\n }\n data.year = n;\n return true;\n });\n } else if (H) {\n actions.push((n, data) => {\n if (n >= 0 && n <= 23) {\n data.hours = n;\n return true;\n }\n return false;\n });\n } else if (M) {\n actions.push((n, data) => {\n if (n >= 0 && n <= 59) {\n data.minutes = n;\n return true;\n }\n return false;\n });\n } else if (s) {\n actions.push((n, data) => {\n if (n >= 0 && n <= 59) {\n data.seconds = n;\n return true;\n }\n return false;\n });\n }\n return \"\";\n });\n }\n const number = /\\d+/g;\n let i = 0;\n let array;\n const data = {\n year: new Date().getFullYear(),\n month: 0,\n day: 1,\n hours: 12,\n minutes: 0,\n seconds: 0\n };\n while ((array = number.exec(cDate)) !== null) {\n if (i < actions.length) {\n if (!actions[i++](parseInt(array[0]), data)) {\n return null;\n }\n } else {\n break;\n }\n }\n if (i === 0) {\n return null;\n }\n return new Date(data.year, data.month, data.day, data.hours, data.minutes, data.seconds);\n }\n scand(cFormat, cDate) {\n return this._scand(cFormat, cDate);\n }\n _scand(cFormat, cDate, strict = false) {\n if (typeof cDate !== \"string\") {\n return new Date(cDate);\n }\n if (cDate === \"\") {\n return new Date();\n }\n switch (cFormat) {\n case 0:\n return this.scand(\"D:yyyymmddHHMMss\", cDate);\n case 1:\n return this.scand(\"yyyy.mm.dd HH:MM:ss\", cDate);\n case 2:\n return this.scand(\"m/d/yy h:MM:ss tt\", cDate);\n }\n if (!this._scandCache.has(cFormat)) {\n const months = this._months;\n const days = this._days;\n const handlers = {\n mmmm: {\n pattern: `(${months.join(\"|\")})`,\n action: (value, data) => {\n data.month = months.indexOf(value);\n }\n },\n mmm: {\n pattern: `(${months.map(month => month.substring(0, 3)).join(\"|\")})`,\n action: (value, data) => {\n data.month = months.findIndex(month => month.substring(0, 3) === value);\n }\n },\n mm: {\n pattern: `(\\\\d{2})`,\n action: (value, data) => {\n data.month = parseInt(value) - 1;\n }\n },\n m: {\n pattern: `(\\\\d{1,2})`,\n action: (value, data) => {\n data.month = parseInt(value) - 1;\n }\n },\n dddd: {\n pattern: `(${days.join(\"|\")})`,\n action: (value, data) => {\n data.day = days.indexOf(value);\n }\n },\n ddd: {\n pattern: `(${days.map(day => day.substring(0, 3)).join(\"|\")})`,\n action: (value, data) => {\n data.day = days.findIndex(day => day.substring(0, 3) === value);\n }\n },\n dd: {\n pattern: \"(\\\\d{2})\",\n action: (value, data) => {\n data.day = parseInt(value);\n }\n },\n d: {\n pattern: \"(\\\\d{1,2})\",\n action: (value, data) => {\n data.day = parseInt(value);\n }\n },\n yyyy: {\n pattern: \"(\\\\d{4})\",\n action: (value, data) => {\n data.year = parseInt(value);\n }\n },\n yy: {\n pattern: \"(\\\\d{2})\",\n action: (value, data) => {\n data.year = 2000 + parseInt(value);\n }\n },\n HH: {\n pattern: \"(\\\\d{2})\",\n action: (value, data) => {\n data.hours = parseInt(value);\n }\n },\n H: {\n pattern: \"(\\\\d{1,2})\",\n action: (value, data) => {\n data.hours = parseInt(value);\n }\n },\n hh: {\n pattern: \"(\\\\d{2})\",\n action: (value, data) => {\n data.hours = parseInt(value);\n }\n },\n h: {\n pattern: \"(\\\\d{1,2})\",\n action: (value, data) => {\n data.hours = parseInt(value);\n }\n },\n MM: {\n pattern: \"(\\\\d{2})\",\n action: (value, data) => {\n data.minutes = parseInt(value);\n }\n },\n M: {\n pattern: \"(\\\\d{1,2})\",\n action: (value, data) => {\n data.minutes = parseInt(value);\n }\n },\n ss: {\n pattern: \"(\\\\d{2})\",\n action: (value, data) => {\n data.seconds = parseInt(value);\n }\n },\n s: {\n pattern: \"(\\\\d{1,2})\",\n action: (value, data) => {\n data.seconds = parseInt(value);\n }\n },\n tt: {\n pattern: \"([aApP][mM])\",\n action: (value, data) => {\n const char = value.charAt(0);\n data.am = char === \"a\" || char === \"A\";\n }\n },\n t: {\n pattern: \"([aApP])\",\n action: (value, data) => {\n data.am = value === \"a\" || value === \"A\";\n }\n }\n };\n const escapedFormat = cFormat.replaceAll(/[.*+\\-?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const patterns = /(mmmm|mmm|mm|m|dddd|ddd|dd|d|yyyy|yy|HH|H|hh|h|MM|M|ss|s|tt|t)/g;\n const actions = [];\n const re = escapedFormat.replaceAll(patterns, function (match, patternElement) {\n const {\n pattern,\n action\n } = handlers[patternElement];\n actions.push(action);\n return pattern;\n });\n this._scandCache.set(cFormat, [re, actions]);\n }\n const [re, actions] = this._scandCache.get(cFormat);\n const matches = new RegExp(`^${re}$`, \"g\").exec(cDate);\n if (!matches || matches.length !== actions.length + 1) {\n return strict ? null : this.#tryToGuessDate(cFormat, cDate);\n }\n const data = {\n year: new Date().getFullYear(),\n month: 0,\n day: 1,\n hours: 12,\n minutes: 0,\n seconds: 0,\n am: null\n };\n actions.forEach((action, i) => action(matches[i + 1], data));\n if (data.am !== null) {\n data.hours = data.hours % 12 + (data.am ? 0 : 12);\n }\n return new Date(data.year, data.month, data.day, data.hours, data.minutes, data.seconds);\n }\n spansToXML() {}\n stringFromStream() {}\n xmlToSpans() {}\n}\n\n;// ./src/scripting_api/initialization.js\n\n\n\n\n\n\n\n\n\n\nfunction initSandbox(params) {\n delete globalThis.pdfjsScripting;\n const externalCall = globalThis.callExternalFunction;\n delete globalThis.callExternalFunction;\n const globalEval = code => globalThis.eval(code);\n const send = data => externalCall(\"send\", [data]);\n const proxyHandler = new ProxyHandler();\n const {\n data\n } = params;\n const doc = new Doc({\n send,\n globalEval,\n ...data.docInfo\n });\n const _document = {\n obj: doc,\n wrapped: new Proxy(doc, proxyHandler)\n };\n const app = new App({\n send,\n globalEval,\n externalCall,\n _document,\n calculationOrder: data.calculationOrder,\n proxyHandler,\n ...data.appInfo\n });\n const util = new Util({\n externalCall\n });\n const appObjects = app._objects;\n if (data.objects) {\n const annotations = [];\n for (const [name, objs] of Object.entries(data.objects)) {\n annotations.length = 0;\n let container = null;\n for (const obj of objs) {\n if (obj.type !== \"\") {\n annotations.push(obj);\n } else {\n container = obj;\n }\n }\n let obj = container;\n if (annotations.length > 0) {\n obj = annotations[0];\n obj.send = send;\n }\n obj.globalEval = globalEval;\n obj.doc = _document;\n obj.fieldPath = name;\n obj.appObjects = appObjects;\n const otherFields = annotations.slice(1);\n let field;\n switch (obj.type) {\n case \"radiobutton\":\n {\n field = new RadioButtonField(otherFields, obj);\n break;\n }\n case \"checkbox\":\n {\n field = new CheckboxField(otherFields, obj);\n break;\n }\n default:\n if (otherFields.length > 0) {\n obj.siblings = otherFields.map(x => x.id);\n }\n field = new Field(obj);\n }\n const wrapped = new Proxy(field, proxyHandler);\n const _object = {\n obj: field,\n wrapped\n };\n doc._addField(name, _object);\n for (const object of objs) {\n appObjects[object.id] = _object;\n }\n if (container) {\n appObjects[container.id] = _object;\n }\n }\n }\n const color = new Color();\n globalThis.event = null;\n globalThis.global = Object.create(null);\n globalThis.app = new Proxy(app, proxyHandler);\n globalThis.color = new Proxy(color, proxyHandler);\n globalThis.console = new Proxy(new Console({\n send\n }), proxyHandler);\n globalThis.util = new Proxy(util, proxyHandler);\n globalThis.border = Border;\n globalThis.cursor = Cursor;\n globalThis.display = Display;\n globalThis.font = Font;\n globalThis.highlight = Highlight;\n globalThis.position = Position;\n globalThis.scaleHow = ScaleHow;\n globalThis.scaleWhen = ScaleWhen;\n globalThis.style = Style;\n globalThis.trans = Trans;\n globalThis.zoomtype = ZoomType;\n globalThis.ADBE = {\n Reader_Value_Asked: true,\n Viewer_Value_Asked: true\n };\n const aform = new AForm(doc, app, util, color);\n for (const name of Object.getOwnPropertyNames(AForm.prototype)) {\n if (name !== \"constructor\" && !name.startsWith(\"_\")) {\n globalThis[name] = aform[name].bind(aform);\n }\n }\n for (const [name, value] of Object.entries(GlobalConstants)) {\n Object.defineProperty(globalThis, name, {\n value,\n writable: false\n });\n }\n Object.defineProperties(globalThis, {\n ColorConvert: {\n value: color.convert.bind(color),\n writable: true\n },\n ColorEqual: {\n value: color.equal.bind(color),\n writable: true\n }\n });\n const properties = Object.create(null);\n for (const name of Object.getOwnPropertyNames(Doc.prototype)) {\n if (name === \"constructor\" || name.startsWith(\"_\")) {\n continue;\n }\n const descriptor = Object.getOwnPropertyDescriptor(Doc.prototype, name);\n if (descriptor.get) {\n properties[name] = {\n get: descriptor.get.bind(doc),\n set: descriptor.set.bind(doc)\n };\n } else {\n properties[name] = {\n value: Doc.prototype[name].bind(doc)\n };\n }\n }\n Object.defineProperties(globalThis, properties);\n const functions = {\n dispatchEvent: app._dispatchEvent.bind(app),\n timeoutCb: app._evalCallback.bind(app)\n };\n return (name, args) => {\n try {\n functions[name](args);\n } catch (error) {\n send(serializeError(error));\n }\n };\n}\n\n;// ./src/pdf.scripting.js\n\nconst pdfjsVersion = \"4.10.38\";\nconst pdfjsBuild = \"f9bea397f\";\nglobalThis.pdfjsScripting = {\n initSandbox: initSandbox\n};\n"]; + code.push("delete dump;"); + let success = false; + let buf = 0; + try { + const sandboxData = JSON.stringify(data); + code.push(`pdfjsScripting.initSandbox({ data: ${sandboxData} })`); + buf = this._module.stringToNewUTF8(code.join("\n")); + success = !!this._module.ccall("init", "number", ["number", "number"], [buf, this._alertOnError]); + } catch (error) { + console.error(error); + } finally { + if (buf) { + this._module.ccall("free", "number", ["number"], [buf]); + } + } + if (success) { + this.support.commFun = this._module.cwrap("commFun", null, ["string", "string"]); + } else { + this.nukeSandbox(); + throw new Error("Cannot start sandbox"); + } + } + dispatchEvent(event) { + this.support?.callSandboxFunction("dispatchEvent", event); + } + dumpMemoryUse() { + this._module?.ccall("dumpMemoryUse", null, []); + } + nukeSandbox() { + if (this._module !== null) { + this.support.destroy(); + this.support = null; + this._module.ccall("nukeSandbox", null, []); + this._module = null; + } + } + evalForTesting(code, key) { + throw new Error("Not implemented: evalForTesting"); + } +} +function QuickJSSandbox() { + return quickjs_eval().then(module => new Sandbox(window, module)); +} + +var __webpack_exports__QuickJSSandbox = __webpack_exports__.QuickJSSandbox; +export { __webpack_exports__QuickJSSandbox as QuickJSSandbox }; + +//# sourceMappingURL=pdf.sandbox.mjs.map \ No newline at end of file diff --git a/public/pdfjs/build/pdf.worker.mjs b/public/pdfjs/build/pdf.worker.mjs new file mode 100644 index 0000000..92eba68 --- /dev/null +++ b/public/pdfjs/build/pdf.worker.mjs @@ -0,0 +1,56979 @@ +/** + * @licstart The following is the entire license notice for the + * JavaScript code in this page + * + * Copyright 2024 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @licend The above is the entire license notice for the + * JavaScript code in this page + */ + +/******/ // The require scope +/******/ var __webpack_require__ = {}; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = globalThis.pdfjsWorker = {}; + +// EXPORTS +__webpack_require__.d(__webpack_exports__, { + WorkerMessageHandler: () => (/* reexport */ WorkerMessageHandler) +}); + +;// ./src/shared/util.js +const isNodeJS = typeof process === "object" && process + "" === "[object process]" && !process.versions.nw && !(process.versions.electron && process.type && process.type !== "browser"); +const IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; +const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; +const MAX_IMAGE_SIZE_TO_CACHE = 10e6; +const LINE_FACTOR = 1.35; +const LINE_DESCENT_FACTOR = 0.35; +const BASELINE_FACTOR = LINE_DESCENT_FACTOR / LINE_FACTOR; +const RenderingIntentFlag = { + ANY: 0x01, + DISPLAY: 0x02, + PRINT: 0x04, + SAVE: 0x08, + ANNOTATIONS_FORMS: 0x10, + ANNOTATIONS_STORAGE: 0x20, + ANNOTATIONS_DISABLE: 0x40, + IS_EDITING: 0x80, + OPLIST: 0x100 +}; +const AnnotationMode = { + DISABLE: 0, + ENABLE: 1, + ENABLE_FORMS: 2, + ENABLE_STORAGE: 3 +}; +const AnnotationEditorPrefix = "pdfjs_internal_editor_"; +const AnnotationEditorType = { + DISABLE: -1, + NONE: 0, + FREETEXT: 3, + HIGHLIGHT: 9, + STAMP: 13, + INK: 15 +}; +const AnnotationEditorParamsType = { + RESIZE: 1, + CREATE: 2, + FREETEXT_SIZE: 11, + FREETEXT_COLOR: 12, + FREETEXT_OPACITY: 13, + INK_COLOR: 21, + INK_THICKNESS: 22, + INK_OPACITY: 23, + HIGHLIGHT_COLOR: 31, + HIGHLIGHT_DEFAULT_COLOR: 32, + HIGHLIGHT_THICKNESS: 33, + HIGHLIGHT_FREE: 34, + HIGHLIGHT_SHOW_ALL: 35, + DRAW_STEP: 41 +}; +const PermissionFlag = { + PRINT: 0x04, + MODIFY_CONTENTS: 0x08, + COPY: 0x10, + MODIFY_ANNOTATIONS: 0x20, + FILL_INTERACTIVE_FORMS: 0x100, + COPY_FOR_ACCESSIBILITY: 0x200, + ASSEMBLE: 0x400, + PRINT_HIGH_QUALITY: 0x800 +}; +const TextRenderingMode = { + FILL: 0, + STROKE: 1, + FILL_STROKE: 2, + INVISIBLE: 3, + FILL_ADD_TO_PATH: 4, + STROKE_ADD_TO_PATH: 5, + FILL_STROKE_ADD_TO_PATH: 6, + ADD_TO_PATH: 7, + FILL_STROKE_MASK: 3, + ADD_TO_PATH_FLAG: 4 +}; +const ImageKind = { + GRAYSCALE_1BPP: 1, + RGB_24BPP: 2, + RGBA_32BPP: 3 +}; +const AnnotationType = { + TEXT: 1, + LINK: 2, + FREETEXT: 3, + LINE: 4, + SQUARE: 5, + CIRCLE: 6, + POLYGON: 7, + POLYLINE: 8, + HIGHLIGHT: 9, + UNDERLINE: 10, + SQUIGGLY: 11, + STRIKEOUT: 12, + STAMP: 13, + CARET: 14, + INK: 15, + POPUP: 16, + FILEATTACHMENT: 17, + SOUND: 18, + MOVIE: 19, + WIDGET: 20, + SCREEN: 21, + PRINTERMARK: 22, + TRAPNET: 23, + WATERMARK: 24, + THREED: 25, + REDACT: 26 +}; +const AnnotationReplyType = { + GROUP: "Group", + REPLY: "R" +}; +const AnnotationFlag = { + INVISIBLE: 0x01, + HIDDEN: 0x02, + PRINT: 0x04, + NOZOOM: 0x08, + NOROTATE: 0x10, + NOVIEW: 0x20, + READONLY: 0x40, + LOCKED: 0x80, + TOGGLENOVIEW: 0x100, + LOCKEDCONTENTS: 0x200 +}; +const AnnotationFieldFlag = { + READONLY: 0x0000001, + REQUIRED: 0x0000002, + NOEXPORT: 0x0000004, + MULTILINE: 0x0001000, + PASSWORD: 0x0002000, + NOTOGGLETOOFF: 0x0004000, + RADIO: 0x0008000, + PUSHBUTTON: 0x0010000, + COMBO: 0x0020000, + EDIT: 0x0040000, + SORT: 0x0080000, + FILESELECT: 0x0100000, + MULTISELECT: 0x0200000, + DONOTSPELLCHECK: 0x0400000, + DONOTSCROLL: 0x0800000, + COMB: 0x1000000, + RICHTEXT: 0x2000000, + RADIOSINUNISON: 0x2000000, + COMMITONSELCHANGE: 0x4000000 +}; +const AnnotationBorderStyleType = { + SOLID: 1, + DASHED: 2, + BEVELED: 3, + INSET: 4, + UNDERLINE: 5 +}; +const AnnotationActionEventType = { + E: "Mouse Enter", + X: "Mouse Exit", + D: "Mouse Down", + U: "Mouse Up", + Fo: "Focus", + Bl: "Blur", + PO: "PageOpen", + PC: "PageClose", + PV: "PageVisible", + PI: "PageInvisible", + K: "Keystroke", + F: "Format", + V: "Validate", + C: "Calculate" +}; +const DocumentActionEventType = { + WC: "WillClose", + WS: "WillSave", + DS: "DidSave", + WP: "WillPrint", + DP: "DidPrint" +}; +const PageActionEventType = { + O: "PageOpen", + C: "PageClose" +}; +const VerbosityLevel = { + ERRORS: 0, + WARNINGS: 1, + INFOS: 5 +}; +const OPS = { + dependency: 1, + setLineWidth: 2, + setLineCap: 3, + setLineJoin: 4, + setMiterLimit: 5, + setDash: 6, + setRenderingIntent: 7, + setFlatness: 8, + setGState: 9, + save: 10, + restore: 11, + transform: 12, + moveTo: 13, + lineTo: 14, + curveTo: 15, + curveTo2: 16, + curveTo3: 17, + closePath: 18, + rectangle: 19, + stroke: 20, + closeStroke: 21, + fill: 22, + eoFill: 23, + fillStroke: 24, + eoFillStroke: 25, + closeFillStroke: 26, + closeEOFillStroke: 27, + endPath: 28, + clip: 29, + eoClip: 30, + beginText: 31, + endText: 32, + setCharSpacing: 33, + setWordSpacing: 34, + setHScale: 35, + setLeading: 36, + setFont: 37, + setTextRenderingMode: 38, + setTextRise: 39, + moveText: 40, + setLeadingMoveText: 41, + setTextMatrix: 42, + nextLine: 43, + showText: 44, + showSpacedText: 45, + nextLineShowText: 46, + nextLineSetSpacingShowText: 47, + setCharWidth: 48, + setCharWidthAndBounds: 49, + setStrokeColorSpace: 50, + setFillColorSpace: 51, + setStrokeColor: 52, + setStrokeColorN: 53, + setFillColor: 54, + setFillColorN: 55, + setStrokeGray: 56, + setFillGray: 57, + setStrokeRGBColor: 58, + setFillRGBColor: 59, + setStrokeCMYKColor: 60, + setFillCMYKColor: 61, + shadingFill: 62, + beginInlineImage: 63, + beginImageData: 64, + endInlineImage: 65, + paintXObject: 66, + markPoint: 67, + markPointProps: 68, + beginMarkedContent: 69, + beginMarkedContentProps: 70, + endMarkedContent: 71, + beginCompat: 72, + endCompat: 73, + paintFormXObjectBegin: 74, + paintFormXObjectEnd: 75, + beginGroup: 76, + endGroup: 77, + beginAnnotation: 80, + endAnnotation: 81, + paintImageMaskXObject: 83, + paintImageMaskXObjectGroup: 84, + paintImageXObject: 85, + paintInlineImageXObject: 86, + paintInlineImageXObjectGroup: 87, + paintImageXObjectRepeat: 88, + paintImageMaskXObjectRepeat: 89, + paintSolidColorImageMask: 90, + constructPath: 91, + setStrokeTransparent: 92, + setFillTransparent: 93 +}; +const PasswordResponses = { + NEED_PASSWORD: 1, + INCORRECT_PASSWORD: 2 +}; +let verbosity = VerbosityLevel.WARNINGS; +function setVerbosityLevel(level) { + if (Number.isInteger(level)) { + verbosity = level; + } +} +function getVerbosityLevel() { + return verbosity; +} +function info(msg) { + if (verbosity >= VerbosityLevel.INFOS) { + console.log(`Info: ${msg}`); + } +} +function warn(msg) { + if (verbosity >= VerbosityLevel.WARNINGS) { + console.log(`Warning: ${msg}`); + } +} +function unreachable(msg) { + throw new Error(msg); +} +function assert(cond, msg) { + if (!cond) { + unreachable(msg); + } +} +function _isValidProtocol(url) { + switch (url?.protocol) { + case "http:": + case "https:": + case "ftp:": + case "mailto:": + case "tel:": + return true; + default: + return false; + } +} +function createValidAbsoluteUrl(url, baseUrl = null, options = null) { + if (!url) { + return null; + } + try { + if (options && typeof url === "string") { + if (options.addDefaultProtocol && url.startsWith("www.")) { + const dots = url.match(/\./g); + if (dots?.length >= 2) { + url = `http://${url}`; + } + } + if (options.tryConvertEncoding) { + try { + url = stringToUTF8String(url); + } catch {} + } + } + const absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url); + if (_isValidProtocol(absoluteUrl)) { + return absoluteUrl; + } + } catch {} + return null; +} +function shadow(obj, prop, value, nonSerializable = false) { + Object.defineProperty(obj, prop, { + value, + enumerable: !nonSerializable, + configurable: true, + writable: false + }); + return value; +} +const BaseException = function BaseExceptionClosure() { + function BaseException(message, name) { + this.message = message; + this.name = name; + } + BaseException.prototype = new Error(); + BaseException.constructor = BaseException; + return BaseException; +}(); +class PasswordException extends BaseException { + constructor(msg, code) { + super(msg, "PasswordException"); + this.code = code; + } +} +class UnknownErrorException extends BaseException { + constructor(msg, details) { + super(msg, "UnknownErrorException"); + this.details = details; + } +} +class InvalidPDFException extends BaseException { + constructor(msg) { + super(msg, "InvalidPDFException"); + } +} +class MissingPDFException extends BaseException { + constructor(msg) { + super(msg, "MissingPDFException"); + } +} +class UnexpectedResponseException extends BaseException { + constructor(msg, status) { + super(msg, "UnexpectedResponseException"); + this.status = status; + } +} +class FormatError extends BaseException { + constructor(msg) { + super(msg, "FormatError"); + } +} +class AbortException extends BaseException { + constructor(msg) { + super(msg, "AbortException"); + } +} +function bytesToString(bytes) { + if (typeof bytes !== "object" || bytes?.length === undefined) { + unreachable("Invalid argument for bytesToString"); + } + const length = bytes.length; + const MAX_ARGUMENT_COUNT = 8192; + if (length < MAX_ARGUMENT_COUNT) { + return String.fromCharCode.apply(null, bytes); + } + const strBuf = []; + for (let i = 0; i < length; i += MAX_ARGUMENT_COUNT) { + const chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length); + const chunk = bytes.subarray(i, chunkEnd); + strBuf.push(String.fromCharCode.apply(null, chunk)); + } + return strBuf.join(""); +} +function stringToBytes(str) { + if (typeof str !== "string") { + unreachable("Invalid argument for stringToBytes"); + } + const length = str.length; + const bytes = new Uint8Array(length); + for (let i = 0; i < length; ++i) { + bytes[i] = str.charCodeAt(i) & 0xff; + } + return bytes; +} +function string32(value) { + return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff); +} +function objectSize(obj) { + return Object.keys(obj).length; +} +function objectFromMap(map) { + const obj = Object.create(null); + for (const [key, value] of map) { + obj[key] = value; + } + return obj; +} +function isLittleEndian() { + const buffer8 = new Uint8Array(4); + buffer8[0] = 1; + const view32 = new Uint32Array(buffer8.buffer, 0, 1); + return view32[0] === 1; +} +function isEvalSupported() { + try { + new Function(""); + return true; + } catch { + return false; + } +} +class FeatureTest { + static get isLittleEndian() { + return shadow(this, "isLittleEndian", isLittleEndian()); + } + static get isEvalSupported() { + return shadow(this, "isEvalSupported", isEvalSupported()); + } + static get isOffscreenCanvasSupported() { + return shadow(this, "isOffscreenCanvasSupported", typeof OffscreenCanvas !== "undefined"); + } + static get isImageDecoderSupported() { + return shadow(this, "isImageDecoderSupported", typeof ImageDecoder !== "undefined"); + } + static get platform() { + if (typeof navigator !== "undefined" && typeof navigator?.platform === "string") { + return shadow(this, "platform", { + isMac: navigator.platform.includes("Mac"), + isWindows: navigator.platform.includes("Win"), + isFirefox: typeof navigator?.userAgent === "string" && navigator.userAgent.includes("Firefox") + }); + } + return shadow(this, "platform", { + isMac: false, + isWindows: false, + isFirefox: false + }); + } + static get isCSSRoundSupported() { + return shadow(this, "isCSSRoundSupported", globalThis.CSS?.supports?.("width: round(1.5px, 1px)")); + } +} +const hexNumbers = Array.from(Array(256).keys(), n => n.toString(16).padStart(2, "0")); +class Util { + static makeHexColor(r, g, b) { + return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`; + } + static scaleMinMax(transform, minMax) { + let temp; + if (transform[0]) { + if (transform[0] < 0) { + temp = minMax[0]; + minMax[0] = minMax[2]; + minMax[2] = temp; + } + minMax[0] *= transform[0]; + minMax[2] *= transform[0]; + if (transform[3] < 0) { + temp = minMax[1]; + minMax[1] = minMax[3]; + minMax[3] = temp; + } + minMax[1] *= transform[3]; + minMax[3] *= transform[3]; + } else { + temp = minMax[0]; + minMax[0] = minMax[1]; + minMax[1] = temp; + temp = minMax[2]; + minMax[2] = minMax[3]; + minMax[3] = temp; + if (transform[1] < 0) { + temp = minMax[1]; + minMax[1] = minMax[3]; + minMax[3] = temp; + } + minMax[1] *= transform[1]; + minMax[3] *= transform[1]; + if (transform[2] < 0) { + temp = minMax[0]; + minMax[0] = minMax[2]; + minMax[2] = temp; + } + minMax[0] *= transform[2]; + minMax[2] *= transform[2]; + } + minMax[0] += transform[4]; + minMax[1] += transform[5]; + minMax[2] += transform[4]; + minMax[3] += transform[5]; + } + static transform(m1, m2) { + return [m1[0] * m2[0] + m1[2] * m2[1], m1[1] * m2[0] + m1[3] * m2[1], m1[0] * m2[2] + m1[2] * m2[3], m1[1] * m2[2] + m1[3] * m2[3], m1[0] * m2[4] + m1[2] * m2[5] + m1[4], m1[1] * m2[4] + m1[3] * m2[5] + m1[5]]; + } + static applyTransform(p, m) { + const xt = p[0] * m[0] + p[1] * m[2] + m[4]; + const yt = p[0] * m[1] + p[1] * m[3] + m[5]; + return [xt, yt]; + } + static applyInverseTransform(p, m) { + const d = m[0] * m[3] - m[1] * m[2]; + const xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d; + const yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d; + return [xt, yt]; + } + static getAxialAlignedBoundingBox(r, m) { + const p1 = this.applyTransform(r, m); + const p2 = this.applyTransform(r.slice(2, 4), m); + const p3 = this.applyTransform([r[0], r[3]], m); + const p4 = this.applyTransform([r[2], r[1]], m); + return [Math.min(p1[0], p2[0], p3[0], p4[0]), Math.min(p1[1], p2[1], p3[1], p4[1]), Math.max(p1[0], p2[0], p3[0], p4[0]), Math.max(p1[1], p2[1], p3[1], p4[1])]; + } + static inverseTransform(m) { + const d = m[0] * m[3] - m[1] * m[2]; + return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d]; + } + static singularValueDecompose2dScale(m) { + const transpose = [m[0], m[2], m[1], m[3]]; + const a = m[0] * transpose[0] + m[1] * transpose[2]; + const b = m[0] * transpose[1] + m[1] * transpose[3]; + const c = m[2] * transpose[0] + m[3] * transpose[2]; + const d = m[2] * transpose[1] + m[3] * transpose[3]; + const first = (a + d) / 2; + const second = Math.sqrt((a + d) ** 2 - 4 * (a * d - c * b)) / 2; + const sx = first + second || 1; + const sy = first - second || 1; + return [Math.sqrt(sx), Math.sqrt(sy)]; + } + static normalizeRect(rect) { + const r = rect.slice(0); + if (rect[0] > rect[2]) { + r[0] = rect[2]; + r[2] = rect[0]; + } + if (rect[1] > rect[3]) { + r[1] = rect[3]; + r[3] = rect[1]; + } + return r; + } + static intersect(rect1, rect2) { + const xLow = Math.max(Math.min(rect1[0], rect1[2]), Math.min(rect2[0], rect2[2])); + const xHigh = Math.min(Math.max(rect1[0], rect1[2]), Math.max(rect2[0], rect2[2])); + if (xLow > xHigh) { + return null; + } + const yLow = Math.max(Math.min(rect1[1], rect1[3]), Math.min(rect2[1], rect2[3])); + const yHigh = Math.min(Math.max(rect1[1], rect1[3]), Math.max(rect2[1], rect2[3])); + if (yLow > yHigh) { + return null; + } + return [xLow, yLow, xHigh, yHigh]; + } + static #getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, t, minMax) { + if (t <= 0 || t >= 1) { + return; + } + const mt = 1 - t; + const tt = t * t; + const ttt = tt * t; + const x = mt * (mt * (mt * x0 + 3 * t * x1) + 3 * tt * x2) + ttt * x3; + const y = mt * (mt * (mt * y0 + 3 * t * y1) + 3 * tt * y2) + ttt * y3; + minMax[0] = Math.min(minMax[0], x); + minMax[1] = Math.min(minMax[1], y); + minMax[2] = Math.max(minMax[2], x); + minMax[3] = Math.max(minMax[3], y); + } + static #getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, a, b, c, minMax) { + if (Math.abs(a) < 1e-12) { + if (Math.abs(b) >= 1e-12) { + this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, -c / b, minMax); + } + return; + } + const delta = b ** 2 - 4 * c * a; + if (delta < 0) { + return; + } + const sqrtDelta = Math.sqrt(delta); + const a2 = 2 * a; + this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b + sqrtDelta) / a2, minMax); + this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b - sqrtDelta) / a2, minMax); + } + static bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax) { + if (minMax) { + minMax[0] = Math.min(minMax[0], x0, x3); + minMax[1] = Math.min(minMax[1], y0, y3); + minMax[2] = Math.max(minMax[2], x0, x3); + minMax[3] = Math.max(minMax[3], y0, y3); + } else { + minMax = [Math.min(x0, x3), Math.min(y0, y3), Math.max(x0, x3), Math.max(y0, y3)]; + } + this.#getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, 3 * (-x0 + 3 * (x1 - x2) + x3), 6 * (x0 - 2 * x1 + x2), 3 * (x1 - x0), minMax); + this.#getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, 3 * (-y0 + 3 * (y1 - y2) + y3), 6 * (y0 - 2 * y1 + y2), 3 * (y1 - y0), minMax); + return minMax; + } +} +const PDFStringTranslateTable = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2d8, 0x2c7, 0x2c6, 0x2d9, 0x2dd, 0x2db, 0x2da, 0x2dc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x192, 0x2044, 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018, 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x141, 0x152, 0x160, 0x178, 0x17d, 0x131, 0x142, 0x153, 0x161, 0x17e, 0, 0x20ac]; +function stringToPDFString(str) { + if (str[0] >= "\xEF") { + let encoding; + if (str[0] === "\xFE" && str[1] === "\xFF") { + encoding = "utf-16be"; + if (str.length % 2 === 1) { + str = str.slice(0, -1); + } + } else if (str[0] === "\xFF" && str[1] === "\xFE") { + encoding = "utf-16le"; + if (str.length % 2 === 1) { + str = str.slice(0, -1); + } + } else if (str[0] === "\xEF" && str[1] === "\xBB" && str[2] === "\xBF") { + encoding = "utf-8"; + } + if (encoding) { + try { + const decoder = new TextDecoder(encoding, { + fatal: true + }); + const buffer = stringToBytes(str); + const decoded = decoder.decode(buffer); + if (!decoded.includes("\x1b")) { + return decoded; + } + return decoded.replaceAll(/\x1b[^\x1b]*(?:\x1b|$)/g, ""); + } catch (ex) { + warn(`stringToPDFString: "${ex}".`); + } + } + } + const strBuf = []; + for (let i = 0, ii = str.length; i < ii; i++) { + const charCode = str.charCodeAt(i); + if (charCode === 0x1b) { + while (++i < ii && str.charCodeAt(i) !== 0x1b) {} + continue; + } + const code = PDFStringTranslateTable[charCode]; + strBuf.push(code ? String.fromCharCode(code) : str.charAt(i)); + } + return strBuf.join(""); +} +function stringToUTF8String(str) { + return decodeURIComponent(escape(str)); +} +function utf8StringToString(str) { + return unescape(encodeURIComponent(str)); +} +function isArrayEqual(arr1, arr2) { + if (arr1.length !== arr2.length) { + return false; + } + for (let i = 0, ii = arr1.length; i < ii; i++) { + if (arr1[i] !== arr2[i]) { + return false; + } + } + return true; +} +function getModificationDate(date = new Date()) { + const buffer = [date.getUTCFullYear().toString(), (date.getUTCMonth() + 1).toString().padStart(2, "0"), date.getUTCDate().toString().padStart(2, "0"), date.getUTCHours().toString().padStart(2, "0"), date.getUTCMinutes().toString().padStart(2, "0"), date.getUTCSeconds().toString().padStart(2, "0")]; + return buffer.join(""); +} +let NormalizeRegex = null; +let NormalizationMap = null; +function normalizeUnicode(str) { + if (!NormalizeRegex) { + NormalizeRegex = /([\u00a0\u00b5\u037e\u0eb3\u2000-\u200a\u202f\u2126\ufb00-\ufb04\ufb06\ufb20-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufba1\ufba4-\ufba9\ufbae-\ufbb1\ufbd3-\ufbdc\ufbde-\ufbe7\ufbea-\ufbf8\ufbfc-\ufbfd\ufc00-\ufc5d\ufc64-\ufcf1\ufcf5-\ufd3d\ufd88\ufdf4\ufdfa-\ufdfb\ufe71\ufe77\ufe79\ufe7b\ufe7d]+)|(\ufb05+)/gu; + NormalizationMap = new Map([["ſt", "ſt"]]); + } + return str.replaceAll(NormalizeRegex, (_, p1, p2) => p1 ? p1.normalize("NFKC") : NormalizationMap.get(p2)); +} +function getUuid() { + if (typeof crypto.randomUUID === "function") { + return crypto.randomUUID(); + } + const buf = new Uint8Array(32); + crypto.getRandomValues(buf); + return bytesToString(buf); +} +const AnnotationPrefix = "pdfjs_internal_id_"; +function toHexUtil(arr) { + if (Uint8Array.prototype.toHex) { + return arr.toHex(); + } + return Array.from(arr, num => hexNumbers[num]).join(""); +} +function toBase64Util(arr) { + if (Uint8Array.prototype.toBase64) { + return arr.toBase64(); + } + return btoa(bytesToString(arr)); +} +function fromBase64Util(str) { + if (Uint8Array.fromBase64) { + return Uint8Array.fromBase64(str); + } + return stringToBytes(atob(str)); +} +if (typeof Promise.try !== "function") { + Promise.try = function (fn, ...args) { + return new Promise(resolve => { + resolve(fn(...args)); + }); + }; +} + +;// ./src/core/primitives.js + +const CIRCULAR_REF = Symbol("CIRCULAR_REF"); +const EOF = Symbol("EOF"); +let CmdCache = Object.create(null); +let NameCache = Object.create(null); +let RefCache = Object.create(null); +function clearPrimitiveCaches() { + CmdCache = Object.create(null); + NameCache = Object.create(null); + RefCache = Object.create(null); +} +class Name { + constructor(name) { + this.name = name; + } + static get(name) { + return NameCache[name] ||= new Name(name); + } +} +class Cmd { + constructor(cmd) { + this.cmd = cmd; + } + static get(cmd) { + return CmdCache[cmd] ||= new Cmd(cmd); + } +} +const nonSerializable = function nonSerializableClosure() { + return nonSerializable; +}; +class Dict { + constructor(xref = null) { + this._map = new Map(); + this.xref = xref; + this.objId = null; + this.suppressEncryption = false; + this.__nonSerializable__ = nonSerializable; + } + assignXref(newXref) { + this.xref = newXref; + } + get size() { + return this._map.size; + } + get(key1, key2, key3) { + let value = this._map.get(key1); + if (value === undefined && key2 !== undefined) { + value = this._map.get(key2); + if (value === undefined && key3 !== undefined) { + value = this._map.get(key3); + } + } + if (value instanceof Ref && this.xref) { + return this.xref.fetch(value, this.suppressEncryption); + } + return value; + } + async getAsync(key1, key2, key3) { + let value = this._map.get(key1); + if (value === undefined && key2 !== undefined) { + value = this._map.get(key2); + if (value === undefined && key3 !== undefined) { + value = this._map.get(key3); + } + } + if (value instanceof Ref && this.xref) { + return this.xref.fetchAsync(value, this.suppressEncryption); + } + return value; + } + getArray(key1, key2, key3) { + let value = this._map.get(key1); + if (value === undefined && key2 !== undefined) { + value = this._map.get(key2); + if (value === undefined && key3 !== undefined) { + value = this._map.get(key3); + } + } + if (value instanceof Ref && this.xref) { + value = this.xref.fetch(value, this.suppressEncryption); + } + if (Array.isArray(value)) { + value = value.slice(); + for (let i = 0, ii = value.length; i < ii; i++) { + if (value[i] instanceof Ref && this.xref) { + value[i] = this.xref.fetch(value[i], this.suppressEncryption); + } + } + } + return value; + } + getRaw(key) { + return this._map.get(key); + } + getKeys() { + return [...this._map.keys()]; + } + getRawValues() { + return [...this._map.values()]; + } + set(key, value) { + this._map.set(key, value); + } + has(key) { + return this._map.has(key); + } + *[Symbol.iterator]() { + for (const [key, value] of this._map) { + yield [key, value instanceof Ref && this.xref ? this.xref.fetch(value, this.suppressEncryption) : value]; + } + } + static get empty() { + const emptyDict = new Dict(null); + emptyDict.set = (key, value) => { + unreachable("Should not call `set` on the empty dictionary."); + }; + return shadow(this, "empty", emptyDict); + } + static merge({ + xref, + dictArray, + mergeSubDicts = false + }) { + const mergedDict = new Dict(xref), + properties = new Map(); + for (const dict of dictArray) { + if (!(dict instanceof Dict)) { + continue; + } + for (const [key, value] of dict._map) { + let property = properties.get(key); + if (property === undefined) { + property = []; + properties.set(key, property); + } else if (!mergeSubDicts || !(value instanceof Dict)) { + continue; + } + property.push(value); + } + } + for (const [name, values] of properties) { + if (values.length === 1 || !(values[0] instanceof Dict)) { + mergedDict._map.set(name, values[0]); + continue; + } + const subDict = new Dict(xref); + for (const dict of values) { + for (const [key, value] of dict._map) { + if (!subDict._map.has(key)) { + subDict._map.set(key, value); + } + } + } + if (subDict.size > 0) { + mergedDict._map.set(name, subDict); + } + } + properties.clear(); + return mergedDict.size > 0 ? mergedDict : Dict.empty; + } + clone() { + const dict = new Dict(this.xref); + for (const key of this.getKeys()) { + dict.set(key, this.getRaw(key)); + } + return dict; + } + delete(key) { + delete this._map[key]; + } +} +class Ref { + constructor(num, gen) { + this.num = num; + this.gen = gen; + } + toString() { + if (this.gen === 0) { + return `${this.num}R`; + } + return `${this.num}R${this.gen}`; + } + static fromString(str) { + const ref = RefCache[str]; + if (ref) { + return ref; + } + const m = /^(\d+)R(\d*)$/.exec(str); + if (!m || m[1] === "0") { + return null; + } + return RefCache[str] = new Ref(parseInt(m[1]), !m[2] ? 0 : parseInt(m[2])); + } + static get(num, gen) { + const key = gen === 0 ? `${num}R` : `${num}R${gen}`; + return RefCache[key] ||= new Ref(num, gen); + } +} +class RefSet { + constructor(parent = null) { + this._set = new Set(parent?._set); + } + has(ref) { + return this._set.has(ref.toString()); + } + put(ref) { + this._set.add(ref.toString()); + } + remove(ref) { + this._set.delete(ref.toString()); + } + [Symbol.iterator]() { + return this._set.values(); + } + clear() { + this._set.clear(); + } +} +class RefSetCache { + constructor() { + this._map = new Map(); + } + get size() { + return this._map.size; + } + get(ref) { + return this._map.get(ref.toString()); + } + has(ref) { + return this._map.has(ref.toString()); + } + put(ref, obj) { + this._map.set(ref.toString(), obj); + } + putAlias(ref, aliasRef) { + this._map.set(ref.toString(), this.get(aliasRef)); + } + [Symbol.iterator]() { + return this._map.values(); + } + clear() { + this._map.clear(); + } + *values() { + yield* this._map.values(); + } + *items() { + for (const [ref, value] of this._map) { + yield [Ref.fromString(ref), value]; + } + } +} +function isName(v, name) { + return v instanceof Name && (name === undefined || v.name === name); +} +function isCmd(v, cmd) { + return v instanceof Cmd && (cmd === undefined || v.cmd === cmd); +} +function isDict(v, type) { + return v instanceof Dict && (type === undefined || isName(v.get("Type"), type)); +} +function isRefsEqual(v1, v2) { + return v1.num === v2.num && v1.gen === v2.gen; +} + +;// ./src/core/base_stream.js + +class BaseStream { + get length() { + unreachable("Abstract getter `length` accessed"); + } + get isEmpty() { + unreachable("Abstract getter `isEmpty` accessed"); + } + get isDataLoaded() { + return shadow(this, "isDataLoaded", true); + } + getByte() { + unreachable("Abstract method `getByte` called"); + } + getBytes(length) { + unreachable("Abstract method `getBytes` called"); + } + async getImageData(length, decoderOptions) { + return this.getBytes(length, decoderOptions); + } + async asyncGetBytes() { + unreachable("Abstract method `asyncGetBytes` called"); + } + get isAsync() { + return false; + } + get canAsyncDecodeImageFromBuffer() { + return false; + } + async getTransferableImage() { + return null; + } + peekByte() { + const peekedByte = this.getByte(); + if (peekedByte !== -1) { + this.pos--; + } + return peekedByte; + } + peekBytes(length) { + const bytes = this.getBytes(length); + this.pos -= bytes.length; + return bytes; + } + getUint16() { + const b0 = this.getByte(); + const b1 = this.getByte(); + if (b0 === -1 || b1 === -1) { + return -1; + } + return (b0 << 8) + b1; + } + getInt32() { + const b0 = this.getByte(); + const b1 = this.getByte(); + const b2 = this.getByte(); + const b3 = this.getByte(); + return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3; + } + getByteRange(begin, end) { + unreachable("Abstract method `getByteRange` called"); + } + getString(length) { + return bytesToString(this.getBytes(length)); + } + skip(n) { + this.pos += n || 1; + } + reset() { + unreachable("Abstract method `reset` called"); + } + moveStart() { + unreachable("Abstract method `moveStart` called"); + } + makeSubStream(start, length, dict = null) { + unreachable("Abstract method `makeSubStream` called"); + } + getBaseStreams() { + return null; + } +} + +;// ./src/core/core_utils.js + + + +const PDF_VERSION_REGEXP = /^[1-9]\.\d$/; +const MAX_INT_32 = 2 ** 31 - 1; +const MIN_INT_32 = -(2 ** 31); +function getLookupTableFactory(initializer) { + let lookup; + return function () { + if (initializer) { + lookup = Object.create(null); + initializer(lookup); + initializer = null; + } + return lookup; + }; +} +class MissingDataException extends BaseException { + constructor(begin, end) { + super(`Missing data [${begin}, ${end})`, "MissingDataException"); + this.begin = begin; + this.end = end; + } +} +class ParserEOFException extends BaseException { + constructor(msg) { + super(msg, "ParserEOFException"); + } +} +class XRefEntryException extends BaseException { + constructor(msg) { + super(msg, "XRefEntryException"); + } +} +class XRefParseException extends BaseException { + constructor(msg) { + super(msg, "XRefParseException"); + } +} +function arrayBuffersToBytes(arr) { + const length = arr.length; + if (length === 0) { + return new Uint8Array(0); + } + if (length === 1) { + return new Uint8Array(arr[0]); + } + let dataLength = 0; + for (let i = 0; i < length; i++) { + dataLength += arr[i].byteLength; + } + const data = new Uint8Array(dataLength); + let pos = 0; + for (let i = 0; i < length; i++) { + const item = new Uint8Array(arr[i]); + data.set(item, pos); + pos += item.byteLength; + } + return data; +} +function getInheritableProperty({ + dict, + key, + getArray = false, + stopWhenFound = true +}) { + let values; + const visited = new RefSet(); + while (dict instanceof Dict && !(dict.objId && visited.has(dict.objId))) { + if (dict.objId) { + visited.put(dict.objId); + } + const value = getArray ? dict.getArray(key) : dict.get(key); + if (value !== undefined) { + if (stopWhenFound) { + return value; + } + (values ||= []).push(value); + } + dict = dict.get("Parent"); + } + return values; +} +function getParentToUpdate(dict, ref, xref) { + const visited = new RefSet(); + const firstDict = dict; + const result = { + dict: null, + ref: null + }; + while (dict instanceof Dict && !visited.has(ref)) { + visited.put(ref); + if (dict.has("T")) { + break; + } + ref = dict.getRaw("Parent"); + if (!(ref instanceof Ref)) { + return result; + } + dict = xref.fetch(ref); + } + if (dict instanceof Dict && dict !== firstDict) { + result.dict = dict; + result.ref = ref; + } + return result; +} +const ROMAN_NUMBER_MAP = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]; +function toRomanNumerals(number, lowerCase = false) { + assert(Number.isInteger(number) && number > 0, "The number should be a positive integer."); + const roman = "M".repeat(number / 1000 | 0) + ROMAN_NUMBER_MAP[number % 1000 / 100 | 0] + ROMAN_NUMBER_MAP[10 + (number % 100 / 10 | 0)] + ROMAN_NUMBER_MAP[20 + number % 10]; + return lowerCase ? roman.toLowerCase() : roman; +} +function log2(x) { + return x > 0 ? Math.ceil(Math.log2(x)) : 0; +} +function readInt8(data, offset) { + return data[offset] << 24 >> 24; +} +function readUint16(data, offset) { + return data[offset] << 8 | data[offset + 1]; +} +function readUint32(data, offset) { + return (data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3]) >>> 0; +} +function isWhiteSpace(ch) { + return ch === 0x20 || ch === 0x09 || ch === 0x0d || ch === 0x0a; +} +function isBooleanArray(arr, len) { + return Array.isArray(arr) && (len === null || arr.length === len) && arr.every(x => typeof x === "boolean"); +} +function isNumberArray(arr, len) { + if (Array.isArray(arr)) { + return (len === null || arr.length === len) && arr.every(x => typeof x === "number"); + } + return ArrayBuffer.isView(arr) && (arr.length === 0 || typeof arr[0] === "number") && (len === null || arr.length === len); +} +function lookupMatrix(arr, fallback) { + return isNumberArray(arr, 6) ? arr : fallback; +} +function lookupRect(arr, fallback) { + return isNumberArray(arr, 4) ? arr : fallback; +} +function lookupNormalRect(arr, fallback) { + return isNumberArray(arr, 4) ? Util.normalizeRect(arr) : fallback; +} +function parseXFAPath(path) { + const positionPattern = /(.+)\[(\d+)\]$/; + return path.split(".").map(component => { + const m = component.match(positionPattern); + if (m) { + return { + name: m[1], + pos: parseInt(m[2], 10) + }; + } + return { + name: component, + pos: 0 + }; + }); +} +function escapePDFName(str) { + const buffer = []; + let start = 0; + for (let i = 0, ii = str.length; i < ii; i++) { + const char = str.charCodeAt(i); + if (char < 0x21 || char > 0x7e || char === 0x23 || char === 0x28 || char === 0x29 || char === 0x3c || char === 0x3e || char === 0x5b || char === 0x5d || char === 0x7b || char === 0x7d || char === 0x2f || char === 0x25) { + if (start < i) { + buffer.push(str.substring(start, i)); + } + buffer.push(`#${char.toString(16)}`); + start = i + 1; + } + } + if (buffer.length === 0) { + return str; + } + if (start < str.length) { + buffer.push(str.substring(start, str.length)); + } + return buffer.join(""); +} +function escapeString(str) { + return str.replaceAll(/([()\\\n\r])/g, match => { + if (match === "\n") { + return "\\n"; + } else if (match === "\r") { + return "\\r"; + } + return `\\${match}`; + }); +} +function _collectJS(entry, xref, list, parents) { + if (!entry) { + return; + } + let parent = null; + if (entry instanceof Ref) { + if (parents.has(entry)) { + return; + } + parent = entry; + parents.put(parent); + entry = xref.fetch(entry); + } + if (Array.isArray(entry)) { + for (const element of entry) { + _collectJS(element, xref, list, parents); + } + } else if (entry instanceof Dict) { + if (isName(entry.get("S"), "JavaScript")) { + const js = entry.get("JS"); + let code; + if (js instanceof BaseStream) { + code = js.getString(); + } else if (typeof js === "string") { + code = js; + } + code &&= stringToPDFString(code).replaceAll("\x00", ""); + if (code) { + list.push(code); + } + } + _collectJS(entry.getRaw("Next"), xref, list, parents); + } + if (parent) { + parents.remove(parent); + } +} +function collectActions(xref, dict, eventType) { + const actions = Object.create(null); + const additionalActionsDicts = getInheritableProperty({ + dict, + key: "AA", + stopWhenFound: false + }); + if (additionalActionsDicts) { + for (let i = additionalActionsDicts.length - 1; i >= 0; i--) { + const additionalActions = additionalActionsDicts[i]; + if (!(additionalActions instanceof Dict)) { + continue; + } + for (const key of additionalActions.getKeys()) { + const action = eventType[key]; + if (!action) { + continue; + } + const actionDict = additionalActions.getRaw(key); + const parents = new RefSet(); + const list = []; + _collectJS(actionDict, xref, list, parents); + if (list.length > 0) { + actions[action] = list; + } + } + } + } + if (dict.has("A")) { + const actionDict = dict.get("A"); + const parents = new RefSet(); + const list = []; + _collectJS(actionDict, xref, list, parents); + if (list.length > 0) { + actions.Action = list; + } + } + return objectSize(actions) > 0 ? actions : null; +} +const XMLEntities = { + 0x3c: "<", + 0x3e: ">", + 0x26: "&", + 0x22: """, + 0x27: "'" +}; +function* codePointIter(str) { + for (let i = 0, ii = str.length; i < ii; i++) { + const char = str.codePointAt(i); + if (char > 0xd7ff && (char < 0xe000 || char > 0xfffd)) { + i++; + } + yield char; + } +} +function encodeToXmlString(str) { + const buffer = []; + let start = 0; + for (let i = 0, ii = str.length; i < ii; i++) { + const char = str.codePointAt(i); + if (0x20 <= char && char <= 0x7e) { + const entity = XMLEntities[char]; + if (entity) { + if (start < i) { + buffer.push(str.substring(start, i)); + } + buffer.push(entity); + start = i + 1; + } + } else { + if (start < i) { + buffer.push(str.substring(start, i)); + } + buffer.push(`&#x${char.toString(16).toUpperCase()};`); + if (char > 0xd7ff && (char < 0xe000 || char > 0xfffd)) { + i++; + } + start = i + 1; + } + } + if (buffer.length === 0) { + return str; + } + if (start < str.length) { + buffer.push(str.substring(start, str.length)); + } + return buffer.join(""); +} +function validateFontName(fontFamily, mustWarn = false) { + const m = /^("|').*("|')$/.exec(fontFamily); + if (m && m[1] === m[2]) { + const re = new RegExp(`[^\\\\]${m[1]}`); + if (re.test(fontFamily.slice(1, -1))) { + if (mustWarn) { + warn(`FontFamily contains unescaped ${m[1]}: ${fontFamily}.`); + } + return false; + } + } else { + for (const ident of fontFamily.split(/[ \t]+/)) { + if (/^(\d|(-(\d|-)))/.test(ident) || !/^[\w-\\]+$/.test(ident)) { + if (mustWarn) { + warn(`FontFamily contains invalid : ${fontFamily}.`); + } + return false; + } + } + } + return true; +} +function validateCSSFont(cssFontInfo) { + const DEFAULT_CSS_FONT_OBLIQUE = "14"; + const DEFAULT_CSS_FONT_WEIGHT = "400"; + const CSS_FONT_WEIGHT_VALUES = new Set(["100", "200", "300", "400", "500", "600", "700", "800", "900", "1000", "normal", "bold", "bolder", "lighter"]); + const { + fontFamily, + fontWeight, + italicAngle + } = cssFontInfo; + if (!validateFontName(fontFamily, true)) { + return false; + } + const weight = fontWeight ? fontWeight.toString() : ""; + cssFontInfo.fontWeight = CSS_FONT_WEIGHT_VALUES.has(weight) ? weight : DEFAULT_CSS_FONT_WEIGHT; + const angle = parseFloat(italicAngle); + cssFontInfo.italicAngle = isNaN(angle) || angle < -90 || angle > 90 ? DEFAULT_CSS_FONT_OBLIQUE : italicAngle.toString(); + return true; +} +function recoverJsURL(str) { + const URL_OPEN_METHODS = ["app.launchURL", "window.open", "xfa.host.gotoURL"]; + const regex = new RegExp("^\\s*(" + URL_OPEN_METHODS.join("|").replaceAll(".", "\\.") + ")\\((?:'|\")([^'\"]*)(?:'|\")(?:,\\s*(\\w+)\\)|\\))", "i"); + const jsUrl = regex.exec(str); + if (jsUrl?.[2]) { + return { + url: jsUrl[2], + newWindow: jsUrl[1] === "app.launchURL" && jsUrl[3] === "true" + }; + } + return null; +} +function numberToString(value) { + if (Number.isInteger(value)) { + return value.toString(); + } + const roundedValue = Math.round(value * 100); + if (roundedValue % 100 === 0) { + return (roundedValue / 100).toString(); + } + if (roundedValue % 10 === 0) { + return value.toFixed(1); + } + return value.toFixed(2); +} +function getNewAnnotationsMap(annotationStorage) { + if (!annotationStorage) { + return null; + } + const newAnnotationsByPage = new Map(); + for (const [key, value] of annotationStorage) { + if (!key.startsWith(AnnotationEditorPrefix)) { + continue; + } + let annotations = newAnnotationsByPage.get(value.pageIndex); + if (!annotations) { + annotations = []; + newAnnotationsByPage.set(value.pageIndex, annotations); + } + annotations.push(value); + } + return newAnnotationsByPage.size > 0 ? newAnnotationsByPage : null; +} +function stringToAsciiOrUTF16BE(str) { + return isAscii(str) ? str : stringToUTF16String(str, true); +} +function isAscii(str) { + return /^[\x00-\x7F]*$/.test(str); +} +function stringToUTF16HexString(str) { + const buf = []; + for (let i = 0, ii = str.length; i < ii; i++) { + const char = str.charCodeAt(i); + buf.push(hexNumbers[char >> 8 & 0xff], hexNumbers[char & 0xff]); + } + return buf.join(""); +} +function stringToUTF16String(str, bigEndian = false) { + const buf = []; + if (bigEndian) { + buf.push("\xFE\xFF"); + } + for (let i = 0, ii = str.length; i < ii; i++) { + const char = str.charCodeAt(i); + buf.push(String.fromCharCode(char >> 8 & 0xff), String.fromCharCode(char & 0xff)); + } + return buf.join(""); +} +function getRotationMatrix(rotation, width, height) { + switch (rotation) { + case 90: + return [0, 1, -1, 0, width, 0]; + case 180: + return [-1, 0, 0, -1, width, height]; + case 270: + return [0, -1, 1, 0, 0, height]; + default: + throw new Error("Invalid rotation"); + } +} +function getSizeInBytes(x) { + return Math.ceil(Math.ceil(Math.log2(1 + x)) / 8); +} + +;// ./src/core/stream.js + + +class Stream extends BaseStream { + constructor(arrayBuffer, start, length, dict) { + super(); + this.bytes = arrayBuffer instanceof Uint8Array ? arrayBuffer : new Uint8Array(arrayBuffer); + this.start = start || 0; + this.pos = this.start; + this.end = start + length || this.bytes.length; + this.dict = dict; + } + get length() { + return this.end - this.start; + } + get isEmpty() { + return this.length === 0; + } + getByte() { + if (this.pos >= this.end) { + return -1; + } + return this.bytes[this.pos++]; + } + getBytes(length) { + const bytes = this.bytes; + const pos = this.pos; + const strEnd = this.end; + if (!length) { + return bytes.subarray(pos, strEnd); + } + let end = pos + length; + if (end > strEnd) { + end = strEnd; + } + this.pos = end; + return bytes.subarray(pos, end); + } + getByteRange(begin, end) { + if (begin < 0) { + begin = 0; + } + if (end > this.end) { + end = this.end; + } + return this.bytes.subarray(begin, end); + } + reset() { + this.pos = this.start; + } + moveStart() { + this.start = this.pos; + } + makeSubStream(start, length, dict = null) { + return new Stream(this.bytes.buffer, start, length, dict); + } +} +class StringStream extends Stream { + constructor(str) { + super(stringToBytes(str)); + } +} +class NullStream extends Stream { + constructor() { + super(new Uint8Array(0)); + } +} + +;// ./src/core/chunked_stream.js + + + +class ChunkedStream extends Stream { + constructor(length, chunkSize, manager) { + super(new Uint8Array(length), 0, length, null); + this.chunkSize = chunkSize; + this._loadedChunks = new Set(); + this.numChunks = Math.ceil(length / chunkSize); + this.manager = manager; + this.progressiveDataLength = 0; + this.lastSuccessfulEnsureByteChunk = -1; + } + getMissingChunks() { + const chunks = []; + for (let chunk = 0, n = this.numChunks; chunk < n; ++chunk) { + if (!this._loadedChunks.has(chunk)) { + chunks.push(chunk); + } + } + return chunks; + } + get numChunksLoaded() { + return this._loadedChunks.size; + } + get isDataLoaded() { + return this.numChunksLoaded === this.numChunks; + } + onReceiveData(begin, chunk) { + const chunkSize = this.chunkSize; + if (begin % chunkSize !== 0) { + throw new Error(`Bad begin offset: ${begin}`); + } + const end = begin + chunk.byteLength; + if (end % chunkSize !== 0 && end !== this.bytes.length) { + throw new Error(`Bad end offset: ${end}`); + } + this.bytes.set(new Uint8Array(chunk), begin); + const beginChunk = Math.floor(begin / chunkSize); + const endChunk = Math.floor((end - 1) / chunkSize) + 1; + for (let curChunk = beginChunk; curChunk < endChunk; ++curChunk) { + this._loadedChunks.add(curChunk); + } + } + onReceiveProgressiveData(data) { + let position = this.progressiveDataLength; + const beginChunk = Math.floor(position / this.chunkSize); + this.bytes.set(new Uint8Array(data), position); + position += data.byteLength; + this.progressiveDataLength = position; + const endChunk = position >= this.end ? this.numChunks : Math.floor(position / this.chunkSize); + for (let curChunk = beginChunk; curChunk < endChunk; ++curChunk) { + this._loadedChunks.add(curChunk); + } + } + ensureByte(pos) { + if (pos < this.progressiveDataLength) { + return; + } + const chunk = Math.floor(pos / this.chunkSize); + if (chunk > this.numChunks) { + return; + } + if (chunk === this.lastSuccessfulEnsureByteChunk) { + return; + } + if (!this._loadedChunks.has(chunk)) { + throw new MissingDataException(pos, pos + 1); + } + this.lastSuccessfulEnsureByteChunk = chunk; + } + ensureRange(begin, end) { + if (begin >= end) { + return; + } + if (end <= this.progressiveDataLength) { + return; + } + const beginChunk = Math.floor(begin / this.chunkSize); + if (beginChunk > this.numChunks) { + return; + } + const endChunk = Math.min(Math.floor((end - 1) / this.chunkSize) + 1, this.numChunks); + for (let chunk = beginChunk; chunk < endChunk; ++chunk) { + if (!this._loadedChunks.has(chunk)) { + throw new MissingDataException(begin, end); + } + } + } + nextEmptyChunk(beginChunk) { + const numChunks = this.numChunks; + for (let i = 0; i < numChunks; ++i) { + const chunk = (beginChunk + i) % numChunks; + if (!this._loadedChunks.has(chunk)) { + return chunk; + } + } + return null; + } + hasChunk(chunk) { + return this._loadedChunks.has(chunk); + } + getByte() { + const pos = this.pos; + if (pos >= this.end) { + return -1; + } + if (pos >= this.progressiveDataLength) { + this.ensureByte(pos); + } + return this.bytes[this.pos++]; + } + getBytes(length) { + const bytes = this.bytes; + const pos = this.pos; + const strEnd = this.end; + if (!length) { + if (strEnd > this.progressiveDataLength) { + this.ensureRange(pos, strEnd); + } + return bytes.subarray(pos, strEnd); + } + let end = pos + length; + if (end > strEnd) { + end = strEnd; + } + if (end > this.progressiveDataLength) { + this.ensureRange(pos, end); + } + this.pos = end; + return bytes.subarray(pos, end); + } + getByteRange(begin, end) { + if (begin < 0) { + begin = 0; + } + if (end > this.end) { + end = this.end; + } + if (end > this.progressiveDataLength) { + this.ensureRange(begin, end); + } + return this.bytes.subarray(begin, end); + } + makeSubStream(start, length, dict = null) { + if (length) { + if (start + length > this.progressiveDataLength) { + this.ensureRange(start, start + length); + } + } else if (start >= this.progressiveDataLength) { + this.ensureByte(start); + } + function ChunkedStreamSubstream() {} + ChunkedStreamSubstream.prototype = Object.create(this); + ChunkedStreamSubstream.prototype.getMissingChunks = function () { + const chunkSize = this.chunkSize; + const beginChunk = Math.floor(this.start / chunkSize); + const endChunk = Math.floor((this.end - 1) / chunkSize) + 1; + const missingChunks = []; + for (let chunk = beginChunk; chunk < endChunk; ++chunk) { + if (!this._loadedChunks.has(chunk)) { + missingChunks.push(chunk); + } + } + return missingChunks; + }; + Object.defineProperty(ChunkedStreamSubstream.prototype, "isDataLoaded", { + get() { + if (this.numChunksLoaded === this.numChunks) { + return true; + } + return this.getMissingChunks().length === 0; + }, + configurable: true + }); + const subStream = new ChunkedStreamSubstream(); + subStream.pos = subStream.start = start; + subStream.end = start + length || this.end; + subStream.dict = dict; + return subStream; + } + getBaseStreams() { + return [this]; + } +} +class ChunkedStreamManager { + constructor(pdfNetworkStream, args) { + this.length = args.length; + this.chunkSize = args.rangeChunkSize; + this.stream = new ChunkedStream(this.length, this.chunkSize, this); + this.pdfNetworkStream = pdfNetworkStream; + this.disableAutoFetch = args.disableAutoFetch; + this.msgHandler = args.msgHandler; + this.currRequestId = 0; + this._chunksNeededByRequest = new Map(); + this._requestsByChunk = new Map(); + this._promisesByRequest = new Map(); + this.progressiveDataLength = 0; + this.aborted = false; + this._loadedStreamCapability = Promise.withResolvers(); + } + sendRequest(begin, end) { + const rangeReader = this.pdfNetworkStream.getRangeReader(begin, end); + if (!rangeReader.isStreamingSupported) { + rangeReader.onProgress = this.onProgress.bind(this); + } + let chunks = [], + loaded = 0; + return new Promise((resolve, reject) => { + const readChunk = ({ + value, + done + }) => { + try { + if (done) { + const chunkData = arrayBuffersToBytes(chunks); + chunks = null; + resolve(chunkData); + return; + } + loaded += value.byteLength; + if (rangeReader.isStreamingSupported) { + this.onProgress({ + loaded + }); + } + chunks.push(value); + rangeReader.read().then(readChunk, reject); + } catch (e) { + reject(e); + } + }; + rangeReader.read().then(readChunk, reject); + }).then(data => { + if (this.aborted) { + return; + } + this.onReceiveData({ + chunk: data, + begin + }); + }); + } + requestAllChunks(noFetch = false) { + if (!noFetch) { + const missingChunks = this.stream.getMissingChunks(); + this._requestChunks(missingChunks); + } + return this._loadedStreamCapability.promise; + } + _requestChunks(chunks) { + const requestId = this.currRequestId++; + const chunksNeeded = new Set(); + this._chunksNeededByRequest.set(requestId, chunksNeeded); + for (const chunk of chunks) { + if (!this.stream.hasChunk(chunk)) { + chunksNeeded.add(chunk); + } + } + if (chunksNeeded.size === 0) { + return Promise.resolve(); + } + const capability = Promise.withResolvers(); + this._promisesByRequest.set(requestId, capability); + const chunksToRequest = []; + for (const chunk of chunksNeeded) { + let requestIds = this._requestsByChunk.get(chunk); + if (!requestIds) { + requestIds = []; + this._requestsByChunk.set(chunk, requestIds); + chunksToRequest.push(chunk); + } + requestIds.push(requestId); + } + if (chunksToRequest.length > 0) { + const groupedChunksToRequest = this.groupChunks(chunksToRequest); + for (const groupedChunk of groupedChunksToRequest) { + const begin = groupedChunk.beginChunk * this.chunkSize; + const end = Math.min(groupedChunk.endChunk * this.chunkSize, this.length); + this.sendRequest(begin, end).catch(capability.reject); + } + } + return capability.promise.catch(reason => { + if (this.aborted) { + return; + } + throw reason; + }); + } + getStream() { + return this.stream; + } + requestRange(begin, end) { + end = Math.min(end, this.length); + const beginChunk = this.getBeginChunk(begin); + const endChunk = this.getEndChunk(end); + const chunks = []; + for (let chunk = beginChunk; chunk < endChunk; ++chunk) { + chunks.push(chunk); + } + return this._requestChunks(chunks); + } + requestRanges(ranges = []) { + const chunksToRequest = []; + for (const range of ranges) { + const beginChunk = this.getBeginChunk(range.begin); + const endChunk = this.getEndChunk(range.end); + for (let chunk = beginChunk; chunk < endChunk; ++chunk) { + if (!chunksToRequest.includes(chunk)) { + chunksToRequest.push(chunk); + } + } + } + chunksToRequest.sort(function (a, b) { + return a - b; + }); + return this._requestChunks(chunksToRequest); + } + groupChunks(chunks) { + const groupedChunks = []; + let beginChunk = -1; + let prevChunk = -1; + for (let i = 0, ii = chunks.length; i < ii; ++i) { + const chunk = chunks[i]; + if (beginChunk < 0) { + beginChunk = chunk; + } + if (prevChunk >= 0 && prevChunk + 1 !== chunk) { + groupedChunks.push({ + beginChunk, + endChunk: prevChunk + 1 + }); + beginChunk = chunk; + } + if (i + 1 === chunks.length) { + groupedChunks.push({ + beginChunk, + endChunk: chunk + 1 + }); + } + prevChunk = chunk; + } + return groupedChunks; + } + onProgress(args) { + this.msgHandler.send("DocProgress", { + loaded: this.stream.numChunksLoaded * this.chunkSize + args.loaded, + total: this.length + }); + } + onReceiveData(args) { + const chunk = args.chunk; + const isProgressive = args.begin === undefined; + const begin = isProgressive ? this.progressiveDataLength : args.begin; + const end = begin + chunk.byteLength; + const beginChunk = Math.floor(begin / this.chunkSize); + const endChunk = end < this.length ? Math.floor(end / this.chunkSize) : Math.ceil(end / this.chunkSize); + if (isProgressive) { + this.stream.onReceiveProgressiveData(chunk); + this.progressiveDataLength = end; + } else { + this.stream.onReceiveData(begin, chunk); + } + if (this.stream.isDataLoaded) { + this._loadedStreamCapability.resolve(this.stream); + } + const loadedRequests = []; + for (let curChunk = beginChunk; curChunk < endChunk; ++curChunk) { + const requestIds = this._requestsByChunk.get(curChunk); + if (!requestIds) { + continue; + } + this._requestsByChunk.delete(curChunk); + for (const requestId of requestIds) { + const chunksNeeded = this._chunksNeededByRequest.get(requestId); + if (chunksNeeded.has(curChunk)) { + chunksNeeded.delete(curChunk); + } + if (chunksNeeded.size > 0) { + continue; + } + loadedRequests.push(requestId); + } + } + if (!this.disableAutoFetch && this._requestsByChunk.size === 0) { + let nextEmptyChunk; + if (this.stream.numChunksLoaded === 1) { + const lastChunk = this.stream.numChunks - 1; + if (!this.stream.hasChunk(lastChunk)) { + nextEmptyChunk = lastChunk; + } + } else { + nextEmptyChunk = this.stream.nextEmptyChunk(endChunk); + } + if (Number.isInteger(nextEmptyChunk)) { + this._requestChunks([nextEmptyChunk]); + } + } + for (const requestId of loadedRequests) { + const capability = this._promisesByRequest.get(requestId); + this._promisesByRequest.delete(requestId); + capability.resolve(); + } + this.msgHandler.send("DocProgress", { + loaded: this.stream.numChunksLoaded * this.chunkSize, + total: this.length + }); + } + onError(err) { + this._loadedStreamCapability.reject(err); + } + getBeginChunk(begin) { + return Math.floor(begin / this.chunkSize); + } + getEndChunk(end) { + return Math.floor((end - 1) / this.chunkSize) + 1; + } + abort(reason) { + this.aborted = true; + this.pdfNetworkStream?.cancelAllRequests(reason); + for (const capability of this._promisesByRequest.values()) { + capability.reject(reason); + } + } +} + +;// ./src/core/colorspace.js + + + + +function resizeRgbImage(src, dest, w1, h1, w2, h2, alpha01) { + const COMPONENTS = 3; + alpha01 = alpha01 !== 1 ? 0 : alpha01; + const xRatio = w1 / w2; + const yRatio = h1 / h2; + let newIndex = 0, + oldIndex; + const xScaled = new Uint16Array(w2); + const w1Scanline = w1 * COMPONENTS; + for (let i = 0; i < w2; i++) { + xScaled[i] = Math.floor(i * xRatio) * COMPONENTS; + } + for (let i = 0; i < h2; i++) { + const py = Math.floor(i * yRatio) * w1Scanline; + for (let j = 0; j < w2; j++) { + oldIndex = py + xScaled[j]; + dest[newIndex++] = src[oldIndex++]; + dest[newIndex++] = src[oldIndex++]; + dest[newIndex++] = src[oldIndex++]; + newIndex += alpha01; + } + } +} +function resizeRgbaImage(src, dest, w1, h1, w2, h2, alpha01) { + const xRatio = w1 / w2; + const yRatio = h1 / h2; + let newIndex = 0; + const xScaled = new Uint16Array(w2); + if (alpha01 === 1) { + for (let i = 0; i < w2; i++) { + xScaled[i] = Math.floor(i * xRatio); + } + const src32 = new Uint32Array(src.buffer); + const dest32 = new Uint32Array(dest.buffer); + const rgbMask = FeatureTest.isLittleEndian ? 0x00ffffff : 0xffffff00; + for (let i = 0; i < h2; i++) { + const buf = src32.subarray(Math.floor(i * yRatio) * w1); + for (let j = 0; j < w2; j++) { + dest32[newIndex++] |= buf[xScaled[j]] & rgbMask; + } + } + } else { + const COMPONENTS = 4; + const w1Scanline = w1 * COMPONENTS; + for (let i = 0; i < w2; i++) { + xScaled[i] = Math.floor(i * xRatio) * COMPONENTS; + } + for (let i = 0; i < h2; i++) { + const buf = src.subarray(Math.floor(i * yRatio) * w1Scanline); + for (let j = 0; j < w2; j++) { + const oldIndex = xScaled[j]; + dest[newIndex++] = buf[oldIndex]; + dest[newIndex++] = buf[oldIndex + 1]; + dest[newIndex++] = buf[oldIndex + 2]; + } + } + } +} +function copyRgbaImage(src, dest, alpha01) { + if (alpha01 === 1) { + const src32 = new Uint32Array(src.buffer); + const dest32 = new Uint32Array(dest.buffer); + const rgbMask = FeatureTest.isLittleEndian ? 0x00ffffff : 0xffffff00; + for (let i = 0, ii = src32.length; i < ii; i++) { + dest32[i] |= src32[i] & rgbMask; + } + } else { + let j = 0; + for (let i = 0, ii = src.length; i < ii; i += 4) { + dest[j++] = src[i]; + dest[j++] = src[i + 1]; + dest[j++] = src[i + 2]; + } + } +} +class ColorSpace { + constructor(name, numComps) { + this.name = name; + this.numComps = numComps; + } + getRgb(src, srcOffset) { + const rgb = new Uint8ClampedArray(3); + this.getRgbItem(src, srcOffset, rgb, 0); + return rgb; + } + getRgbItem(src, srcOffset, dest, destOffset) { + unreachable("Should not call ColorSpace.getRgbItem"); + } + getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) { + unreachable("Should not call ColorSpace.getRgbBuffer"); + } + getOutputLength(inputLength, alpha01) { + unreachable("Should not call ColorSpace.getOutputLength"); + } + isPassthrough(bits) { + return false; + } + isDefaultDecode(decodeMap, bpc) { + return ColorSpace.isDefaultDecode(decodeMap, this.numComps); + } + fillRgb(dest, originalWidth, originalHeight, width, height, actualHeight, bpc, comps, alpha01) { + const count = originalWidth * originalHeight; + let rgbBuf = null; + const numComponentColors = 1 << bpc; + const needsResizing = originalHeight !== height || originalWidth !== width; + if (this.isPassthrough(bpc)) { + rgbBuf = comps; + } else if (this.numComps === 1 && count > numComponentColors && this.name !== "DeviceGray" && this.name !== "DeviceRGB") { + const allColors = bpc <= 8 ? new Uint8Array(numComponentColors) : new Uint16Array(numComponentColors); + for (let i = 0; i < numComponentColors; i++) { + allColors[i] = i; + } + const colorMap = new Uint8ClampedArray(numComponentColors * 3); + this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bpc, 0); + if (!needsResizing) { + let destPos = 0; + for (let i = 0; i < count; ++i) { + const key = comps[i] * 3; + dest[destPos++] = colorMap[key]; + dest[destPos++] = colorMap[key + 1]; + dest[destPos++] = colorMap[key + 2]; + destPos += alpha01; + } + } else { + rgbBuf = new Uint8Array(count * 3); + let rgbPos = 0; + for (let i = 0; i < count; ++i) { + const key = comps[i] * 3; + rgbBuf[rgbPos++] = colorMap[key]; + rgbBuf[rgbPos++] = colorMap[key + 1]; + rgbBuf[rgbPos++] = colorMap[key + 2]; + } + } + } else if (!needsResizing) { + this.getRgbBuffer(comps, 0, width * actualHeight, dest, 0, bpc, alpha01); + } else { + rgbBuf = new Uint8ClampedArray(count * 3); + this.getRgbBuffer(comps, 0, count, rgbBuf, 0, bpc, 0); + } + if (rgbBuf) { + if (needsResizing) { + resizeRgbImage(rgbBuf, dest, originalWidth, originalHeight, width, height, alpha01); + } else { + let destPos = 0, + rgbPos = 0; + for (let i = 0, ii = width * actualHeight; i < ii; i++) { + dest[destPos++] = rgbBuf[rgbPos++]; + dest[destPos++] = rgbBuf[rgbPos++]; + dest[destPos++] = rgbBuf[rgbPos++]; + destPos += alpha01; + } + } + } + } + get usesZeroToOneRange() { + return shadow(this, "usesZeroToOneRange", true); + } + static _cache(cacheKey, xref, localColorSpaceCache, parsedColorSpace) { + if (!localColorSpaceCache) { + throw new Error('ColorSpace._cache - expected "localColorSpaceCache" argument.'); + } + if (!parsedColorSpace) { + throw new Error('ColorSpace._cache - expected "parsedColorSpace" argument.'); + } + let csName, csRef; + if (cacheKey instanceof Ref) { + csRef = cacheKey; + cacheKey = xref.fetch(cacheKey); + } + if (cacheKey instanceof Name) { + csName = cacheKey.name; + } + if (csName || csRef) { + localColorSpaceCache.set(csName, csRef, parsedColorSpace); + } + } + static getCached(cacheKey, xref, localColorSpaceCache) { + if (!localColorSpaceCache) { + throw new Error('ColorSpace.getCached - expected "localColorSpaceCache" argument.'); + } + if (cacheKey instanceof Ref) { + const localColorSpace = localColorSpaceCache.getByRef(cacheKey); + if (localColorSpace) { + return localColorSpace; + } + try { + cacheKey = xref.fetch(cacheKey); + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + } + } + if (cacheKey instanceof Name) { + const localColorSpace = localColorSpaceCache.getByName(cacheKey.name); + if (localColorSpace) { + return localColorSpace; + } + } + return null; + } + static async parseAsync({ + cs, + xref, + resources = null, + pdfFunctionFactory, + localColorSpaceCache + }) { + const parsedColorSpace = this._parse(cs, xref, resources, pdfFunctionFactory); + this._cache(cs, xref, localColorSpaceCache, parsedColorSpace); + return parsedColorSpace; + } + static parse({ + cs, + xref, + resources = null, + pdfFunctionFactory, + localColorSpaceCache + }) { + const cachedColorSpace = this.getCached(cs, xref, localColorSpaceCache); + if (cachedColorSpace) { + return cachedColorSpace; + } + const parsedColorSpace = this._parse(cs, xref, resources, pdfFunctionFactory); + this._cache(cs, xref, localColorSpaceCache, parsedColorSpace); + return parsedColorSpace; + } + static _parse(cs, xref, resources = null, pdfFunctionFactory) { + cs = xref.fetchIfRef(cs); + if (cs instanceof Name) { + switch (cs.name) { + case "G": + case "DeviceGray": + return this.singletons.gray; + case "RGB": + case "DeviceRGB": + return this.singletons.rgb; + case "DeviceRGBA": + return this.singletons.rgba; + case "CMYK": + case "DeviceCMYK": + return this.singletons.cmyk; + case "Pattern": + return new PatternCS(null); + default: + if (resources instanceof Dict) { + const colorSpaces = resources.get("ColorSpace"); + if (colorSpaces instanceof Dict) { + const resourcesCS = colorSpaces.get(cs.name); + if (resourcesCS) { + if (resourcesCS instanceof Name) { + return this._parse(resourcesCS, xref, resources, pdfFunctionFactory); + } + cs = resourcesCS; + break; + } + } + } + warn(`Unrecognized ColorSpace: ${cs.name}`); + return this.singletons.gray; + } + } + if (Array.isArray(cs)) { + const mode = xref.fetchIfRef(cs[0]).name; + let params, numComps, baseCS, whitePoint, blackPoint, gamma; + switch (mode) { + case "G": + case "DeviceGray": + return this.singletons.gray; + case "RGB": + case "DeviceRGB": + return this.singletons.rgb; + case "CMYK": + case "DeviceCMYK": + return this.singletons.cmyk; + case "CalGray": + params = xref.fetchIfRef(cs[1]); + whitePoint = params.getArray("WhitePoint"); + blackPoint = params.getArray("BlackPoint"); + gamma = params.get("Gamma"); + return new CalGrayCS(whitePoint, blackPoint, gamma); + case "CalRGB": + params = xref.fetchIfRef(cs[1]); + whitePoint = params.getArray("WhitePoint"); + blackPoint = params.getArray("BlackPoint"); + gamma = params.getArray("Gamma"); + const matrix = params.getArray("Matrix"); + return new CalRGBCS(whitePoint, blackPoint, gamma, matrix); + case "ICCBased": + const stream = xref.fetchIfRef(cs[1]); + const dict = stream.dict; + numComps = dict.get("N"); + const alt = dict.get("Alternate"); + if (alt) { + const altCS = this._parse(alt, xref, resources, pdfFunctionFactory); + if (altCS.numComps === numComps) { + return altCS; + } + warn("ICCBased color space: Ignoring incorrect /Alternate entry."); + } + if (numComps === 1) { + return this.singletons.gray; + } else if (numComps === 3) { + return this.singletons.rgb; + } else if (numComps === 4) { + return this.singletons.cmyk; + } + break; + case "Pattern": + baseCS = cs[1] || null; + if (baseCS) { + baseCS = this._parse(baseCS, xref, resources, pdfFunctionFactory); + } + return new PatternCS(baseCS); + case "I": + case "Indexed": + baseCS = this._parse(cs[1], xref, resources, pdfFunctionFactory); + const hiVal = Math.max(0, Math.min(xref.fetchIfRef(cs[2]), 255)); + const lookup = xref.fetchIfRef(cs[3]); + return new IndexedCS(baseCS, hiVal, lookup); + case "Separation": + case "DeviceN": + const name = xref.fetchIfRef(cs[1]); + numComps = Array.isArray(name) ? name.length : 1; + baseCS = this._parse(cs[2], xref, resources, pdfFunctionFactory); + const tintFn = pdfFunctionFactory.create(cs[3]); + return new AlternateCS(numComps, baseCS, tintFn); + case "Lab": + params = xref.fetchIfRef(cs[1]); + whitePoint = params.getArray("WhitePoint"); + blackPoint = params.getArray("BlackPoint"); + const range = params.getArray("Range"); + return new LabCS(whitePoint, blackPoint, range); + default: + warn(`Unimplemented ColorSpace object: ${mode}`); + return this.singletons.gray; + } + } + warn(`Unrecognized ColorSpace object: ${cs}`); + return this.singletons.gray; + } + static isDefaultDecode(decode, numComps) { + if (!Array.isArray(decode)) { + return true; + } + if (numComps * 2 !== decode.length) { + warn("The decode map is not the correct length"); + return true; + } + for (let i = 0, ii = decode.length; i < ii; i += 2) { + if (decode[i] !== 0 || decode[i + 1] !== 1) { + return false; + } + } + return true; + } + static get singletons() { + return shadow(this, "singletons", { + get gray() { + return shadow(this, "gray", new DeviceGrayCS()); + }, + get rgb() { + return shadow(this, "rgb", new DeviceRgbCS()); + }, + get rgba() { + return shadow(this, "rgba", new DeviceRgbaCS()); + }, + get cmyk() { + return shadow(this, "cmyk", new DeviceCmykCS()); + } + }); + } +} +class AlternateCS extends ColorSpace { + constructor(numComps, base, tintFn) { + super("Alternate", numComps); + this.base = base; + this.tintFn = tintFn; + this.tmpBuf = new Float32Array(base.numComps); + } + getRgbItem(src, srcOffset, dest, destOffset) { + const tmpBuf = this.tmpBuf; + this.tintFn(src, srcOffset, tmpBuf, 0); + this.base.getRgbItem(tmpBuf, 0, dest, destOffset); + } + getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) { + const tintFn = this.tintFn; + const base = this.base; + const scale = 1 / ((1 << bits) - 1); + const baseNumComps = base.numComps; + const usesZeroToOneRange = base.usesZeroToOneRange; + const isPassthrough = (base.isPassthrough(8) || !usesZeroToOneRange) && alpha01 === 0; + let pos = isPassthrough ? destOffset : 0; + const baseBuf = isPassthrough ? dest : new Uint8ClampedArray(baseNumComps * count); + const numComps = this.numComps; + const scaled = new Float32Array(numComps); + const tinted = new Float32Array(baseNumComps); + let i, j; + for (i = 0; i < count; i++) { + for (j = 0; j < numComps; j++) { + scaled[j] = src[srcOffset++] * scale; + } + tintFn(scaled, 0, tinted, 0); + if (usesZeroToOneRange) { + for (j = 0; j < baseNumComps; j++) { + baseBuf[pos++] = tinted[j] * 255; + } + } else { + base.getRgbItem(tinted, 0, baseBuf, pos); + pos += baseNumComps; + } + } + if (!isPassthrough) { + base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8, alpha01); + } + } + getOutputLength(inputLength, alpha01) { + return this.base.getOutputLength(inputLength * this.base.numComps / this.numComps, alpha01); + } +} +class PatternCS extends ColorSpace { + constructor(baseCS) { + super("Pattern", null); + this.base = baseCS; + } + isDefaultDecode(decodeMap, bpc) { + unreachable("Should not call PatternCS.isDefaultDecode"); + } +} +class IndexedCS extends ColorSpace { + constructor(base, highVal, lookup) { + super("Indexed", 1); + this.base = base; + const length = base.numComps * (highVal + 1); + this.lookup = new Uint8Array(length); + if (lookup instanceof BaseStream) { + const bytes = lookup.getBytes(length); + this.lookup.set(bytes); + } else if (typeof lookup === "string") { + for (let i = 0; i < length; ++i) { + this.lookup[i] = lookup.charCodeAt(i) & 0xff; + } + } else { + throw new FormatError(`IndexedCS - unrecognized lookup table: ${lookup}`); + } + } + getRgbItem(src, srcOffset, dest, destOffset) { + const numComps = this.base.numComps; + const start = src[srcOffset] * numComps; + this.base.getRgbBuffer(this.lookup, start, 1, dest, destOffset, 8, 0); + } + getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) { + const base = this.base; + const numComps = base.numComps; + const outputDelta = base.getOutputLength(numComps, alpha01); + const lookup = this.lookup; + for (let i = 0; i < count; ++i) { + const lookupPos = src[srcOffset++] * numComps; + base.getRgbBuffer(lookup, lookupPos, 1, dest, destOffset, 8, alpha01); + destOffset += outputDelta; + } + } + getOutputLength(inputLength, alpha01) { + return this.base.getOutputLength(inputLength * this.base.numComps, alpha01); + } + isDefaultDecode(decodeMap, bpc) { + if (!Array.isArray(decodeMap)) { + return true; + } + if (decodeMap.length !== 2) { + warn("Decode map length is not correct"); + return true; + } + if (!Number.isInteger(bpc) || bpc < 1) { + warn("Bits per component is not correct"); + return true; + } + return decodeMap[0] === 0 && decodeMap[1] === (1 << bpc) - 1; + } +} +class DeviceGrayCS extends ColorSpace { + constructor() { + super("DeviceGray", 1); + } + getRgbItem(src, srcOffset, dest, destOffset) { + const c = src[srcOffset] * 255; + dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c; + } + getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) { + const scale = 255 / ((1 << bits) - 1); + let j = srcOffset, + q = destOffset; + for (let i = 0; i < count; ++i) { + const c = scale * src[j++]; + dest[q++] = c; + dest[q++] = c; + dest[q++] = c; + q += alpha01; + } + } + getOutputLength(inputLength, alpha01) { + return inputLength * (3 + alpha01); + } +} +class DeviceRgbCS extends ColorSpace { + constructor() { + super("DeviceRGB", 3); + } + getRgbItem(src, srcOffset, dest, destOffset) { + dest[destOffset] = src[srcOffset] * 255; + dest[destOffset + 1] = src[srcOffset + 1] * 255; + dest[destOffset + 2] = src[srcOffset + 2] * 255; + } + getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) { + if (bits === 8 && alpha01 === 0) { + dest.set(src.subarray(srcOffset, srcOffset + count * 3), destOffset); + return; + } + const scale = 255 / ((1 << bits) - 1); + let j = srcOffset, + q = destOffset; + for (let i = 0; i < count; ++i) { + dest[q++] = scale * src[j++]; + dest[q++] = scale * src[j++]; + dest[q++] = scale * src[j++]; + q += alpha01; + } + } + getOutputLength(inputLength, alpha01) { + return inputLength * (3 + alpha01) / 3 | 0; + } + isPassthrough(bits) { + return bits === 8; + } +} +class DeviceRgbaCS extends ColorSpace { + constructor() { + super("DeviceRGBA", 4); + } + getOutputLength(inputLength, _alpha01) { + return inputLength * 4; + } + isPassthrough(bits) { + return bits === 8; + } + fillRgb(dest, originalWidth, originalHeight, width, height, actualHeight, bpc, comps, alpha01) { + if (originalHeight !== height || originalWidth !== width) { + resizeRgbaImage(comps, dest, originalWidth, originalHeight, width, height, alpha01); + } else { + copyRgbaImage(comps, dest, alpha01); + } + } +} +class DeviceCmykCS extends ColorSpace { + constructor() { + super("DeviceCMYK", 4); + } + #toRgb(src, srcOffset, srcScale, dest, destOffset) { + const c = src[srcOffset] * srcScale; + const m = src[srcOffset + 1] * srcScale; + const y = src[srcOffset + 2] * srcScale; + const k = src[srcOffset + 3] * srcScale; + dest[destOffset] = 255 + c * (-4.387332384609988 * c + 54.48615194189176 * m + 18.82290502165302 * y + 212.25662451639585 * k + -285.2331026137004) + m * (1.7149763477362134 * m - 5.6096736904047315 * y + -17.873870861415444 * k - 5.497006427196366) + y * (-2.5217340131683033 * y - 21.248923337353073 * k + 17.5119270841813) + k * (-21.86122147463605 * k - 189.48180835922747); + dest[destOffset + 1] = 255 + c * (8.841041422036149 * c + 60.118027045597366 * m + 6.871425592049007 * y + 31.159100130055922 * k + -79.2970844816548) + m * (-15.310361306967817 * m + 17.575251261109482 * y + 131.35250912493976 * k - 190.9453302588951) + y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) + k * (-20.737325471181034 * k - 187.80453709719578); + dest[destOffset + 2] = 255 + c * (0.8842522430003296 * c + 8.078677503112928 * m + 30.89978309703729 * y - 0.23883238689178934 * k + -14.183576799673286) + m * (10.49593273432072 * m + 63.02378494754052 * y + 50.606957656360734 * k - 112.23884253719248) + y * (0.03296041114873217 * y + 115.60384449646641 * k + -193.58209356861505) + k * (-22.33816807309886 * k - 180.12613974708367); + } + getRgbItem(src, srcOffset, dest, destOffset) { + this.#toRgb(src, srcOffset, 1, dest, destOffset); + } + getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) { + const scale = 1 / ((1 << bits) - 1); + for (let i = 0; i < count; i++) { + this.#toRgb(src, srcOffset, scale, dest, destOffset); + srcOffset += 4; + destOffset += 3 + alpha01; + } + } + getOutputLength(inputLength, alpha01) { + return inputLength / 4 * (3 + alpha01) | 0; + } +} +class CalGrayCS extends ColorSpace { + constructor(whitePoint, blackPoint, gamma) { + super("CalGray", 1); + if (!whitePoint) { + throw new FormatError("WhitePoint missing - required for color space CalGray"); + } + [this.XW, this.YW, this.ZW] = whitePoint; + [this.XB, this.YB, this.ZB] = blackPoint || [0, 0, 0]; + this.G = gamma || 1; + if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) { + throw new FormatError(`Invalid WhitePoint components for ${this.name}, no fallback available`); + } + if (this.XB < 0 || this.YB < 0 || this.ZB < 0) { + info(`Invalid BlackPoint for ${this.name}, falling back to default.`); + this.XB = this.YB = this.ZB = 0; + } + if (this.XB !== 0 || this.YB !== 0 || this.ZB !== 0) { + warn(`${this.name}, BlackPoint: XB: ${this.XB}, YB: ${this.YB}, ` + `ZB: ${this.ZB}, only default values are supported.`); + } + if (this.G < 1) { + info(`Invalid Gamma: ${this.G} for ${this.name}, falling back to default.`); + this.G = 1; + } + } + #toRgb(src, srcOffset, dest, destOffset, scale) { + const A = src[srcOffset] * scale; + const AG = A ** this.G; + const L = this.YW * AG; + const val = Math.max(295.8 * L ** 0.3333333333333333 - 40.8, 0); + dest[destOffset] = val; + dest[destOffset + 1] = val; + dest[destOffset + 2] = val; + } + getRgbItem(src, srcOffset, dest, destOffset) { + this.#toRgb(src, srcOffset, dest, destOffset, 1); + } + getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) { + const scale = 1 / ((1 << bits) - 1); + for (let i = 0; i < count; ++i) { + this.#toRgb(src, srcOffset, dest, destOffset, scale); + srcOffset += 1; + destOffset += 3 + alpha01; + } + } + getOutputLength(inputLength, alpha01) { + return inputLength * (3 + alpha01); + } +} +class CalRGBCS extends ColorSpace { + static #BRADFORD_SCALE_MATRIX = new Float32Array([0.8951, 0.2664, -0.1614, -0.7502, 1.7135, 0.0367, 0.0389, -0.0685, 1.0296]); + static #BRADFORD_SCALE_INVERSE_MATRIX = new Float32Array([0.9869929, -0.1470543, 0.1599627, 0.4323053, 0.5183603, 0.0492912, -0.0085287, 0.0400428, 0.9684867]); + static #SRGB_D65_XYZ_TO_RGB_MATRIX = new Float32Array([3.2404542, -1.5371385, -0.4985314, -0.9692660, 1.8760108, 0.0415560, 0.0556434, -0.2040259, 1.0572252]); + static #FLAT_WHITEPOINT_MATRIX = new Float32Array([1, 1, 1]); + static #tempNormalizeMatrix = new Float32Array(3); + static #tempConvertMatrix1 = new Float32Array(3); + static #tempConvertMatrix2 = new Float32Array(3); + static #DECODE_L_CONSTANT = ((8 + 16) / 116) ** 3 / 8.0; + constructor(whitePoint, blackPoint, gamma, matrix) { + super("CalRGB", 3); + if (!whitePoint) { + throw new FormatError("WhitePoint missing - required for color space CalRGB"); + } + const [XW, YW, ZW] = this.whitePoint = whitePoint; + const [XB, YB, ZB] = this.blackPoint = blackPoint || new Float32Array(3); + [this.GR, this.GG, this.GB] = gamma || new Float32Array([1, 1, 1]); + [this.MXA, this.MYA, this.MZA, this.MXB, this.MYB, this.MZB, this.MXC, this.MYC, this.MZC] = matrix || new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]); + if (XW < 0 || ZW < 0 || YW !== 1) { + throw new FormatError(`Invalid WhitePoint components for ${this.name}, no fallback available`); + } + if (XB < 0 || YB < 0 || ZB < 0) { + info(`Invalid BlackPoint for ${this.name} [${XB}, ${YB}, ${ZB}], ` + "falling back to default."); + this.blackPoint = new Float32Array(3); + } + if (this.GR < 0 || this.GG < 0 || this.GB < 0) { + info(`Invalid Gamma [${this.GR}, ${this.GG}, ${this.GB}] for ` + `${this.name}, falling back to default.`); + this.GR = this.GG = this.GB = 1; + } + } + #matrixProduct(a, b, result) { + result[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + result[1] = a[3] * b[0] + a[4] * b[1] + a[5] * b[2]; + result[2] = a[6] * b[0] + a[7] * b[1] + a[8] * b[2]; + } + #toFlat(sourceWhitePoint, LMS, result) { + result[0] = LMS[0] * 1 / sourceWhitePoint[0]; + result[1] = LMS[1] * 1 / sourceWhitePoint[1]; + result[2] = LMS[2] * 1 / sourceWhitePoint[2]; + } + #toD65(sourceWhitePoint, LMS, result) { + const D65X = 0.95047; + const D65Y = 1; + const D65Z = 1.08883; + result[0] = LMS[0] * D65X / sourceWhitePoint[0]; + result[1] = LMS[1] * D65Y / sourceWhitePoint[1]; + result[2] = LMS[2] * D65Z / sourceWhitePoint[2]; + } + #sRGBTransferFunction(color) { + if (color <= 0.0031308) { + return this.#adjustToRange(0, 1, 12.92 * color); + } + if (color >= 0.99554525) { + return 1; + } + return this.#adjustToRange(0, 1, (1 + 0.055) * color ** (1 / 2.4) - 0.055); + } + #adjustToRange(min, max, value) { + return Math.max(min, Math.min(max, value)); + } + #decodeL(L) { + if (L < 0) { + return -this.#decodeL(-L); + } + if (L > 8.0) { + return ((L + 16) / 116) ** 3; + } + return L * CalRGBCS.#DECODE_L_CONSTANT; + } + #compensateBlackPoint(sourceBlackPoint, XYZ_Flat, result) { + if (sourceBlackPoint[0] === 0 && sourceBlackPoint[1] === 0 && sourceBlackPoint[2] === 0) { + result[0] = XYZ_Flat[0]; + result[1] = XYZ_Flat[1]; + result[2] = XYZ_Flat[2]; + return; + } + const zeroDecodeL = this.#decodeL(0); + const X_DST = zeroDecodeL; + const X_SRC = this.#decodeL(sourceBlackPoint[0]); + const Y_DST = zeroDecodeL; + const Y_SRC = this.#decodeL(sourceBlackPoint[1]); + const Z_DST = zeroDecodeL; + const Z_SRC = this.#decodeL(sourceBlackPoint[2]); + const X_Scale = (1 - X_DST) / (1 - X_SRC); + const X_Offset = 1 - X_Scale; + const Y_Scale = (1 - Y_DST) / (1 - Y_SRC); + const Y_Offset = 1 - Y_Scale; + const Z_Scale = (1 - Z_DST) / (1 - Z_SRC); + const Z_Offset = 1 - Z_Scale; + result[0] = XYZ_Flat[0] * X_Scale + X_Offset; + result[1] = XYZ_Flat[1] * Y_Scale + Y_Offset; + result[2] = XYZ_Flat[2] * Z_Scale + Z_Offset; + } + #normalizeWhitePointToFlat(sourceWhitePoint, XYZ_In, result) { + if (sourceWhitePoint[0] === 1 && sourceWhitePoint[2] === 1) { + result[0] = XYZ_In[0]; + result[1] = XYZ_In[1]; + result[2] = XYZ_In[2]; + return; + } + const LMS = result; + this.#matrixProduct(CalRGBCS.#BRADFORD_SCALE_MATRIX, XYZ_In, LMS); + const LMS_Flat = CalRGBCS.#tempNormalizeMatrix; + this.#toFlat(sourceWhitePoint, LMS, LMS_Flat); + this.#matrixProduct(CalRGBCS.#BRADFORD_SCALE_INVERSE_MATRIX, LMS_Flat, result); + } + #normalizeWhitePointToD65(sourceWhitePoint, XYZ_In, result) { + const LMS = result; + this.#matrixProduct(CalRGBCS.#BRADFORD_SCALE_MATRIX, XYZ_In, LMS); + const LMS_D65 = CalRGBCS.#tempNormalizeMatrix; + this.#toD65(sourceWhitePoint, LMS, LMS_D65); + this.#matrixProduct(CalRGBCS.#BRADFORD_SCALE_INVERSE_MATRIX, LMS_D65, result); + } + #toRgb(src, srcOffset, dest, destOffset, scale) { + const A = this.#adjustToRange(0, 1, src[srcOffset] * scale); + const B = this.#adjustToRange(0, 1, src[srcOffset + 1] * scale); + const C = this.#adjustToRange(0, 1, src[srcOffset + 2] * scale); + const AGR = A === 1 ? 1 : A ** this.GR; + const BGG = B === 1 ? 1 : B ** this.GG; + const CGB = C === 1 ? 1 : C ** this.GB; + const X = this.MXA * AGR + this.MXB * BGG + this.MXC * CGB; + const Y = this.MYA * AGR + this.MYB * BGG + this.MYC * CGB; + const Z = this.MZA * AGR + this.MZB * BGG + this.MZC * CGB; + const XYZ = CalRGBCS.#tempConvertMatrix1; + XYZ[0] = X; + XYZ[1] = Y; + XYZ[2] = Z; + const XYZ_Flat = CalRGBCS.#tempConvertMatrix2; + this.#normalizeWhitePointToFlat(this.whitePoint, XYZ, XYZ_Flat); + const XYZ_Black = CalRGBCS.#tempConvertMatrix1; + this.#compensateBlackPoint(this.blackPoint, XYZ_Flat, XYZ_Black); + const XYZ_D65 = CalRGBCS.#tempConvertMatrix2; + this.#normalizeWhitePointToD65(CalRGBCS.#FLAT_WHITEPOINT_MATRIX, XYZ_Black, XYZ_D65); + const SRGB = CalRGBCS.#tempConvertMatrix1; + this.#matrixProduct(CalRGBCS.#SRGB_D65_XYZ_TO_RGB_MATRIX, XYZ_D65, SRGB); + dest[destOffset] = this.#sRGBTransferFunction(SRGB[0]) * 255; + dest[destOffset + 1] = this.#sRGBTransferFunction(SRGB[1]) * 255; + dest[destOffset + 2] = this.#sRGBTransferFunction(SRGB[2]) * 255; + } + getRgbItem(src, srcOffset, dest, destOffset) { + this.#toRgb(src, srcOffset, dest, destOffset, 1); + } + getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) { + const scale = 1 / ((1 << bits) - 1); + for (let i = 0; i < count; ++i) { + this.#toRgb(src, srcOffset, dest, destOffset, scale); + srcOffset += 3; + destOffset += 3 + alpha01; + } + } + getOutputLength(inputLength, alpha01) { + return inputLength * (3 + alpha01) / 3 | 0; + } +} +class LabCS extends ColorSpace { + constructor(whitePoint, blackPoint, range) { + super("Lab", 3); + if (!whitePoint) { + throw new FormatError("WhitePoint missing - required for color space Lab"); + } + [this.XW, this.YW, this.ZW] = whitePoint; + [this.amin, this.amax, this.bmin, this.bmax] = range || [-100, 100, -100, 100]; + [this.XB, this.YB, this.ZB] = blackPoint || [0, 0, 0]; + if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) { + throw new FormatError("Invalid WhitePoint components, no fallback available"); + } + if (this.XB < 0 || this.YB < 0 || this.ZB < 0) { + info("Invalid BlackPoint, falling back to default"); + this.XB = this.YB = this.ZB = 0; + } + if (this.amin > this.amax || this.bmin > this.bmax) { + info("Invalid Range, falling back to defaults"); + this.amin = -100; + this.amax = 100; + this.bmin = -100; + this.bmax = 100; + } + } + #fn_g(x) { + return x >= 6 / 29 ? x ** 3 : 108 / 841 * (x - 4 / 29); + } + #decode(value, high1, low2, high2) { + return low2 + value * (high2 - low2) / high1; + } + #toRgb(src, srcOffset, maxVal, dest, destOffset) { + let Ls = src[srcOffset]; + let as = src[srcOffset + 1]; + let bs = src[srcOffset + 2]; + if (maxVal !== false) { + Ls = this.#decode(Ls, maxVal, 0, 100); + as = this.#decode(as, maxVal, this.amin, this.amax); + bs = this.#decode(bs, maxVal, this.bmin, this.bmax); + } + if (as > this.amax) { + as = this.amax; + } else if (as < this.amin) { + as = this.amin; + } + if (bs > this.bmax) { + bs = this.bmax; + } else if (bs < this.bmin) { + bs = this.bmin; + } + const M = (Ls + 16) / 116; + const L = M + as / 500; + const N = M - bs / 200; + const X = this.XW * this.#fn_g(L); + const Y = this.YW * this.#fn_g(M); + const Z = this.ZW * this.#fn_g(N); + let r, g, b; + if (this.ZW < 1) { + r = X * 3.1339 + Y * -1.617 + Z * -0.4906; + g = X * -0.9785 + Y * 1.916 + Z * 0.0333; + b = X * 0.072 + Y * -0.229 + Z * 1.4057; + } else { + r = X * 3.2406 + Y * -1.5372 + Z * -0.4986; + g = X * -0.9689 + Y * 1.8758 + Z * 0.0415; + b = X * 0.0557 + Y * -0.204 + Z * 1.057; + } + dest[destOffset] = Math.sqrt(r) * 255; + dest[destOffset + 1] = Math.sqrt(g) * 255; + dest[destOffset + 2] = Math.sqrt(b) * 255; + } + getRgbItem(src, srcOffset, dest, destOffset) { + this.#toRgb(src, srcOffset, false, dest, destOffset); + } + getRgbBuffer(src, srcOffset, count, dest, destOffset, bits, alpha01) { + const maxVal = (1 << bits) - 1; + for (let i = 0; i < count; i++) { + this.#toRgb(src, srcOffset, maxVal, dest, destOffset); + srcOffset += 3; + destOffset += 3 + alpha01; + } + } + getOutputLength(inputLength, alpha01) { + return inputLength * (3 + alpha01) / 3 | 0; + } + isDefaultDecode(decodeMap, bpc) { + return true; + } + get usesZeroToOneRange() { + return shadow(this, "usesZeroToOneRange", false); + } +} + +;// ./src/core/binary_cmap.js + +function hexToInt(a, size) { + let n = 0; + for (let i = 0; i <= size; i++) { + n = n << 8 | a[i]; + } + return n >>> 0; +} +function hexToStr(a, size) { + if (size === 1) { + return String.fromCharCode(a[0], a[1]); + } + if (size === 3) { + return String.fromCharCode(a[0], a[1], a[2], a[3]); + } + return String.fromCharCode(...a.subarray(0, size + 1)); +} +function addHex(a, b, size) { + let c = 0; + for (let i = size; i >= 0; i--) { + c += a[i] + b[i]; + a[i] = c & 255; + c >>= 8; + } +} +function incHex(a, size) { + let c = 1; + for (let i = size; i >= 0 && c > 0; i--) { + c += a[i]; + a[i] = c & 255; + c >>= 8; + } +} +const MAX_NUM_SIZE = 16; +const MAX_ENCODED_NUM_SIZE = 19; +class BinaryCMapStream { + constructor(data) { + this.buffer = data; + this.pos = 0; + this.end = data.length; + this.tmpBuf = new Uint8Array(MAX_ENCODED_NUM_SIZE); + } + readByte() { + if (this.pos >= this.end) { + return -1; + } + return this.buffer[this.pos++]; + } + readNumber() { + let n = 0; + let last; + do { + const b = this.readByte(); + if (b < 0) { + throw new FormatError("unexpected EOF in bcmap"); + } + last = !(b & 0x80); + n = n << 7 | b & 0x7f; + } while (!last); + return n; + } + readSigned() { + const n = this.readNumber(); + return n & 1 ? ~(n >>> 1) : n >>> 1; + } + readHex(num, size) { + num.set(this.buffer.subarray(this.pos, this.pos + size + 1)); + this.pos += size + 1; + } + readHexNumber(num, size) { + let last; + const stack = this.tmpBuf; + let sp = 0; + do { + const b = this.readByte(); + if (b < 0) { + throw new FormatError("unexpected EOF in bcmap"); + } + last = !(b & 0x80); + stack[sp++] = b & 0x7f; + } while (!last); + let i = size, + buffer = 0, + bufferSize = 0; + while (i >= 0) { + while (bufferSize < 8 && stack.length > 0) { + buffer |= stack[--sp] << bufferSize; + bufferSize += 7; + } + num[i] = buffer & 255; + i--; + buffer >>= 8; + bufferSize -= 8; + } + } + readHexSigned(num, size) { + this.readHexNumber(num, size); + const sign = num[size] & 1 ? 255 : 0; + let c = 0; + for (let i = 0; i <= size; i++) { + c = (c & 1) << 8 | num[i]; + num[i] = c >> 1 ^ sign; + } + } + readString() { + const len = this.readNumber(), + buf = new Array(len); + for (let i = 0; i < len; i++) { + buf[i] = this.readNumber(); + } + return String.fromCharCode(...buf); + } +} +class BinaryCMapReader { + async process(data, cMap, extend) { + const stream = new BinaryCMapStream(data); + const header = stream.readByte(); + cMap.vertical = !!(header & 1); + let useCMap = null; + const start = new Uint8Array(MAX_NUM_SIZE); + const end = new Uint8Array(MAX_NUM_SIZE); + const char = new Uint8Array(MAX_NUM_SIZE); + const charCode = new Uint8Array(MAX_NUM_SIZE); + const tmp = new Uint8Array(MAX_NUM_SIZE); + let code; + let b; + while ((b = stream.readByte()) >= 0) { + const type = b >> 5; + if (type === 7) { + switch (b & 0x1f) { + case 0: + stream.readString(); + break; + case 1: + useCMap = stream.readString(); + break; + } + continue; + } + const sequence = !!(b & 0x10); + const dataSize = b & 15; + if (dataSize + 1 > MAX_NUM_SIZE) { + throw new Error("BinaryCMapReader.process: Invalid dataSize."); + } + const ucs2DataSize = 1; + const subitemsCount = stream.readNumber(); + switch (type) { + case 0: + stream.readHex(start, dataSize); + stream.readHexNumber(end, dataSize); + addHex(end, start, dataSize); + cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize), hexToInt(end, dataSize)); + for (let i = 1; i < subitemsCount; i++) { + incHex(end, dataSize); + stream.readHexNumber(start, dataSize); + addHex(start, end, dataSize); + stream.readHexNumber(end, dataSize); + addHex(end, start, dataSize); + cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize), hexToInt(end, dataSize)); + } + break; + case 1: + stream.readHex(start, dataSize); + stream.readHexNumber(end, dataSize); + addHex(end, start, dataSize); + stream.readNumber(); + for (let i = 1; i < subitemsCount; i++) { + incHex(end, dataSize); + stream.readHexNumber(start, dataSize); + addHex(start, end, dataSize); + stream.readHexNumber(end, dataSize); + addHex(end, start, dataSize); + stream.readNumber(); + } + break; + case 2: + stream.readHex(char, dataSize); + code = stream.readNumber(); + cMap.mapOne(hexToInt(char, dataSize), code); + for (let i = 1; i < subitemsCount; i++) { + incHex(char, dataSize); + if (!sequence) { + stream.readHexNumber(tmp, dataSize); + addHex(char, tmp, dataSize); + } + code = stream.readSigned() + (code + 1); + cMap.mapOne(hexToInt(char, dataSize), code); + } + break; + case 3: + stream.readHex(start, dataSize); + stream.readHexNumber(end, dataSize); + addHex(end, start, dataSize); + code = stream.readNumber(); + cMap.mapCidRange(hexToInt(start, dataSize), hexToInt(end, dataSize), code); + for (let i = 1; i < subitemsCount; i++) { + incHex(end, dataSize); + if (!sequence) { + stream.readHexNumber(start, dataSize); + addHex(start, end, dataSize); + } else { + start.set(end); + } + stream.readHexNumber(end, dataSize); + addHex(end, start, dataSize); + code = stream.readNumber(); + cMap.mapCidRange(hexToInt(start, dataSize), hexToInt(end, dataSize), code); + } + break; + case 4: + stream.readHex(char, ucs2DataSize); + stream.readHex(charCode, dataSize); + cMap.mapOne(hexToInt(char, ucs2DataSize), hexToStr(charCode, dataSize)); + for (let i = 1; i < subitemsCount; i++) { + incHex(char, ucs2DataSize); + if (!sequence) { + stream.readHexNumber(tmp, ucs2DataSize); + addHex(char, tmp, ucs2DataSize); + } + incHex(charCode, dataSize); + stream.readHexSigned(tmp, dataSize); + addHex(charCode, tmp, dataSize); + cMap.mapOne(hexToInt(char, ucs2DataSize), hexToStr(charCode, dataSize)); + } + break; + case 5: + stream.readHex(start, ucs2DataSize); + stream.readHexNumber(end, ucs2DataSize); + addHex(end, start, ucs2DataSize); + stream.readHex(charCode, dataSize); + cMap.mapBfRange(hexToInt(start, ucs2DataSize), hexToInt(end, ucs2DataSize), hexToStr(charCode, dataSize)); + for (let i = 1; i < subitemsCount; i++) { + incHex(end, ucs2DataSize); + if (!sequence) { + stream.readHexNumber(start, ucs2DataSize); + addHex(start, end, ucs2DataSize); + } else { + start.set(end); + } + stream.readHexNumber(end, ucs2DataSize); + addHex(end, start, ucs2DataSize); + stream.readHex(charCode, dataSize); + cMap.mapBfRange(hexToInt(start, ucs2DataSize), hexToInt(end, ucs2DataSize), hexToStr(charCode, dataSize)); + } + break; + default: + throw new Error(`BinaryCMapReader.process - unknown type: ${type}`); + } + } + if (useCMap) { + return extend(useCMap); + } + return cMap; + } +} + +;// ./src/core/decode_stream.js + + +const emptyBuffer = new Uint8Array(0); +class DecodeStream extends BaseStream { + constructor(maybeMinBufferLength) { + super(); + this._rawMinBufferLength = maybeMinBufferLength || 0; + this.pos = 0; + this.bufferLength = 0; + this.eof = false; + this.buffer = emptyBuffer; + this.minBufferLength = 512; + if (maybeMinBufferLength) { + while (this.minBufferLength < maybeMinBufferLength) { + this.minBufferLength *= 2; + } + } + } + get isEmpty() { + while (!this.eof && this.bufferLength === 0) { + this.readBlock(); + } + return this.bufferLength === 0; + } + ensureBuffer(requested) { + const buffer = this.buffer; + if (requested <= buffer.byteLength) { + return buffer; + } + let size = this.minBufferLength; + while (size < requested) { + size *= 2; + } + const buffer2 = new Uint8Array(size); + buffer2.set(buffer); + return this.buffer = buffer2; + } + getByte() { + const pos = this.pos; + while (this.bufferLength <= pos) { + if (this.eof) { + return -1; + } + this.readBlock(); + } + return this.buffer[this.pos++]; + } + getBytes(length, decoderOptions = null) { + const pos = this.pos; + let end; + if (length) { + this.ensureBuffer(pos + length); + end = pos + length; + while (!this.eof && this.bufferLength < end) { + this.readBlock(decoderOptions); + } + const bufEnd = this.bufferLength; + if (end > bufEnd) { + end = bufEnd; + } + } else { + while (!this.eof) { + this.readBlock(decoderOptions); + } + end = this.bufferLength; + } + this.pos = end; + return this.buffer.subarray(pos, end); + } + async getImageData(length, decoderOptions = null) { + if (!this.canAsyncDecodeImageFromBuffer) { + return this.getBytes(length, decoderOptions); + } + const data = await this.stream.asyncGetBytes(); + return this.decodeImage(data, decoderOptions); + } + reset() { + this.pos = 0; + } + makeSubStream(start, length, dict = null) { + if (length === undefined) { + while (!this.eof) { + this.readBlock(); + } + } else { + const end = start + length; + while (this.bufferLength <= end && !this.eof) { + this.readBlock(); + } + } + return new Stream(this.buffer, start, length, dict); + } + getBaseStreams() { + return this.str ? this.str.getBaseStreams() : null; + } +} +class StreamsSequenceStream extends DecodeStream { + constructor(streams, onError = null) { + streams = streams.filter(s => s instanceof BaseStream); + let maybeLength = 0; + for (const stream of streams) { + maybeLength += stream instanceof DecodeStream ? stream._rawMinBufferLength : stream.length; + } + super(maybeLength); + this.streams = streams; + this._onError = onError; + } + readBlock() { + const streams = this.streams; + if (streams.length === 0) { + this.eof = true; + return; + } + const stream = streams.shift(); + let chunk; + try { + chunk = stream.getBytes(); + } catch (reason) { + if (this._onError) { + this._onError(reason, stream.dict?.objId); + return; + } + throw reason; + } + const bufferLength = this.bufferLength; + const newLength = bufferLength + chunk.length; + const buffer = this.ensureBuffer(newLength); + buffer.set(chunk, bufferLength); + this.bufferLength = newLength; + } + getBaseStreams() { + const baseStreamsBuf = []; + for (const stream of this.streams) { + const baseStreams = stream.getBaseStreams(); + if (baseStreams) { + baseStreamsBuf.push(...baseStreams); + } + } + return baseStreamsBuf.length > 0 ? baseStreamsBuf : null; + } +} + +;// ./src/core/ascii_85_stream.js + + +class Ascii85Stream extends DecodeStream { + constructor(str, maybeLength) { + if (maybeLength) { + maybeLength *= 0.8; + } + super(maybeLength); + this.str = str; + this.dict = str.dict; + this.input = new Uint8Array(5); + } + readBlock() { + const TILDA_CHAR = 0x7e; + const Z_LOWER_CHAR = 0x7a; + const EOF = -1; + const str = this.str; + let c = str.getByte(); + while (isWhiteSpace(c)) { + c = str.getByte(); + } + if (c === EOF || c === TILDA_CHAR) { + this.eof = true; + return; + } + const bufferLength = this.bufferLength; + let buffer, i; + if (c === Z_LOWER_CHAR) { + buffer = this.ensureBuffer(bufferLength + 4); + for (i = 0; i < 4; ++i) { + buffer[bufferLength + i] = 0; + } + this.bufferLength += 4; + } else { + const input = this.input; + input[0] = c; + for (i = 1; i < 5; ++i) { + c = str.getByte(); + while (isWhiteSpace(c)) { + c = str.getByte(); + } + input[i] = c; + if (c === EOF || c === TILDA_CHAR) { + break; + } + } + buffer = this.ensureBuffer(bufferLength + i - 1); + this.bufferLength += i - 1; + if (i < 5) { + for (; i < 5; ++i) { + input[i] = 0x21 + 84; + } + this.eof = true; + } + let t = 0; + for (i = 0; i < 5; ++i) { + t = t * 85 + (input[i] - 0x21); + } + for (i = 3; i >= 0; --i) { + buffer[bufferLength + i] = t & 0xff; + t >>= 8; + } + } + } +} + +;// ./src/core/ascii_hex_stream.js + +class AsciiHexStream extends DecodeStream { + constructor(str, maybeLength) { + if (maybeLength) { + maybeLength *= 0.5; + } + super(maybeLength); + this.str = str; + this.dict = str.dict; + this.firstDigit = -1; + } + readBlock() { + const UPSTREAM_BLOCK_SIZE = 8000; + const bytes = this.str.getBytes(UPSTREAM_BLOCK_SIZE); + if (!bytes.length) { + this.eof = true; + return; + } + const maxDecodeLength = bytes.length + 1 >> 1; + const buffer = this.ensureBuffer(this.bufferLength + maxDecodeLength); + let bufferLength = this.bufferLength; + let firstDigit = this.firstDigit; + for (const ch of bytes) { + let digit; + if (ch >= 0x30 && ch <= 0x39) { + digit = ch & 0x0f; + } else if (ch >= 0x41 && ch <= 0x46 || ch >= 0x61 && ch <= 0x66) { + digit = (ch & 0x0f) + 9; + } else if (ch === 0x3e) { + this.eof = true; + break; + } else { + continue; + } + if (firstDigit < 0) { + firstDigit = digit; + } else { + buffer[bufferLength++] = firstDigit << 4 | digit; + firstDigit = -1; + } + } + if (firstDigit >= 0 && this.eof) { + buffer[bufferLength++] = firstDigit << 4; + firstDigit = -1; + } + this.firstDigit = firstDigit; + this.bufferLength = bufferLength; + } +} + +;// ./src/core/ccitt.js + +const ccittEOL = -2; +const ccittEOF = -1; +const twoDimPass = 0; +const twoDimHoriz = 1; +const twoDimVert0 = 2; +const twoDimVertR1 = 3; +const twoDimVertL1 = 4; +const twoDimVertR2 = 5; +const twoDimVertL2 = 6; +const twoDimVertR3 = 7; +const twoDimVertL3 = 8; +const twoDimTable = [[-1, -1], [-1, -1], [7, twoDimVertL3], [7, twoDimVertR3], [6, twoDimVertL2], [6, twoDimVertL2], [6, twoDimVertR2], [6, twoDimVertR2], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0]]; +const whiteTable1 = [[-1, -1], [12, ccittEOL], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [11, 1792], [11, 1792], [12, 1984], [12, 2048], [12, 2112], [12, 2176], [12, 2240], [12, 2304], [11, 1856], [11, 1856], [11, 1920], [11, 1920], [12, 2368], [12, 2432], [12, 2496], [12, 2560]]; +const whiteTable2 = [[-1, -1], [-1, -1], [-1, -1], [-1, -1], [8, 29], [8, 29], [8, 30], [8, 30], [8, 45], [8, 45], [8, 46], [8, 46], [7, 22], [7, 22], [7, 22], [7, 22], [7, 23], [7, 23], [7, 23], [7, 23], [8, 47], [8, 47], [8, 48], [8, 48], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [7, 20], [7, 20], [7, 20], [7, 20], [8, 33], [8, 33], [8, 34], [8, 34], [8, 35], [8, 35], [8, 36], [8, 36], [8, 37], [8, 37], [8, 38], [8, 38], [7, 19], [7, 19], [7, 19], [7, 19], [8, 31], [8, 31], [8, 32], [8, 32], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [8, 53], [8, 53], [8, 54], [8, 54], [7, 26], [7, 26], [7, 26], [7, 26], [8, 39], [8, 39], [8, 40], [8, 40], [8, 41], [8, 41], [8, 42], [8, 42], [8, 43], [8, 43], [8, 44], [8, 44], [7, 21], [7, 21], [7, 21], [7, 21], [7, 28], [7, 28], [7, 28], [7, 28], [8, 61], [8, 61], [8, 62], [8, 62], [8, 63], [8, 63], [8, 0], [8, 0], [8, 320], [8, 320], [8, 384], [8, 384], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [7, 27], [7, 27], [7, 27], [7, 27], [8, 59], [8, 59], [8, 60], [8, 60], [9, 1472], [9, 1536], [9, 1600], [9, 1728], [7, 18], [7, 18], [7, 18], [7, 18], [7, 24], [7, 24], [7, 24], [7, 24], [8, 49], [8, 49], [8, 50], [8, 50], [8, 51], [8, 51], [8, 52], [8, 52], [7, 25], [7, 25], [7, 25], [7, 25], [8, 55], [8, 55], [8, 56], [8, 56], [8, 57], [8, 57], [8, 58], [8, 58], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [8, 448], [8, 448], [8, 512], [8, 512], [9, 704], [9, 768], [8, 640], [8, 640], [8, 576], [8, 576], [9, 832], [9, 896], [9, 960], [9, 1024], [9, 1088], [9, 1152], [9, 1216], [9, 1280], [9, 1344], [9, 1408], [7, 256], [7, 256], [7, 256], [7, 256], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7]]; +const blackTable1 = [[-1, -1], [-1, -1], [12, ccittEOL], [12, ccittEOL], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [11, 1792], [11, 1792], [11, 1792], [11, 1792], [12, 1984], [12, 1984], [12, 2048], [12, 2048], [12, 2112], [12, 2112], [12, 2176], [12, 2176], [12, 2240], [12, 2240], [12, 2304], [12, 2304], [11, 1856], [11, 1856], [11, 1856], [11, 1856], [11, 1920], [11, 1920], [11, 1920], [11, 1920], [12, 2368], [12, 2368], [12, 2432], [12, 2432], [12, 2496], [12, 2496], [12, 2560], [12, 2560], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [12, 52], [12, 52], [13, 640], [13, 704], [13, 768], [13, 832], [12, 55], [12, 55], [12, 56], [12, 56], [13, 1280], [13, 1344], [13, 1408], [13, 1472], [12, 59], [12, 59], [12, 60], [12, 60], [13, 1536], [13, 1600], [11, 24], [11, 24], [11, 24], [11, 24], [11, 25], [11, 25], [11, 25], [11, 25], [13, 1664], [13, 1728], [12, 320], [12, 320], [12, 384], [12, 384], [12, 448], [12, 448], [13, 512], [13, 576], [12, 53], [12, 53], [12, 54], [12, 54], [13, 896], [13, 960], [13, 1024], [13, 1088], [13, 1152], [13, 1216], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64]]; +const blackTable2 = [[8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [11, 23], [11, 23], [12, 50], [12, 51], [12, 44], [12, 45], [12, 46], [12, 47], [12, 57], [12, 58], [12, 61], [12, 256], [10, 16], [10, 16], [10, 16], [10, 16], [10, 17], [10, 17], [10, 17], [10, 17], [12, 48], [12, 49], [12, 62], [12, 63], [12, 30], [12, 31], [12, 32], [12, 33], [12, 40], [12, 41], [11, 22], [11, 22], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [12, 128], [12, 192], [12, 26], [12, 27], [12, 28], [12, 29], [11, 19], [11, 19], [11, 20], [11, 20], [12, 34], [12, 35], [12, 36], [12, 37], [12, 38], [12, 39], [11, 21], [11, 21], [12, 42], [12, 43], [10, 0], [10, 0], [10, 0], [10, 0], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12]]; +const blackTable3 = [[-1, -1], [-1, -1], [-1, -1], [-1, -1], [6, 9], [6, 8], [5, 7], [5, 7], [4, 6], [4, 6], [4, 6], [4, 6], [4, 5], [4, 5], [4, 5], [4, 5], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2]]; +class CCITTFaxDecoder { + constructor(source, options = {}) { + if (typeof source?.next !== "function") { + throw new Error('CCITTFaxDecoder - invalid "source" parameter.'); + } + this.source = source; + this.eof = false; + this.encoding = options.K || 0; + this.eoline = options.EndOfLine || false; + this.byteAlign = options.EncodedByteAlign || false; + this.columns = options.Columns || 1728; + this.rows = options.Rows || 0; + this.eoblock = options.EndOfBlock ?? true; + this.black = options.BlackIs1 || false; + this.codingLine = new Uint32Array(this.columns + 1); + this.refLine = new Uint32Array(this.columns + 2); + this.codingLine[0] = this.columns; + this.codingPos = 0; + this.row = 0; + this.nextLine2D = this.encoding < 0; + this.inputBits = 0; + this.inputBuf = 0; + this.outputBits = 0; + this.rowsDone = false; + let code1; + while ((code1 = this._lookBits(12)) === 0) { + this._eatBits(1); + } + if (code1 === 1) { + this._eatBits(12); + } + if (this.encoding > 0) { + this.nextLine2D = !this._lookBits(1); + this._eatBits(1); + } + } + readNextChar() { + if (this.eof) { + return -1; + } + const refLine = this.refLine; + const codingLine = this.codingLine; + const columns = this.columns; + let refPos, blackPixels, bits, i; + if (this.outputBits === 0) { + if (this.rowsDone) { + this.eof = true; + } + if (this.eof) { + return -1; + } + this.err = false; + let code1, code2, code3; + if (this.nextLine2D) { + for (i = 0; codingLine[i] < columns; ++i) { + refLine[i] = codingLine[i]; + } + refLine[i++] = columns; + refLine[i] = columns; + codingLine[0] = 0; + this.codingPos = 0; + refPos = 0; + blackPixels = 0; + while (codingLine[this.codingPos] < columns) { + code1 = this._getTwoDimCode(); + switch (code1) { + case twoDimPass: + this._addPixels(refLine[refPos + 1], blackPixels); + if (refLine[refPos + 1] < columns) { + refPos += 2; + } + break; + case twoDimHoriz: + code1 = code2 = 0; + if (blackPixels) { + do { + code1 += code3 = this._getBlackCode(); + } while (code3 >= 64); + do { + code2 += code3 = this._getWhiteCode(); + } while (code3 >= 64); + } else { + do { + code1 += code3 = this._getWhiteCode(); + } while (code3 >= 64); + do { + code2 += code3 = this._getBlackCode(); + } while (code3 >= 64); + } + this._addPixels(codingLine[this.codingPos] + code1, blackPixels); + if (codingLine[this.codingPos] < columns) { + this._addPixels(codingLine[this.codingPos] + code2, blackPixels ^ 1); + } + while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) { + refPos += 2; + } + break; + case twoDimVertR3: + this._addPixels(refLine[refPos] + 3, blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { + ++refPos; + while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) { + refPos += 2; + } + } + break; + case twoDimVertR2: + this._addPixels(refLine[refPos] + 2, blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { + ++refPos; + while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) { + refPos += 2; + } + } + break; + case twoDimVertR1: + this._addPixels(refLine[refPos] + 1, blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { + ++refPos; + while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) { + refPos += 2; + } + } + break; + case twoDimVert0: + this._addPixels(refLine[refPos], blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { + ++refPos; + while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) { + refPos += 2; + } + } + break; + case twoDimVertL3: + this._addPixelsNeg(refLine[refPos] - 3, blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { + if (refPos > 0) { + --refPos; + } else { + ++refPos; + } + while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) { + refPos += 2; + } + } + break; + case twoDimVertL2: + this._addPixelsNeg(refLine[refPos] - 2, blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { + if (refPos > 0) { + --refPos; + } else { + ++refPos; + } + while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) { + refPos += 2; + } + } + break; + case twoDimVertL1: + this._addPixelsNeg(refLine[refPos] - 1, blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { + if (refPos > 0) { + --refPos; + } else { + ++refPos; + } + while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) { + refPos += 2; + } + } + break; + case ccittEOF: + this._addPixels(columns, 0); + this.eof = true; + break; + default: + info("bad 2d code"); + this._addPixels(columns, 0); + this.err = true; + } + } + } else { + codingLine[0] = 0; + this.codingPos = 0; + blackPixels = 0; + while (codingLine[this.codingPos] < columns) { + code1 = 0; + if (blackPixels) { + do { + code1 += code3 = this._getBlackCode(); + } while (code3 >= 64); + } else { + do { + code1 += code3 = this._getWhiteCode(); + } while (code3 >= 64); + } + this._addPixels(codingLine[this.codingPos] + code1, blackPixels); + blackPixels ^= 1; + } + } + let gotEOL = false; + if (this.byteAlign) { + this.inputBits &= ~7; + } + if (!this.eoblock && this.row === this.rows - 1) { + this.rowsDone = true; + } else { + code1 = this._lookBits(12); + if (this.eoline) { + while (code1 !== ccittEOF && code1 !== 1) { + this._eatBits(1); + code1 = this._lookBits(12); + } + } else { + while (code1 === 0) { + this._eatBits(1); + code1 = this._lookBits(12); + } + } + if (code1 === 1) { + this._eatBits(12); + gotEOL = true; + } else if (code1 === ccittEOF) { + this.eof = true; + } + } + if (!this.eof && this.encoding > 0 && !this.rowsDone) { + this.nextLine2D = !this._lookBits(1); + this._eatBits(1); + } + if (this.eoblock && gotEOL && this.byteAlign) { + code1 = this._lookBits(12); + if (code1 === 1) { + this._eatBits(12); + if (this.encoding > 0) { + this._lookBits(1); + this._eatBits(1); + } + if (this.encoding >= 0) { + for (i = 0; i < 4; ++i) { + code1 = this._lookBits(12); + if (code1 !== 1) { + info("bad rtc code: " + code1); + } + this._eatBits(12); + if (this.encoding > 0) { + this._lookBits(1); + this._eatBits(1); + } + } + } + this.eof = true; + } + } else if (this.err && this.eoline) { + while (true) { + code1 = this._lookBits(13); + if (code1 === ccittEOF) { + this.eof = true; + return -1; + } + if (code1 >> 1 === 1) { + break; + } + this._eatBits(1); + } + this._eatBits(12); + if (this.encoding > 0) { + this._eatBits(1); + this.nextLine2D = !(code1 & 1); + } + } + this.outputBits = codingLine[0] > 0 ? codingLine[this.codingPos = 0] : codingLine[this.codingPos = 1]; + this.row++; + } + let c; + if (this.outputBits >= 8) { + c = this.codingPos & 1 ? 0 : 0xff; + this.outputBits -= 8; + if (this.outputBits === 0 && codingLine[this.codingPos] < columns) { + this.codingPos++; + this.outputBits = codingLine[this.codingPos] - codingLine[this.codingPos - 1]; + } + } else { + bits = 8; + c = 0; + do { + if (typeof this.outputBits !== "number") { + throw new FormatError('Invalid /CCITTFaxDecode data, "outputBits" must be a number.'); + } + if (this.outputBits > bits) { + c <<= bits; + if (!(this.codingPos & 1)) { + c |= 0xff >> 8 - bits; + } + this.outputBits -= bits; + bits = 0; + } else { + c <<= this.outputBits; + if (!(this.codingPos & 1)) { + c |= 0xff >> 8 - this.outputBits; + } + bits -= this.outputBits; + this.outputBits = 0; + if (codingLine[this.codingPos] < columns) { + this.codingPos++; + this.outputBits = codingLine[this.codingPos] - codingLine[this.codingPos - 1]; + } else if (bits > 0) { + c <<= bits; + bits = 0; + } + } + } while (bits); + } + if (this.black) { + c ^= 0xff; + } + return c; + } + _addPixels(a1, blackPixels) { + const codingLine = this.codingLine; + let codingPos = this.codingPos; + if (a1 > codingLine[codingPos]) { + if (a1 > this.columns) { + info("row is wrong length"); + this.err = true; + a1 = this.columns; + } + if (codingPos & 1 ^ blackPixels) { + ++codingPos; + } + codingLine[codingPos] = a1; + } + this.codingPos = codingPos; + } + _addPixelsNeg(a1, blackPixels) { + const codingLine = this.codingLine; + let codingPos = this.codingPos; + if (a1 > codingLine[codingPos]) { + if (a1 > this.columns) { + info("row is wrong length"); + this.err = true; + a1 = this.columns; + } + if (codingPos & 1 ^ blackPixels) { + ++codingPos; + } + codingLine[codingPos] = a1; + } else if (a1 < codingLine[codingPos]) { + if (a1 < 0) { + info("invalid code"); + this.err = true; + a1 = 0; + } + while (codingPos > 0 && a1 < codingLine[codingPos - 1]) { + --codingPos; + } + codingLine[codingPos] = a1; + } + this.codingPos = codingPos; + } + _findTableCode(start, end, table, limit) { + const limitValue = limit || 0; + for (let i = start; i <= end; ++i) { + let code = this._lookBits(i); + if (code === ccittEOF) { + return [true, 1, false]; + } + if (i < end) { + code <<= end - i; + } + if (!limitValue || code >= limitValue) { + const p = table[code - limitValue]; + if (p[0] === i) { + this._eatBits(i); + return [true, p[1], true]; + } + } + } + return [false, 0, false]; + } + _getTwoDimCode() { + let code = 0; + let p; + if (this.eoblock) { + code = this._lookBits(7); + p = twoDimTable[code]; + if (p?.[0] > 0) { + this._eatBits(p[0]); + return p[1]; + } + } else { + const result = this._findTableCode(1, 7, twoDimTable); + if (result[0] && result[2]) { + return result[1]; + } + } + info("Bad two dim code"); + return ccittEOF; + } + _getWhiteCode() { + let code = 0; + let p; + if (this.eoblock) { + code = this._lookBits(12); + if (code === ccittEOF) { + return 1; + } + p = code >> 5 === 0 ? whiteTable1[code] : whiteTable2[code >> 3]; + if (p[0] > 0) { + this._eatBits(p[0]); + return p[1]; + } + } else { + let result = this._findTableCode(1, 9, whiteTable2); + if (result[0]) { + return result[1]; + } + result = this._findTableCode(11, 12, whiteTable1); + if (result[0]) { + return result[1]; + } + } + info("bad white code"); + this._eatBits(1); + return 1; + } + _getBlackCode() { + let code, p; + if (this.eoblock) { + code = this._lookBits(13); + if (code === ccittEOF) { + return 1; + } + if (code >> 7 === 0) { + p = blackTable1[code]; + } else if (code >> 9 === 0 && code >> 7 !== 0) { + p = blackTable2[(code >> 1) - 64]; + } else { + p = blackTable3[code >> 7]; + } + if (p[0] > 0) { + this._eatBits(p[0]); + return p[1]; + } + } else { + let result = this._findTableCode(2, 6, blackTable3); + if (result[0]) { + return result[1]; + } + result = this._findTableCode(7, 12, blackTable2, 64); + if (result[0]) { + return result[1]; + } + result = this._findTableCode(10, 13, blackTable1); + if (result[0]) { + return result[1]; + } + } + info("bad black code"); + this._eatBits(1); + return 1; + } + _lookBits(n) { + let c; + while (this.inputBits < n) { + if ((c = this.source.next()) === -1) { + if (this.inputBits === 0) { + return ccittEOF; + } + return this.inputBuf << n - this.inputBits & 0xffff >> 16 - n; + } + this.inputBuf = this.inputBuf << 8 | c; + this.inputBits += 8; + } + return this.inputBuf >> this.inputBits - n & 0xffff >> 16 - n; + } + _eatBits(n) { + if ((this.inputBits -= n) < 0) { + this.inputBits = 0; + } + } +} + +;// ./src/core/ccitt_stream.js + + + +class CCITTFaxStream extends DecodeStream { + constructor(str, maybeLength, params) { + super(maybeLength); + this.str = str; + this.dict = str.dict; + if (!(params instanceof Dict)) { + params = Dict.empty; + } + const source = { + next() { + return str.getByte(); + } + }; + this.ccittFaxDecoder = new CCITTFaxDecoder(source, { + K: params.get("K"), + EndOfLine: params.get("EndOfLine"), + EncodedByteAlign: params.get("EncodedByteAlign"), + Columns: params.get("Columns"), + Rows: params.get("Rows"), + EndOfBlock: params.get("EndOfBlock"), + BlackIs1: params.get("BlackIs1") + }); + } + readBlock() { + while (!this.eof) { + const c = this.ccittFaxDecoder.readNextChar(); + if (c === -1) { + this.eof = true; + return; + } + this.ensureBuffer(this.bufferLength + 1); + this.buffer[this.bufferLength++] = c; + } + } +} + +;// ./src/core/flate_stream.js + + + +const codeLenCodeMap = new Int32Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]); +const lengthDecode = new Int32Array([0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009, 0x0000a, 0x1000b, 0x1000d, 0x1000f, 0x10011, 0x20013, 0x20017, 0x2001b, 0x2001f, 0x30023, 0x3002b, 0x30033, 0x3003b, 0x40043, 0x40053, 0x40063, 0x40073, 0x50083, 0x500a3, 0x500c3, 0x500e3, 0x00102, 0x00102, 0x00102]); +const distDecode = new Int32Array([0x00001, 0x00002, 0x00003, 0x00004, 0x10005, 0x10007, 0x20009, 0x2000d, 0x30011, 0x30019, 0x40021, 0x40031, 0x50041, 0x50061, 0x60081, 0x600c1, 0x70101, 0x70181, 0x80201, 0x80301, 0x90401, 0x90601, 0xa0801, 0xa0c01, 0xb1001, 0xb1801, 0xc2001, 0xc3001, 0xd4001, 0xd6001]); +const fixedLitCodeTab = [new Int32Array([0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c0, 0x70108, 0x80060, 0x80020, 0x900a0, 0x80000, 0x80080, 0x80040, 0x900e0, 0x70104, 0x80058, 0x80018, 0x90090, 0x70114, 0x80078, 0x80038, 0x900d0, 0x7010c, 0x80068, 0x80028, 0x900b0, 0x80008, 0x80088, 0x80048, 0x900f0, 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c8, 0x7010a, 0x80064, 0x80024, 0x900a8, 0x80004, 0x80084, 0x80044, 0x900e8, 0x70106, 0x8005c, 0x8001c, 0x90098, 0x70116, 0x8007c, 0x8003c, 0x900d8, 0x7010e, 0x8006c, 0x8002c, 0x900b8, 0x8000c, 0x8008c, 0x8004c, 0x900f8, 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c4, 0x70109, 0x80062, 0x80022, 0x900a4, 0x80002, 0x80082, 0x80042, 0x900e4, 0x70105, 0x8005a, 0x8001a, 0x90094, 0x70115, 0x8007a, 0x8003a, 0x900d4, 0x7010d, 0x8006a, 0x8002a, 0x900b4, 0x8000a, 0x8008a, 0x8004a, 0x900f4, 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cc, 0x7010b, 0x80066, 0x80026, 0x900ac, 0x80006, 0x80086, 0x80046, 0x900ec, 0x70107, 0x8005e, 0x8001e, 0x9009c, 0x70117, 0x8007e, 0x8003e, 0x900dc, 0x7010f, 0x8006e, 0x8002e, 0x900bc, 0x8000e, 0x8008e, 0x8004e, 0x900fc, 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c2, 0x70108, 0x80061, 0x80021, 0x900a2, 0x80001, 0x80081, 0x80041, 0x900e2, 0x70104, 0x80059, 0x80019, 0x90092, 0x70114, 0x80079, 0x80039, 0x900d2, 0x7010c, 0x80069, 0x80029, 0x900b2, 0x80009, 0x80089, 0x80049, 0x900f2, 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900ca, 0x7010a, 0x80065, 0x80025, 0x900aa, 0x80005, 0x80085, 0x80045, 0x900ea, 0x70106, 0x8005d, 0x8001d, 0x9009a, 0x70116, 0x8007d, 0x8003d, 0x900da, 0x7010e, 0x8006d, 0x8002d, 0x900ba, 0x8000d, 0x8008d, 0x8004d, 0x900fa, 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c6, 0x70109, 0x80063, 0x80023, 0x900a6, 0x80003, 0x80083, 0x80043, 0x900e6, 0x70105, 0x8005b, 0x8001b, 0x90096, 0x70115, 0x8007b, 0x8003b, 0x900d6, 0x7010d, 0x8006b, 0x8002b, 0x900b6, 0x8000b, 0x8008b, 0x8004b, 0x900f6, 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900ce, 0x7010b, 0x80067, 0x80027, 0x900ae, 0x80007, 0x80087, 0x80047, 0x900ee, 0x70107, 0x8005f, 0x8001f, 0x9009e, 0x70117, 0x8007f, 0x8003f, 0x900de, 0x7010f, 0x8006f, 0x8002f, 0x900be, 0x8000f, 0x8008f, 0x8004f, 0x900fe, 0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c1, 0x70108, 0x80060, 0x80020, 0x900a1, 0x80000, 0x80080, 0x80040, 0x900e1, 0x70104, 0x80058, 0x80018, 0x90091, 0x70114, 0x80078, 0x80038, 0x900d1, 0x7010c, 0x80068, 0x80028, 0x900b1, 0x80008, 0x80088, 0x80048, 0x900f1, 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c9, 0x7010a, 0x80064, 0x80024, 0x900a9, 0x80004, 0x80084, 0x80044, 0x900e9, 0x70106, 0x8005c, 0x8001c, 0x90099, 0x70116, 0x8007c, 0x8003c, 0x900d9, 0x7010e, 0x8006c, 0x8002c, 0x900b9, 0x8000c, 0x8008c, 0x8004c, 0x900f9, 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c5, 0x70109, 0x80062, 0x80022, 0x900a5, 0x80002, 0x80082, 0x80042, 0x900e5, 0x70105, 0x8005a, 0x8001a, 0x90095, 0x70115, 0x8007a, 0x8003a, 0x900d5, 0x7010d, 0x8006a, 0x8002a, 0x900b5, 0x8000a, 0x8008a, 0x8004a, 0x900f5, 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cd, 0x7010b, 0x80066, 0x80026, 0x900ad, 0x80006, 0x80086, 0x80046, 0x900ed, 0x70107, 0x8005e, 0x8001e, 0x9009d, 0x70117, 0x8007e, 0x8003e, 0x900dd, 0x7010f, 0x8006e, 0x8002e, 0x900bd, 0x8000e, 0x8008e, 0x8004e, 0x900fd, 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c3, 0x70108, 0x80061, 0x80021, 0x900a3, 0x80001, 0x80081, 0x80041, 0x900e3, 0x70104, 0x80059, 0x80019, 0x90093, 0x70114, 0x80079, 0x80039, 0x900d3, 0x7010c, 0x80069, 0x80029, 0x900b3, 0x80009, 0x80089, 0x80049, 0x900f3, 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900cb, 0x7010a, 0x80065, 0x80025, 0x900ab, 0x80005, 0x80085, 0x80045, 0x900eb, 0x70106, 0x8005d, 0x8001d, 0x9009b, 0x70116, 0x8007d, 0x8003d, 0x900db, 0x7010e, 0x8006d, 0x8002d, 0x900bb, 0x8000d, 0x8008d, 0x8004d, 0x900fb, 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c7, 0x70109, 0x80063, 0x80023, 0x900a7, 0x80003, 0x80083, 0x80043, 0x900e7, 0x70105, 0x8005b, 0x8001b, 0x90097, 0x70115, 0x8007b, 0x8003b, 0x900d7, 0x7010d, 0x8006b, 0x8002b, 0x900b7, 0x8000b, 0x8008b, 0x8004b, 0x900f7, 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900cf, 0x7010b, 0x80067, 0x80027, 0x900af, 0x80007, 0x80087, 0x80047, 0x900ef, 0x70107, 0x8005f, 0x8001f, 0x9009f, 0x70117, 0x8007f, 0x8003f, 0x900df, 0x7010f, 0x8006f, 0x8002f, 0x900bf, 0x8000f, 0x8008f, 0x8004f, 0x900ff]), 9]; +const fixedDistCodeTab = [new Int32Array([0x50000, 0x50010, 0x50008, 0x50018, 0x50004, 0x50014, 0x5000c, 0x5001c, 0x50002, 0x50012, 0x5000a, 0x5001a, 0x50006, 0x50016, 0x5000e, 0x00000, 0x50001, 0x50011, 0x50009, 0x50019, 0x50005, 0x50015, 0x5000d, 0x5001d, 0x50003, 0x50013, 0x5000b, 0x5001b, 0x50007, 0x50017, 0x5000f, 0x00000]), 5]; +class FlateStream extends DecodeStream { + constructor(str, maybeLength) { + super(maybeLength); + this.str = str; + this.dict = str.dict; + const cmf = str.getByte(); + const flg = str.getByte(); + if (cmf === -1 || flg === -1) { + throw new FormatError(`Invalid header in flate stream: ${cmf}, ${flg}`); + } + if ((cmf & 0x0f) !== 0x08) { + throw new FormatError(`Unknown compression method in flate stream: ${cmf}, ${flg}`); + } + if (((cmf << 8) + flg) % 31 !== 0) { + throw new FormatError(`Bad FCHECK in flate stream: ${cmf}, ${flg}`); + } + if (flg & 0x20) { + throw new FormatError(`FDICT bit set in flate stream: ${cmf}, ${flg}`); + } + this.codeSize = 0; + this.codeBuf = 0; + } + async getImageData(length, _decoderOptions) { + const data = await this.asyncGetBytes(); + return data?.subarray(0, length) || this.getBytes(length); + } + async asyncGetBytes() { + this.str.reset(); + const bytes = this.str.getBytes(); + try { + const { + readable, + writable + } = new DecompressionStream("deflate"); + const writer = writable.getWriter(); + await writer.ready; + writer.write(bytes).then(async () => { + await writer.ready; + await writer.close(); + }).catch(() => {}); + const chunks = []; + let totalLength = 0; + for await (const chunk of readable) { + chunks.push(chunk); + totalLength += chunk.byteLength; + } + const data = new Uint8Array(totalLength); + let offset = 0; + for (const chunk of chunks) { + data.set(chunk, offset); + offset += chunk.byteLength; + } + return data; + } catch { + this.str = new Stream(bytes, 2, bytes.length, this.str.dict); + this.reset(); + return null; + } + } + get isAsync() { + return true; + } + getBits(bits) { + const str = this.str; + let codeSize = this.codeSize; + let codeBuf = this.codeBuf; + let b; + while (codeSize < bits) { + if ((b = str.getByte()) === -1) { + throw new FormatError("Bad encoding in flate stream"); + } + codeBuf |= b << codeSize; + codeSize += 8; + } + b = codeBuf & (1 << bits) - 1; + this.codeBuf = codeBuf >> bits; + this.codeSize = codeSize -= bits; + return b; + } + getCode(table) { + const str = this.str; + const codes = table[0]; + const maxLen = table[1]; + let codeSize = this.codeSize; + let codeBuf = this.codeBuf; + let b; + while (codeSize < maxLen) { + if ((b = str.getByte()) === -1) { + break; + } + codeBuf |= b << codeSize; + codeSize += 8; + } + const code = codes[codeBuf & (1 << maxLen) - 1]; + const codeLen = code >> 16; + const codeVal = code & 0xffff; + if (codeLen < 1 || codeSize < codeLen) { + throw new FormatError("Bad encoding in flate stream"); + } + this.codeBuf = codeBuf >> codeLen; + this.codeSize = codeSize - codeLen; + return codeVal; + } + generateHuffmanTable(lengths) { + const n = lengths.length; + let maxLen = 0; + let i; + for (i = 0; i < n; ++i) { + if (lengths[i] > maxLen) { + maxLen = lengths[i]; + } + } + const size = 1 << maxLen; + const codes = new Int32Array(size); + for (let len = 1, code = 0, skip = 2; len <= maxLen; ++len, code <<= 1, skip <<= 1) { + for (let val = 0; val < n; ++val) { + if (lengths[val] === len) { + let code2 = 0; + let t = code; + for (i = 0; i < len; ++i) { + code2 = code2 << 1 | t & 1; + t >>= 1; + } + for (i = code2; i < size; i += skip) { + codes[i] = len << 16 | val; + } + ++code; + } + } + } + return [codes, maxLen]; + } + #endsStreamOnError(err) { + info(err); + this.eof = true; + } + readBlock() { + let buffer, hdr, len; + const str = this.str; + try { + hdr = this.getBits(3); + } catch (ex) { + this.#endsStreamOnError(ex.message); + return; + } + if (hdr & 1) { + this.eof = true; + } + hdr >>= 1; + if (hdr === 0) { + let b; + if ((b = str.getByte()) === -1) { + this.#endsStreamOnError("Bad block header in flate stream"); + return; + } + let blockLen = b; + if ((b = str.getByte()) === -1) { + this.#endsStreamOnError("Bad block header in flate stream"); + return; + } + blockLen |= b << 8; + if ((b = str.getByte()) === -1) { + this.#endsStreamOnError("Bad block header in flate stream"); + return; + } + let check = b; + if ((b = str.getByte()) === -1) { + this.#endsStreamOnError("Bad block header in flate stream"); + return; + } + check |= b << 8; + if (check !== (~blockLen & 0xffff) && (blockLen !== 0 || check !== 0)) { + throw new FormatError("Bad uncompressed block length in flate stream"); + } + this.codeBuf = 0; + this.codeSize = 0; + const bufferLength = this.bufferLength, + end = bufferLength + blockLen; + buffer = this.ensureBuffer(end); + this.bufferLength = end; + if (blockLen === 0) { + if (str.peekByte() === -1) { + this.eof = true; + } + } else { + const block = str.getBytes(blockLen); + buffer.set(block, bufferLength); + if (block.length < blockLen) { + this.eof = true; + } + } + return; + } + let litCodeTable; + let distCodeTable; + if (hdr === 1) { + litCodeTable = fixedLitCodeTab; + distCodeTable = fixedDistCodeTab; + } else if (hdr === 2) { + const numLitCodes = this.getBits(5) + 257; + const numDistCodes = this.getBits(5) + 1; + const numCodeLenCodes = this.getBits(4) + 4; + const codeLenCodeLengths = new Uint8Array(codeLenCodeMap.length); + let i; + for (i = 0; i < numCodeLenCodes; ++i) { + codeLenCodeLengths[codeLenCodeMap[i]] = this.getBits(3); + } + const codeLenCodeTab = this.generateHuffmanTable(codeLenCodeLengths); + len = 0; + i = 0; + const codes = numLitCodes + numDistCodes; + const codeLengths = new Uint8Array(codes); + let bitsLength, bitsOffset, what; + while (i < codes) { + const code = this.getCode(codeLenCodeTab); + if (code === 16) { + bitsLength = 2; + bitsOffset = 3; + what = len; + } else if (code === 17) { + bitsLength = 3; + bitsOffset = 3; + what = len = 0; + } else if (code === 18) { + bitsLength = 7; + bitsOffset = 11; + what = len = 0; + } else { + codeLengths[i++] = len = code; + continue; + } + let repeatLength = this.getBits(bitsLength) + bitsOffset; + while (repeatLength-- > 0) { + codeLengths[i++] = what; + } + } + litCodeTable = this.generateHuffmanTable(codeLengths.subarray(0, numLitCodes)); + distCodeTable = this.generateHuffmanTable(codeLengths.subarray(numLitCodes, codes)); + } else { + throw new FormatError("Unknown block type in flate stream"); + } + buffer = this.buffer; + let limit = buffer ? buffer.length : 0; + let pos = this.bufferLength; + while (true) { + let code1 = this.getCode(litCodeTable); + if (code1 < 256) { + if (pos + 1 >= limit) { + buffer = this.ensureBuffer(pos + 1); + limit = buffer.length; + } + buffer[pos++] = code1; + continue; + } + if (code1 === 256) { + this.bufferLength = pos; + return; + } + code1 -= 257; + code1 = lengthDecode[code1]; + let code2 = code1 >> 16; + if (code2 > 0) { + code2 = this.getBits(code2); + } + len = (code1 & 0xffff) + code2; + code1 = this.getCode(distCodeTable); + code1 = distDecode[code1]; + code2 = code1 >> 16; + if (code2 > 0) { + code2 = this.getBits(code2); + } + const dist = (code1 & 0xffff) + code2; + if (pos + len >= limit) { + buffer = this.ensureBuffer(pos + len); + limit = buffer.length; + } + for (let k = 0; k < len; ++k, ++pos) { + buffer[pos] = buffer[pos - dist]; + } + } + } +} + +;// ./src/core/arithmetic_decoder.js +const QeTable = [{ + qe: 0x5601, + nmps: 1, + nlps: 1, + switchFlag: 1 +}, { + qe: 0x3401, + nmps: 2, + nlps: 6, + switchFlag: 0 +}, { + qe: 0x1801, + nmps: 3, + nlps: 9, + switchFlag: 0 +}, { + qe: 0x0ac1, + nmps: 4, + nlps: 12, + switchFlag: 0 +}, { + qe: 0x0521, + nmps: 5, + nlps: 29, + switchFlag: 0 +}, { + qe: 0x0221, + nmps: 38, + nlps: 33, + switchFlag: 0 +}, { + qe: 0x5601, + nmps: 7, + nlps: 6, + switchFlag: 1 +}, { + qe: 0x5401, + nmps: 8, + nlps: 14, + switchFlag: 0 +}, { + qe: 0x4801, + nmps: 9, + nlps: 14, + switchFlag: 0 +}, { + qe: 0x3801, + nmps: 10, + nlps: 14, + switchFlag: 0 +}, { + qe: 0x3001, + nmps: 11, + nlps: 17, + switchFlag: 0 +}, { + qe: 0x2401, + nmps: 12, + nlps: 18, + switchFlag: 0 +}, { + qe: 0x1c01, + nmps: 13, + nlps: 20, + switchFlag: 0 +}, { + qe: 0x1601, + nmps: 29, + nlps: 21, + switchFlag: 0 +}, { + qe: 0x5601, + nmps: 15, + nlps: 14, + switchFlag: 1 +}, { + qe: 0x5401, + nmps: 16, + nlps: 14, + switchFlag: 0 +}, { + qe: 0x5101, + nmps: 17, + nlps: 15, + switchFlag: 0 +}, { + qe: 0x4801, + nmps: 18, + nlps: 16, + switchFlag: 0 +}, { + qe: 0x3801, + nmps: 19, + nlps: 17, + switchFlag: 0 +}, { + qe: 0x3401, + nmps: 20, + nlps: 18, + switchFlag: 0 +}, { + qe: 0x3001, + nmps: 21, + nlps: 19, + switchFlag: 0 +}, { + qe: 0x2801, + nmps: 22, + nlps: 19, + switchFlag: 0 +}, { + qe: 0x2401, + nmps: 23, + nlps: 20, + switchFlag: 0 +}, { + qe: 0x2201, + nmps: 24, + nlps: 21, + switchFlag: 0 +}, { + qe: 0x1c01, + nmps: 25, + nlps: 22, + switchFlag: 0 +}, { + qe: 0x1801, + nmps: 26, + nlps: 23, + switchFlag: 0 +}, { + qe: 0x1601, + nmps: 27, + nlps: 24, + switchFlag: 0 +}, { + qe: 0x1401, + nmps: 28, + nlps: 25, + switchFlag: 0 +}, { + qe: 0x1201, + nmps: 29, + nlps: 26, + switchFlag: 0 +}, { + qe: 0x1101, + nmps: 30, + nlps: 27, + switchFlag: 0 +}, { + qe: 0x0ac1, + nmps: 31, + nlps: 28, + switchFlag: 0 +}, { + qe: 0x09c1, + nmps: 32, + nlps: 29, + switchFlag: 0 +}, { + qe: 0x08a1, + nmps: 33, + nlps: 30, + switchFlag: 0 +}, { + qe: 0x0521, + nmps: 34, + nlps: 31, + switchFlag: 0 +}, { + qe: 0x0441, + nmps: 35, + nlps: 32, + switchFlag: 0 +}, { + qe: 0x02a1, + nmps: 36, + nlps: 33, + switchFlag: 0 +}, { + qe: 0x0221, + nmps: 37, + nlps: 34, + switchFlag: 0 +}, { + qe: 0x0141, + nmps: 38, + nlps: 35, + switchFlag: 0 +}, { + qe: 0x0111, + nmps: 39, + nlps: 36, + switchFlag: 0 +}, { + qe: 0x0085, + nmps: 40, + nlps: 37, + switchFlag: 0 +}, { + qe: 0x0049, + nmps: 41, + nlps: 38, + switchFlag: 0 +}, { + qe: 0x0025, + nmps: 42, + nlps: 39, + switchFlag: 0 +}, { + qe: 0x0015, + nmps: 43, + nlps: 40, + switchFlag: 0 +}, { + qe: 0x0009, + nmps: 44, + nlps: 41, + switchFlag: 0 +}, { + qe: 0x0005, + nmps: 45, + nlps: 42, + switchFlag: 0 +}, { + qe: 0x0001, + nmps: 45, + nlps: 43, + switchFlag: 0 +}, { + qe: 0x5601, + nmps: 46, + nlps: 46, + switchFlag: 0 +}]; +class ArithmeticDecoder { + constructor(data, start, end) { + this.data = data; + this.bp = start; + this.dataEnd = end; + this.chigh = data[start]; + this.clow = 0; + this.byteIn(); + this.chigh = this.chigh << 7 & 0xffff | this.clow >> 9 & 0x7f; + this.clow = this.clow << 7 & 0xffff; + this.ct -= 7; + this.a = 0x8000; + } + byteIn() { + const data = this.data; + let bp = this.bp; + if (data[bp] === 0xff) { + if (data[bp + 1] > 0x8f) { + this.clow += 0xff00; + this.ct = 8; + } else { + bp++; + this.clow += data[bp] << 9; + this.ct = 7; + this.bp = bp; + } + } else { + bp++; + this.clow += bp < this.dataEnd ? data[bp] << 8 : 0xff00; + this.ct = 8; + this.bp = bp; + } + if (this.clow > 0xffff) { + this.chigh += this.clow >> 16; + this.clow &= 0xffff; + } + } + readBit(contexts, pos) { + let cx_index = contexts[pos] >> 1, + cx_mps = contexts[pos] & 1; + const qeTableIcx = QeTable[cx_index]; + const qeIcx = qeTableIcx.qe; + let d; + let a = this.a - qeIcx; + if (this.chigh < qeIcx) { + if (a < qeIcx) { + a = qeIcx; + d = cx_mps; + cx_index = qeTableIcx.nmps; + } else { + a = qeIcx; + d = 1 ^ cx_mps; + if (qeTableIcx.switchFlag === 1) { + cx_mps = d; + } + cx_index = qeTableIcx.nlps; + } + } else { + this.chigh -= qeIcx; + if ((a & 0x8000) !== 0) { + this.a = a; + return cx_mps; + } + if (a < qeIcx) { + d = 1 ^ cx_mps; + if (qeTableIcx.switchFlag === 1) { + cx_mps = d; + } + cx_index = qeTableIcx.nlps; + } else { + d = cx_mps; + cx_index = qeTableIcx.nmps; + } + } + do { + if (this.ct === 0) { + this.byteIn(); + } + a <<= 1; + this.chigh = this.chigh << 1 & 0xffff | this.clow >> 15 & 1; + this.clow = this.clow << 1 & 0xffff; + this.ct--; + } while ((a & 0x8000) === 0); + this.a = a; + contexts[pos] = cx_index << 1 | cx_mps; + return d; + } +} + +;// ./src/core/jbig2.js + + + + +class Jbig2Error extends BaseException { + constructor(msg) { + super(msg, "Jbig2Error"); + } +} +class ContextCache { + getContexts(id) { + if (id in this) { + return this[id]; + } + return this[id] = new Int8Array(1 << 16); + } +} +class DecodingContext { + constructor(data, start, end) { + this.data = data; + this.start = start; + this.end = end; + } + get decoder() { + const decoder = new ArithmeticDecoder(this.data, this.start, this.end); + return shadow(this, "decoder", decoder); + } + get contextCache() { + const cache = new ContextCache(); + return shadow(this, "contextCache", cache); + } +} +function decodeInteger(contextCache, procedure, decoder) { + const contexts = contextCache.getContexts(procedure); + let prev = 1; + function readBits(length) { + let v = 0; + for (let i = 0; i < length; i++) { + const bit = decoder.readBit(contexts, prev); + prev = prev < 256 ? prev << 1 | bit : (prev << 1 | bit) & 511 | 256; + v = v << 1 | bit; + } + return v >>> 0; + } + const sign = readBits(1); + const value = readBits(1) ? readBits(1) ? readBits(1) ? readBits(1) ? readBits(1) ? readBits(32) + 4436 : readBits(12) + 340 : readBits(8) + 84 : readBits(6) + 20 : readBits(4) + 4 : readBits(2); + let signedValue; + if (sign === 0) { + signedValue = value; + } else if (value > 0) { + signedValue = -value; + } + if (signedValue >= MIN_INT_32 && signedValue <= MAX_INT_32) { + return signedValue; + } + return null; +} +function decodeIAID(contextCache, decoder, codeLength) { + const contexts = contextCache.getContexts("IAID"); + let prev = 1; + for (let i = 0; i < codeLength; i++) { + const bit = decoder.readBit(contexts, prev); + prev = prev << 1 | bit; + } + if (codeLength < 31) { + return prev & (1 << codeLength) - 1; + } + return prev & 0x7fffffff; +} +const SegmentTypes = ["SymbolDictionary", null, null, null, "IntermediateTextRegion", null, "ImmediateTextRegion", "ImmediateLosslessTextRegion", null, null, null, null, null, null, null, null, "PatternDictionary", null, null, null, "IntermediateHalftoneRegion", null, "ImmediateHalftoneRegion", "ImmediateLosslessHalftoneRegion", null, null, null, null, null, null, null, null, null, null, null, null, "IntermediateGenericRegion", null, "ImmediateGenericRegion", "ImmediateLosslessGenericRegion", "IntermediateGenericRefinementRegion", null, "ImmediateGenericRefinementRegion", "ImmediateLosslessGenericRefinementRegion", null, null, null, null, "PageInformation", "EndOfPage", "EndOfStripe", "EndOfFile", "Profiles", "Tables", null, null, null, null, null, null, null, null, "Extension"]; +const CodingTemplates = [[{ + x: -1, + y: -2 +}, { + x: 0, + y: -2 +}, { + x: 1, + y: -2 +}, { + x: -2, + y: -1 +}, { + x: -1, + y: -1 +}, { + x: 0, + y: -1 +}, { + x: 1, + y: -1 +}, { + x: 2, + y: -1 +}, { + x: -4, + y: 0 +}, { + x: -3, + y: 0 +}, { + x: -2, + y: 0 +}, { + x: -1, + y: 0 +}], [{ + x: -1, + y: -2 +}, { + x: 0, + y: -2 +}, { + x: 1, + y: -2 +}, { + x: 2, + y: -2 +}, { + x: -2, + y: -1 +}, { + x: -1, + y: -1 +}, { + x: 0, + y: -1 +}, { + x: 1, + y: -1 +}, { + x: 2, + y: -1 +}, { + x: -3, + y: 0 +}, { + x: -2, + y: 0 +}, { + x: -1, + y: 0 +}], [{ + x: -1, + y: -2 +}, { + x: 0, + y: -2 +}, { + x: 1, + y: -2 +}, { + x: -2, + y: -1 +}, { + x: -1, + y: -1 +}, { + x: 0, + y: -1 +}, { + x: 1, + y: -1 +}, { + x: -2, + y: 0 +}, { + x: -1, + y: 0 +}], [{ + x: -3, + y: -1 +}, { + x: -2, + y: -1 +}, { + x: -1, + y: -1 +}, { + x: 0, + y: -1 +}, { + x: 1, + y: -1 +}, { + x: -4, + y: 0 +}, { + x: -3, + y: 0 +}, { + x: -2, + y: 0 +}, { + x: -1, + y: 0 +}]]; +const RefinementTemplates = [{ + coding: [{ + x: 0, + y: -1 + }, { + x: 1, + y: -1 + }, { + x: -1, + y: 0 + }], + reference: [{ + x: 0, + y: -1 + }, { + x: 1, + y: -1 + }, { + x: -1, + y: 0 + }, { + x: 0, + y: 0 + }, { + x: 1, + y: 0 + }, { + x: -1, + y: 1 + }, { + x: 0, + y: 1 + }, { + x: 1, + y: 1 + }] +}, { + coding: [{ + x: -1, + y: -1 + }, { + x: 0, + y: -1 + }, { + x: 1, + y: -1 + }, { + x: -1, + y: 0 + }], + reference: [{ + x: 0, + y: -1 + }, { + x: -1, + y: 0 + }, { + x: 0, + y: 0 + }, { + x: 1, + y: 0 + }, { + x: 0, + y: 1 + }, { + x: 1, + y: 1 + }] +}]; +const ReusedContexts = [0x9b25, 0x0795, 0x00e5, 0x0195]; +const RefinementReusedContexts = [0x0020, 0x0008]; +function decodeBitmapTemplate0(width, height, decodingContext) { + const decoder = decodingContext.decoder; + const contexts = decodingContext.contextCache.getContexts("GB"); + const bitmap = []; + let contextLabel, i, j, pixel, row, row1, row2; + const OLD_PIXEL_MASK = 0x7bf7; + for (i = 0; i < height; i++) { + row = bitmap[i] = new Uint8Array(width); + row1 = i < 1 ? row : bitmap[i - 1]; + row2 = i < 2 ? row : bitmap[i - 2]; + contextLabel = row2[0] << 13 | row2[1] << 12 | row2[2] << 11 | row1[0] << 7 | row1[1] << 6 | row1[2] << 5 | row1[3] << 4; + for (j = 0; j < width; j++) { + row[j] = pixel = decoder.readBit(contexts, contextLabel); + contextLabel = (contextLabel & OLD_PIXEL_MASK) << 1 | (j + 3 < width ? row2[j + 3] << 11 : 0) | (j + 4 < width ? row1[j + 4] << 4 : 0) | pixel; + } + } + return bitmap; +} +function decodeBitmap(mmr, width, height, templateIndex, prediction, skip, at, decodingContext) { + if (mmr) { + const input = new Reader(decodingContext.data, decodingContext.start, decodingContext.end); + return decodeMMRBitmap(input, width, height, false); + } + if (templateIndex === 0 && !skip && !prediction && at.length === 4 && at[0].x === 3 && at[0].y === -1 && at[1].x === -3 && at[1].y === -1 && at[2].x === 2 && at[2].y === -2 && at[3].x === -2 && at[3].y === -2) { + return decodeBitmapTemplate0(width, height, decodingContext); + } + const useskip = !!skip; + const template = CodingTemplates[templateIndex].concat(at); + template.sort(function (a, b) { + return a.y - b.y || a.x - b.x; + }); + const templateLength = template.length; + const templateX = new Int8Array(templateLength); + const templateY = new Int8Array(templateLength); + const changingTemplateEntries = []; + let reuseMask = 0, + minX = 0, + maxX = 0, + minY = 0; + let c, k; + for (k = 0; k < templateLength; k++) { + templateX[k] = template[k].x; + templateY[k] = template[k].y; + minX = Math.min(minX, template[k].x); + maxX = Math.max(maxX, template[k].x); + minY = Math.min(minY, template[k].y); + if (k < templateLength - 1 && template[k].y === template[k + 1].y && template[k].x === template[k + 1].x - 1) { + reuseMask |= 1 << templateLength - 1 - k; + } else { + changingTemplateEntries.push(k); + } + } + const changingEntriesLength = changingTemplateEntries.length; + const changingTemplateX = new Int8Array(changingEntriesLength); + const changingTemplateY = new Int8Array(changingEntriesLength); + const changingTemplateBit = new Uint16Array(changingEntriesLength); + for (c = 0; c < changingEntriesLength; c++) { + k = changingTemplateEntries[c]; + changingTemplateX[c] = template[k].x; + changingTemplateY[c] = template[k].y; + changingTemplateBit[c] = 1 << templateLength - 1 - k; + } + const sbb_left = -minX; + const sbb_top = -minY; + const sbb_right = width - maxX; + const pseudoPixelContext = ReusedContexts[templateIndex]; + let row = new Uint8Array(width); + const bitmap = []; + const decoder = decodingContext.decoder; + const contexts = decodingContext.contextCache.getContexts("GB"); + let ltp = 0, + j, + i0, + j0, + contextLabel = 0, + bit, + shift; + for (let i = 0; i < height; i++) { + if (prediction) { + const sltp = decoder.readBit(contexts, pseudoPixelContext); + ltp ^= sltp; + if (ltp) { + bitmap.push(row); + continue; + } + } + row = new Uint8Array(row); + bitmap.push(row); + for (j = 0; j < width; j++) { + if (useskip && skip[i][j]) { + row[j] = 0; + continue; + } + if (j >= sbb_left && j < sbb_right && i >= sbb_top) { + contextLabel = contextLabel << 1 & reuseMask; + for (k = 0; k < changingEntriesLength; k++) { + i0 = i + changingTemplateY[k]; + j0 = j + changingTemplateX[k]; + bit = bitmap[i0][j0]; + if (bit) { + bit = changingTemplateBit[k]; + contextLabel |= bit; + } + } + } else { + contextLabel = 0; + shift = templateLength - 1; + for (k = 0; k < templateLength; k++, shift--) { + j0 = j + templateX[k]; + if (j0 >= 0 && j0 < width) { + i0 = i + templateY[k]; + if (i0 >= 0) { + bit = bitmap[i0][j0]; + if (bit) { + contextLabel |= bit << shift; + } + } + } + } + } + const pixel = decoder.readBit(contexts, contextLabel); + row[j] = pixel; + } + } + return bitmap; +} +function decodeRefinement(width, height, templateIndex, referenceBitmap, offsetX, offsetY, prediction, at, decodingContext) { + let codingTemplate = RefinementTemplates[templateIndex].coding; + if (templateIndex === 0) { + codingTemplate = codingTemplate.concat([at[0]]); + } + const codingTemplateLength = codingTemplate.length; + const codingTemplateX = new Int32Array(codingTemplateLength); + const codingTemplateY = new Int32Array(codingTemplateLength); + let k; + for (k = 0; k < codingTemplateLength; k++) { + codingTemplateX[k] = codingTemplate[k].x; + codingTemplateY[k] = codingTemplate[k].y; + } + let referenceTemplate = RefinementTemplates[templateIndex].reference; + if (templateIndex === 0) { + referenceTemplate = referenceTemplate.concat([at[1]]); + } + const referenceTemplateLength = referenceTemplate.length; + const referenceTemplateX = new Int32Array(referenceTemplateLength); + const referenceTemplateY = new Int32Array(referenceTemplateLength); + for (k = 0; k < referenceTemplateLength; k++) { + referenceTemplateX[k] = referenceTemplate[k].x; + referenceTemplateY[k] = referenceTemplate[k].y; + } + const referenceWidth = referenceBitmap[0].length; + const referenceHeight = referenceBitmap.length; + const pseudoPixelContext = RefinementReusedContexts[templateIndex]; + const bitmap = []; + const decoder = decodingContext.decoder; + const contexts = decodingContext.contextCache.getContexts("GR"); + let ltp = 0; + for (let i = 0; i < height; i++) { + if (prediction) { + const sltp = decoder.readBit(contexts, pseudoPixelContext); + ltp ^= sltp; + if (ltp) { + throw new Jbig2Error("prediction is not supported"); + } + } + const row = new Uint8Array(width); + bitmap.push(row); + for (let j = 0; j < width; j++) { + let i0, j0; + let contextLabel = 0; + for (k = 0; k < codingTemplateLength; k++) { + i0 = i + codingTemplateY[k]; + j0 = j + codingTemplateX[k]; + if (i0 < 0 || j0 < 0 || j0 >= width) { + contextLabel <<= 1; + } else { + contextLabel = contextLabel << 1 | bitmap[i0][j0]; + } + } + for (k = 0; k < referenceTemplateLength; k++) { + i0 = i + referenceTemplateY[k] - offsetY; + j0 = j + referenceTemplateX[k] - offsetX; + if (i0 < 0 || i0 >= referenceHeight || j0 < 0 || j0 >= referenceWidth) { + contextLabel <<= 1; + } else { + contextLabel = contextLabel << 1 | referenceBitmap[i0][j0]; + } + } + const pixel = decoder.readBit(contexts, contextLabel); + row[j] = pixel; + } + } + return bitmap; +} +function decodeSymbolDictionary(huffman, refinement, symbols, numberOfNewSymbols, numberOfExportedSymbols, huffmanTables, templateIndex, at, refinementTemplateIndex, refinementAt, decodingContext, huffmanInput) { + if (huffman && refinement) { + throw new Jbig2Error("symbol refinement with Huffman is not supported"); + } + const newSymbols = []; + let currentHeight = 0; + let symbolCodeLength = log2(symbols.length + numberOfNewSymbols); + const decoder = decodingContext.decoder; + const contextCache = decodingContext.contextCache; + let tableB1, symbolWidths; + if (huffman) { + tableB1 = getStandardTable(1); + symbolWidths = []; + symbolCodeLength = Math.max(symbolCodeLength, 1); + } + while (newSymbols.length < numberOfNewSymbols) { + const deltaHeight = huffman ? huffmanTables.tableDeltaHeight.decode(huffmanInput) : decodeInteger(contextCache, "IADH", decoder); + currentHeight += deltaHeight; + let currentWidth = 0, + totalWidth = 0; + const firstSymbol = huffman ? symbolWidths.length : 0; + while (true) { + const deltaWidth = huffman ? huffmanTables.tableDeltaWidth.decode(huffmanInput) : decodeInteger(contextCache, "IADW", decoder); + if (deltaWidth === null) { + break; + } + currentWidth += deltaWidth; + totalWidth += currentWidth; + let bitmap; + if (refinement) { + const numberOfInstances = decodeInteger(contextCache, "IAAI", decoder); + if (numberOfInstances > 1) { + bitmap = decodeTextRegion(huffman, refinement, currentWidth, currentHeight, 0, numberOfInstances, 1, symbols.concat(newSymbols), symbolCodeLength, 0, 0, 1, 0, huffmanTables, refinementTemplateIndex, refinementAt, decodingContext, 0, huffmanInput); + } else { + const symbolId = decodeIAID(contextCache, decoder, symbolCodeLength); + const rdx = decodeInteger(contextCache, "IARDX", decoder); + const rdy = decodeInteger(contextCache, "IARDY", decoder); + const symbol = symbolId < symbols.length ? symbols[symbolId] : newSymbols[symbolId - symbols.length]; + bitmap = decodeRefinement(currentWidth, currentHeight, refinementTemplateIndex, symbol, rdx, rdy, false, refinementAt, decodingContext); + } + newSymbols.push(bitmap); + } else if (huffman) { + symbolWidths.push(currentWidth); + } else { + bitmap = decodeBitmap(false, currentWidth, currentHeight, templateIndex, false, null, at, decodingContext); + newSymbols.push(bitmap); + } + } + if (huffman && !refinement) { + const bitmapSize = huffmanTables.tableBitmapSize.decode(huffmanInput); + huffmanInput.byteAlign(); + let collectiveBitmap; + if (bitmapSize === 0) { + collectiveBitmap = readUncompressedBitmap(huffmanInput, totalWidth, currentHeight); + } else { + const originalEnd = huffmanInput.end; + const bitmapEnd = huffmanInput.position + bitmapSize; + huffmanInput.end = bitmapEnd; + collectiveBitmap = decodeMMRBitmap(huffmanInput, totalWidth, currentHeight, false); + huffmanInput.end = originalEnd; + huffmanInput.position = bitmapEnd; + } + const numberOfSymbolsDecoded = symbolWidths.length; + if (firstSymbol === numberOfSymbolsDecoded - 1) { + newSymbols.push(collectiveBitmap); + } else { + let i, + y, + xMin = 0, + xMax, + bitmapWidth, + symbolBitmap; + for (i = firstSymbol; i < numberOfSymbolsDecoded; i++) { + bitmapWidth = symbolWidths[i]; + xMax = xMin + bitmapWidth; + symbolBitmap = []; + for (y = 0; y < currentHeight; y++) { + symbolBitmap.push(collectiveBitmap[y].subarray(xMin, xMax)); + } + newSymbols.push(symbolBitmap); + xMin = xMax; + } + } + } + } + const exportedSymbols = [], + flags = []; + let currentFlag = false, + i, + ii; + const totalSymbolsLength = symbols.length + numberOfNewSymbols; + while (flags.length < totalSymbolsLength) { + let runLength = huffman ? tableB1.decode(huffmanInput) : decodeInteger(contextCache, "IAEX", decoder); + while (runLength--) { + flags.push(currentFlag); + } + currentFlag = !currentFlag; + } + for (i = 0, ii = symbols.length; i < ii; i++) { + if (flags[i]) { + exportedSymbols.push(symbols[i]); + } + } + for (let j = 0; j < numberOfNewSymbols; i++, j++) { + if (flags[i]) { + exportedSymbols.push(newSymbols[j]); + } + } + return exportedSymbols; +} +function decodeTextRegion(huffman, refinement, width, height, defaultPixelValue, numberOfSymbolInstances, stripSize, inputSymbols, symbolCodeLength, transposed, dsOffset, referenceCorner, combinationOperator, huffmanTables, refinementTemplateIndex, refinementAt, decodingContext, logStripSize, huffmanInput) { + if (huffman && refinement) { + throw new Jbig2Error("refinement with Huffman is not supported"); + } + const bitmap = []; + let i, row; + for (i = 0; i < height; i++) { + row = new Uint8Array(width); + if (defaultPixelValue) { + for (let j = 0; j < width; j++) { + row[j] = defaultPixelValue; + } + } + bitmap.push(row); + } + const decoder = decodingContext.decoder; + const contextCache = decodingContext.contextCache; + let stripT = huffman ? -huffmanTables.tableDeltaT.decode(huffmanInput) : -decodeInteger(contextCache, "IADT", decoder); + let firstS = 0; + i = 0; + while (i < numberOfSymbolInstances) { + const deltaT = huffman ? huffmanTables.tableDeltaT.decode(huffmanInput) : decodeInteger(contextCache, "IADT", decoder); + stripT += deltaT; + const deltaFirstS = huffman ? huffmanTables.tableFirstS.decode(huffmanInput) : decodeInteger(contextCache, "IAFS", decoder); + firstS += deltaFirstS; + let currentS = firstS; + do { + let currentT = 0; + if (stripSize > 1) { + currentT = huffman ? huffmanInput.readBits(logStripSize) : decodeInteger(contextCache, "IAIT", decoder); + } + const t = stripSize * stripT + currentT; + const symbolId = huffman ? huffmanTables.symbolIDTable.decode(huffmanInput) : decodeIAID(contextCache, decoder, symbolCodeLength); + const applyRefinement = refinement && (huffman ? huffmanInput.readBit() : decodeInteger(contextCache, "IARI", decoder)); + let symbolBitmap = inputSymbols[symbolId]; + let symbolWidth = symbolBitmap[0].length; + let symbolHeight = symbolBitmap.length; + if (applyRefinement) { + const rdw = decodeInteger(contextCache, "IARDW", decoder); + const rdh = decodeInteger(contextCache, "IARDH", decoder); + const rdx = decodeInteger(contextCache, "IARDX", decoder); + const rdy = decodeInteger(contextCache, "IARDY", decoder); + symbolWidth += rdw; + symbolHeight += rdh; + symbolBitmap = decodeRefinement(symbolWidth, symbolHeight, refinementTemplateIndex, symbolBitmap, (rdw >> 1) + rdx, (rdh >> 1) + rdy, false, refinementAt, decodingContext); + } + let increment = 0; + if (!transposed) { + if (referenceCorner > 1) { + currentS += symbolWidth - 1; + } else { + increment = symbolWidth - 1; + } + } else if (!(referenceCorner & 1)) { + currentS += symbolHeight - 1; + } else { + increment = symbolHeight - 1; + } + const offsetT = t - (referenceCorner & 1 ? 0 : symbolHeight - 1); + const offsetS = currentS - (referenceCorner & 2 ? symbolWidth - 1 : 0); + let s2, t2, symbolRow; + if (transposed) { + for (s2 = 0; s2 < symbolHeight; s2++) { + row = bitmap[offsetS + s2]; + if (!row) { + continue; + } + symbolRow = symbolBitmap[s2]; + const maxWidth = Math.min(width - offsetT, symbolWidth); + switch (combinationOperator) { + case 0: + for (t2 = 0; t2 < maxWidth; t2++) { + row[offsetT + t2] |= symbolRow[t2]; + } + break; + case 2: + for (t2 = 0; t2 < maxWidth; t2++) { + row[offsetT + t2] ^= symbolRow[t2]; + } + break; + default: + throw new Jbig2Error(`operator ${combinationOperator} is not supported`); + } + } + } else { + for (t2 = 0; t2 < symbolHeight; t2++) { + row = bitmap[offsetT + t2]; + if (!row) { + continue; + } + symbolRow = symbolBitmap[t2]; + switch (combinationOperator) { + case 0: + for (s2 = 0; s2 < symbolWidth; s2++) { + row[offsetS + s2] |= symbolRow[s2]; + } + break; + case 2: + for (s2 = 0; s2 < symbolWidth; s2++) { + row[offsetS + s2] ^= symbolRow[s2]; + } + break; + default: + throw new Jbig2Error(`operator ${combinationOperator} is not supported`); + } + } + } + i++; + const deltaS = huffman ? huffmanTables.tableDeltaS.decode(huffmanInput) : decodeInteger(contextCache, "IADS", decoder); + if (deltaS === null) { + break; + } + currentS += increment + deltaS + dsOffset; + } while (true); + } + return bitmap; +} +function decodePatternDictionary(mmr, patternWidth, patternHeight, maxPatternIndex, template, decodingContext) { + const at = []; + if (!mmr) { + at.push({ + x: -patternWidth, + y: 0 + }); + if (template === 0) { + at.push({ + x: -3, + y: -1 + }, { + x: 2, + y: -2 + }, { + x: -2, + y: -2 + }); + } + } + const collectiveWidth = (maxPatternIndex + 1) * patternWidth; + const collectiveBitmap = decodeBitmap(mmr, collectiveWidth, patternHeight, template, false, null, at, decodingContext); + const patterns = []; + for (let i = 0; i <= maxPatternIndex; i++) { + const patternBitmap = []; + const xMin = patternWidth * i; + const xMax = xMin + patternWidth; + for (let y = 0; y < patternHeight; y++) { + patternBitmap.push(collectiveBitmap[y].subarray(xMin, xMax)); + } + patterns.push(patternBitmap); + } + return patterns; +} +function decodeHalftoneRegion(mmr, patterns, template, regionWidth, regionHeight, defaultPixelValue, enableSkip, combinationOperator, gridWidth, gridHeight, gridOffsetX, gridOffsetY, gridVectorX, gridVectorY, decodingContext) { + const skip = null; + if (enableSkip) { + throw new Jbig2Error("skip is not supported"); + } + if (combinationOperator !== 0) { + throw new Jbig2Error(`operator "${combinationOperator}" is not supported in halftone region`); + } + const regionBitmap = []; + let i, j, row; + for (i = 0; i < regionHeight; i++) { + row = new Uint8Array(regionWidth); + if (defaultPixelValue) { + for (j = 0; j < regionWidth; j++) { + row[j] = defaultPixelValue; + } + } + regionBitmap.push(row); + } + const numberOfPatterns = patterns.length; + const pattern0 = patterns[0]; + const patternWidth = pattern0[0].length, + patternHeight = pattern0.length; + const bitsPerValue = log2(numberOfPatterns); + const at = []; + if (!mmr) { + at.push({ + x: template <= 1 ? 3 : 2, + y: -1 + }); + if (template === 0) { + at.push({ + x: -3, + y: -1 + }, { + x: 2, + y: -2 + }, { + x: -2, + y: -2 + }); + } + } + const grayScaleBitPlanes = []; + let mmrInput, bitmap; + if (mmr) { + mmrInput = new Reader(decodingContext.data, decodingContext.start, decodingContext.end); + } + for (i = bitsPerValue - 1; i >= 0; i--) { + if (mmr) { + bitmap = decodeMMRBitmap(mmrInput, gridWidth, gridHeight, true); + } else { + bitmap = decodeBitmap(false, gridWidth, gridHeight, template, false, skip, at, decodingContext); + } + grayScaleBitPlanes[i] = bitmap; + } + let mg, ng, bit, patternIndex, patternBitmap, x, y, patternRow, regionRow; + for (mg = 0; mg < gridHeight; mg++) { + for (ng = 0; ng < gridWidth; ng++) { + bit = 0; + patternIndex = 0; + for (j = bitsPerValue - 1; j >= 0; j--) { + bit ^= grayScaleBitPlanes[j][mg][ng]; + patternIndex |= bit << j; + } + patternBitmap = patterns[patternIndex]; + x = gridOffsetX + mg * gridVectorY + ng * gridVectorX >> 8; + y = gridOffsetY + mg * gridVectorX - ng * gridVectorY >> 8; + if (x >= 0 && x + patternWidth <= regionWidth && y >= 0 && y + patternHeight <= regionHeight) { + for (i = 0; i < patternHeight; i++) { + regionRow = regionBitmap[y + i]; + patternRow = patternBitmap[i]; + for (j = 0; j < patternWidth; j++) { + regionRow[x + j] |= patternRow[j]; + } + } + } else { + let regionX, regionY; + for (i = 0; i < patternHeight; i++) { + regionY = y + i; + if (regionY < 0 || regionY >= regionHeight) { + continue; + } + regionRow = regionBitmap[regionY]; + patternRow = patternBitmap[i]; + for (j = 0; j < patternWidth; j++) { + regionX = x + j; + if (regionX >= 0 && regionX < regionWidth) { + regionRow[regionX] |= patternRow[j]; + } + } + } + } + } + } + return regionBitmap; +} +function readSegmentHeader(data, start) { + const segmentHeader = {}; + segmentHeader.number = readUint32(data, start); + const flags = data[start + 4]; + const segmentType = flags & 0x3f; + if (!SegmentTypes[segmentType]) { + throw new Jbig2Error("invalid segment type: " + segmentType); + } + segmentHeader.type = segmentType; + segmentHeader.typeName = SegmentTypes[segmentType]; + segmentHeader.deferredNonRetain = !!(flags & 0x80); + const pageAssociationFieldSize = !!(flags & 0x40); + const referredFlags = data[start + 5]; + let referredToCount = referredFlags >> 5 & 7; + const retainBits = [referredFlags & 31]; + let position = start + 6; + if (referredFlags === 7) { + referredToCount = readUint32(data, position - 1) & 0x1fffffff; + position += 3; + let bytes = referredToCount + 7 >> 3; + retainBits[0] = data[position++]; + while (--bytes > 0) { + retainBits.push(data[position++]); + } + } else if (referredFlags === 5 || referredFlags === 6) { + throw new Jbig2Error("invalid referred-to flags"); + } + segmentHeader.retainBits = retainBits; + let referredToSegmentNumberSize = 4; + if (segmentHeader.number <= 256) { + referredToSegmentNumberSize = 1; + } else if (segmentHeader.number <= 65536) { + referredToSegmentNumberSize = 2; + } + const referredTo = []; + let i, ii; + for (i = 0; i < referredToCount; i++) { + let number; + if (referredToSegmentNumberSize === 1) { + number = data[position]; + } else if (referredToSegmentNumberSize === 2) { + number = readUint16(data, position); + } else { + number = readUint32(data, position); + } + referredTo.push(number); + position += referredToSegmentNumberSize; + } + segmentHeader.referredTo = referredTo; + if (!pageAssociationFieldSize) { + segmentHeader.pageAssociation = data[position++]; + } else { + segmentHeader.pageAssociation = readUint32(data, position); + position += 4; + } + segmentHeader.length = readUint32(data, position); + position += 4; + if (segmentHeader.length === 0xffffffff) { + if (segmentType === 38) { + const genericRegionInfo = readRegionSegmentInformation(data, position); + const genericRegionSegmentFlags = data[position + RegionSegmentInformationFieldLength]; + const genericRegionMmr = !!(genericRegionSegmentFlags & 1); + const searchPatternLength = 6; + const searchPattern = new Uint8Array(searchPatternLength); + if (!genericRegionMmr) { + searchPattern[0] = 0xff; + searchPattern[1] = 0xac; + } + searchPattern[2] = genericRegionInfo.height >>> 24 & 0xff; + searchPattern[3] = genericRegionInfo.height >> 16 & 0xff; + searchPattern[4] = genericRegionInfo.height >> 8 & 0xff; + searchPattern[5] = genericRegionInfo.height & 0xff; + for (i = position, ii = data.length; i < ii; i++) { + let j = 0; + while (j < searchPatternLength && searchPattern[j] === data[i + j]) { + j++; + } + if (j === searchPatternLength) { + segmentHeader.length = i + searchPatternLength; + break; + } + } + if (segmentHeader.length === 0xffffffff) { + throw new Jbig2Error("segment end was not found"); + } + } else { + throw new Jbig2Error("invalid unknown segment length"); + } + } + segmentHeader.headerEnd = position; + return segmentHeader; +} +function readSegments(header, data, start, end) { + const segments = []; + let position = start; + while (position < end) { + const segmentHeader = readSegmentHeader(data, position); + position = segmentHeader.headerEnd; + const segment = { + header: segmentHeader, + data + }; + if (!header.randomAccess) { + segment.start = position; + position += segmentHeader.length; + segment.end = position; + } + segments.push(segment); + if (segmentHeader.type === 51) { + break; + } + } + if (header.randomAccess) { + for (let i = 0, ii = segments.length; i < ii; i++) { + segments[i].start = position; + position += segments[i].header.length; + segments[i].end = position; + } + } + return segments; +} +function readRegionSegmentInformation(data, start) { + return { + width: readUint32(data, start), + height: readUint32(data, start + 4), + x: readUint32(data, start + 8), + y: readUint32(data, start + 12), + combinationOperator: data[start + 16] & 7 + }; +} +const RegionSegmentInformationFieldLength = 17; +function processSegment(segment, visitor) { + const header = segment.header; + const data = segment.data, + end = segment.end; + let position = segment.start; + let args, at, i, atLength; + switch (header.type) { + case 0: + const dictionary = {}; + const dictionaryFlags = readUint16(data, position); + dictionary.huffman = !!(dictionaryFlags & 1); + dictionary.refinement = !!(dictionaryFlags & 2); + dictionary.huffmanDHSelector = dictionaryFlags >> 2 & 3; + dictionary.huffmanDWSelector = dictionaryFlags >> 4 & 3; + dictionary.bitmapSizeSelector = dictionaryFlags >> 6 & 1; + dictionary.aggregationInstancesSelector = dictionaryFlags >> 7 & 1; + dictionary.bitmapCodingContextUsed = !!(dictionaryFlags & 256); + dictionary.bitmapCodingContextRetained = !!(dictionaryFlags & 512); + dictionary.template = dictionaryFlags >> 10 & 3; + dictionary.refinementTemplate = dictionaryFlags >> 12 & 1; + position += 2; + if (!dictionary.huffman) { + atLength = dictionary.template === 0 ? 4 : 1; + at = []; + for (i = 0; i < atLength; i++) { + at.push({ + x: readInt8(data, position), + y: readInt8(data, position + 1) + }); + position += 2; + } + dictionary.at = at; + } + if (dictionary.refinement && !dictionary.refinementTemplate) { + at = []; + for (i = 0; i < 2; i++) { + at.push({ + x: readInt8(data, position), + y: readInt8(data, position + 1) + }); + position += 2; + } + dictionary.refinementAt = at; + } + dictionary.numberOfExportedSymbols = readUint32(data, position); + position += 4; + dictionary.numberOfNewSymbols = readUint32(data, position); + position += 4; + args = [dictionary, header.number, header.referredTo, data, position, end]; + break; + case 6: + case 7: + const textRegion = {}; + textRegion.info = readRegionSegmentInformation(data, position); + position += RegionSegmentInformationFieldLength; + const textRegionSegmentFlags = readUint16(data, position); + position += 2; + textRegion.huffman = !!(textRegionSegmentFlags & 1); + textRegion.refinement = !!(textRegionSegmentFlags & 2); + textRegion.logStripSize = textRegionSegmentFlags >> 2 & 3; + textRegion.stripSize = 1 << textRegion.logStripSize; + textRegion.referenceCorner = textRegionSegmentFlags >> 4 & 3; + textRegion.transposed = !!(textRegionSegmentFlags & 64); + textRegion.combinationOperator = textRegionSegmentFlags >> 7 & 3; + textRegion.defaultPixelValue = textRegionSegmentFlags >> 9 & 1; + textRegion.dsOffset = textRegionSegmentFlags << 17 >> 27; + textRegion.refinementTemplate = textRegionSegmentFlags >> 15 & 1; + if (textRegion.huffman) { + const textRegionHuffmanFlags = readUint16(data, position); + position += 2; + textRegion.huffmanFS = textRegionHuffmanFlags & 3; + textRegion.huffmanDS = textRegionHuffmanFlags >> 2 & 3; + textRegion.huffmanDT = textRegionHuffmanFlags >> 4 & 3; + textRegion.huffmanRefinementDW = textRegionHuffmanFlags >> 6 & 3; + textRegion.huffmanRefinementDH = textRegionHuffmanFlags >> 8 & 3; + textRegion.huffmanRefinementDX = textRegionHuffmanFlags >> 10 & 3; + textRegion.huffmanRefinementDY = textRegionHuffmanFlags >> 12 & 3; + textRegion.huffmanRefinementSizeSelector = !!(textRegionHuffmanFlags & 0x4000); + } + if (textRegion.refinement && !textRegion.refinementTemplate) { + at = []; + for (i = 0; i < 2; i++) { + at.push({ + x: readInt8(data, position), + y: readInt8(data, position + 1) + }); + position += 2; + } + textRegion.refinementAt = at; + } + textRegion.numberOfSymbolInstances = readUint32(data, position); + position += 4; + args = [textRegion, header.referredTo, data, position, end]; + break; + case 16: + const patternDictionary = {}; + const patternDictionaryFlags = data[position++]; + patternDictionary.mmr = !!(patternDictionaryFlags & 1); + patternDictionary.template = patternDictionaryFlags >> 1 & 3; + patternDictionary.patternWidth = data[position++]; + patternDictionary.patternHeight = data[position++]; + patternDictionary.maxPatternIndex = readUint32(data, position); + position += 4; + args = [patternDictionary, header.number, data, position, end]; + break; + case 22: + case 23: + const halftoneRegion = {}; + halftoneRegion.info = readRegionSegmentInformation(data, position); + position += RegionSegmentInformationFieldLength; + const halftoneRegionFlags = data[position++]; + halftoneRegion.mmr = !!(halftoneRegionFlags & 1); + halftoneRegion.template = halftoneRegionFlags >> 1 & 3; + halftoneRegion.enableSkip = !!(halftoneRegionFlags & 8); + halftoneRegion.combinationOperator = halftoneRegionFlags >> 4 & 7; + halftoneRegion.defaultPixelValue = halftoneRegionFlags >> 7 & 1; + halftoneRegion.gridWidth = readUint32(data, position); + position += 4; + halftoneRegion.gridHeight = readUint32(data, position); + position += 4; + halftoneRegion.gridOffsetX = readUint32(data, position) & 0xffffffff; + position += 4; + halftoneRegion.gridOffsetY = readUint32(data, position) & 0xffffffff; + position += 4; + halftoneRegion.gridVectorX = readUint16(data, position); + position += 2; + halftoneRegion.gridVectorY = readUint16(data, position); + position += 2; + args = [halftoneRegion, header.referredTo, data, position, end]; + break; + case 38: + case 39: + const genericRegion = {}; + genericRegion.info = readRegionSegmentInformation(data, position); + position += RegionSegmentInformationFieldLength; + const genericRegionSegmentFlags = data[position++]; + genericRegion.mmr = !!(genericRegionSegmentFlags & 1); + genericRegion.template = genericRegionSegmentFlags >> 1 & 3; + genericRegion.prediction = !!(genericRegionSegmentFlags & 8); + if (!genericRegion.mmr) { + atLength = genericRegion.template === 0 ? 4 : 1; + at = []; + for (i = 0; i < atLength; i++) { + at.push({ + x: readInt8(data, position), + y: readInt8(data, position + 1) + }); + position += 2; + } + genericRegion.at = at; + } + args = [genericRegion, data, position, end]; + break; + case 48: + const pageInfo = { + width: readUint32(data, position), + height: readUint32(data, position + 4), + resolutionX: readUint32(data, position + 8), + resolutionY: readUint32(data, position + 12) + }; + if (pageInfo.height === 0xffffffff) { + delete pageInfo.height; + } + const pageSegmentFlags = data[position + 16]; + readUint16(data, position + 17); + pageInfo.lossless = !!(pageSegmentFlags & 1); + pageInfo.refinement = !!(pageSegmentFlags & 2); + pageInfo.defaultPixelValue = pageSegmentFlags >> 2 & 1; + pageInfo.combinationOperator = pageSegmentFlags >> 3 & 3; + pageInfo.requiresBuffer = !!(pageSegmentFlags & 32); + pageInfo.combinationOperatorOverride = !!(pageSegmentFlags & 64); + args = [pageInfo]; + break; + case 49: + break; + case 50: + break; + case 51: + break; + case 53: + args = [header.number, data, position, end]; + break; + case 62: + break; + default: + throw new Jbig2Error(`segment type ${header.typeName}(${header.type}) is not implemented`); + } + const callbackName = "on" + header.typeName; + if (callbackName in visitor) { + visitor[callbackName].apply(visitor, args); + } +} +function processSegments(segments, visitor) { + for (let i = 0, ii = segments.length; i < ii; i++) { + processSegment(segments[i], visitor); + } +} +function parseJbig2Chunks(chunks) { + const visitor = new SimpleSegmentVisitor(); + for (let i = 0, ii = chunks.length; i < ii; i++) { + const chunk = chunks[i]; + const segments = readSegments({}, chunk.data, chunk.start, chunk.end); + processSegments(segments, visitor); + } + return visitor.buffer; +} +function parseJbig2(data) { + throw new Error("Not implemented: parseJbig2"); +} +class SimpleSegmentVisitor { + onPageInformation(info) { + this.currentPageInfo = info; + const rowSize = info.width + 7 >> 3; + const buffer = new Uint8ClampedArray(rowSize * info.height); + if (info.defaultPixelValue) { + buffer.fill(0xff); + } + this.buffer = buffer; + } + drawBitmap(regionInfo, bitmap) { + const pageInfo = this.currentPageInfo; + const width = regionInfo.width, + height = regionInfo.height; + const rowSize = pageInfo.width + 7 >> 3; + const combinationOperator = pageInfo.combinationOperatorOverride ? regionInfo.combinationOperator : pageInfo.combinationOperator; + const buffer = this.buffer; + const mask0 = 128 >> (regionInfo.x & 7); + let offset0 = regionInfo.y * rowSize + (regionInfo.x >> 3); + let i, j, mask, offset; + switch (combinationOperator) { + case 0: + for (i = 0; i < height; i++) { + mask = mask0; + offset = offset0; + for (j = 0; j < width; j++) { + if (bitmap[i][j]) { + buffer[offset] |= mask; + } + mask >>= 1; + if (!mask) { + mask = 128; + offset++; + } + } + offset0 += rowSize; + } + break; + case 2: + for (i = 0; i < height; i++) { + mask = mask0; + offset = offset0; + for (j = 0; j < width; j++) { + if (bitmap[i][j]) { + buffer[offset] ^= mask; + } + mask >>= 1; + if (!mask) { + mask = 128; + offset++; + } + } + offset0 += rowSize; + } + break; + default: + throw new Jbig2Error(`operator ${combinationOperator} is not supported`); + } + } + onImmediateGenericRegion(region, data, start, end) { + const regionInfo = region.info; + const decodingContext = new DecodingContext(data, start, end); + const bitmap = decodeBitmap(region.mmr, regionInfo.width, regionInfo.height, region.template, region.prediction, null, region.at, decodingContext); + this.drawBitmap(regionInfo, bitmap); + } + onImmediateLosslessGenericRegion() { + this.onImmediateGenericRegion(...arguments); + } + onSymbolDictionary(dictionary, currentSegment, referredSegments, data, start, end) { + let huffmanTables, huffmanInput; + if (dictionary.huffman) { + huffmanTables = getSymbolDictionaryHuffmanTables(dictionary, referredSegments, this.customTables); + huffmanInput = new Reader(data, start, end); + } + let symbols = this.symbols; + if (!symbols) { + this.symbols = symbols = {}; + } + const inputSymbols = []; + for (const referredSegment of referredSegments) { + const referredSymbols = symbols[referredSegment]; + if (referredSymbols) { + inputSymbols.push(...referredSymbols); + } + } + const decodingContext = new DecodingContext(data, start, end); + symbols[currentSegment] = decodeSymbolDictionary(dictionary.huffman, dictionary.refinement, inputSymbols, dictionary.numberOfNewSymbols, dictionary.numberOfExportedSymbols, huffmanTables, dictionary.template, dictionary.at, dictionary.refinementTemplate, dictionary.refinementAt, decodingContext, huffmanInput); + } + onImmediateTextRegion(region, referredSegments, data, start, end) { + const regionInfo = region.info; + let huffmanTables, huffmanInput; + const symbols = this.symbols; + const inputSymbols = []; + for (const referredSegment of referredSegments) { + const referredSymbols = symbols[referredSegment]; + if (referredSymbols) { + inputSymbols.push(...referredSymbols); + } + } + const symbolCodeLength = log2(inputSymbols.length); + if (region.huffman) { + huffmanInput = new Reader(data, start, end); + huffmanTables = getTextRegionHuffmanTables(region, referredSegments, this.customTables, inputSymbols.length, huffmanInput); + } + const decodingContext = new DecodingContext(data, start, end); + const bitmap = decodeTextRegion(region.huffman, region.refinement, regionInfo.width, regionInfo.height, region.defaultPixelValue, region.numberOfSymbolInstances, region.stripSize, inputSymbols, symbolCodeLength, region.transposed, region.dsOffset, region.referenceCorner, region.combinationOperator, huffmanTables, region.refinementTemplate, region.refinementAt, decodingContext, region.logStripSize, huffmanInput); + this.drawBitmap(regionInfo, bitmap); + } + onImmediateLosslessTextRegion() { + this.onImmediateTextRegion(...arguments); + } + onPatternDictionary(dictionary, currentSegment, data, start, end) { + let patterns = this.patterns; + if (!patterns) { + this.patterns = patterns = {}; + } + const decodingContext = new DecodingContext(data, start, end); + patterns[currentSegment] = decodePatternDictionary(dictionary.mmr, dictionary.patternWidth, dictionary.patternHeight, dictionary.maxPatternIndex, dictionary.template, decodingContext); + } + onImmediateHalftoneRegion(region, referredSegments, data, start, end) { + const patterns = this.patterns[referredSegments[0]]; + const regionInfo = region.info; + const decodingContext = new DecodingContext(data, start, end); + const bitmap = decodeHalftoneRegion(region.mmr, patterns, region.template, regionInfo.width, regionInfo.height, region.defaultPixelValue, region.enableSkip, region.combinationOperator, region.gridWidth, region.gridHeight, region.gridOffsetX, region.gridOffsetY, region.gridVectorX, region.gridVectorY, decodingContext); + this.drawBitmap(regionInfo, bitmap); + } + onImmediateLosslessHalftoneRegion() { + this.onImmediateHalftoneRegion(...arguments); + } + onTables(currentSegment, data, start, end) { + let customTables = this.customTables; + if (!customTables) { + this.customTables = customTables = {}; + } + customTables[currentSegment] = decodeTablesSegment(data, start, end); + } +} +class HuffmanLine { + constructor(lineData) { + if (lineData.length === 2) { + this.isOOB = true; + this.rangeLow = 0; + this.prefixLength = lineData[0]; + this.rangeLength = 0; + this.prefixCode = lineData[1]; + this.isLowerRange = false; + } else { + this.isOOB = false; + this.rangeLow = lineData[0]; + this.prefixLength = lineData[1]; + this.rangeLength = lineData[2]; + this.prefixCode = lineData[3]; + this.isLowerRange = lineData[4] === "lower"; + } + } +} +class HuffmanTreeNode { + constructor(line) { + this.children = []; + if (line) { + this.isLeaf = true; + this.rangeLength = line.rangeLength; + this.rangeLow = line.rangeLow; + this.isLowerRange = line.isLowerRange; + this.isOOB = line.isOOB; + } else { + this.isLeaf = false; + } + } + buildTree(line, shift) { + const bit = line.prefixCode >> shift & 1; + if (shift <= 0) { + this.children[bit] = new HuffmanTreeNode(line); + } else { + let node = this.children[bit]; + if (!node) { + this.children[bit] = node = new HuffmanTreeNode(null); + } + node.buildTree(line, shift - 1); + } + } + decodeNode(reader) { + if (this.isLeaf) { + if (this.isOOB) { + return null; + } + const htOffset = reader.readBits(this.rangeLength); + return this.rangeLow + (this.isLowerRange ? -htOffset : htOffset); + } + const node = this.children[reader.readBit()]; + if (!node) { + throw new Jbig2Error("invalid Huffman data"); + } + return node.decodeNode(reader); + } +} +class HuffmanTable { + constructor(lines, prefixCodesDone) { + if (!prefixCodesDone) { + this.assignPrefixCodes(lines); + } + this.rootNode = new HuffmanTreeNode(null); + for (let i = 0, ii = lines.length; i < ii; i++) { + const line = lines[i]; + if (line.prefixLength > 0) { + this.rootNode.buildTree(line, line.prefixLength - 1); + } + } + } + decode(reader) { + return this.rootNode.decodeNode(reader); + } + assignPrefixCodes(lines) { + const linesLength = lines.length; + let prefixLengthMax = 0; + for (let i = 0; i < linesLength; i++) { + prefixLengthMax = Math.max(prefixLengthMax, lines[i].prefixLength); + } + const histogram = new Uint32Array(prefixLengthMax + 1); + for (let i = 0; i < linesLength; i++) { + histogram[lines[i].prefixLength]++; + } + let currentLength = 1, + firstCode = 0, + currentCode, + currentTemp, + line; + histogram[0] = 0; + while (currentLength <= prefixLengthMax) { + firstCode = firstCode + histogram[currentLength - 1] << 1; + currentCode = firstCode; + currentTemp = 0; + while (currentTemp < linesLength) { + line = lines[currentTemp]; + if (line.prefixLength === currentLength) { + line.prefixCode = currentCode; + currentCode++; + } + currentTemp++; + } + currentLength++; + } + } +} +function decodeTablesSegment(data, start, end) { + const flags = data[start]; + const lowestValue = readUint32(data, start + 1) & 0xffffffff; + const highestValue = readUint32(data, start + 5) & 0xffffffff; + const reader = new Reader(data, start + 9, end); + const prefixSizeBits = (flags >> 1 & 7) + 1; + const rangeSizeBits = (flags >> 4 & 7) + 1; + const lines = []; + let prefixLength, + rangeLength, + currentRangeLow = lowestValue; + do { + prefixLength = reader.readBits(prefixSizeBits); + rangeLength = reader.readBits(rangeSizeBits); + lines.push(new HuffmanLine([currentRangeLow, prefixLength, rangeLength, 0])); + currentRangeLow += 1 << rangeLength; + } while (currentRangeLow < highestValue); + prefixLength = reader.readBits(prefixSizeBits); + lines.push(new HuffmanLine([lowestValue - 1, prefixLength, 32, 0, "lower"])); + prefixLength = reader.readBits(prefixSizeBits); + lines.push(new HuffmanLine([highestValue, prefixLength, 32, 0])); + if (flags & 1) { + prefixLength = reader.readBits(prefixSizeBits); + lines.push(new HuffmanLine([prefixLength, 0])); + } + return new HuffmanTable(lines, false); +} +const standardTablesCache = {}; +function getStandardTable(number) { + let table = standardTablesCache[number]; + if (table) { + return table; + } + let lines; + switch (number) { + case 1: + lines = [[0, 1, 4, 0x0], [16, 2, 8, 0x2], [272, 3, 16, 0x6], [65808, 3, 32, 0x7]]; + break; + case 2: + lines = [[0, 1, 0, 0x0], [1, 2, 0, 0x2], [2, 3, 0, 0x6], [3, 4, 3, 0xe], [11, 5, 6, 0x1e], [75, 6, 32, 0x3e], [6, 0x3f]]; + break; + case 3: + lines = [[-256, 8, 8, 0xfe], [0, 1, 0, 0x0], [1, 2, 0, 0x2], [2, 3, 0, 0x6], [3, 4, 3, 0xe], [11, 5, 6, 0x1e], [-257, 8, 32, 0xff, "lower"], [75, 7, 32, 0x7e], [6, 0x3e]]; + break; + case 4: + lines = [[1, 1, 0, 0x0], [2, 2, 0, 0x2], [3, 3, 0, 0x6], [4, 4, 3, 0xe], [12, 5, 6, 0x1e], [76, 5, 32, 0x1f]]; + break; + case 5: + lines = [[-255, 7, 8, 0x7e], [1, 1, 0, 0x0], [2, 2, 0, 0x2], [3, 3, 0, 0x6], [4, 4, 3, 0xe], [12, 5, 6, 0x1e], [-256, 7, 32, 0x7f, "lower"], [76, 6, 32, 0x3e]]; + break; + case 6: + lines = [[-2048, 5, 10, 0x1c], [-1024, 4, 9, 0x8], [-512, 4, 8, 0x9], [-256, 4, 7, 0xa], [-128, 5, 6, 0x1d], [-64, 5, 5, 0x1e], [-32, 4, 5, 0xb], [0, 2, 7, 0x0], [128, 3, 7, 0x2], [256, 3, 8, 0x3], [512, 4, 9, 0xc], [1024, 4, 10, 0xd], [-2049, 6, 32, 0x3e, "lower"], [2048, 6, 32, 0x3f]]; + break; + case 7: + lines = [[-1024, 4, 9, 0x8], [-512, 3, 8, 0x0], [-256, 4, 7, 0x9], [-128, 5, 6, 0x1a], [-64, 5, 5, 0x1b], [-32, 4, 5, 0xa], [0, 4, 5, 0xb], [32, 5, 5, 0x1c], [64, 5, 6, 0x1d], [128, 4, 7, 0xc], [256, 3, 8, 0x1], [512, 3, 9, 0x2], [1024, 3, 10, 0x3], [-1025, 5, 32, 0x1e, "lower"], [2048, 5, 32, 0x1f]]; + break; + case 8: + lines = [[-15, 8, 3, 0xfc], [-7, 9, 1, 0x1fc], [-5, 8, 1, 0xfd], [-3, 9, 0, 0x1fd], [-2, 7, 0, 0x7c], [-1, 4, 0, 0xa], [0, 2, 1, 0x0], [2, 5, 0, 0x1a], [3, 6, 0, 0x3a], [4, 3, 4, 0x4], [20, 6, 1, 0x3b], [22, 4, 4, 0xb], [38, 4, 5, 0xc], [70, 5, 6, 0x1b], [134, 5, 7, 0x1c], [262, 6, 7, 0x3c], [390, 7, 8, 0x7d], [646, 6, 10, 0x3d], [-16, 9, 32, 0x1fe, "lower"], [1670, 9, 32, 0x1ff], [2, 0x1]]; + break; + case 9: + lines = [[-31, 8, 4, 0xfc], [-15, 9, 2, 0x1fc], [-11, 8, 2, 0xfd], [-7, 9, 1, 0x1fd], [-5, 7, 1, 0x7c], [-3, 4, 1, 0xa], [-1, 3, 1, 0x2], [1, 3, 1, 0x3], [3, 5, 1, 0x1a], [5, 6, 1, 0x3a], [7, 3, 5, 0x4], [39, 6, 2, 0x3b], [43, 4, 5, 0xb], [75, 4, 6, 0xc], [139, 5, 7, 0x1b], [267, 5, 8, 0x1c], [523, 6, 8, 0x3c], [779, 7, 9, 0x7d], [1291, 6, 11, 0x3d], [-32, 9, 32, 0x1fe, "lower"], [3339, 9, 32, 0x1ff], [2, 0x0]]; + break; + case 10: + lines = [[-21, 7, 4, 0x7a], [-5, 8, 0, 0xfc], [-4, 7, 0, 0x7b], [-3, 5, 0, 0x18], [-2, 2, 2, 0x0], [2, 5, 0, 0x19], [3, 6, 0, 0x36], [4, 7, 0, 0x7c], [5, 8, 0, 0xfd], [6, 2, 6, 0x1], [70, 5, 5, 0x1a], [102, 6, 5, 0x37], [134, 6, 6, 0x38], [198, 6, 7, 0x39], [326, 6, 8, 0x3a], [582, 6, 9, 0x3b], [1094, 6, 10, 0x3c], [2118, 7, 11, 0x7d], [-22, 8, 32, 0xfe, "lower"], [4166, 8, 32, 0xff], [2, 0x2]]; + break; + case 11: + lines = [[1, 1, 0, 0x0], [2, 2, 1, 0x2], [4, 4, 0, 0xc], [5, 4, 1, 0xd], [7, 5, 1, 0x1c], [9, 5, 2, 0x1d], [13, 6, 2, 0x3c], [17, 7, 2, 0x7a], [21, 7, 3, 0x7b], [29, 7, 4, 0x7c], [45, 7, 5, 0x7d], [77, 7, 6, 0x7e], [141, 7, 32, 0x7f]]; + break; + case 12: + lines = [[1, 1, 0, 0x0], [2, 2, 0, 0x2], [3, 3, 1, 0x6], [5, 5, 0, 0x1c], [6, 5, 1, 0x1d], [8, 6, 1, 0x3c], [10, 7, 0, 0x7a], [11, 7, 1, 0x7b], [13, 7, 2, 0x7c], [17, 7, 3, 0x7d], [25, 7, 4, 0x7e], [41, 8, 5, 0xfe], [73, 8, 32, 0xff]]; + break; + case 13: + lines = [[1, 1, 0, 0x0], [2, 3, 0, 0x4], [3, 4, 0, 0xc], [4, 5, 0, 0x1c], [5, 4, 1, 0xd], [7, 3, 3, 0x5], [15, 6, 1, 0x3a], [17, 6, 2, 0x3b], [21, 6, 3, 0x3c], [29, 6, 4, 0x3d], [45, 6, 5, 0x3e], [77, 7, 6, 0x7e], [141, 7, 32, 0x7f]]; + break; + case 14: + lines = [[-2, 3, 0, 0x4], [-1, 3, 0, 0x5], [0, 1, 0, 0x0], [1, 3, 0, 0x6], [2, 3, 0, 0x7]]; + break; + case 15: + lines = [[-24, 7, 4, 0x7c], [-8, 6, 2, 0x3c], [-4, 5, 1, 0x1c], [-2, 4, 0, 0xc], [-1, 3, 0, 0x4], [0, 1, 0, 0x0], [1, 3, 0, 0x5], [2, 4, 0, 0xd], [3, 5, 1, 0x1d], [5, 6, 2, 0x3d], [9, 7, 4, 0x7d], [-25, 7, 32, 0x7e, "lower"], [25, 7, 32, 0x7f]]; + break; + default: + throw new Jbig2Error(`standard table B.${number} does not exist`); + } + for (let i = 0, ii = lines.length; i < ii; i++) { + lines[i] = new HuffmanLine(lines[i]); + } + table = new HuffmanTable(lines, true); + standardTablesCache[number] = table; + return table; +} +class Reader { + constructor(data, start, end) { + this.data = data; + this.start = start; + this.end = end; + this.position = start; + this.shift = -1; + this.currentByte = 0; + } + readBit() { + if (this.shift < 0) { + if (this.position >= this.end) { + throw new Jbig2Error("end of data while reading bit"); + } + this.currentByte = this.data[this.position++]; + this.shift = 7; + } + const bit = this.currentByte >> this.shift & 1; + this.shift--; + return bit; + } + readBits(numBits) { + let result = 0, + i; + for (i = numBits - 1; i >= 0; i--) { + result |= this.readBit() << i; + } + return result; + } + byteAlign() { + this.shift = -1; + } + next() { + if (this.position >= this.end) { + return -1; + } + return this.data[this.position++]; + } +} +function getCustomHuffmanTable(index, referredTo, customTables) { + let currentIndex = 0; + for (let i = 0, ii = referredTo.length; i < ii; i++) { + const table = customTables[referredTo[i]]; + if (table) { + if (index === currentIndex) { + return table; + } + currentIndex++; + } + } + throw new Jbig2Error("can't find custom Huffman table"); +} +function getTextRegionHuffmanTables(textRegion, referredTo, customTables, numberOfSymbols, reader) { + const codes = []; + for (let i = 0; i <= 34; i++) { + const codeLength = reader.readBits(4); + codes.push(new HuffmanLine([i, codeLength, 0, 0])); + } + const runCodesTable = new HuffmanTable(codes, false); + codes.length = 0; + for (let i = 0; i < numberOfSymbols;) { + const codeLength = runCodesTable.decode(reader); + if (codeLength >= 32) { + let repeatedLength, numberOfRepeats, j; + switch (codeLength) { + case 32: + if (i === 0) { + throw new Jbig2Error("no previous value in symbol ID table"); + } + numberOfRepeats = reader.readBits(2) + 3; + repeatedLength = codes[i - 1].prefixLength; + break; + case 33: + numberOfRepeats = reader.readBits(3) + 3; + repeatedLength = 0; + break; + case 34: + numberOfRepeats = reader.readBits(7) + 11; + repeatedLength = 0; + break; + default: + throw new Jbig2Error("invalid code length in symbol ID table"); + } + for (j = 0; j < numberOfRepeats; j++) { + codes.push(new HuffmanLine([i, repeatedLength, 0, 0])); + i++; + } + } else { + codes.push(new HuffmanLine([i, codeLength, 0, 0])); + i++; + } + } + reader.byteAlign(); + const symbolIDTable = new HuffmanTable(codes, false); + let customIndex = 0, + tableFirstS, + tableDeltaS, + tableDeltaT; + switch (textRegion.huffmanFS) { + case 0: + case 1: + tableFirstS = getStandardTable(textRegion.huffmanFS + 6); + break; + case 3: + tableFirstS = getCustomHuffmanTable(customIndex, referredTo, customTables); + customIndex++; + break; + default: + throw new Jbig2Error("invalid Huffman FS selector"); + } + switch (textRegion.huffmanDS) { + case 0: + case 1: + case 2: + tableDeltaS = getStandardTable(textRegion.huffmanDS + 8); + break; + case 3: + tableDeltaS = getCustomHuffmanTable(customIndex, referredTo, customTables); + customIndex++; + break; + default: + throw new Jbig2Error("invalid Huffman DS selector"); + } + switch (textRegion.huffmanDT) { + case 0: + case 1: + case 2: + tableDeltaT = getStandardTable(textRegion.huffmanDT + 11); + break; + case 3: + tableDeltaT = getCustomHuffmanTable(customIndex, referredTo, customTables); + customIndex++; + break; + default: + throw new Jbig2Error("invalid Huffman DT selector"); + } + if (textRegion.refinement) { + throw new Jbig2Error("refinement with Huffman is not supported"); + } + return { + symbolIDTable, + tableFirstS, + tableDeltaS, + tableDeltaT + }; +} +function getSymbolDictionaryHuffmanTables(dictionary, referredTo, customTables) { + let customIndex = 0, + tableDeltaHeight, + tableDeltaWidth; + switch (dictionary.huffmanDHSelector) { + case 0: + case 1: + tableDeltaHeight = getStandardTable(dictionary.huffmanDHSelector + 4); + break; + case 3: + tableDeltaHeight = getCustomHuffmanTable(customIndex, referredTo, customTables); + customIndex++; + break; + default: + throw new Jbig2Error("invalid Huffman DH selector"); + } + switch (dictionary.huffmanDWSelector) { + case 0: + case 1: + tableDeltaWidth = getStandardTable(dictionary.huffmanDWSelector + 2); + break; + case 3: + tableDeltaWidth = getCustomHuffmanTable(customIndex, referredTo, customTables); + customIndex++; + break; + default: + throw new Jbig2Error("invalid Huffman DW selector"); + } + let tableBitmapSize, tableAggregateInstances; + if (dictionary.bitmapSizeSelector) { + tableBitmapSize = getCustomHuffmanTable(customIndex, referredTo, customTables); + customIndex++; + } else { + tableBitmapSize = getStandardTable(1); + } + if (dictionary.aggregationInstancesSelector) { + tableAggregateInstances = getCustomHuffmanTable(customIndex, referredTo, customTables); + } else { + tableAggregateInstances = getStandardTable(1); + } + return { + tableDeltaHeight, + tableDeltaWidth, + tableBitmapSize, + tableAggregateInstances + }; +} +function readUncompressedBitmap(reader, width, height) { + const bitmap = []; + for (let y = 0; y < height; y++) { + const row = new Uint8Array(width); + bitmap.push(row); + for (let x = 0; x < width; x++) { + row[x] = reader.readBit(); + } + reader.byteAlign(); + } + return bitmap; +} +function decodeMMRBitmap(input, width, height, endOfBlock) { + const params = { + K: -1, + Columns: width, + Rows: height, + BlackIs1: true, + EndOfBlock: endOfBlock + }; + const decoder = new CCITTFaxDecoder(input, params); + const bitmap = []; + let currentByte, + eof = false; + for (let y = 0; y < height; y++) { + const row = new Uint8Array(width); + bitmap.push(row); + let shift = -1; + for (let x = 0; x < width; x++) { + if (shift < 0) { + currentByte = decoder.readNextChar(); + if (currentByte === -1) { + currentByte = 0; + eof = true; + } + shift = 7; + } + row[x] = currentByte >> shift & 1; + shift--; + } + } + if (endOfBlock && !eof) { + const lookForEOFLimit = 5; + for (let i = 0; i < lookForEOFLimit; i++) { + if (decoder.readNextChar() === -1) { + break; + } + } + } + return bitmap; +} +class Jbig2Image { + parseChunks(chunks) { + return parseJbig2Chunks(chunks); + } + parse(data) { + throw new Error("Not implemented: Jbig2Image.parse"); + } +} + +;// ./src/core/jbig2_stream.js + + + + + +class Jbig2Stream extends DecodeStream { + constructor(stream, maybeLength, params) { + super(maybeLength); + this.stream = stream; + this.dict = stream.dict; + this.maybeLength = maybeLength; + this.params = params; + } + get bytes() { + return shadow(this, "bytes", this.stream.getBytes(this.maybeLength)); + } + ensureBuffer(requested) {} + readBlock() { + this.decodeImage(); + } + decodeImage(bytes) { + if (this.eof) { + return this.buffer; + } + bytes ||= this.bytes; + const jbig2Image = new Jbig2Image(); + const chunks = []; + if (this.params instanceof Dict) { + const globalsStream = this.params.get("JBIG2Globals"); + if (globalsStream instanceof BaseStream) { + const globals = globalsStream.getBytes(); + chunks.push({ + data: globals, + start: 0, + end: globals.length + }); + } + } + chunks.push({ + data: bytes, + start: 0, + end: bytes.length + }); + const data = jbig2Image.parseChunks(chunks); + const dataLength = data.length; + for (let i = 0; i < dataLength; i++) { + data[i] ^= 0xff; + } + this.buffer = data; + this.bufferLength = dataLength; + this.eof = true; + return this.buffer; + } + get canAsyncDecodeImageFromBuffer() { + return this.stream.isAsync; + } +} + +;// ./src/shared/image_utils.js + +function convertToRGBA(params) { + switch (params.kind) { + case ImageKind.GRAYSCALE_1BPP: + return convertBlackAndWhiteToRGBA(params); + case ImageKind.RGB_24BPP: + return convertRGBToRGBA(params); + } + return null; +} +function convertBlackAndWhiteToRGBA({ + src, + srcPos = 0, + dest, + width, + height, + nonBlackColor = 0xffffffff, + inverseDecode = false +}) { + const black = FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff; + const [zeroMapping, oneMapping] = inverseDecode ? [nonBlackColor, black] : [black, nonBlackColor]; + const widthInSource = width >> 3; + const widthRemainder = width & 7; + const srcLength = src.length; + dest = new Uint32Array(dest.buffer); + let destPos = 0; + for (let i = 0; i < height; i++) { + for (const max = srcPos + widthInSource; srcPos < max; srcPos++) { + const elem = srcPos < srcLength ? src[srcPos] : 255; + dest[destPos++] = elem & 0b10000000 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b1000000 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b100000 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b10000 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b1000 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b100 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b10 ? oneMapping : zeroMapping; + dest[destPos++] = elem & 0b1 ? oneMapping : zeroMapping; + } + if (widthRemainder === 0) { + continue; + } + const elem = srcPos < srcLength ? src[srcPos++] : 255; + for (let j = 0; j < widthRemainder; j++) { + dest[destPos++] = elem & 1 << 7 - j ? oneMapping : zeroMapping; + } + } + return { + srcPos, + destPos + }; +} +function convertRGBToRGBA({ + src, + srcPos = 0, + dest, + destPos = 0, + width, + height +}) { + let i = 0; + const len = width * height * 3; + const len32 = len >> 2; + const src32 = new Uint32Array(src.buffer, srcPos, len32); + if (FeatureTest.isLittleEndian) { + for (; i < len32 - 2; i += 3, destPos += 4) { + const s1 = src32[i]; + const s2 = src32[i + 1]; + const s3 = src32[i + 2]; + dest[destPos] = s1 | 0xff000000; + dest[destPos + 1] = s1 >>> 24 | s2 << 8 | 0xff000000; + dest[destPos + 2] = s2 >>> 16 | s3 << 16 | 0xff000000; + dest[destPos + 3] = s3 >>> 8 | 0xff000000; + } + for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) { + dest[destPos++] = src[j] | src[j + 1] << 8 | src[j + 2] << 16 | 0xff000000; + } + } else { + for (; i < len32 - 2; i += 3, destPos += 4) { + const s1 = src32[i]; + const s2 = src32[i + 1]; + const s3 = src32[i + 2]; + dest[destPos] = s1 | 0xff; + dest[destPos + 1] = s1 << 24 | s2 >>> 8 | 0xff; + dest[destPos + 2] = s2 << 16 | s3 >>> 16 | 0xff; + dest[destPos + 3] = s3 << 8 | 0xff; + } + for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) { + dest[destPos++] = src[j] << 24 | src[j + 1] << 16 | src[j + 2] << 8 | 0xff; + } + } + return { + srcPos: srcPos + len, + destPos + }; +} +function grayToRGBA(src, dest) { + if (FeatureTest.isLittleEndian) { + for (let i = 0, ii = src.length; i < ii; i++) { + dest[i] = src[i] * 0x10101 | 0xff000000; + } + } else { + for (let i = 0, ii = src.length; i < ii; i++) { + dest[i] = src[i] * 0x1010100 | 0x000000ff; + } + } +} + +;// ./src/core/jpg.js + + + +class JpegError extends BaseException { + constructor(msg) { + super(msg, "JpegError"); + } +} +class DNLMarkerError extends BaseException { + constructor(message, scanLines) { + super(message, "DNLMarkerError"); + this.scanLines = scanLines; + } +} +class EOIMarkerError extends BaseException { + constructor(msg) { + super(msg, "EOIMarkerError"); + } +} +const dctZigZag = new Uint8Array([0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63]); +const dctCos1 = 4017; +const dctSin1 = 799; +const dctCos3 = 3406; +const dctSin3 = 2276; +const dctCos6 = 1567; +const dctSin6 = 3784; +const dctSqrt2 = 5793; +const dctSqrt1d2 = 2896; +function buildHuffmanTable(codeLengths, values) { + let k = 0, + i, + j, + length = 16; + while (length > 0 && !codeLengths[length - 1]) { + length--; + } + const code = [{ + children: [], + index: 0 + }]; + let p = code[0], + q; + for (i = 0; i < length; i++) { + for (j = 0; j < codeLengths[i]; j++) { + p = code.pop(); + p.children[p.index] = values[k]; + while (p.index > 0) { + p = code.pop(); + } + p.index++; + code.push(p); + while (code.length <= i) { + code.push(q = { + children: [], + index: 0 + }); + p.children[p.index] = q.children; + p = q; + } + k++; + } + if (i + 1 < length) { + code.push(q = { + children: [], + index: 0 + }); + p.children[p.index] = q.children; + p = q; + } + } + return code[0].children; +} +function getBlockBufferOffset(component, row, col) { + return 64 * ((component.blocksPerLine + 1) * row + col); +} +function decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successivePrev, successive, parseDNLMarker = false) { + const mcusPerLine = frame.mcusPerLine; + const progressive = frame.progressive; + const startOffset = offset; + let bitsData = 0, + bitsCount = 0; + function readBit() { + if (bitsCount > 0) { + bitsCount--; + return bitsData >> bitsCount & 1; + } + bitsData = data[offset++]; + if (bitsData === 0xff) { + const nextByte = data[offset++]; + if (nextByte) { + if (nextByte === 0xdc && parseDNLMarker) { + offset += 2; + const scanLines = readUint16(data, offset); + offset += 2; + if (scanLines > 0 && scanLines !== frame.scanLines) { + throw new DNLMarkerError("Found DNL marker (0xFFDC) while parsing scan data", scanLines); + } + } else if (nextByte === 0xd9) { + if (parseDNLMarker) { + const maybeScanLines = blockRow * (frame.precision === 8 ? 8 : 0); + if (maybeScanLines > 0 && Math.round(frame.scanLines / maybeScanLines) >= 5) { + throw new DNLMarkerError("Found EOI marker (0xFFD9) while parsing scan data, " + "possibly caused by incorrect `scanLines` parameter", maybeScanLines); + } + } + throw new EOIMarkerError("Found EOI marker (0xFFD9) while parsing scan data"); + } + throw new JpegError(`unexpected marker ${(bitsData << 8 | nextByte).toString(16)}`); + } + } + bitsCount = 7; + return bitsData >>> 7; + } + function decodeHuffman(tree) { + let node = tree; + while (true) { + node = node[readBit()]; + switch (typeof node) { + case "number": + return node; + case "object": + continue; + } + throw new JpegError("invalid huffman sequence"); + } + } + function receive(length) { + let n = 0; + while (length > 0) { + n = n << 1 | readBit(); + length--; + } + return n; + } + function receiveAndExtend(length) { + if (length === 1) { + return readBit() === 1 ? 1 : -1; + } + const n = receive(length); + if (n >= 1 << length - 1) { + return n; + } + return n + (-1 << length) + 1; + } + function decodeBaseline(component, blockOffset) { + const t = decodeHuffman(component.huffmanTableDC); + const diff = t === 0 ? 0 : receiveAndExtend(t); + component.blockData[blockOffset] = component.pred += diff; + let k = 1; + while (k < 64) { + const rs = decodeHuffman(component.huffmanTableAC); + const s = rs & 15, + r = rs >> 4; + if (s === 0) { + if (r < 15) { + break; + } + k += 16; + continue; + } + k += r; + const z = dctZigZag[k]; + component.blockData[blockOffset + z] = receiveAndExtend(s); + k++; + } + } + function decodeDCFirst(component, blockOffset) { + const t = decodeHuffman(component.huffmanTableDC); + const diff = t === 0 ? 0 : receiveAndExtend(t) << successive; + component.blockData[blockOffset] = component.pred += diff; + } + function decodeDCSuccessive(component, blockOffset) { + component.blockData[blockOffset] |= readBit() << successive; + } + let eobrun = 0; + function decodeACFirst(component, blockOffset) { + if (eobrun > 0) { + eobrun--; + return; + } + let k = spectralStart; + const e = spectralEnd; + while (k <= e) { + const rs = decodeHuffman(component.huffmanTableAC); + const s = rs & 15, + r = rs >> 4; + if (s === 0) { + if (r < 15) { + eobrun = receive(r) + (1 << r) - 1; + break; + } + k += 16; + continue; + } + k += r; + const z = dctZigZag[k]; + component.blockData[blockOffset + z] = receiveAndExtend(s) * (1 << successive); + k++; + } + } + let successiveACState = 0, + successiveACNextValue; + function decodeACSuccessive(component, blockOffset) { + let k = spectralStart; + const e = spectralEnd; + let r = 0; + let s; + let rs; + while (k <= e) { + const offsetZ = blockOffset + dctZigZag[k]; + const sign = component.blockData[offsetZ] < 0 ? -1 : 1; + switch (successiveACState) { + case 0: + rs = decodeHuffman(component.huffmanTableAC); + s = rs & 15; + r = rs >> 4; + if (s === 0) { + if (r < 15) { + eobrun = receive(r) + (1 << r); + successiveACState = 4; + } else { + r = 16; + successiveACState = 1; + } + } else { + if (s !== 1) { + throw new JpegError("invalid ACn encoding"); + } + successiveACNextValue = receiveAndExtend(s); + successiveACState = r ? 2 : 3; + } + continue; + case 1: + case 2: + if (component.blockData[offsetZ]) { + component.blockData[offsetZ] += sign * (readBit() << successive); + } else { + r--; + if (r === 0) { + successiveACState = successiveACState === 2 ? 3 : 0; + } + } + break; + case 3: + if (component.blockData[offsetZ]) { + component.blockData[offsetZ] += sign * (readBit() << successive); + } else { + component.blockData[offsetZ] = successiveACNextValue << successive; + successiveACState = 0; + } + break; + case 4: + if (component.blockData[offsetZ]) { + component.blockData[offsetZ] += sign * (readBit() << successive); + } + break; + } + k++; + } + if (successiveACState === 4) { + eobrun--; + if (eobrun === 0) { + successiveACState = 0; + } + } + } + let blockRow = 0; + function decodeMcu(component, decode, mcu, row, col) { + const mcuRow = mcu / mcusPerLine | 0; + const mcuCol = mcu % mcusPerLine; + blockRow = mcuRow * component.v + row; + const blockCol = mcuCol * component.h + col; + const blockOffset = getBlockBufferOffset(component, blockRow, blockCol); + decode(component, blockOffset); + } + function decodeBlock(component, decode, mcu) { + blockRow = mcu / component.blocksPerLine | 0; + const blockCol = mcu % component.blocksPerLine; + const blockOffset = getBlockBufferOffset(component, blockRow, blockCol); + decode(component, blockOffset); + } + const componentsLength = components.length; + let component, i, j, k, n; + let decodeFn; + if (progressive) { + if (spectralStart === 0) { + decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive; + } else { + decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive; + } + } else { + decodeFn = decodeBaseline; + } + let mcu = 0, + fileMarker; + const mcuExpected = componentsLength === 1 ? components[0].blocksPerLine * components[0].blocksPerColumn : mcusPerLine * frame.mcusPerColumn; + let h, v; + while (mcu <= mcuExpected) { + const mcuToRead = resetInterval ? Math.min(mcuExpected - mcu, resetInterval) : mcuExpected; + if (mcuToRead > 0) { + for (i = 0; i < componentsLength; i++) { + components[i].pred = 0; + } + eobrun = 0; + if (componentsLength === 1) { + component = components[0]; + for (n = 0; n < mcuToRead; n++) { + decodeBlock(component, decodeFn, mcu); + mcu++; + } + } else { + for (n = 0; n < mcuToRead; n++) { + for (i = 0; i < componentsLength; i++) { + component = components[i]; + h = component.h; + v = component.v; + for (j = 0; j < v; j++) { + for (k = 0; k < h; k++) { + decodeMcu(component, decodeFn, mcu, j, k); + } + } + } + mcu++; + } + } + } + bitsCount = 0; + fileMarker = findNextFileMarker(data, offset); + if (!fileMarker) { + break; + } + if (fileMarker.invalid) { + const partialMsg = mcuToRead > 0 ? "unexpected" : "excessive"; + warn(`decodeScan - ${partialMsg} MCU data, current marker is: ${fileMarker.invalid}`); + offset = fileMarker.offset; + } + if (fileMarker.marker >= 0xffd0 && fileMarker.marker <= 0xffd7) { + offset += 2; + } else { + break; + } + } + return offset - startOffset; +} +function quantizeAndInverse(component, blockBufferOffset, p) { + const qt = component.quantizationTable, + blockData = component.blockData; + let v0, v1, v2, v3, v4, v5, v6, v7; + let p0, p1, p2, p3, p4, p5, p6, p7; + let t; + if (!qt) { + throw new JpegError("missing required Quantization Table."); + } + for (let row = 0; row < 64; row += 8) { + p0 = blockData[blockBufferOffset + row]; + p1 = blockData[blockBufferOffset + row + 1]; + p2 = blockData[blockBufferOffset + row + 2]; + p3 = blockData[blockBufferOffset + row + 3]; + p4 = blockData[blockBufferOffset + row + 4]; + p5 = blockData[blockBufferOffset + row + 5]; + p6 = blockData[blockBufferOffset + row + 6]; + p7 = blockData[blockBufferOffset + row + 7]; + p0 *= qt[row]; + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) { + t = dctSqrt2 * p0 + 512 >> 10; + p[row] = t; + p[row + 1] = t; + p[row + 2] = t; + p[row + 3] = t; + p[row + 4] = t; + p[row + 5] = t; + p[row + 6] = t; + p[row + 7] = t; + continue; + } + p1 *= qt[row + 1]; + p2 *= qt[row + 2]; + p3 *= qt[row + 3]; + p4 *= qt[row + 4]; + p5 *= qt[row + 5]; + p6 *= qt[row + 6]; + p7 *= qt[row + 7]; + v0 = dctSqrt2 * p0 + 128 >> 8; + v1 = dctSqrt2 * p4 + 128 >> 8; + v2 = p2; + v3 = p6; + v4 = dctSqrt1d2 * (p1 - p7) + 128 >> 8; + v7 = dctSqrt1d2 * (p1 + p7) + 128 >> 8; + v5 = p3 << 4; + v6 = p5 << 4; + v0 = v0 + v1 + 1 >> 1; + v1 = v0 - v1; + t = v2 * dctSin6 + v3 * dctCos6 + 128 >> 8; + v2 = v2 * dctCos6 - v3 * dctSin6 + 128 >> 8; + v3 = t; + v4 = v4 + v6 + 1 >> 1; + v6 = v4 - v6; + v7 = v7 + v5 + 1 >> 1; + v5 = v7 - v5; + v0 = v0 + v3 + 1 >> 1; + v3 = v0 - v3; + v1 = v1 + v2 + 1 >> 1; + v2 = v1 - v2; + t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12; + v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12; + v7 = t; + t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12; + v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12; + v6 = t; + p[row] = v0 + v7; + p[row + 7] = v0 - v7; + p[row + 1] = v1 + v6; + p[row + 6] = v1 - v6; + p[row + 2] = v2 + v5; + p[row + 5] = v2 - v5; + p[row + 3] = v3 + v4; + p[row + 4] = v3 - v4; + } + for (let col = 0; col < 8; ++col) { + p0 = p[col]; + p1 = p[col + 8]; + p2 = p[col + 16]; + p3 = p[col + 24]; + p4 = p[col + 32]; + p5 = p[col + 40]; + p6 = p[col + 48]; + p7 = p[col + 56]; + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) { + t = dctSqrt2 * p0 + 8192 >> 14; + if (t < -2040) { + t = 0; + } else if (t >= 2024) { + t = 255; + } else { + t = t + 2056 >> 4; + } + blockData[blockBufferOffset + col] = t; + blockData[blockBufferOffset + col + 8] = t; + blockData[blockBufferOffset + col + 16] = t; + blockData[blockBufferOffset + col + 24] = t; + blockData[blockBufferOffset + col + 32] = t; + blockData[blockBufferOffset + col + 40] = t; + blockData[blockBufferOffset + col + 48] = t; + blockData[blockBufferOffset + col + 56] = t; + continue; + } + v0 = dctSqrt2 * p0 + 2048 >> 12; + v1 = dctSqrt2 * p4 + 2048 >> 12; + v2 = p2; + v3 = p6; + v4 = dctSqrt1d2 * (p1 - p7) + 2048 >> 12; + v7 = dctSqrt1d2 * (p1 + p7) + 2048 >> 12; + v5 = p3; + v6 = p5; + v0 = (v0 + v1 + 1 >> 1) + 4112; + v1 = v0 - v1; + t = v2 * dctSin6 + v3 * dctCos6 + 2048 >> 12; + v2 = v2 * dctCos6 - v3 * dctSin6 + 2048 >> 12; + v3 = t; + v4 = v4 + v6 + 1 >> 1; + v6 = v4 - v6; + v7 = v7 + v5 + 1 >> 1; + v5 = v7 - v5; + v0 = v0 + v3 + 1 >> 1; + v3 = v0 - v3; + v1 = v1 + v2 + 1 >> 1; + v2 = v1 - v2; + t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12; + v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12; + v7 = t; + t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12; + v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12; + v6 = t; + p0 = v0 + v7; + p7 = v0 - v7; + p1 = v1 + v6; + p6 = v1 - v6; + p2 = v2 + v5; + p5 = v2 - v5; + p3 = v3 + v4; + p4 = v3 - v4; + if (p0 < 16) { + p0 = 0; + } else if (p0 >= 4080) { + p0 = 255; + } else { + p0 >>= 4; + } + if (p1 < 16) { + p1 = 0; + } else if (p1 >= 4080) { + p1 = 255; + } else { + p1 >>= 4; + } + if (p2 < 16) { + p2 = 0; + } else if (p2 >= 4080) { + p2 = 255; + } else { + p2 >>= 4; + } + if (p3 < 16) { + p3 = 0; + } else if (p3 >= 4080) { + p3 = 255; + } else { + p3 >>= 4; + } + if (p4 < 16) { + p4 = 0; + } else if (p4 >= 4080) { + p4 = 255; + } else { + p4 >>= 4; + } + if (p5 < 16) { + p5 = 0; + } else if (p5 >= 4080) { + p5 = 255; + } else { + p5 >>= 4; + } + if (p6 < 16) { + p6 = 0; + } else if (p6 >= 4080) { + p6 = 255; + } else { + p6 >>= 4; + } + if (p7 < 16) { + p7 = 0; + } else if (p7 >= 4080) { + p7 = 255; + } else { + p7 >>= 4; + } + blockData[blockBufferOffset + col] = p0; + blockData[blockBufferOffset + col + 8] = p1; + blockData[blockBufferOffset + col + 16] = p2; + blockData[blockBufferOffset + col + 24] = p3; + blockData[blockBufferOffset + col + 32] = p4; + blockData[blockBufferOffset + col + 40] = p5; + blockData[blockBufferOffset + col + 48] = p6; + blockData[blockBufferOffset + col + 56] = p7; + } +} +function buildComponentData(frame, component) { + const blocksPerLine = component.blocksPerLine; + const blocksPerColumn = component.blocksPerColumn; + const computationBuffer = new Int16Array(64); + for (let blockRow = 0; blockRow < blocksPerColumn; blockRow++) { + for (let blockCol = 0; blockCol < blocksPerLine; blockCol++) { + const offset = getBlockBufferOffset(component, blockRow, blockCol); + quantizeAndInverse(component, offset, computationBuffer); + } + } + return component.blockData; +} +function findNextFileMarker(data, currentPos, startPos = currentPos) { + const maxPos = data.length - 1; + let newPos = startPos < currentPos ? startPos : currentPos; + if (currentPos >= maxPos) { + return null; + } + const currentMarker = readUint16(data, currentPos); + if (currentMarker >= 0xffc0 && currentMarker <= 0xfffe) { + return { + invalid: null, + marker: currentMarker, + offset: currentPos + }; + } + let newMarker = readUint16(data, newPos); + while (!(newMarker >= 0xffc0 && newMarker <= 0xfffe)) { + if (++newPos >= maxPos) { + return null; + } + newMarker = readUint16(data, newPos); + } + return { + invalid: currentMarker.toString(16), + marker: newMarker, + offset: newPos + }; +} +function prepareComponents(frame) { + const mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH); + const mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV); + for (const component of frame.components) { + const blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / frame.maxH); + const blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / frame.maxV); + const blocksPerLineForMcu = mcusPerLine * component.h; + const blocksPerColumnForMcu = mcusPerColumn * component.v; + const blocksBufferSize = 64 * blocksPerColumnForMcu * (blocksPerLineForMcu + 1); + component.blockData = new Int16Array(blocksBufferSize); + component.blocksPerLine = blocksPerLine; + component.blocksPerColumn = blocksPerColumn; + } + frame.mcusPerLine = mcusPerLine; + frame.mcusPerColumn = mcusPerColumn; +} +function readDataBlock(data, offset) { + const length = readUint16(data, offset); + offset += 2; + let endOffset = offset + length - 2; + const fileMarker = findNextFileMarker(data, endOffset, offset); + if (fileMarker?.invalid) { + warn("readDataBlock - incorrect length, current marker is: " + fileMarker.invalid); + endOffset = fileMarker.offset; + } + const array = data.subarray(offset, endOffset); + offset += array.length; + return { + appData: array, + newOffset: offset + }; +} +function skipData(data, offset) { + const length = readUint16(data, offset); + offset += 2; + const endOffset = offset + length - 2; + const fileMarker = findNextFileMarker(data, endOffset, offset); + if (fileMarker?.invalid) { + return fileMarker.offset; + } + return endOffset; +} +class JpegImage { + constructor({ + decodeTransform = null, + colorTransform = -1 + } = {}) { + this._decodeTransform = decodeTransform; + this._colorTransform = colorTransform; + } + static canUseImageDecoder(data, colorTransform = -1) { + let offset = 0; + let numComponents = null; + let fileMarker = readUint16(data, offset); + offset += 2; + if (fileMarker !== 0xffd8) { + throw new JpegError("SOI not found"); + } + fileMarker = readUint16(data, offset); + offset += 2; + markerLoop: while (fileMarker !== 0xffd9) { + switch (fileMarker) { + case 0xffc0: + case 0xffc1: + case 0xffc2: + numComponents = data[offset + (2 + 1 + 2 + 2)]; + break markerLoop; + case 0xffff: + if (data[offset] !== 0xff) { + offset--; + } + break; + } + offset = skipData(data, offset); + fileMarker = readUint16(data, offset); + offset += 2; + } + if (numComponents === 4) { + return false; + } + if (numComponents === 3 && colorTransform === 0) { + return false; + } + return true; + } + parse(data, { + dnlScanLines = null + } = {}) { + let offset = 0; + let jfif = null; + let adobe = null; + let frame, resetInterval; + let numSOSMarkers = 0; + const quantizationTables = []; + const huffmanTablesAC = [], + huffmanTablesDC = []; + let fileMarker = readUint16(data, offset); + offset += 2; + if (fileMarker !== 0xffd8) { + throw new JpegError("SOI not found"); + } + fileMarker = readUint16(data, offset); + offset += 2; + markerLoop: while (fileMarker !== 0xffd9) { + let i, j, l; + switch (fileMarker) { + case 0xffe0: + case 0xffe1: + case 0xffe2: + case 0xffe3: + case 0xffe4: + case 0xffe5: + case 0xffe6: + case 0xffe7: + case 0xffe8: + case 0xffe9: + case 0xffea: + case 0xffeb: + case 0xffec: + case 0xffed: + case 0xffee: + case 0xffef: + case 0xfffe: + const { + appData, + newOffset + } = readDataBlock(data, offset); + offset = newOffset; + if (fileMarker === 0xffe0) { + if (appData[0] === 0x4a && appData[1] === 0x46 && appData[2] === 0x49 && appData[3] === 0x46 && appData[4] === 0) { + jfif = { + version: { + major: appData[5], + minor: appData[6] + }, + densityUnits: appData[7], + xDensity: appData[8] << 8 | appData[9], + yDensity: appData[10] << 8 | appData[11], + thumbWidth: appData[12], + thumbHeight: appData[13], + thumbData: appData.subarray(14, 14 + 3 * appData[12] * appData[13]) + }; + } + } + if (fileMarker === 0xffee) { + if (appData[0] === 0x41 && appData[1] === 0x64 && appData[2] === 0x6f && appData[3] === 0x62 && appData[4] === 0x65) { + adobe = { + version: appData[5] << 8 | appData[6], + flags0: appData[7] << 8 | appData[8], + flags1: appData[9] << 8 | appData[10], + transformCode: appData[11] + }; + } + } + break; + case 0xffdb: + const quantizationTablesLength = readUint16(data, offset); + offset += 2; + const quantizationTablesEnd = quantizationTablesLength + offset - 2; + let z; + while (offset < quantizationTablesEnd) { + const quantizationTableSpec = data[offset++]; + const tableData = new Uint16Array(64); + if (quantizationTableSpec >> 4 === 0) { + for (j = 0; j < 64; j++) { + z = dctZigZag[j]; + tableData[z] = data[offset++]; + } + } else if (quantizationTableSpec >> 4 === 1) { + for (j = 0; j < 64; j++) { + z = dctZigZag[j]; + tableData[z] = readUint16(data, offset); + offset += 2; + } + } else { + throw new JpegError("DQT - invalid table spec"); + } + quantizationTables[quantizationTableSpec & 15] = tableData; + } + break; + case 0xffc0: + case 0xffc1: + case 0xffc2: + if (frame) { + throw new JpegError("Only single frame JPEGs supported"); + } + offset += 2; + frame = {}; + frame.extended = fileMarker === 0xffc1; + frame.progressive = fileMarker === 0xffc2; + frame.precision = data[offset++]; + const sofScanLines = readUint16(data, offset); + offset += 2; + frame.scanLines = dnlScanLines || sofScanLines; + frame.samplesPerLine = readUint16(data, offset); + offset += 2; + frame.components = []; + frame.componentIds = {}; + const componentsCount = data[offset++]; + let maxH = 0, + maxV = 0; + for (i = 0; i < componentsCount; i++) { + const componentId = data[offset]; + const h = data[offset + 1] >> 4; + const v = data[offset + 1] & 15; + if (maxH < h) { + maxH = h; + } + if (maxV < v) { + maxV = v; + } + const qId = data[offset + 2]; + l = frame.components.push({ + h, + v, + quantizationId: qId, + quantizationTable: null + }); + frame.componentIds[componentId] = l - 1; + offset += 3; + } + frame.maxH = maxH; + frame.maxV = maxV; + prepareComponents(frame); + break; + case 0xffc4: + const huffmanLength = readUint16(data, offset); + offset += 2; + for (i = 2; i < huffmanLength;) { + const huffmanTableSpec = data[offset++]; + const codeLengths = new Uint8Array(16); + let codeLengthSum = 0; + for (j = 0; j < 16; j++, offset++) { + codeLengthSum += codeLengths[j] = data[offset]; + } + const huffmanValues = new Uint8Array(codeLengthSum); + for (j = 0; j < codeLengthSum; j++, offset++) { + huffmanValues[j] = data[offset]; + } + i += 17 + codeLengthSum; + (huffmanTableSpec >> 4 === 0 ? huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = buildHuffmanTable(codeLengths, huffmanValues); + } + break; + case 0xffdd: + offset += 2; + resetInterval = readUint16(data, offset); + offset += 2; + break; + case 0xffda: + const parseDNLMarker = ++numSOSMarkers === 1 && !dnlScanLines; + offset += 2; + const selectorsCount = data[offset++], + components = []; + for (i = 0; i < selectorsCount; i++) { + const index = data[offset++]; + const componentIndex = frame.componentIds[index]; + const component = frame.components[componentIndex]; + component.index = index; + const tableSpec = data[offset++]; + component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4]; + component.huffmanTableAC = huffmanTablesAC[tableSpec & 15]; + components.push(component); + } + const spectralStart = data[offset++], + spectralEnd = data[offset++], + successiveApproximation = data[offset++]; + try { + const processed = decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successiveApproximation >> 4, successiveApproximation & 15, parseDNLMarker); + offset += processed; + } catch (ex) { + if (ex instanceof DNLMarkerError) { + warn(`${ex.message} -- attempting to re-parse the JPEG image.`); + return this.parse(data, { + dnlScanLines: ex.scanLines + }); + } else if (ex instanceof EOIMarkerError) { + warn(`${ex.message} -- ignoring the rest of the image data.`); + break markerLoop; + } + throw ex; + } + break; + case 0xffdc: + offset += 4; + break; + case 0xffff: + if (data[offset] !== 0xff) { + offset--; + } + break; + default: + const nextFileMarker = findNextFileMarker(data, offset - 2, offset - 3); + if (nextFileMarker?.invalid) { + warn("JpegImage.parse - unexpected data, current marker is: " + nextFileMarker.invalid); + offset = nextFileMarker.offset; + break; + } + if (!nextFileMarker || offset >= data.length - 1) { + warn("JpegImage.parse - reached the end of the image data " + "without finding an EOI marker (0xFFD9)."); + break markerLoop; + } + throw new JpegError("JpegImage.parse - unknown marker: " + fileMarker.toString(16)); + } + fileMarker = readUint16(data, offset); + offset += 2; + } + if (!frame) { + throw new JpegError("JpegImage.parse - no frame data found."); + } + this.width = frame.samplesPerLine; + this.height = frame.scanLines; + this.jfif = jfif; + this.adobe = adobe; + this.components = []; + for (const component of frame.components) { + const quantizationTable = quantizationTables[component.quantizationId]; + if (quantizationTable) { + component.quantizationTable = quantizationTable; + } + this.components.push({ + index: component.index, + output: buildComponentData(frame, component), + scaleX: component.h / frame.maxH, + scaleY: component.v / frame.maxV, + blocksPerLine: component.blocksPerLine, + blocksPerColumn: component.blocksPerColumn + }); + } + this.numComponents = this.components.length; + return undefined; + } + _getLinearizedBlockData(width, height, isSourcePDF = false) { + const scaleX = this.width / width, + scaleY = this.height / height; + let component, componentScaleX, componentScaleY, blocksPerScanline; + let x, y, i, j, k; + let index; + let offset = 0; + let output; + const numComponents = this.components.length; + const dataLength = width * height * numComponents; + const data = new Uint8ClampedArray(dataLength); + const xScaleBlockOffset = new Uint32Array(width); + const mask3LSB = 0xfffffff8; + let lastComponentScaleX; + for (i = 0; i < numComponents; i++) { + component = this.components[i]; + componentScaleX = component.scaleX * scaleX; + componentScaleY = component.scaleY * scaleY; + offset = i; + output = component.output; + blocksPerScanline = component.blocksPerLine + 1 << 3; + if (componentScaleX !== lastComponentScaleX) { + for (x = 0; x < width; x++) { + j = 0 | x * componentScaleX; + xScaleBlockOffset[x] = (j & mask3LSB) << 3 | j & 7; + } + lastComponentScaleX = componentScaleX; + } + for (y = 0; y < height; y++) { + j = 0 | y * componentScaleY; + index = blocksPerScanline * (j & mask3LSB) | (j & 7) << 3; + for (x = 0; x < width; x++) { + data[offset] = output[index + xScaleBlockOffset[x]]; + offset += numComponents; + } + } + } + let transform = this._decodeTransform; + if (!isSourcePDF && numComponents === 4 && !transform) { + transform = new Int32Array([-256, 255, -256, 255, -256, 255, -256, 255]); + } + if (transform) { + for (i = 0; i < dataLength;) { + for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) { + data[i] = (data[i] * transform[k] >> 8) + transform[k + 1]; + } + } + } + return data; + } + get _isColorConversionNeeded() { + if (this.adobe) { + return !!this.adobe.transformCode; + } + if (this.numComponents === 3) { + if (this._colorTransform === 0) { + return false; + } else if (this.components[0].index === 0x52 && this.components[1].index === 0x47 && this.components[2].index === 0x42) { + return false; + } + return true; + } + if (this._colorTransform === 1) { + return true; + } + return false; + } + _convertYccToRgb(data) { + let Y, Cb, Cr; + for (let i = 0, length = data.length; i < length; i += 3) { + Y = data[i]; + Cb = data[i + 1]; + Cr = data[i + 2]; + data[i] = Y - 179.456 + 1.402 * Cr; + data[i + 1] = Y + 135.459 - 0.344 * Cb - 0.714 * Cr; + data[i + 2] = Y - 226.816 + 1.772 * Cb; + } + return data; + } + _convertYccToRgba(data, out) { + for (let i = 0, j = 0, length = data.length; i < length; i += 3, j += 4) { + const Y = data[i]; + const Cb = data[i + 1]; + const Cr = data[i + 2]; + out[j] = Y - 179.456 + 1.402 * Cr; + out[j + 1] = Y + 135.459 - 0.344 * Cb - 0.714 * Cr; + out[j + 2] = Y - 226.816 + 1.772 * Cb; + out[j + 3] = 255; + } + return out; + } + _convertYcckToRgb(data) { + let Y, Cb, Cr, k; + let offset = 0; + for (let i = 0, length = data.length; i < length; i += 4) { + Y = data[i]; + Cb = data[i + 1]; + Cr = data[i + 2]; + k = data[i + 3]; + data[offset++] = -122.67195406894 + Cb * (-6.60635669420364e-5 * Cb + 0.000437130475926232 * Cr - 5.4080610064599e-5 * Y + 0.00048449797120281 * k - 0.154362151871126) + Cr * (-0.000957964378445773 * Cr + 0.000817076911346625 * Y - 0.00477271405408747 * k + 1.53380253221734) + Y * (0.000961250184130688 * Y - 0.00266257332283933 * k + 0.48357088451265) + k * (-0.000336197177618394 * k + 0.484791561490776); + data[offset++] = 107.268039397724 + Cb * (2.19927104525741e-5 * Cb - 0.000640992018297945 * Cr + 0.000659397001245577 * Y + 0.000426105652938837 * k - 0.176491792462875) + Cr * (-0.000778269941513683 * Cr + 0.00130872261408275 * Y + 0.000770482631801132 * k - 0.151051492775562) + Y * (0.00126935368114843 * Y - 0.00265090189010898 * k + 0.25802910206845) + k * (-0.000318913117588328 * k - 0.213742400323665); + data[offset++] = -20.810012546947 + Cb * (-0.000570115196973677 * Cb - 2.63409051004589e-5 * Cr + 0.0020741088115012 * Y - 0.00288260236853442 * k + 0.814272968359295) + Cr * (-1.53496057440975e-5 * Cr - 0.000132689043961446 * Y + 0.000560833691242812 * k - 0.195152027534049) + Y * (0.00174418132927582 * Y - 0.00255243321439347 * k + 0.116935020465145) + k * (-0.000343531996510555 * k + 0.24165260232407); + } + return data.subarray(0, offset); + } + _convertYcckToRgba(data) { + for (let i = 0, length = data.length; i < length; i += 4) { + const Y = data[i]; + const Cb = data[i + 1]; + const Cr = data[i + 2]; + const k = data[i + 3]; + data[i] = -122.67195406894 + Cb * (-6.60635669420364e-5 * Cb + 0.000437130475926232 * Cr - 5.4080610064599e-5 * Y + 0.00048449797120281 * k - 0.154362151871126) + Cr * (-0.000957964378445773 * Cr + 0.000817076911346625 * Y - 0.00477271405408747 * k + 1.53380253221734) + Y * (0.000961250184130688 * Y - 0.00266257332283933 * k + 0.48357088451265) + k * (-0.000336197177618394 * k + 0.484791561490776); + data[i + 1] = 107.268039397724 + Cb * (2.19927104525741e-5 * Cb - 0.000640992018297945 * Cr + 0.000659397001245577 * Y + 0.000426105652938837 * k - 0.176491792462875) + Cr * (-0.000778269941513683 * Cr + 0.00130872261408275 * Y + 0.000770482631801132 * k - 0.151051492775562) + Y * (0.00126935368114843 * Y - 0.00265090189010898 * k + 0.25802910206845) + k * (-0.000318913117588328 * k - 0.213742400323665); + data[i + 2] = -20.810012546947 + Cb * (-0.000570115196973677 * Cb - 2.63409051004589e-5 * Cr + 0.0020741088115012 * Y - 0.00288260236853442 * k + 0.814272968359295) + Cr * (-1.53496057440975e-5 * Cr - 0.000132689043961446 * Y + 0.000560833691242812 * k - 0.195152027534049) + Y * (0.00174418132927582 * Y - 0.00255243321439347 * k + 0.116935020465145) + k * (-0.000343531996510555 * k + 0.24165260232407); + data[i + 3] = 255; + } + return data; + } + _convertYcckToCmyk(data) { + let Y, Cb, Cr; + for (let i = 0, length = data.length; i < length; i += 4) { + Y = data[i]; + Cb = data[i + 1]; + Cr = data[i + 2]; + data[i] = 434.456 - Y - 1.402 * Cr; + data[i + 1] = 119.541 - Y + 0.344 * Cb + 0.714 * Cr; + data[i + 2] = 481.816 - Y - 1.772 * Cb; + } + return data; + } + _convertCmykToRgb(data) { + let c, m, y, k; + let offset = 0; + for (let i = 0, length = data.length; i < length; i += 4) { + c = data[i]; + m = data[i + 1]; + y = data[i + 2]; + k = data[i + 3]; + data[offset++] = 255 + c * (-0.00006747147073602441 * c + 0.0008379262121013727 * m + 0.0002894718188643294 * y + 0.003264231057537806 * k - 1.1185611867203937) + m * (0.000026374107616089405 * m - 0.00008626949158638572 * y - 0.0002748769067499491 * k - 0.02155688794978967) + y * (-0.00003878099212869363 * y - 0.0003267808279485286 * k + 0.0686742238595345) - k * (0.0003361971776183937 * k + 0.7430659151342254); + data[offset++] = 255 + c * (0.00013596372813588848 * c + 0.000924537132573585 * m + 0.00010567359618683593 * y + 0.0004791864687436512 * k - 0.3109689587515875) + m * (-0.00023545346108370344 * m + 0.0002702845253534714 * y + 0.0020200308977307156 * k - 0.7488052167015494) + y * (0.00006834815998235662 * y + 0.00015168452363460973 * k - 0.09751927774728933) - k * (0.0003189131175883281 * k + 0.7364883807733168); + data[offset++] = 255 + c * (0.000013598650411385307 * c + 0.00012423956175490851 * m + 0.0004751985097583589 * y - 0.0000036729317476630422 * k - 0.05562186980264034) + m * (0.00016141380598724676 * m + 0.0009692239130725186 * y + 0.0007782692450036253 * k - 0.44015232367526463) + y * (5.068882914068769e-7 * y + 0.0017778369011375071 * k - 0.7591454649749609) - k * (0.0003435319965105553 * k + 0.7063770186160144); + } + return data.subarray(0, offset); + } + _convertCmykToRgba(data) { + for (let i = 0, length = data.length; i < length; i += 4) { + const c = data[i]; + const m = data[i + 1]; + const y = data[i + 2]; + const k = data[i + 3]; + data[i] = 255 + c * (-0.00006747147073602441 * c + 0.0008379262121013727 * m + 0.0002894718188643294 * y + 0.003264231057537806 * k - 1.1185611867203937) + m * (0.000026374107616089405 * m - 0.00008626949158638572 * y - 0.0002748769067499491 * k - 0.02155688794978967) + y * (-0.00003878099212869363 * y - 0.0003267808279485286 * k + 0.0686742238595345) - k * (0.0003361971776183937 * k + 0.7430659151342254); + data[i + 1] = 255 + c * (0.00013596372813588848 * c + 0.000924537132573585 * m + 0.00010567359618683593 * y + 0.0004791864687436512 * k - 0.3109689587515875) + m * (-0.00023545346108370344 * m + 0.0002702845253534714 * y + 0.0020200308977307156 * k - 0.7488052167015494) + y * (0.00006834815998235662 * y + 0.00015168452363460973 * k - 0.09751927774728933) - k * (0.0003189131175883281 * k + 0.7364883807733168); + data[i + 2] = 255 + c * (0.000013598650411385307 * c + 0.00012423956175490851 * m + 0.0004751985097583589 * y - 0.0000036729317476630422 * k - 0.05562186980264034) + m * (0.00016141380598724676 * m + 0.0009692239130725186 * y + 0.0007782692450036253 * k - 0.44015232367526463) + y * (5.068882914068769e-7 * y + 0.0017778369011375071 * k - 0.7591454649749609) - k * (0.0003435319965105553 * k + 0.7063770186160144); + data[i + 3] = 255; + } + return data; + } + getData({ + width, + height, + forceRGBA = false, + forceRGB = false, + isSourcePDF = false + }) { + if (this.numComponents > 4) { + throw new JpegError("Unsupported color mode"); + } + const data = this._getLinearizedBlockData(width, height, isSourcePDF); + if (this.numComponents === 1 && (forceRGBA || forceRGB)) { + const len = data.length * (forceRGBA ? 4 : 3); + const rgbaData = new Uint8ClampedArray(len); + let offset = 0; + if (forceRGBA) { + grayToRGBA(data, new Uint32Array(rgbaData.buffer)); + } else { + for (const grayColor of data) { + rgbaData[offset++] = grayColor; + rgbaData[offset++] = grayColor; + rgbaData[offset++] = grayColor; + } + } + return rgbaData; + } else if (this.numComponents === 3 && this._isColorConversionNeeded) { + if (forceRGBA) { + const rgbaData = new Uint8ClampedArray(data.length / 3 * 4); + return this._convertYccToRgba(data, rgbaData); + } + return this._convertYccToRgb(data); + } else if (this.numComponents === 4) { + if (this._isColorConversionNeeded) { + if (forceRGBA) { + return this._convertYcckToRgba(data); + } + if (forceRGB) { + return this._convertYcckToRgb(data); + } + return this._convertYcckToCmyk(data); + } else if (forceRGBA) { + return this._convertCmykToRgba(data); + } else if (forceRGB) { + return this._convertCmykToRgb(data); + } + } + return data; + } +} + +;// ./src/core/jpeg_stream.js + + + + +class JpegStream extends DecodeStream { + static #isImageDecoderSupported = FeatureTest.isImageDecoderSupported; + constructor(stream, maybeLength, params) { + super(maybeLength); + this.stream = stream; + this.dict = stream.dict; + this.maybeLength = maybeLength; + this.params = params; + } + static get canUseImageDecoder() { + return shadow(this, "canUseImageDecoder", this.#isImageDecoderSupported ? ImageDecoder.isTypeSupported("image/jpeg") : Promise.resolve(false)); + } + static setOptions({ + isImageDecoderSupported = false + }) { + this.#isImageDecoderSupported = isImageDecoderSupported; + } + get bytes() { + return shadow(this, "bytes", this.stream.getBytes(this.maybeLength)); + } + ensureBuffer(requested) {} + readBlock() { + this.decodeImage(); + } + get jpegOptions() { + const jpegOptions = { + decodeTransform: undefined, + colorTransform: undefined + }; + const decodeArr = this.dict.getArray("D", "Decode"); + if ((this.forceRGBA || this.forceRGB) && Array.isArray(decodeArr)) { + const bitsPerComponent = this.dict.get("BPC", "BitsPerComponent") || 8; + const decodeArrLength = decodeArr.length; + const transform = new Int32Array(decodeArrLength); + let transformNeeded = false; + const maxValue = (1 << bitsPerComponent) - 1; + for (let i = 0; i < decodeArrLength; i += 2) { + transform[i] = (decodeArr[i + 1] - decodeArr[i]) * 256 | 0; + transform[i + 1] = decodeArr[i] * maxValue | 0; + if (transform[i] !== 256 || transform[i + 1] !== 0) { + transformNeeded = true; + } + } + if (transformNeeded) { + jpegOptions.decodeTransform = transform; + } + } + if (this.params instanceof Dict) { + const colorTransform = this.params.get("ColorTransform"); + if (Number.isInteger(colorTransform)) { + jpegOptions.colorTransform = colorTransform; + } + } + return shadow(this, "jpegOptions", jpegOptions); + } + #skipUselessBytes(data) { + for (let i = 0, ii = data.length - 1; i < ii; i++) { + if (data[i] === 0xff && data[i + 1] === 0xd8) { + if (i > 0) { + data = data.subarray(i); + } + break; + } + } + return data; + } + decodeImage(bytes) { + if (this.eof) { + return this.buffer; + } + bytes = this.#skipUselessBytes(bytes || this.bytes); + const jpegImage = new JpegImage(this.jpegOptions); + jpegImage.parse(bytes); + const data = jpegImage.getData({ + width: this.drawWidth, + height: this.drawHeight, + forceRGBA: this.forceRGBA, + forceRGB: this.forceRGB, + isSourcePDF: true + }); + this.buffer = data; + this.bufferLength = data.length; + this.eof = true; + return this.buffer; + } + get canAsyncDecodeImageFromBuffer() { + return this.stream.isAsync; + } + async getTransferableImage() { + if (!(await JpegStream.canUseImageDecoder)) { + return null; + } + const jpegOptions = this.jpegOptions; + if (jpegOptions.decodeTransform) { + return null; + } + let decoder; + try { + const bytes = this.canAsyncDecodeImageFromBuffer && (await this.stream.asyncGetBytes()) || this.bytes; + if (!bytes) { + return null; + } + const data = this.#skipUselessBytes(bytes); + if (!JpegImage.canUseImageDecoder(data, jpegOptions.colorTransform)) { + return null; + } + decoder = new ImageDecoder({ + data, + type: "image/jpeg", + preferAnimation: false + }); + return (await decoder.decode()).image; + } catch (reason) { + warn(`getTransferableImage - failed: "${reason}".`); + return null; + } finally { + decoder?.close(); + } + } +} + +;// ./external/openjpeg/openjpeg.js +var OpenJPEG = (() => { + var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; + return function (moduleArg = {}) { + var moduleRtn; + var Module = moduleArg; + var readyPromiseResolve, readyPromiseReject; + var readyPromise = new Promise((resolve, reject) => { + readyPromiseResolve = resolve; + readyPromiseReject = reject; + }); + var ENVIRONMENT_IS_WEB = true; + var ENVIRONMENT_IS_WORKER = false; + Module.decode = function (bytes, { + numComponents = 4, + isIndexedColormap = false, + smaskInData = false + }) { + const size = bytes.length; + const ptr = Module._malloc(size); + Module.HEAPU8.set(bytes, ptr); + const ret = Module._jp2_decode(ptr, size, numComponents > 0 ? numComponents : 0, !!isIndexedColormap, !!smaskInData); + Module._free(ptr); + if (ret) { + const { + errorMessages + } = Module; + if (errorMessages) { + delete Module.errorMessages; + return errorMessages; + } + return "Unknown error"; + } + const { + imageData + } = Module; + Module.imageData = null; + return imageData; + }; + var moduleOverrides = Object.assign({}, Module); + var arguments_ = []; + var thisProgram = "./this.program"; + var quit_ = (status, toThrow) => { + throw toThrow; + }; + var scriptDirectory = ""; + var readAsync, readBinary; + if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { + if (ENVIRONMENT_IS_WORKER) { + scriptDirectory = self.location.href; + } else if (typeof document != "undefined" && document.currentScript) { + scriptDirectory = document.currentScript.src; + } + if (_scriptName) { + scriptDirectory = _scriptName; + } + if (scriptDirectory.startsWith("blob:")) { + scriptDirectory = ""; + } else { + scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf("/") + 1); + } + readAsync = url => fetch(url, { + credentials: "same-origin" + }).then(response => { + if (response.ok) { + return response.arrayBuffer(); + } + return Promise.reject(new Error(response.status + " : " + response.url)); + }); + } else {} + var out = Module["print"] || console.log.bind(console); + var err = Module["printErr"] || console.error.bind(console); + Object.assign(Module, moduleOverrides); + moduleOverrides = null; + if (Module["arguments"]) arguments_ = Module["arguments"]; + if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; + var wasmBinary = Module["wasmBinary"]; + function intArrayFromBase64(s) { + var decoded = atob(s); + var bytes = new Uint8Array(decoded.length); + for (var i = 0; i < decoded.length; ++i) { + bytes[i] = decoded.charCodeAt(i); + } + return bytes; + } + function tryParseAsDataURI(filename) { + if (!isDataURI(filename)) { + return; + } + return intArrayFromBase64(filename.slice(dataURIPrefix.length)); + } + var wasmMemory; + var ABORT = false; + var EXITSTATUS; + var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64; + function updateMemoryViews() { + var b = wasmMemory.buffer; + Module["HEAP8"] = HEAP8 = new Int8Array(b); + Module["HEAP16"] = HEAP16 = new Int16Array(b); + Module["HEAPU8"] = HEAPU8 = new Uint8Array(b); + Module["HEAPU16"] = HEAPU16 = new Uint16Array(b); + Module["HEAP32"] = HEAP32 = new Int32Array(b); + Module["HEAPU32"] = HEAPU32 = new Uint32Array(b); + Module["HEAPF32"] = HEAPF32 = new Float32Array(b); + Module["HEAPF64"] = HEAPF64 = new Float64Array(b); + } + var __ATPRERUN__ = []; + var __ATINIT__ = []; + var __ATPOSTRUN__ = []; + var runtimeInitialized = false; + function preRun() { + if (Module["preRun"]) { + if (typeof Module["preRun"] == "function") Module["preRun"] = [Module["preRun"]]; + while (Module["preRun"].length) { + addOnPreRun(Module["preRun"].shift()); + } + } + callRuntimeCallbacks(__ATPRERUN__); + } + function initRuntime() { + runtimeInitialized = true; + callRuntimeCallbacks(__ATINIT__); + } + function postRun() { + if (Module["postRun"]) { + if (typeof Module["postRun"] == "function") Module["postRun"] = [Module["postRun"]]; + while (Module["postRun"].length) { + addOnPostRun(Module["postRun"].shift()); + } + } + callRuntimeCallbacks(__ATPOSTRUN__); + } + function addOnPreRun(cb) { + __ATPRERUN__.unshift(cb); + } + function addOnInit(cb) { + __ATINIT__.unshift(cb); + } + function addOnPostRun(cb) { + __ATPOSTRUN__.unshift(cb); + } + var runDependencies = 0; + var runDependencyWatcher = null; + var dependenciesFulfilled = null; + function addRunDependency(id) { + runDependencies++; + Module["monitorRunDependencies"]?.(runDependencies); + } + function removeRunDependency(id) { + runDependencies--; + Module["monitorRunDependencies"]?.(runDependencies); + if (runDependencies == 0) { + if (runDependencyWatcher !== null) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + } + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); + } + } + } + function abort(what) { + Module["onAbort"]?.(what); + what = "Aborted(" + what + ")"; + err(what); + ABORT = true; + what += ". Build with -sASSERTIONS for more info."; + var e = new WebAssembly.RuntimeError(what); + readyPromiseReject(e); + throw e; + } + var dataURIPrefix = "data:application/octet-stream;base64,"; + var isDataURI = filename => filename.startsWith(dataURIPrefix); + function findWasmBinary() { + var f = "data:application/octet-stream;base64,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A3FFBEACQCAAQQNxRQRAIAAhAgwBCyACRQRAIAAhAgwBCyAAIQIDQCACIAEtAAA6AAAgAUEBaiEBIAJBAWoiAkEDcUUNASACIANJDQALCyADQXxxIQQCQCADQcAASQ0AIAIgBEFAaiIFSw0AA0AgAiABKAIANgIAIAIgASgCBDYCBCACIAEoAgg2AgggAiABKAIMNgIMIAIgASgCEDYCECACIAEoAhQ2AhQgAiABKAIYNgIYIAIgASgCHDYCHCACIAEoAiA2AiAgAiABKAIkNgIkIAIgASgCKDYCKCACIAEoAiw2AiwgAiABKAIwNgIwIAIgASgCNDYCNCACIAEoAjg2AjggAiABKAI8NgI8IAFBQGshASACQUBrIgIgBU0NAAsLIAIgBE8NAQNAIAIgASgCADYCACABQQRqIQEgAkEEaiICIARJDQALDAELIANBBEkEQCAAIQIMAQsgA0EEayIEIABJBEAgACECDAELIAAhAgNAIAIgAS0AADoAACACIAEtAAE6AAEgAiABLQACOgACIAIgAS0AAzoAAyABQQRqIQEgAkEEaiICIARNDQALCyACIANJBEADQCACIAEtAAA6AAAgAUEBaiEBIAJBAWoiAiADRw0ACwsgAAswAQF/AkAgAEUNACABRQ0AQQggACABbCIBECkiAARAIABBACABEBkaCyAAIQILIAILEQAgAEUEQEEADwtBCCAAECkL8gICAn8BfgJAIAJFDQAgACABOgAAIAAgAmoiA0EBayABOgAAIAJBA0kNACAAIAE6AAIgACABOgABIANBA2sgAToAACADQQJrIAE6AAAgAkEHSQ0AIAAgAToAAyADQQRrIAE6AAAgAkEJSQ0AIABBACAAa0EDcSIEaiIDIAFB/wFxQYGChAhsIgE2AgAgAyACIARrQXxxIgRqIgJBBGsgATYCACAEQQlJDQAgAyABNgIIIAMgATYCBCACQQhrIAE2AgAgAkEMayABNgIAIARBGUkNACADIAE2AhggAyABNgIUIAMgATYCECADIAE2AgwgAkEQayABNgIAIAJBFGsgATYCACACQRhrIAE2AgAgAkEcayABNgIAIAQgA0EEcUEYciIEayICQSBJDQAgAa1CgYCAgBB+IQUgAyAEaiEBA0AgASAFNwMYIAEgBTcDECABIAU3AwggASAFNwMAIAFBIGohASAC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CyABQf8PTQRAIAAgAUE/cUGAAXI6AAEgACABQQZ2QcABcjoAAEECDwsgAUGAQHFBgMADRyABQYCwA09xRQRAIAAgAUE/cUGAAXI6AAIgACABQQx2QeABcjoAACAAIAFBBnZBP3FBgAFyOgABQQMPCyABQYCABGtB//8/TQRAIAAgAUE/cUGAAXI6AAMgACABQRJ2QfABcjoAACAAIAFBBnZBP3FBgAFyOgACIAAgAUEMdkE/cUGAAXI6AAFBBA8LC0HUzQFBGTYCAEF/DwsgACABOgAAQQELfgIBfwF+IAC9IgNCNIinQf8PcSICQf8PRwR8IAJFBEAgASAARAAAAAAAAAAAYQR/QQAFIABEAAAAAAAA8EOiIAEQcCEAIAEoAgBBQGoLNgIAIAAPCyABIAJB/gdrNgIAIANC/////////4eAf4NCgICAgICAgPA/hL8FIAALC7wCAAJAAkACQAJAAkACQAJAAkACQAJAAkAgAUEJaw4SAAgJCggJAQIDBAoJCgoICQUGBwsgAiACKAIAIgFBBGo2AgAgACABKAIANgIADwsgAiACKAIAIgFBBGo2AgAgACABMgEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMwEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMAAANwMADwsgAiACKAIAIgFBBGo2AgAgACABMQAANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKwMAOQMADwsgACACIAMRAwALDwsgAiACKAIAIgFBBGo2AgAgACABNAIANwMADwsgAiACKAIAIgFBBGo2AgAgACABNQIANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKQMANwMAC28BBX8gACgCACIDLAAAQTBrIgFBCUsEQEEADwsDQEF/IQQgAkHMmbPmAE0EQEF/IAEgAkEKbCIFaiABIAVB/////wdzSxshBAsgACADQQFqIgU2AgAgAywAASAEIQIgBSEDQTBrIgFBCkkNAAsgAgtJAQF/AkBBAUEsEBciAQRAIAFBADYCEAJAIABBAEwEQCABQQFBCBAXIgA2AiQgAEUNAQwDCyABQQA2AgwLIAEQFAtBACEBCyABC64UAhJ/An4jAEFAaiIIJAAgCCABNgI8IAhBJ2ohFyAIQShqIRICQAJAAkACQANAQQAhBwNA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KAIcIgdrIgYgBCAGSRsiBgRAIAUgByAGEBYaIAMgAygCACAGaiIFNgIAIAMgAygCBCAGayIENgIECyAEIAIgAiAESxsiBARAIAUgASAEEBYaIAMgAygCACAEaiIFNgIAIAMgAygCBCAEazYCBAsgBUEAOgAAIAAgACgCLCIBNgIcIAAgATYCFCACC6YFAgZ+BH8gASABKAIAQQdqQXhxIgFBEGo2AgAgACABKQMAIQIgASkDCCEHIwBBIGsiCCQAIAdC////////P4MhBAJ+IAdCMIhC//8BgyIDpyIKQYH4AGtB/Q9NBEAgBEIEhiACQjyIhCEDIApBgPgAa60hBAJAIAJC//////////8PgyICQoGAgICAgICACFoEQCADQgF8IQMMAQsgAkKAgICAgICAgAhSDQAgA0IBgyADfCEDC0IAIAMgA0L/////////B1YiABshAiAArSAEfAwBCwJAIAIgBIRQDQAgA0L//wFSDQAgBEIEhiACQjyIhEKAgICAgICABIQhAkL/DwwBCyAKQf6HAUsEQEIAIQJC/w8MAQtBgPgAQYH4ACADUCIBGyIAIAprIglB8ABKBEBCACECQgAMAQsgAiEDIAQgBEKAgICAgIDAAIQgARsiBSEGAkBBgAEgCWsiAUHAAHEEQCACIAFBQGqthiEGQgAhAwwBCyABRQ0AIAYgAa0iBIYgA0HAACABa62IhCEGIAMgBIYhAwsgCCADNwMQIAggBjcDGAJAIAlBwABxBEAgBSAJQUBqrYghAkIAIQUMAQsgCUUNACAFQcAAIAlrrYYgAiAJrSIDiIQhAiAFIAOIIQULIAggAjcDACAIIAU3AwggCCkDCEIEhiAIKQMAIgNCPIiEIQICQCAAIApHIAgpAxAgCCkDGIRCAFJxrSADQv//////////D4OEIgNCgYCAgICAgIAIWgRAIAJCAXwhAgwBCyADQoCAgICAgICACFINACACQgGDIAJ8IQILIAJCgICAgICAgAiFIAIgAkL/////////B1YiABshAiAArQshAyAIQSBqJAAgB0KAgICAgICAgIB/gyADQjSGhCAChL85AwAL9BcDEn8BfAN+IwBBsARrIgwkACAMQQA2AiwCQCABvSIZQgBTBEBBASEQQboIIRQgAZoiAb0hGQwBCyAEQYAQcQRAQQEhEEG9CCEUDAELQcAI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ACgCRCICQQN0aiIFIAQoAhQ7AQAgBSAEKAIQNgIEQQEhBSAAIAJBAWo2AkQgASAJaiECIAZBAWoiBiAKRw0ACwwCC0EBIAYgBkEBTRshByAAKAJEIQFBACEGA0AgBCABNgIUIAEgACgChAEgACgCgAFsTw0BIAIgBEEQaiAJEBUgCCAAKAJEIgpBA3RqIgUgATsBACAFIAQoAhA2AgRBASEFIAAgCkEBaiIBNgJEIAIgCWohAiAGQQFqIgYgB0cNAAsMAQsgAEEBNgJMIAQgATYCACADQQJB0jwgBBATCyAEQSBqJAAgBQsEAEEACwvLwQEhAEGACAvgmQFjYW5ub3QgYWxsb2NhdGUgb3BqX3RjZF9zZWdfZGF0YV9jaHVua190KiBhcnJheQAtKyAgIDBYMHgALTBYKzBYIDBYLTB4KzB4IDB4AFVua25vd24gZm9ybWF0AEZhaWxlZCB0byBzZXR1cCB0aGUgZGVjb2RlcgBGYWlsZWQgdG8gcmVhZCB0aGUgaGVhZGVyAG5hbgAqbF90aWxlX2xlbiA+IFVJTlRfTUFYIC0gT1BKX0NPTU1PTl9DQkxLX0RBVEFfRVhUUkEgLSBwX2oyay0+bV9zcGVjaWZpY19wYXJhbS5tX2RlY29kZXIubV9zb3RfbGVuZ3RoAGluZgBGYWlsZWQgdG8gZGVjb2RlIHRoZSBpbWFnZQBJbnZhbGlkIGFjY2VzcyB0byBwaS0+aW5jbHVkZQAvdG1wL29wZW5qcGVnL3NyYy9iaW4vY29tbW9uL2NvbG9yLmMAQUxMX0NQVVMAT1BKX05VTV9USFJFQURTAE5BTgBPSlBfRE9fTk9UX0RJU1BMQVlfVElMRV9JTkRFWF9JRl9UTE0ASU5GAHBfajJrLT5tX3NwZWNpZmljX3BhcmFtLm1fZGVjb2Rlci5tX3NvdF9sZW5ndGggPiBVSU5UX01BWCAtIE9QSl9DT01NT05fQ0JMS19EQVRBX0VYVFJBAAkJCSBwcmVjY2ludHNpemUgKHcsaCk9AAkJCSBzdGVwc2l6ZXMgKG0sZSk9AFNPVCBtYXJrZXIgZm9yIHRpbGUgJXUgZGVjbGFyZXMgbW9yZSB0aWxlLXBhcnRzIHRoYW4gZm91bmQgaW4gVExNIG1hcmtlci4AKG51bGwpACglZCwlZCkgACVzfQoACQkgfQoAW0RFVl0gRHVtcCBhbiBpbWFnZV9jb21wX2hlYWRlciBzdHJ1Y3QgewoAW0RFVl0gRHVtcCBhbiBpbWFnZV9oZWFkZXIgc3RydWN0IHsKAEltYWdlIGluZm8gewoACSBkZWZhdWx0IHRpbGUgewoAJXMJIGNvbXBvbmVudCAlZCB7CgAJCSBjb21wICVkIHsKAAkgVGlsZSBpbmRleDogewoACSBNYXJrZXIgbGlzdDogewoAQ29kZXN0cmVhbSBpbmRleCBmcm9tIG1haW4gaGVhZGVyOiB7CgBDb2Rlc3RyZWFtIGluZm8gZnJvbSBtYWluIGhlYWRlcjogewoAU3RyZWFtIGVycm9yIHdoaWxlIHJlYWRpbmcgSlAyIEhlYWRlciBib3gKAEZvdW5kIGEgbWlzcGxhY2VkICclYyVjJWMlYycgYm94IG91dHNpZGUganAyaCBib3gKAE1hbGZvcm1lZCBKUDIgZmlsZSBmb3JtYXQ6IGZpcnN0IGJveCBtdXN0IGJlIEpQRUcgMjAwMCBzaWduYXR1cmUgYm94CgBNYWxmb3JtZWQgSlAyIGZpbGUgZm9ybWF0OiBzZWNvbmQgYm94IG11c3QgYmUgZmlsZSB0eXBlIGJveAoATm90IGVub3VnaCBtZW1vcnkgdG8gaGFuZGxlIGpwZWcyMDAwIGJveAoATm90IGVub3VnaCBtZW1vcnkgd2l0aCBGVFlQIEJveAoAQSBtYXJrZXIgSUQgd2FzIGV4cGVjdGVkICgweGZmLS0pIGluc3RlYWQgb2YgJS44eAoACQkgbWN0PSV4CgAJCQkgY2Jsa3N0eT0lI3gKAAkJCSBjc3R5PSUjeAoACQkgcHJnPSUjeAoASW50ZWdlciBvdmVyZmxvdwoACSB0ZHg9JXUsIHRkeT0ldQoACSB0dz0ldSwgdGg9JXUKAAkgdHgwPSV1LCB0eTA9JXUKAEludmFsaWQgY29tcG9uZW50IGluZGV4OiAldQoAU3RyZWFtIHRvbyBzaG9ydAoATWFya2VyIGhhbmRsZXIgZnVuY3Rpb24gZmFpbGVkIHRvIHJlYWQgdGhlIG1hcmtlciBzZWdtZW50CgBOb3QgZW5vdWdoIG1lbW9yeSBmb3IgY3VycmVudCBwcmVjaW5jdCBjb2RlYmxvY2sgZWxlbWVudAoARXJyb3IgcmVhZGluZyBTUENvZCBTUENvYyBlbGVtZW50CgBFcnJvciByZWFkaW5nIFNRY2Qgb3IgU1FjYyBlbGVtZW50CgBBIEJQQ0MgaGVhZGVyIGJveCBpcyBhdmFpbGFibGUgYWx0aG91Z2ggQlBDIGdpdmVuIGJ5IHRoZSBJSERSIGJveCAoJWQpIGluZGljYXRlIGNvbXBvbmVudHMgYml0IGRlcHRoIGlzIGNvbnN0YW50CgBFcnJvciB3aXRoIFNJWiBtYXJrZXI6IGlsbGVnYWwgdGlsZSBvZmZzZXQKAEludmFsaWQgcHJlY2luY3QKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIGhhbmRsZSBiYW5kIHByZWNpbnRzCgBGYWlsZWQgdG8gZGVjb2RlIGFsbCB1c2VkIGNvbXBvbmVudHMKAFNpemUgb2YgY29kZSBibG9jayBkYXRhIGV4Y2VlZHMgc3lzdGVtIGxpbWl0cwoAU2l6ZSBvZiB0aWxlIGRhdGEgZXhjZWVkcyBzeXN0ZW0gbGltaXRzCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgbXVsdGlwbGUgTUNUIG1hcmtlcnMKAENvcnJ1cHRlZCBQUE0gbWFya2VycwoATm90IGVub3VnaCBtZW1vcnkgZm9yIHRpbGUgcmVzb2x1dGlvbnMKAENhbm5vdCB0YWtlIGluIGNoYXJnZSBtdWx0aXBsZSBjb2xsZWN0aW9ucwoASW52YWxpZCBQQ0xSIGJveC4gUmVwb3J0cyAwIHBhbGV0dGUgY29sdW1ucwoAV2UgZG8gbm90IHN1cHBvcnQgUk9JIGluIGRlY29kaW5nIEhUIGNvZGVibG9ja3MKAENhbm5vdCBoYW5kbGUgYm94IG9mIHVuZGVmaW5lZCBzaXplcwoAQ2Fubm90IHRha2UgaW4gY2hhcmdlIGNvbGxlY3Rpb25zIHdpdGhvdXQgc2FtZSBudW1iZXIgb2YgaW5kaXhlcwoASW52YWxpZCB0aWxlYy0+d2luX3h4eCB2YWx1ZXMKAENhbm5vdCBoYW5kbGUgYm94IG9mIGxlc3MgdGhhbiA4IGJ5dGVzCgBDYW5ub3QgaGFuZGxlIFhMIGJveCBvZiBsZXNzIHRoYW4gMTYgYnl0ZXMKAENvbXBvbmVudCBpbmRleCAldSB1c2VkIHNldmVyYWwgdGltZXMKAEludmFsaWQgUENMUiBib3guIFJlcG9ydHMgJWQgZW50cmllcwoATm90IGVub3VnaCBtZW1vcnkgdG8gY3JlYXRlIFRhZy10cmVlIG5vZGVzCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgbWN0IGRhdGEgd2l0aGluIG11bHRpcGxlIE1DVCByZWNvcmRzCgBDYW5ub3QgZGVjb2RlIHRpbGUsIG1lbW9yeSBlcnJvcgoAb3BqX2oya19hcHBseV9uYl90aWxlX3BhcnRzX2NvcnJlY3Rpb24gZXJyb3IKAFByb2JsZW0gd2l0aCBza2lwcGluZyBKUEVHMjAwMCBib3gsIHN0cmVhbSBlcnJvcgoAUHJvYmxlbSB3aXRoIHJlYWRpbmcgSlBFRzIwMDAgYm94LCBzdHJlYW0gZXJyb3IKAFVua25vd24gbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBhZGQgdGwgbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBhZGQgbWggbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byB0YWtlIGluIGNoYXJnZSBTSVogbWFya2VyCgBFcnJvciByZWFkaW5nIFBQVCBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHJlYWQgUFBUIG1hcmtlcgoARXJyb3IgcmVhZGluZyBTT1QgbWFya2VyCgBEaWQgbm90IGdldCBleHBlY3RlZCBTT1QgbWFya2VyCgBFcnJvciByZWFkaW5nIFBMVCBtYXJrZXIKAEVycm9yIHJlYWRpbmcgTUNUIG1hcmtlcgoATm90IGVub3VnaCBtZW1vcnkgdG8gcmVhZCBNQ1QgbWFya2VyCgBOb3QgZW5vdWdoIHNwYWNlIGZvciBleHBlY3RlZCBTT1AgbWFya2VyCgBFeHBlY3RlZCBTT1AgbWFya2VyCgBFcnJvciByZWFkaW5nIE1DTyBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUkdOIG1hcmtlcgoARXJyb3IgcmVhZGluZyBQUE0gbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWFkIFBQTSBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUExNIG1hcmtlcgoARXhwZWN0ZWQgRVBIIG1hcmtlcgoATm90IGVub3VnaCBzcGFjZSBmb3IgcmVxdWlyZWQgRVBIIG1hcmtlcgoARXJyb3IgcmVhZGluZyBDUkcgbWFya2VyCgBVbmtub3duIHByb2dyZXNzaW9uIG9yZGVyIGluIENPRCBtYXJrZXIKAFVua25vd24gU2NvZCB2YWx1ZSBpbiBDT0QgbWFya2VyCgBFcnJvciByZWFkaW5nIENPRCBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUUNEIG1hcmtlcgoAQ3Jyb3IgcmVhZGluZyBDQkQgbWFya2VyCgBFcnJvciByZWFkaW5nIFBPQyBtYXJrZXIKAEVycm9yIHJlYWRpbmcgQ09DIG1hcmtlcgoARXJyb3IgcmVhZGluZyBRQ0MgbWFya2VyCgBFcnJvciByZWFkaW5nIE1DQyBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHJlYWQgTUNDIG1hcmtlcgoAcmVxdWlyZWQgU0laIG1hcmtlciBub3QgZm91bmQgaW4gbWFpbiBoZWFkZXIKAHJlcXVpcmVkIENPRCBtYXJrZXIgbm90IGZvdW5kIGluIG1haW4gaGVhZGVyCgByZXF1aXJlZCBRQ0QgbWFya2VyIG5vdCBmb3VuZCBpbiBtYWluIGhlYWRlcgoATm90IGVub3VnaCBtZW1vcnkgdG8gaGFuZGxlIGpwZWcyMDAwIGZpbGUgaGVhZGVyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWFkIGhlYWRlcgoARXJyb3Igd2l0aCBKUCBTaWduYXR1cmUgOiBiYWQgbWFnaWMgbnVtYmVyCgBJbiBTT1QgbWFya2VyLCBUUFNvdCAoJWQpIGlzIG5vdCB2YWxpZCByZWdhcmRzIHRvIHRoZSBjdXJyZW50IG51bWJlciBvZiB0aWxlLXBhcnQgKCVkKSwgZ2l2aW5nIHVwCgBJbiBTT1QgbWFya2VyLCBUUFNvdCAoJWQpIGlzIG5vdCB2YWxpZCByZWdhcmRzIHRvIHRoZSBwcmV2aW91cyBudW1iZXIgb2YgdGlsZS1wYXJ0ICglZCksIGdpdmluZyB1cAoASW4gU09UIG1hcmtlciwgVFBTb3QgKCVkKSBpcyBub3QgdmFsaWQgcmVnYXJkcyB0byB0aGUgY3VycmVudCBudW1iZXIgb2YgdGlsZS1wYXJ0IChoZWFkZXIpICglZCksIGdpdmluZyB1cAoAdGlsZXMgcmVxdWlyZSBhdCBsZWFzdCBvbmUgcmVzb2x1dGlvbgoATWFya2VyIGlzIG5vdCBjb21wbGlhbnQgd2l0aCBpdHMgcG9zaXRpb24KAFByb2JsZW0gd2l0aCBzZWVrIGZ1bmN0aW9uCgBFcnJvciByZWFkaW5nIFNQQ29kIFNQQ29jIGVsZW1lbnQsIEludmFsaWQgY2Jsa3cvY2Jsa2ggY29tYmluYXRpb24KAEludmFsaWQgbXVsdGlwbGUgY29tcG9uZW50IHRyYW5zZm9ybWF0aW9uCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgY29sbGVjdGlvbnMgb3RoZXIgdGhhbiBhcnJheSBkZWNvcnJlbGF0aW9uCgBUb28gbGFyZ2UgdmFsdWUgZm9yIE5wcG0KAE5vdCBlbm91Z2ggYnl0ZXMgdG8gcmVhZCBOcHBtCgBiYWQgcGxhY2VkIGpwZWcgY29kZXN0cmVhbQoACSBNYWluIGhlYWRlciBzdGFydCBwb3NpdGlvbj0lbGxpCgkgTWFpbiBoZWFkZXIgZW5kIHBvc2l0aW9uPSVsbGkKAE1hcmtlciBzaXplIGluY29uc2lzdGVudCB3aXRoIHN0cmVhbSBsZW5ndGgKAFRpbGUgcGFydCBsZW5ndGggc2l6ZSBpbmNvbnNpc3RlbnQgd2l0aCBzdHJlYW0gbGVuZ3RoCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgbXVsdGlwbGUgZGF0YSBzcGFubmluZwoAV3JvbmcgZmxhZwoARXJyb3Igd2l0aCBGVFlQIHNpZ25hdHVyZSBCb3ggc2l6ZQoARXJyb3Igd2l0aCBKUCBzaWduYXR1cmUgQm94IHNpemUKAEludmFsaWQgcHJlY2luY3Qgc2l6ZQoASW5jb25zaXN0ZW50IG1hcmtlciBzaXplCgBJbnZhbGlkIG1hcmtlciBzaXplCgBFcnJvciB3aXRoIFNJWiBtYXJrZXIgc2l6ZQoATm90IGVub3VnaCBtZW1vcnkgdG8gYWRkIGEgbmV3IHZhbGlkYXRpb24gcHJvY2VkdXJlCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBkZWNvZGUgdGlsZQoARmFpbGVkIHRvIGRlY29kZSB0aGUgY29kZXN0cmVhbSBpbiB0aGUgSlAyIGZpbGUKAENhbm5vdCB0YWtlIGluIGNoYXJnZSBjb2xsZWN0aW9ucyB3aXRoIGluZGl4IHNodWZmbGUKAENhbm5vdCBhbGxvY2F0ZSBUaWVyIDEgaGFuZGxlCgBObyBkZWNvZGVkIGFyZWEgcGFyYW1ldGVycywgc2V0IHRoZSBkZWNvZGVkIGFyZWEgdG8gdGhlIHdob2xlIGltYWdlCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBjcmVhdGUgVGFnLXRyZWUKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHJlaW5pdGlhbGl6ZSB0aGUgdGFnIHRyZWUKAEVycm9yIHJlYWRpbmcgU1BDb2QgU1BDb2MgZWxlbWVudCwgSW52YWxpZCB0cmFuc2Zvcm1hdGlvbiBmb3VuZAoARXJyb3IgcmVhZGluZyBTUENvZCBTUENvYyBlbGVtZW50LiBVbnN1cHBvcnRlZCBNaXhlZCBIVCBjb2RlLWJsb2NrIHN0eWxlIGZvdW5kCgBUaWxlIFkgY29vcmRpbmF0ZXMgYXJlIG5vdCBzdXBwb3J0ZWQKAFRpbGUgWCBjb29yZGluYXRlcyBhcmUgbm90IHN1cHBvcnRlZAoASW1hZ2UgY29vcmRpbmF0ZXMgYWJvdmUgSU5UX01BWCBhcmUgbm90IHN1cHBvcnRlZAoASlBFRzIwMDAgSGVhZGVyIGJveCBub3QgcmVhZCB5ZXQsICclYyVjJWMlYycgYm94IHdpbGwgYmUgaWdub3JlZAoAb3BqX2oya19tZXJnZV9wcHQoKSBoYXMgYWxyZWFkeSBiZWVuIGNhbGxlZAoAb3BqX2oya19idWlsZF90cF9pbmRleF9mcm9tX3RsbSgpOiB0aWxlIGluZGV4IGFsbG9jYXRpb24gZmFpbGVkCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWFkIFNPVCBtYXJrZXIuIFRpbGUgaW5kZXggYWxsb2NhdGlvbiBmYWlsZWQKAElnbm9yaW5nIGloZHIgYm94LiBGaXJzdCBpaGRyIGJveCBhbHJlYWR5IHJlYWQKAFpwcHQgJXUgYWxyZWFkeSByZWFkCgBacHBtICV1IGFscmVhZHkgcmVhZAoAUFRFUk0gY2hlY2sgZmFpbHVyZTogJWQgc3ludGhlc2l6ZWQgMHhGRiBtYXJrZXJzIHJlYWQKAAkJCSBjYmxrdz0yXiVkCgAJCQkgY2Jsa2g9Ml4lZAoACQkJIHFudHN0eT0lZAoAJXMgZHg9JWQsIGR5PSVkCgAJCQkgcm9pc2hpZnQ9JWQKAAkJCSBudW1nYml0cz0lZAoACQkgbnVtbGF5ZXJzPSVkCgAlcyBudW1jb21wcz0lZAoAb3BqX2pwMl9hcHBseV9jZGVmOiBhY249JWQsIG51bWNvbXBzPSVkCgBvcGpfanAyX2FwcGx5X2NkZWY6IGNuPSVkLCBudW1jb21wcz0lZAoACQkJIG51bXJlc29sdXRpb25zPSVkCgAJCSB0eXBlPSUjeCwgcG9zPSVsbGksIGxlbj0lZAoAJXMgc2duZD0lZAoACQkJIHFtZmJpZD0lZAoAJXMgcHJlYz0lZAoACQkgbmIgb2YgdGlsZS1wYXJ0IGluIHRpbGUgWyVkXT0lZAoAJXMgeDE9JWQsIHkxPSVkCgAlcyB4MD0lZCwgeTA9JWQKAEZhaWxlZCB0byBkZWNvZGUgdGlsZSAlZC8lZAoAU2V0dGluZyBkZWNvZGluZyBhcmVhIHRvICVkLCVkLCVkLCVkCgBGYWlsZWQgdG8gZGVjb2RlIGNvbXBvbmVudCAlZAoASW52YWxpZCB2YWx1ZSBmb3IgbnVtcmVzb2x1dGlvbnMgOiAlZCwgbWF4IHZhbHVlIGlzIHNldCBpbiBvcGVuanBlZy5oIGF0ICVkCgBJbnZhbGlkIGNvbXBvbmVudCBudW1iZXI6ICVkLCByZWdhcmRpbmcgdGhlIG51bWJlciBvZiBjb21wb25lbnRzICVkCgBUb28gbWFueSBQT0NzICVkCgBvcGpfajJrX3JlYWRfdGxtKCk6IGludmFsaWQgdGlsZSBudW1iZXIgJWQKAEludmFsaWQgdGlsZSBudW1iZXIgJWQKAEludmFsaWQgdGlsZSBwYXJ0IGluZGV4IGZvciB0aWxlIG51bWJlciAlZC4gR290ICVkLCBleHBlY3RlZCAlZAoARXJyb3Igd2l0aCBTSVogbWFya2VyOiBudW1iZXIgb2YgY29tcG9uZW50IGlzIGlsbGVnYWwgLT4gJWQKAE5vdCBlbm91Z2ggbWVtb3J5IGZvciBjaWVsYWIKAENhbm5vdCBhbGxvY2F0ZSBjYmxrLT5kZWNvZGVkX2RhdGEKAEZhaWxlZCB0byBtZXJnZSBQUFQgZGF0YQoARmFpbGVkIHRvIG1lcmdlIFBQTSBkYXRhCgBJbnZhbGlkIG51bWJlciBvZiBsYXllcnMgaW4gQ09EIG1hcmtlciA6ICVkIG5vdCBpbiByYW5nZSBbMS02NTUzNV0KACVzOiVkOmNvbG9yX2NteWtfdG9fcmdiCglDQU4gTk9UIENPTlZFUlQKACVzOiVkOmNvbG9yX2VzeWNjX3RvX3JnYgoJQ0FOIE5PVCBDT05WRVJUCgAlczolZDpjb2xvcl9zeWNjX3RvX3JnYgoJQ0FOIE5PVCBDT05WRVJUCgBTdHJlYW0gdG9vIHNob3J0LCBleHBlY3RlZCBTT1QKAFVuYWJsZSB0byBzZXQgdDEgaGFuZGxlIGFzIFRMUwoAU290IGxlbmd0aCBpcyBsZXNzIHRoYW4gbWFya2VyIHNpemUgKyBtYXJrZXIgSUQKAFN0cmVhbSBkb2VzIG5vdCBlbmQgd2l0aCBFT0MKAENhbm5vdCBoYW5kbGUgYm94IHNpemVzIGhpZ2hlciB0aGFuIDJeMzIKAG9wal9waV9uZXh0X2xyY3AoKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal9waV9uZXh0X3JsY3AoKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal9waV9uZXh0X2NwcmwoKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal9waV9uZXh0X3BjcmwoKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal9waV9uZXh0X3JwY2woKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal90MV9kZWNvZGVfY2JsaygpOiB1bnN1cHBvcnRlZCBicG5vX3BsdXNfb25lID0gJWQgPj0gMzEKAEZhaWxlZCB0byBkZWNvZGUgdGlsZSAxLzEKAEluc3VmZmljaWVudCBkYXRhIGZvciBDTUFQIGJveC4KAE5lZWQgdG8gcmVhZCBhIFBDTFIgYm94IGJlZm9yZSB0aGUgQ01BUCBib3guCgBJbnN1ZmZpY2llbnQgZGF0YSBmb3IgQ0RFRiBib3guCgBOdW1iZXIgb2YgY2hhbm5lbCBkZXNjcmlwdGlvbiBpcyBlcXVhbCB0byB6ZXJvIGluIENERUYgYm94LgoAU3RyZWFtIGVycm9yIHdoaWxlIHJlYWRpbmcgSlAyIEhlYWRlciBib3g6IG5vICdpaGRyJyBib3guCgBOb24gY29uZm9ybWFudCBjb2Rlc3RyZWFtIFRQc290PT1UTnNvdC4KAFN0cmVhbSBlcnJvciB3aGlsZSByZWFkaW5nIEpQMiBIZWFkZXIgYm94OiBib3ggbGVuZ3RoIGlzIGluY29uc2lzdGVudC4KAEJveCBsZW5ndGggaXMgaW5jb25zaXN0ZW50LgoAUmVzb2x1dGlvbiBmYWN0b3IgaXMgZ3JlYXRlciB0aGFuIHRoZSBtYXhpbXVtIHJlc29sdXRpb24gaW4gdGhlIGNvbXBvbmVudC4KAENvbXBvbmVudCBtYXBwaW5nIHNlZW1zIHdyb25nLiBUcnlpbmcgdG8gY29ycmVjdC4KAG9wal9qMmtfYnVpbGRfdHBfaW5kZXhfZnJvbV90bG0oKTogdGlsZSAlZCBoYXMgbm8gcmVnaXN0ZXJlZCB0aWxlLXBhcnQgaW4gVExNIG1hcmtlciBzZWdtZW50cy4KAG9wal9qMmtfcmVhZF90bG0oKTogdG9vIG1hbnkgVExNIG1hcmtlcnMuCgBvcGpfajJrX3JlYWRfdGxtKCk6IGNhbm5vdCBhbGxvY2F0ZSBtX3RpbGVfcGFydF9pbmZvcy4KAEluY29tcGxldGUgY2hhbm5lbCBkZWZpbml0aW9ucy4KAE1hbGZvcm1lZCBIVCBjb2RlYmxvY2suIEludmFsaWQgY29kZWJsb2NrIGxlbmd0aCB2YWx1ZXMuCgBXZSBkbyBub3Qgc3VwcG9ydCBtb3JlIHRoYW4gMyBjb2RpbmcgcGFzc2VzIGluIGFuIEhUIGNvZGVibG9jazsgVGhpcyBjb2RlYmxvY2tzIGhhcyAlZCBwYXNzZXMuCgBNYWxmb3JtZWQgSFQgY29kZWJsb2NrLiBEZWNvZGluZyB0aGlzIGNvZGVibG9jayBpcyBzdG9wcGVkLiBUaGVyZSBhcmUgJWQgemVybyBiaXRwbGFuZXMgaW4gJWQgYml0cGxhbmVzLgoAQ2Fubm90IHRha2UgaW4gY2hhcmdlIG11bHRpcGxlIHRyYW5zZm9ybWF0aW9uIHN0YWdlcy4KAFVua25vd24gbWFya2VyIGhhcyBiZWVuIGRldGVjdGVkIGFuZCBnZW5lcmF0ZWQgZXJyb3IuCgBDb2RlYyBwcm92aWRlZCB0byB0aGUgb3BqX3NldHVwX2RlY29kZXIgZnVuY3Rpb24gaXMgbm90IGEgZGVjb21wcmVzc29yIGhhbmRsZXIuCgBDb2RlYyBwcm92aWRlZCB0byB0aGUgb3BqX3JlYWRfaGVhZGVyIGZ1bmN0aW9uIGlzIG5vdCBhIGRlY29tcHJlc3NvciBoYW5kbGVyLgoARXJyb3IgcmVhZGluZyBUTE0gbWFya2VyLgoAVGlsZXMgZG9uJ3QgYWxsIGhhdmUgdGhlIHNhbWUgZGltZW5zaW9uLiBTa2lwIHRoZSBNQ1Qgc3RlcC4KAE51bWJlciBvZiBjb21wb25lbnRzICglZCkgaXMgaW5jb25zaXN0ZW50IHdpdGggYSBNQ1QuIFNraXAgdGhlIE1DVCBzdGVwLgoASlAyIGJveCB3aGljaCBhcmUgYWZ0ZXIgdGhlIGNvZGVzdHJlYW0gd2lsbCBub3QgYmUgcmVhZCBieSB0aGlzIGZ1bmN0aW9uLgoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gV2hlbiB0aGUgbnVtYmVyIG9mIHplcm8gcGxhbmVzIGJpdHBsYW5lcyBpcyBlcXVhbCB0byB0aGUgbnVtYmVyIG9mIGJpdHBsYW5lcywgb25seSB0aGUgY2xlYW51cCBwYXNzIG1ha2VzIHNlbnNlLCBidXQgd2UgaGF2ZSAlZCBwYXNzZXMgaW4gdGhpcyBjb2RlYmxvY2suIFRoZXJlZm9yZSwgb25seSB0aGUgY2xlYW51cCBwYXNzIHdpbGwgYmUgZGVjb2RlZC4gVGhpcyBtZXNzYWdlIHdpbGwgbm90IGJlIGRpc3BsYXllZCBhZ2Fpbi4KAEltYWdlIGhhcyBsZXNzIGNvbXBvbmVudHMgdGhhbiBjb2Rlc3RyZWFtLgoATmVlZCB0byBkZWNvZGUgdGhlIG1haW4gaGVhZGVyIGJlZm9yZSBiZWdpbiB0byBkZWNvZGUgdGhlIHJlbWFpbmluZyBjb2Rlc3RyZWFtLgoAUHNvdCB2YWx1ZSBvZiB0aGUgY3VycmVudCB0aWxlLXBhcnQgaXMgZXF1YWwgdG8gemVybywgd2UgYXNzdW1pbmcgaXQgaXMgdGhlIGxhc3QgdGlsZS1wYXJ0IG9mIHRoZSBjb2Rlc3RyZWFtLgoAQSBtYWxmb3JtZWQgY29kZWJsb2NrIHRoYXQgaGFzIG1vcmUgdGhhbiBvbmUgY29kaW5nIHBhc3MsIGJ1dCB6ZXJvIGxlbmd0aCBmb3IgMm5kIGFuZCBwb3RlbnRpYWxseSB0aGUgM3JkIHBhc3MgaW4gYW4gSFQgY29kZWJsb2NrLgoACQkJIHRpbGUtcGFydFslZF06IHN0YXJfcG9zPSVsbGksIGVuZF9oZWFkZXI9JWxsaSwgZW5kX3Bvcz0lbGxpLgoAVGlsZSAldSBoYXMgVFBzb3QgPT0gMCBhbmQgVE5zb3QgPT0gMCwgYnV0IG5vIG90aGVyIHRpbGUtcGFydHMgd2VyZSBmb3VuZC4gRU9DIGlzIGFsc28gbWlzc2luZy4KAENvbXBvbmVudCAlZCBkb2Vzbid0IGhhdmUgYSBtYXBwaW5nLgoAb3BqX2oya19yZWFkX3RsbSgpOiBUTE0gbWFya2VyIG5vdCBvZiBleHBlY3RlZCBzaXplLgoAQSBjb25mb3JtaW5nIEpQMiByZWFkZXIgc2hhbGwgaWdub3JlIGFsbCBDb2xvdXIgU3BlY2lmaWNhdGlvbiBib3hlcyBhZnRlciB0aGUgZmlyc3QsIHNvIHdlIGlnbm9yZSB0aGlzIG9uZS4KAFRoZSBzaWduYXR1cmUgYm94IG11c3QgYmUgdGhlIGZpcnN0IGJveCBpbiB0aGUgZmlsZS4KAFRoZSAgYm94IG11c3QgYmUgdGhlIGZpcnN0IGJveCBpbiB0aGUgZmlsZS4KAFRoZSBmdHlwIGJveCBtdXN0IGJlIHRoZSBzZWNvbmQgYm94IGluIHRoZSBmaWxlLgoARmFpbGVkIHRvIGRlY29kZS4KAE1hbGZvcm1lZCBIVCBjb2RlYmxvY2suIEluY29ycmVjdCBNRUwgc2VnbWVudCBzZXF1ZW5jZS4KAENvbXBvbmVudCAlZCBpcyBtYXBwZWQgdHdpY2UuCgBvcGpfajJrX3JlYWRfdGxtKCk6IFNUID0gMyBpcyBpbnZhbGlkLgoAT25seSBvbmUgQ01BUCBib3ggaXMgYWxsb3dlZC4KAFdlIG5lZWQgYW4gaW1hZ2UgcHJldmlvdXNseSBjcmVhdGVkLgoASUhEUiBib3hfbWlzc2luZy4gUmVxdWlyZWQuCgBKUDJIIGJveCBtaXNzaW5nLiBSZXF1aXJlZC4KAE5vdCBzdXJlIGhvdyB0aGF0IGhhcHBlbmVkLgoATWFpbiBoZWFkZXIgaGFzIGJlZW4gY29ycmVjdGx5IGRlY29kZWQuCgBUaWxlICVkLyVkIGhhcyBiZWVuIGRlY29kZWQuCgBIZWFkZXIgb2YgdGlsZSAlZCAvICVkIGhhcyBiZWVuIHJlYWQuCgBFbXB0eSBTT1QgbWFya2VyIGRldGVjdGVkOiBQc290PSVkLgoARGlyZWN0IHVzZSBhdCAjJWQgaG93ZXZlciBwY29sPSVkLgoASW1wbGVtZW50YXRpb24gbGltaXRhdGlvbjogZm9yIHBhbGV0dGUgbWFwcGluZywgcGNvbFslZF0gc2hvdWxkIGJlIGVxdWFsIHRvICVkLCBidXQgaXMgZXF1YWwgdG8gJWQuCgBJbnZhbGlkIGNvbXBvbmVudC9wYWxldHRlIGluZGV4IGZvciBkaXJlY3QgbWFwcGluZyAlZC4KAEludmFsaWQgdmFsdWUgZm9yIGNtYXBbJWRdLm10eXAgPSAlZC4KAFBzb3QgdmFsdWUgaXMgbm90IGNvcnJlY3QgcmVnYXJkcyB0byB0aGUgSlBFRzIwMDAgbm9ybTogJWQuCgBNYWxmb3JtZWQgSFQgY29kZWJsb2NrLiBWTEMgY29kZSBwcm9kdWNlcyBzaWduaWZpY2FudCBzYW1wbGVzIG91dHNpZGUgdGhlIGNvZGVibG9jayBhcmVhLgoAVW5leHBlY3RlZCBPT00uCgAzMiBiaXRzIGFyZSBub3QgZW5vdWdoIHRvIGRlY29kZSB0aGlzIGNvZGVibG9jaywgc2luY2UgdGhlIG51bWJlciBvZiBiaXRwbGFuZSwgJWQsIGlzIGxhcmdlciB0aGFuIDMwLgoAQm90dG9tIHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl95MT0lZCkgc2hvdWxkIGJlID4gMC4KAFJpZ2h0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MT0lZCkgc2hvdWxkIGJlID4gMC4KAFVwIHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl95MD0lZCkgc2hvdWxkIGJlID49IDAuCgBMZWZ0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MD0lZCkgc2hvdWxkIGJlID49IDAuCgBFcnJvciByZWFkaW5nIFBQVCBtYXJrZXI6IHBhY2tldCBoZWFkZXIgaGF2ZSBiZWVuIHByZXZpb3VzbHkgZm91bmQgaW4gdGhlIG1haW4gaGVhZGVyIChQUE0gbWFya2VyKS4KAFN0YXJ0IHRvIHJlYWQgajJrIG1haW4gaGVhZGVyICglbGxkKS4KAEJvdHRvbSBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feTE9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFlzaXo9JWQpLgoAVXAgcG9zaXRpb24gb2YgdGhlIGRlY29kZWQgYXJlYSAocmVnaW9uX3kwPSVkKSBpcyBvdXRzaWRlIHRoZSBpbWFnZSBhcmVhIChZc2l6PSVkKS4KAFJpZ2h0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MT0lZCkgaXMgb3V0c2lkZSB0aGUgaW1hZ2UgYXJlYSAoWHNpej0lZCkuCgBMZWZ0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MD0lZCkgaXMgb3V0c2lkZSB0aGUgaW1hZ2UgYXJlYSAoWHNpej0lZCkuCgBCb3R0b20gcG9zaXRpb24gb2YgdGhlIGRlY29kZWQgYXJlYSAocmVnaW9uX3kxPSVkKSBpcyBvdXRzaWRlIHRoZSBpbWFnZSBhcmVhIChZT3Npej0lZCkuCgBVcCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feTA9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFlPc2l6PSVkKS4KAFJpZ2h0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MT0lZCkgaXMgb3V0c2lkZSB0aGUgaW1hZ2UgYXJlYSAoWE9zaXo9JWQpLgoATGVmdCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feDA9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFhPc2l6PSVkKS4KAFNpemUgeCBvZiB0aGUgZGVjb2RlZCBjb21wb25lbnQgaW1hZ2UgaXMgaW5jb3JyZWN0IChjb21wWyVkXS53PSVkKS4KAFNpemUgeSBvZiB0aGUgZGVjb2RlZCBjb21wb25lbnQgaW1hZ2UgaXMgaW5jb3JyZWN0IChjb21wWyVkXS5oPSVkKS4KAFRpbGUgcmVhZCwgZGVjb2RlZCBhbmQgdXBkYXRlZCBpcyBub3QgdGhlIGRlc2lyZWQgb25lICglZCB2cyAlZCkuCgBJbnZhbGlkIGNvbXBvbmVudCBpbmRleCAlZCAoPj0gJWQpLgoAb3BqX3JlYWRfaGVhZGVyKCkgc2hvdWxkIGJlIGNhbGxlZCBiZWZvcmUgb3BqX3NldF9kZWNvZGVkX2NvbXBvbmVudHMoKS4KAE1lbW9yeSBhbGxvY2F0aW9uIGZhaWx1cmUgaW4gb3BqX2pwMl9hcHBseV9wY2xyKCkuCgBpbWFnZS0+Y29tcHNbJWRdLmRhdGEgPT0gTlVMTCBpbiBvcGpfanAyX2FwcGx5X3BjbHIoKS4KAGludmFsaWQgYm94IHNpemUgJWQgKCV4KQoARmFpbCB0byByZWFkIHRoZSBjdXJyZW50IG1hcmtlciBzZWdtZW50ICglI3gpCgBFcnJvciB3aXRoIFNJWiBtYXJrZXI6IElIRFIgdygldSkgaCgldSkgdnMuIFNJWiB3KCV1KSBoKCV1KQoARXJyb3IgcmVhZGluZyBDT0MgbWFya2VyIChiYWQgbnVtYmVyIG9mIGNvbXBvbmVudHMpCgBJbnZhbGlkIG51bWJlciBvZiB0aWxlcyA6ICV1IHggJXUgKG1heGltdW0gZml4ZWQgYnkganBlZzIwMDAgbm9ybSBpcyA2NTUzNSB0aWxlcykKAEludmFsaWQgbnVtYmVyIG9mIGNvbXBvbmVudHMgKGloZHIpCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBoYW5kbGUgaW1hZ2UgaGVhZGVyIChpaGRyKQoAV3JvbmcgdmFsdWVzIGZvcjogdyglZCkgaCglZCkgbnVtY29tcHMoJWQpIChpaGRyKQoASW52YWxpZCB2YWx1ZXMgZm9yIGNvbXAgPSAlZCA6IGR4PSV1IGR5PSV1IChzaG91bGQgYmUgYmV0d2VlbiAxIGFuZCAyNTUgYWNjb3JkaW5nIHRvIHRoZSBKUEVHMjAwMCBub3JtKQoAQmFkIGltYWdlIGhlYWRlciBib3ggKGJhZCBzaXplKQoAQmFkIENPTFIgaGVhZGVyIGJveCAoYmFkIHNpemUpCgBCYWQgQlBDQyBoZWFkZXIgYm94IChiYWQgc2l6ZSkKAEVycm9yIHdpdGggU0laIG1hcmtlcjogbmVnYXRpdmUgb3IgemVybyBpbWFnZSBzaXplICglbGxkIHggJWxsZCkKAHNraXA6IHNlZ21lbnQgdG9vIGxvbmcgKCVkKSB3aXRoIG1heCAoJWQpIGZvciBjb2RlYmxvY2sgJWQgKHA9JWQsIGI9JWQsIHI9JWQsIGM9JWQpCgByZWFkOiBzZWdtZW50IHRvbyBsb25nICglZCkgd2l0aCBtYXggKCVkKSBmb3IgY29kZWJsb2NrICVkIChwPSVkLCBiPSVkLCByPSVkLCBjPSVkKQoARGVzcGl0ZSBKUDIgQlBDIT0yNTUsIHByZWNpc2lvbiBhbmQvb3Igc2duZCB2YWx1ZXMgZm9yIGNvbXBbJWRdIGlzIGRpZmZlcmVudCB0aGFuIGNvbXBbMF06CiAgICAgICAgWzBdIHByZWMoJWQpIHNnbmQoJWQpIFslZF0gcHJlYyglZCkgc2duZCglZCkKAGJhZCBjb21wb25lbnQgbnVtYmVyIGluIFJHTiAoJWQgd2hlbiB0aGVyZSBhcmUgb25seSAlZCkKAEVycm9yIHdpdGggU0laIG1hcmtlcjogbnVtYmVyIG9mIGNvbXBvbmVudCBpcyBub3QgY29tcGF0aWJsZSB3aXRoIHRoZSByZW1haW5pbmcgbnVtYmVyIG9mIHBhcmFtZXRlcnMgKCAlZCB2cyAlZCkKAEVycm9yIHdpdGggU0laIG1hcmtlcjogaW52YWxpZCB0aWxlIHNpemUgKHRkeDogJWQsIHRkeTogJWQpCgBCYWQgQ09MUiBoZWFkZXIgYm94IChiYWQgc2l6ZTogJWQpCgBCYWQgQ09MUiBoZWFkZXIgYm94IChDSUVMYWIsIGJhZCBzaXplOiAlZCkKAFBURVJNIGNoZWNrIGZhaWx1cmU6ICVkIHJlbWFpbmluZyBieXRlcyBpbiBjb2RlIGJsb2NrICglZCB1c2VkIC8gJWQpCgBNYWxmb3JtZWQgSFQgY29kZWJsb2NrLiBPbmUgb2YgdGhlIGZvbGxvd2luZyBjb25kaXRpb24gaXMgbm90IG1ldDogMiA8PSBTY3VwIDw9IG1pbihMY3VwLCA0MDc5KQoASW52YWxpZCB2YWx1ZXMgZm9yIGNvbXAgPSAlZCA6IHByZWM9JXUgKHNob3VsZCBiZSBiZXR3ZWVuIDEgYW5kIDM4IGFjY29yZGluZyB0byB0aGUgSlBFRzIwMDAgbm9ybS4gT3BlbkpwZWcgb25seSBzdXBwb3J0cyB1cCB0byAzMSkKAEludmFsaWQgYml0IG51bWJlciAlZCBpbiBvcGpfdDJfcmVhZF9wYWNrZXRfaGVhZGVyKCkKAFN0cmVhbSBlcnJvciEKAEVycm9yIG9uIHdyaXRpbmcgc3RyZWFtIQoAU3RyZWFtIHJlYWNoZWQgaXRzIGVuZCAhCgBFeHBlY3RlZCBhIFNPQyBtYXJrZXIgCgBJbnZhbGlkIGJveCBzaXplICVkIGZvciBib3ggJyVjJWMlYyVjJy4gTmVlZCAlZCBieXRlcywgJWQgYnl0ZXMgcmVtYWluaW5nIAoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gRGVjb2RpbmcgdGhpcyBjb2RlYmxvY2sgaXMgc3RvcHBlZC4gVV9xIGlzIGxhcmdlciB0aGFuIHplcm8gYml0cGxhbmVzICsgMSAKAE1hbGZvcm1lZCBIVCBjb2RlYmxvY2suIERlY29kaW5nIHRoaXMgY29kZWJsb2NrIGlzIHN0b3BwZWQuIFVfcSBpc2xhcmdlciB0aGFuIGJpdHBsYW5lcyArIDEgCgBDT0xSIEJPWCBtZXRoIHZhbHVlIGlzIG5vdCBhIHJlZ3VsYXIgdmFsdWUgKCVkKSwgc28gd2Ugd2lsbCBpZ25vcmUgdGhlIGVudGlyZSBDb2xvdXIgU3BlY2lmaWNhdGlvbiBib3guIAoAV2hpbGUgcmVhZGluZyBDQ1BfUU5UU1RZIGVsZW1lbnQgaW5zaWRlIFFDRCBvciBRQ0MgbWFy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AAAAAFQAAKBTAAABJAAAAAAAABBUAACwUwAAASQAAAEAAAAgVAAAwFMAAAEiAAAAAAAAMFQAANBTAAABIgAAAQAAAEBUAADgUwAAARwAAAAAAABQVAAA8FMAAAEcAAABAAAAYFQAAABUAAABGAAAAAAAAHBUAAAQVAAAARgAAAEAAACAVAAAIFQAAAEWAAAAAAAAkFQAADBUAAABFgAAAQAAAKBUAABAVAAAARQAAAAAAACwVAAAUFQAAAEUAAABAAAAwFQAAGBUAAABEgAAAAAAANBUAABwVAAAARIAAAEAAADgVAAAgFQAAAERAAAAAAAA8FQAAJBUAAABEQAAAQAAAABVAACgVAAAwQoAAAAAAAAQVQAAsFQAAMEKAAABAAAAIFUAAMBUAADBCQAAAAAAADBVAADQVAAAwQkAAAEAAABAVQAA4FQAAKEIAAAAAAAAUFUAAPBUAAChCAAAAQAAAGBVAAAAVQAAIQUAAAAAAABwVQAAEFUAACEFAAABAAAAgFUAACBVAABBBAAAAAAAAJBVAAAwVQAAQQQAAAEAAACgVQAAQFUAAKECAAAAAAAAsFUAAFBVAAChAgAAAQAAAMBVAABgVQAAIQIAAAAAAADQVQAAcFUAACECAAABAAAA4FUAAIBVAABBAQAAAAAAAPBVAACQVQAAQQEAAAEAAAAAVgAAoFUAABEBAAAAAAAAEFYAALBVAAARAQAAAQAAACBWAADAVQAAhQAAAAAAAAAwVgAA0FUAAIUAAAABAAAAQFYAAOBVAABJAAAAAAAAAFBWAADwVQAASQAAAAEAAABgVgAAAFYAACUAAAAAAAAAcFYAABBWAAAlAAAAAQAAAIBWAAAgVgAAFQAAAAAAAACQVgAAMFYAABUAAAABAAAAoFYAAEBWAAAJAAAAAAAAALBWAABQVgAACQAAAAEAAADAVgAAYFYAAAUAAAAAAAAA0FYAAHBWAAAFAAAAAQAAAOBWAACAVgAAAQAAAAAAAADQVgAAkFYAAAEAAAABAAAA4FYAAKBWAAABVgAAAAAAAPBWAADwVgAAAVYAAAEAAAAAVwAAAFcAAAABAwMBAgMDBQYHBwYGBwcAAQMDAQIDAwUGBwcGBgcHBQYHBwYGBwcICAgICAgICAUGBwcGBgcHCAgICAgICAgBAgMDAgIDAwYGBwcGBgcHAQIDAwICAwMGBgcHBgYHBwYGBwcGBgcHCAgICAgICAgGBgcHBgYHBwgICAgICAgIAwMEBAMDBAQHBwcHBwcHBwMDBAQDAwQEBwcHBwcHBwcHBwcHBwcHBwgICAgICAgIBwcHBwcHBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgBAgMDAgIDAwYGBwcGBgcHAQIDAwICAwMGBgcHBgYHBwYGBwcGBgcHCAgICAgICAgGBgcHBgYHBwgICAgICAgIAgIDAwICAwMGBgcHBgYHBwICAwMCAgMDBgYHBwYGBwcGBgcHBgYHBwgICAgICAgIBgYHBwYGBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgDAwQEAwMEBAcHBwcHBwcHAwMEBAMDBAQHBwcHBwcHBwcHBwcHBwcHCAgICAgICAgHBwcHBwcHBwgICAgICAgIAAEFBgECBgYDAwcHAwMHBwABBQYBAgYGAwMHBwMDBwcDAwcHAwMHBwQEBwcEBAcHAwMHBwMDBwcEBAcHBAQHBwECBgYCAgYGAwMHBwMDBwcBAgYGAgIGBgMDBwcDAwcHAwMHBwMDBwcEBAcHBAQHBwMDBwcDAwcHBAQHBwQEBwcFBggIBgYICAcHCAgHBwgIBQYICAYGCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBgYICAYGCAgHBwgIBwcICAYGCAgGBggIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAECBgYCAgYGAwMHBwMDBwcBAgYGAgIGBgMDBwcDAwcHAwMHBwMDBwcEBAcHBAQHBwMDBwcDAwcHBAQHBwQEBwcCAgYGAgIGBgMDBwcDAwcHAgIGBgICBgYDAwcHAwMHBwMDBwcDAwcHBAQHBwQEBwcDAwcHAwMHBwQEBwcEBAcHBgYICAYGCAgHBwgIBwcICAYGCAgGBggIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAYGCAgGBggIBwcICAcHCAgGBggIBgYICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgAAQMDAQIDAwUGBwcGBgcHAAEDAwECAwMFBgcHBgYHBwUGBwcGBgcHCAgICAgICAgFBgcHBgYHBwgICAgICAgIAQIDAwICAwMGBgcHBgYHBwECAwMCAgMDBgYHBwYGBwcGBgcHBgYHBwgICAgICAgIBgYHBwYGBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgDAwQEAwMEBAcHBwcHBwcHAwMEBAMDBAQHBwcHBwcHBwcHBwcHBwcHCAgICAgICAgHBwcHBwcHBwgICAgICAgIAQIDAwICAwMGBgcHBgYHBwECAwMCAgMDBgYHBwYGBwcGBgcHBgYHBwgICAgICAgIBgYHBwYGBwcICAgICAgICAICAwMCAgMDBgYHBwYGBwcCAgMDAgIDAwYGBwcGBgcHBgYHBwYGBwcICAgICAgICAYGBwcGBgcHCAgICAgICAgDAwQEAwMEBAcHBwcHBwcHAwMEBAMDBAQHBwcHBwcHBwcHBwcHBwcHCAgICAgICAgHBwcHBwcHBwgICAgICAgIAwMEBAMDBAQHBwcHBwcHBwMDBAQDAwQEBwcHBwcHBwcHBwcHBwcHBwgICAgICAgIBwcHBwcHBwcICAgICAgICAADAQQDBgQHAQQCBQQHBQcAAwEEAwYEBwEEAgUEBwUHAQQCBQQHBQcCBQIFBQcFBwEEAgUEBwUHAgUCBQUHBQcDBgQHBggHCAQHBQcHCAcIAwYEBwYIBwgEBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgEBwUHBwgHCAUHBQcHCAcIAQQCBQQHBQcCBQIFBQcFBwEEAgUEBwUHAgUCBQUHBQcCBQIFBQcFBwIFAgUFBwUHAgUCBQUHBQcCBQIFBQcFBwQHBQcHCAcIBQcFBwcIBwgEBwUHBwgHCAUHBQcHCAcIBQcFBwcIBwgFBwUHBwgHCAUHBQcHCAcIBQcFBwcIBwgDBgQHBggHCAQHBQcHCAcIAwYEBwYIBwgEBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgEBwUHBwgHCAUHBQcHCAcIBggHCAgICAgHCAcICAgICAYIBwgICAgIBwgHCAgICAgH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"; + return f; + } + var wasmBinaryFile; + function getBinarySync(file) { + if (file == wasmBinaryFile && wasmBinary) { + return new Uint8Array(wasmBinary); + } + var binary = tryParseAsDataURI(file); + if (binary) { + return binary; + } + if (readBinary) { + return readBinary(file); + } + throw 'sync fetching of the wasm failed: you can preload it to Module["wasmBinary"] manually, or emcc.py will do that for you when generating HTML (but not JS)'; + } + function instantiateSync(file, info) { + var module; + var binary = getBinarySync(file); + module = new WebAssembly.Module(binary); + var instance = new WebAssembly.Instance(module, info); + return [instance, module]; + } + function getWasmImports() { + return { + a: wasmImports + }; + } + function createWasm() { + function receiveInstance(instance, module) { + wasmExports = instance.exports; + wasmMemory = wasmExports["t"]; + updateMemoryViews(); + addOnInit(wasmExports["u"]); + removeRunDependency("wasm-instantiate"); + return wasmExports; + } + addRunDependency("wasm-instantiate"); + var info = getWasmImports(); + if (Module["instantiateWasm"]) { + try { + return Module["instantiateWasm"](info, receiveInstance); + } catch (e) { + err(`Module.instantiateWasm callback failed with error: ${e}`); + readyPromiseReject(e); + } + } + wasmBinaryFile ??= findWasmBinary(); + var result = instantiateSync(wasmBinaryFile, info); + return receiveInstance(result[0]); + } + class ExitStatus { + name = "ExitStatus"; + constructor(status) { + this.message = `Program terminated with exit(${status})`; + this.status = status; + } + } + var callRuntimeCallbacks = callbacks => { + while (callbacks.length > 0) { + callbacks.shift()(Module); + } + }; + var noExitRuntime = Module["noExitRuntime"] || true; + var __abort_js = () => abort(""); + var __emscripten_memcpy_js = (dest, src, num) => HEAPU8.copyWithin(dest, src, src + num); + var runtimeKeepaliveCounter = 0; + var __emscripten_runtime_keepalive_clear = () => { + noExitRuntime = false; + runtimeKeepaliveCounter = 0; + }; + var timers = {}; + var handleException = e => { + if (e instanceof ExitStatus || e == "unwind") { + return EXITSTATUS; + } + quit_(1, e); + }; + var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0; + var _proc_exit = code => { + EXITSTATUS = code; + if (!keepRuntimeAlive()) { + Module["onExit"]?.(code); + ABORT = true; + } + quit_(code, new ExitStatus(code)); + }; + var exitJS = (status, implicit) => { + EXITSTATUS = status; + _proc_exit(status); + }; + var _exit = exitJS; + var maybeExit = () => { + if (!keepRuntimeAlive()) { + try { + _exit(EXITSTATUS); + } catch (e) { + handleException(e); + } + } + }; + var callUserCallback = func => { + if (ABORT) { + return; + } + try { + func(); + maybeExit(); + } catch (e) { + handleException(e); + } + }; + var _emscripten_get_now = () => performance.now(); + var __setitimer_js = (which, timeout_ms) => { + if (timers[which]) { + clearTimeout(timers[which].id); + delete timers[which]; + } + if (!timeout_ms) return 0; + var id = setTimeout(() => { + delete timers[which]; + callUserCallback(() => __emscripten_timeout(which, _emscripten_get_now())); + }, timeout_ms); + timers[which] = { + id, + timeout_ms + }; + return 0; + }; + function _copy_pixels_1(compG_ptr, nb_pixels) { + compG_ptr >>= 2; + const imageData = Module.imageData = new Uint8ClampedArray(nb_pixels); + const compG = Module.HEAP32.subarray(compG_ptr, compG_ptr + nb_pixels); + imageData.set(compG); + } + function _copy_pixels_3(compR_ptr, compG_ptr, compB_ptr, nb_pixels) { + compR_ptr >>= 2; + compG_ptr >>= 2; + compB_ptr >>= 2; + const imageData = Module.imageData = new Uint8ClampedArray(nb_pixels * 3); + const compR = Module.HEAP32.subarray(compR_ptr, compR_ptr + nb_pixels); + const compG = Module.HEAP32.subarray(compG_ptr, compG_ptr + nb_pixels); + const compB = Module.HEAP32.subarray(compB_ptr, compB_ptr + nb_pixels); + for (let i = 0; i < nb_pixels; i++) { + imageData[3 * i] = compR[i]; + imageData[3 * i + 1] = compG[i]; + imageData[3 * i + 2] = compB[i]; + } + } + function _copy_pixels_4(compR_ptr, compG_ptr, compB_ptr, compA_ptr, nb_pixels) { + compR_ptr >>= 2; + compG_ptr >>= 2; + compB_ptr >>= 2; + compA_ptr >>= 2; + const imageData = Module.imageData = new Uint8ClampedArray(nb_pixels * 4); + const compR = Module.HEAP32.subarray(compR_ptr, compR_ptr + nb_pixels); + const compG = Module.HEAP32.subarray(compG_ptr, compG_ptr + nb_pixels); + const compB = Module.HEAP32.subarray(compB_ptr, compB_ptr + nb_pixels); + const compA = Module.HEAP32.subarray(compA_ptr, compA_ptr + nb_pixels); + for (let i = 0; i < nb_pixels; i++) { + imageData[4 * i] = compR[i]; + imageData[4 * i + 1] = compG[i]; + imageData[4 * i + 2] = compB[i]; + imageData[4 * i + 3] = compA[i]; + } + } + var getHeapMax = () => 2147483648; + var alignMemory = (size, alignment) => Math.ceil(size / alignment) * alignment; + var growMemory = size => { + var b = wasmMemory.buffer; + var pages = (size - b.byteLength + 65535) / 65536 | 0; + try { + wasmMemory.grow(pages); + updateMemoryViews(); + return 1; + } catch (e) {} + }; + var _emscripten_resize_heap = requestedSize => { + var oldSize = HEAPU8.length; + requestedSize >>>= 0; + var maxHeapSize = getHeapMax(); + if (requestedSize > maxHeapSize) { + return false; + } + for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { + var overGrownHeapSize = oldSize * (1 + .2 / cutDown); + overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296); + var newSize = Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), 65536)); + var replacement = growMemory(newSize); + if (replacement) { + return true; + } + } + return false; + }; + var ENV = {}; + var getExecutableName = () => thisProgram || "./this.program"; + var getEnvStrings = () => { + if (!getEnvStrings.strings) { + var lang = (typeof navigator == "object" && navigator.languages && navigator.languages[0] || "C").replace("-", "_") + ".UTF-8"; + var env = { + USER: "web_user", + LOGNAME: "web_user", + PATH: "/", + PWD: "/", + HOME: "/home/web_user", + LANG: lang, + _: getExecutableName() + }; + for (var x in ENV) { + if (ENV[x] === undefined) delete env[x];else env[x] = ENV[x]; + } + var strings = []; + for (var x in env) { + strings.push(`${x}=${env[x]}`); + } + getEnvStrings.strings = strings; + } + return getEnvStrings.strings; + }; + var stringToAscii = (str, buffer) => { + for (var i = 0; i < str.length; ++i) { + HEAP8[buffer++] = str.charCodeAt(i); + } + HEAP8[buffer] = 0; + }; + var _environ_get = (__environ, environ_buf) => { + var bufSize = 0; + getEnvStrings().forEach((string, i) => { + var ptr = environ_buf + bufSize; + HEAPU32[__environ + i * 4 >> 2] = ptr; + stringToAscii(string, ptr); + bufSize += string.length + 1; + }); + return 0; + }; + var _environ_sizes_get = (penviron_count, penviron_buf_size) => { + var strings = getEnvStrings(); + HEAPU32[penviron_count >> 2] = strings.length; + var bufSize = 0; + strings.forEach(string => bufSize += string.length + 1); + HEAPU32[penviron_buf_size >> 2] = bufSize; + return 0; + }; + var _fd_close = fd => 52; + var convertI32PairToI53Checked = (lo, hi) => hi + 2097152 >>> 0 < 4194305 - !!lo ? (lo >>> 0) + hi * 4294967296 : NaN; + function _fd_seek(fd, offset_low, offset_high, whence, newOffset) { + var offset = convertI32PairToI53Checked(offset_low, offset_high); + return 70; + } + var printCharBuffers = [null, [], []]; + var UTF8Decoder = typeof TextDecoder != "undefined" ? new TextDecoder() : undefined; + var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead = NaN) => { + var endIdx = idx + maxBytesToRead; + var endPtr = idx; + while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; + if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { + return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); + } + var str = ""; + while (idx < endPtr) { + var u0 = heapOrArray[idx++]; + if (!(u0 & 128)) { + str += String.fromCharCode(u0); + continue; + } + var u1 = heapOrArray[idx++] & 63; + if ((u0 & 224) == 192) { + str += String.fromCharCode((u0 & 31) << 6 | u1); + continue; + } + var u2 = heapOrArray[idx++] & 63; + if ((u0 & 240) == 224) { + u0 = (u0 & 15) << 12 | u1 << 6 | u2; + } else { + u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | heapOrArray[idx++] & 63; + } + if (u0 < 65536) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 65536; + str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023); + } + } + return str; + }; + var printChar = (stream, curr) => { + var buffer = printCharBuffers[stream]; + if (curr === 0 || curr === 10) { + (stream === 1 ? out : err)(UTF8ArrayToString(buffer)); + buffer.length = 0; + } else { + buffer.push(curr); + } + }; + var UTF8ToString = (ptr, maxBytesToRead) => ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ""; + var _fd_write = (fd, iov, iovcnt, pnum) => { + var num = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAPU32[iov >> 2]; + var len = HEAPU32[iov + 4 >> 2]; + iov += 8; + for (var j = 0; j < len; j++) { + printChar(fd, HEAPU8[ptr + j]); + } + num += len; + } + HEAPU32[pnum >> 2] = num; + return 0; + }; + function _gray_to_rgba(compG_ptr, nb_pixels) { + compG_ptr >>= 2; + const imageData = Module.imageData = new Uint8ClampedArray(nb_pixels * 4); + const compG = Module.HEAP32.subarray(compG_ptr, compG_ptr + nb_pixels); + for (let i = 0; i < nb_pixels; i++) { + imageData[4 * i] = imageData[4 * i + 1] = imageData[4 * i + 2] = compG[i]; + imageData[4 * i + 3] = 255; + } + } + function _graya_to_rgba(compG_ptr, compA_ptr, nb_pixels) { + compG_ptr >>= 2; + compA_ptr >>= 2; + const imageData = Module.imageData = new Uint8ClampedArray(nb_pixels * 4); + const compG = Module.HEAP32.subarray(compG_ptr, compG_ptr + nb_pixels); + const compA = Module.HEAP32.subarray(compA_ptr, compA_ptr + nb_pixels); + for (let i = 0; i < nb_pixels; i++) { + imageData[4 * i] = imageData[4 * i + 1] = imageData[4 * i + 2] = compG[i]; + imageData[4 * i + 3] = compA[i]; + } + } + function _jsPrintWarning(message_ptr) { + const message = UTF8ToString(message_ptr); + (Module.warn || console.warn)(`OpenJPEG: ${message}`); + } + function _rgb_to_rgba(compR_ptr, compG_ptr, compB_ptr, nb_pixels) { + compR_ptr >>= 2; + compG_ptr >>= 2; + compB_ptr >>= 2; + const imageData = Module.imageData = new Uint8ClampedArray(nb_pixels * 4); + const compR = Module.HEAP32.subarray(compR_ptr, compR_ptr + nb_pixels); + const compG = Module.HEAP32.subarray(compG_ptr, compG_ptr + nb_pixels); + const compB = Module.HEAP32.subarray(compB_ptr, compB_ptr + nb_pixels); + for (let i = 0; i < nb_pixels; i++) { + imageData[4 * i] = compR[i]; + imageData[4 * i + 1] = compG[i]; + imageData[4 * i + 2] = compB[i]; + imageData[4 * i + 3] = 255; + } + } + function _storeErrorMessage(message_ptr) { + const message = UTF8ToString(message_ptr); + if (!Module.errorMessages) { + Module.errorMessages = message; + } else { + Module.errorMessages += "\n" + message; + } + } + var wasmImports = { + m: __abort_js, + c: __emscripten_memcpy_js, + l: __emscripten_runtime_keepalive_clear, + n: __setitimer_js, + g: _copy_pixels_1, + f: _copy_pixels_3, + e: _copy_pixels_4, + o: _emscripten_resize_heap, + p: _environ_get, + q: _environ_sizes_get, + r: _fd_close, + j: _fd_seek, + b: _fd_write, + s: _gray_to_rgba, + i: _graya_to_rgba, + d: _jsPrintWarning, + k: _proc_exit, + h: _rgb_to_rgba, + a: _storeErrorMessage + }; + var wasmExports = createWasm(); + var ___wasm_call_ctors = wasmExports["u"]; + var _malloc = Module["_malloc"] = wasmExports["v"]; + var _free = Module["_free"] = wasmExports["w"]; + var _jp2_decode = Module["_jp2_decode"] = wasmExports["y"]; + var __emscripten_timeout = wasmExports["z"]; + var calledRun; + dependenciesFulfilled = function runCaller() { + if (!calledRun) run(); + if (!calledRun) dependenciesFulfilled = runCaller; + }; + function run() { + if (runDependencies > 0) { + return; + } + preRun(); + if (runDependencies > 0) { + return; + } + function doRun() { + if (calledRun) return; + calledRun = true; + Module["calledRun"] = true; + if (ABORT) return; + initRuntime(); + readyPromiseResolve(Module); + Module["onRuntimeInitialized"]?.(); + postRun(); + } + if (Module["setStatus"]) { + Module["setStatus"]("Running..."); + setTimeout(() => { + setTimeout(() => Module["setStatus"](""), 1); + doRun(); + }, 1); + } else { + doRun(); + } + } + if (Module["preInit"]) { + if (typeof Module["preInit"] == "function") Module["preInit"] = [Module["preInit"]]; + while (Module["preInit"].length > 0) { + Module["preInit"].pop()(); + } + } + run(); + moduleRtn = Module; + return moduleRtn; + }; +})(); +/* harmony default export */ const openjpeg = (OpenJPEG); +;// ./src/core/jpx.js + + + +class JpxError extends BaseException { + constructor(msg) { + super(msg, "JpxError"); + } +} +class JpxImage { + static #module = null; + static decode(data, decoderOptions) { + decoderOptions ||= {}; + this.#module ||= openjpeg({ + warn: warn + }); + const imageData = this.#module.decode(data, decoderOptions); + if (typeof imageData === "string") { + throw new JpxError(imageData); + } + return imageData; + } + static cleanup() { + this.#module = null; + } + static parseImageProperties(stream) { + let newByte = stream.getByte(); + while (newByte >= 0) { + const oldByte = newByte; + newByte = stream.getByte(); + const code = oldByte << 8 | newByte; + if (code === 0xff51) { + stream.skip(4); + const Xsiz = stream.getInt32() >>> 0; + const Ysiz = stream.getInt32() >>> 0; + const XOsiz = stream.getInt32() >>> 0; + const YOsiz = stream.getInt32() >>> 0; + stream.skip(16); + const Csiz = stream.getUint16(); + return { + width: Xsiz - XOsiz, + height: Ysiz - YOsiz, + bitsPerComponent: 8, + componentsCount: Csiz + }; + } + } + throw new JpxError("No size marker found in JPX stream"); + } +} + +;// ./src/core/jpx_stream.js + + + +class JpxStream extends DecodeStream { + constructor(stream, maybeLength, params) { + super(maybeLength); + this.stream = stream; + this.dict = stream.dict; + this.maybeLength = maybeLength; + this.params = params; + } + get bytes() { + return shadow(this, "bytes", this.stream.getBytes(this.maybeLength)); + } + ensureBuffer(requested) {} + readBlock(decoderOptions) { + this.decodeImage(null, decoderOptions); + } + decodeImage(bytes, decoderOptions) { + if (this.eof) { + return this.buffer; + } + bytes ||= this.bytes; + this.buffer = JpxImage.decode(bytes, decoderOptions); + this.bufferLength = this.buffer.length; + this.eof = true; + return this.buffer; + } + get canAsyncDecodeImageFromBuffer() { + return this.stream.isAsync; + } +} + +;// ./src/core/lzw_stream.js + +class LZWStream extends DecodeStream { + constructor(str, maybeLength, earlyChange) { + super(maybeLength); + this.str = str; + this.dict = str.dict; + this.cachedData = 0; + this.bitsCached = 0; + const maxLzwDictionarySize = 4096; + const lzwState = { + earlyChange, + codeLength: 9, + nextCode: 258, + dictionaryValues: new Uint8Array(maxLzwDictionarySize), + dictionaryLengths: new Uint16Array(maxLzwDictionarySize), + dictionaryPrevCodes: new Uint16Array(maxLzwDictionarySize), + currentSequence: new Uint8Array(maxLzwDictionarySize), + currentSequenceLength: 0 + }; + for (let i = 0; i < 256; ++i) { + lzwState.dictionaryValues[i] = i; + lzwState.dictionaryLengths[i] = 1; + } + this.lzwState = lzwState; + } + readBits(n) { + let bitsCached = this.bitsCached; + let cachedData = this.cachedData; + while (bitsCached < n) { + const c = this.str.getByte(); + if (c === -1) { + this.eof = true; + return null; + } + cachedData = cachedData << 8 | c; + bitsCached += 8; + } + this.bitsCached = bitsCached -= n; + this.cachedData = cachedData; + this.lastCode = null; + return cachedData >>> bitsCached & (1 << n) - 1; + } + readBlock() { + const blockSize = 512, + decodedSizeDelta = blockSize; + let estimatedDecodedSize = blockSize * 2; + let i, j, q; + const lzwState = this.lzwState; + if (!lzwState) { + return; + } + const earlyChange = lzwState.earlyChange; + let nextCode = lzwState.nextCode; + const dictionaryValues = lzwState.dictionaryValues; + const dictionaryLengths = lzwState.dictionaryLengths; + const dictionaryPrevCodes = lzwState.dictionaryPrevCodes; + let codeLength = lzwState.codeLength; + let prevCode = lzwState.prevCode; + const currentSequence = lzwState.currentSequence; + let currentSequenceLength = lzwState.currentSequenceLength; + let decodedLength = 0; + let currentBufferLength = this.bufferLength; + let buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize); + for (i = 0; i < blockSize; i++) { + const code = this.readBits(codeLength); + const hasPrev = currentSequenceLength > 0; + if (code < 256) { + currentSequence[0] = code; + currentSequenceLength = 1; + } else if (code >= 258) { + if (code < nextCode) { + currentSequenceLength = dictionaryLengths[code]; + for (j = currentSequenceLength - 1, q = code; j >= 0; j--) { + currentSequence[j] = dictionaryValues[q]; + q = dictionaryPrevCodes[q]; + } + } else { + currentSequence[currentSequenceLength++] = currentSequence[0]; + } + } else if (code === 256) { + codeLength = 9; + nextCode = 258; + currentSequenceLength = 0; + continue; + } else { + this.eof = true; + delete this.lzwState; + break; + } + if (hasPrev) { + dictionaryPrevCodes[nextCode] = prevCode; + dictionaryLengths[nextCode] = dictionaryLengths[prevCode] + 1; + dictionaryValues[nextCode] = currentSequence[0]; + nextCode++; + codeLength = nextCode + earlyChange & nextCode + earlyChange - 1 ? codeLength : Math.min(Math.log(nextCode + earlyChange) / 0.6931471805599453 + 1, 12) | 0; + } + prevCode = code; + decodedLength += currentSequenceLength; + if (estimatedDecodedSize < decodedLength) { + do { + estimatedDecodedSize += decodedSizeDelta; + } while (estimatedDecodedSize < decodedLength); + buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize); + } + for (j = 0; j < currentSequenceLength; j++) { + buffer[currentBufferLength++] = currentSequence[j]; + } + } + lzwState.nextCode = nextCode; + lzwState.codeLength = codeLength; + lzwState.prevCode = prevCode; + lzwState.currentSequenceLength = currentSequenceLength; + this.bufferLength = currentBufferLength; + } +} + +;// ./src/core/predictor_stream.js + + + +class PredictorStream extends DecodeStream { + constructor(str, maybeLength, params) { + super(maybeLength); + if (!(params instanceof Dict)) { + return str; + } + const predictor = this.predictor = params.get("Predictor") || 1; + if (predictor <= 1) { + return str; + } + if (predictor !== 2 && (predictor < 10 || predictor > 15)) { + throw new FormatError(`Unsupported predictor: ${predictor}`); + } + this.readBlock = predictor === 2 ? this.readBlockTiff : this.readBlockPng; + this.str = str; + this.dict = str.dict; + const colors = this.colors = params.get("Colors") || 1; + const bits = this.bits = params.get("BPC", "BitsPerComponent") || 8; + const columns = this.columns = params.get("Columns") || 1; + this.pixBytes = colors * bits + 7 >> 3; + this.rowBytes = columns * colors * bits + 7 >> 3; + return this; + } + readBlockTiff() { + const rowBytes = this.rowBytes; + const bufferLength = this.bufferLength; + const buffer = this.ensureBuffer(bufferLength + rowBytes); + const bits = this.bits; + const colors = this.colors; + const rawBytes = this.str.getBytes(rowBytes); + this.eof = !rawBytes.length; + if (this.eof) { + return; + } + let inbuf = 0, + outbuf = 0; + let inbits = 0, + outbits = 0; + let pos = bufferLength; + let i; + if (bits === 1 && colors === 1) { + for (i = 0; i < rowBytes; ++i) { + let c = rawBytes[i] ^ inbuf; + c ^= c >> 1; + c ^= c >> 2; + c ^= c >> 4; + inbuf = (c & 1) << 7; + buffer[pos++] = c; + } + } else if (bits === 8) { + for (i = 0; i < colors; ++i) { + buffer[pos++] = rawBytes[i]; + } + for (; i < rowBytes; ++i) { + buffer[pos] = buffer[pos - colors] + rawBytes[i]; + pos++; + } + } else if (bits === 16) { + const bytesPerPixel = colors * 2; + for (i = 0; i < bytesPerPixel; ++i) { + buffer[pos++] = rawBytes[i]; + } + for (; i < rowBytes; i += 2) { + const sum = ((rawBytes[i] & 0xff) << 8) + (rawBytes[i + 1] & 0xff) + ((buffer[pos - bytesPerPixel] & 0xff) << 8) + (buffer[pos - bytesPerPixel + 1] & 0xff); + buffer[pos++] = sum >> 8 & 0xff; + buffer[pos++] = sum & 0xff; + } + } else { + const compArray = new Uint8Array(colors + 1); + const bitMask = (1 << bits) - 1; + let j = 0, + k = bufferLength; + const columns = this.columns; + for (i = 0; i < columns; ++i) { + for (let kk = 0; kk < colors; ++kk) { + if (inbits < bits) { + inbuf = inbuf << 8 | rawBytes[j++] & 0xff; + inbits += 8; + } + compArray[kk] = compArray[kk] + (inbuf >> inbits - bits) & bitMask; + inbits -= bits; + outbuf = outbuf << bits | compArray[kk]; + outbits += bits; + if (outbits >= 8) { + buffer[k++] = outbuf >> outbits - 8 & 0xff; + outbits -= 8; + } + } + } + if (outbits > 0) { + buffer[k++] = (outbuf << 8 - outbits) + (inbuf & (1 << 8 - outbits) - 1); + } + } + this.bufferLength += rowBytes; + } + readBlockPng() { + const rowBytes = this.rowBytes; + const pixBytes = this.pixBytes; + const predictor = this.str.getByte(); + const rawBytes = this.str.getBytes(rowBytes); + this.eof = !rawBytes.length; + if (this.eof) { + return; + } + const bufferLength = this.bufferLength; + const buffer = this.ensureBuffer(bufferLength + rowBytes); + let prevRow = buffer.subarray(bufferLength - rowBytes, bufferLength); + if (prevRow.length === 0) { + prevRow = new Uint8Array(rowBytes); + } + let i, + j = bufferLength, + up, + c; + switch (predictor) { + case 0: + for (i = 0; i < rowBytes; ++i) { + buffer[j++] = rawBytes[i]; + } + break; + case 1: + for (i = 0; i < pixBytes; ++i) { + buffer[j++] = rawBytes[i]; + } + for (; i < rowBytes; ++i) { + buffer[j] = buffer[j - pixBytes] + rawBytes[i] & 0xff; + j++; + } + break; + case 2: + for (i = 0; i < rowBytes; ++i) { + buffer[j++] = prevRow[i] + rawBytes[i] & 0xff; + } + break; + case 3: + for (i = 0; i < pixBytes; ++i) { + buffer[j++] = (prevRow[i] >> 1) + rawBytes[i]; + } + for (; i < rowBytes; ++i) { + buffer[j] = (prevRow[i] + buffer[j - pixBytes] >> 1) + rawBytes[i] & 0xff; + j++; + } + break; + case 4: + for (i = 0; i < pixBytes; ++i) { + up = prevRow[i]; + c = rawBytes[i]; + buffer[j++] = up + c; + } + for (; i < rowBytes; ++i) { + up = prevRow[i]; + const upLeft = prevRow[i - pixBytes]; + const left = buffer[j - pixBytes]; + const p = left + up - upLeft; + let pa = p - left; + if (pa < 0) { + pa = -pa; + } + let pb = p - up; + if (pb < 0) { + pb = -pb; + } + let pc = p - upLeft; + if (pc < 0) { + pc = -pc; + } + c = rawBytes[i]; + if (pa <= pb && pa <= pc) { + buffer[j++] = left + c; + } else if (pb <= pc) { + buffer[j++] = up + c; + } else { + buffer[j++] = upLeft + c; + } + } + break; + default: + throw new FormatError(`Unsupported predictor: ${predictor}`); + } + this.bufferLength += rowBytes; + } +} + +;// ./src/core/run_length_stream.js + +class RunLengthStream extends DecodeStream { + constructor(str, maybeLength) { + super(maybeLength); + this.str = str; + this.dict = str.dict; + } + readBlock() { + const repeatHeader = this.str.getBytes(2); + if (!repeatHeader || repeatHeader.length < 2 || repeatHeader[0] === 128) { + this.eof = true; + return; + } + let buffer; + let bufferLength = this.bufferLength; + let n = repeatHeader[0]; + if (n < 128) { + buffer = this.ensureBuffer(bufferLength + n + 1); + buffer[bufferLength++] = repeatHeader[1]; + if (n > 0) { + const source = this.str.getBytes(n); + buffer.set(source, bufferLength); + bufferLength += n; + } + } else { + n = 257 - n; + const b = repeatHeader[1]; + buffer = this.ensureBuffer(bufferLength + n + 1); + for (let i = 0; i < n; i++) { + buffer[bufferLength++] = b; + } + } + this.bufferLength = bufferLength; + } +} + +;// ./src/core/parser.js + + + + + + + + + + + + + + +const MAX_LENGTH_TO_CACHE = 1000; +function getInlineImageCacheKey(bytes) { + const strBuf = [], + ii = bytes.length; + let i = 0; + while (i < ii - 1) { + strBuf.push(bytes[i++] << 8 | bytes[i++]); + } + if (i < ii) { + strBuf.push(bytes[i]); + } + return ii + "_" + String.fromCharCode.apply(null, strBuf); +} +class Parser { + constructor({ + lexer, + xref, + allowStreams = false, + recoveryMode = false + }) { + this.lexer = lexer; + this.xref = xref; + this.allowStreams = allowStreams; + this.recoveryMode = recoveryMode; + this.imageCache = Object.create(null); + this._imageId = 0; + this.refill(); + } + refill() { + this.buf1 = this.lexer.getObj(); + this.buf2 = this.lexer.getObj(); + } + shift() { + if (this.buf2 instanceof Cmd && this.buf2.cmd === "ID") { + this.buf1 = this.buf2; + this.buf2 = null; + } else { + this.buf1 = this.buf2; + this.buf2 = this.lexer.getObj(); + } + } + tryShift() { + try { + this.shift(); + return true; + } catch (e) { + if (e instanceof MissingDataException) { + throw e; + } + return false; + } + } + getObj(cipherTransform = null) { + const buf1 = this.buf1; + this.shift(); + if (buf1 instanceof Cmd) { + switch (buf1.cmd) { + case "BI": + return this.makeInlineImage(cipherTransform); + case "[": + const array = []; + while (!isCmd(this.buf1, "]") && this.buf1 !== EOF) { + array.push(this.getObj(cipherTransform)); + } + if (this.buf1 === EOF) { + if (this.recoveryMode) { + return array; + } + throw new ParserEOFException("End of file inside array."); + } + this.shift(); + return array; + case "<<": + const dict = new Dict(this.xref); + while (!isCmd(this.buf1, ">>") && this.buf1 !== EOF) { + if (!(this.buf1 instanceof Name)) { + info("Malformed dictionary: key must be a name object"); + this.shift(); + continue; + } + const key = this.buf1.name; + this.shift(); + if (this.buf1 === EOF) { + break; + } + dict.set(key, this.getObj(cipherTransform)); + } + if (this.buf1 === EOF) { + if (this.recoveryMode) { + return dict; + } + throw new ParserEOFException("End of file inside dictionary."); + } + if (isCmd(this.buf2, "stream")) { + return this.allowStreams ? this.makeStream(dict, cipherTransform) : dict; + } + this.shift(); + return dict; + default: + return buf1; + } + } + if (Number.isInteger(buf1)) { + if (Number.isInteger(this.buf1) && isCmd(this.buf2, "R")) { + const ref = Ref.get(buf1, this.buf1); + this.shift(); + this.shift(); + return ref; + } + return buf1; + } + if (typeof buf1 === "string") { + if (cipherTransform) { + return cipherTransform.decryptString(buf1); + } + return buf1; + } + return buf1; + } + findDefaultInlineStreamEnd(stream) { + const E = 0x45, + I = 0x49, + SPACE = 0x20, + LF = 0xa, + CR = 0xd, + NUL = 0x0; + const { + knownCommands + } = this.lexer, + startPos = stream.pos, + n = 15; + let state = 0, + ch, + maybeEIPos; + while ((ch = stream.getByte()) !== -1) { + if (state === 0) { + state = ch === E ? 1 : 0; + } else if (state === 1) { + state = ch === I ? 2 : 0; + } else { + if (ch === SPACE || ch === LF || ch === CR) { + maybeEIPos = stream.pos; + const followingBytes = stream.peekBytes(n); + const ii = followingBytes.length; + if (ii === 0) { + break; + } + for (let i = 0; i < ii; i++) { + ch = followingBytes[i]; + if (ch === NUL && followingBytes[i + 1] !== NUL) { + continue; + } + if (ch !== LF && ch !== CR && (ch < SPACE || ch > 0x7f)) { + state = 0; + break; + } + } + if (state !== 2) { + continue; + } + if (!knownCommands) { + warn("findDefaultInlineStreamEnd - `lexer.knownCommands` is undefined."); + continue; + } + const tmpLexer = new Lexer(new Stream(followingBytes.slice()), knownCommands); + tmpLexer._hexStringWarn = () => {}; + let numArgs = 0; + while (true) { + const nextObj = tmpLexer.getObj(); + if (nextObj === EOF) { + state = 0; + break; + } + if (nextObj instanceof Cmd) { + const knownCommand = knownCommands[nextObj.cmd]; + if (!knownCommand) { + state = 0; + break; + } else if (knownCommand.variableArgs ? numArgs <= knownCommand.numArgs : numArgs === knownCommand.numArgs) { + break; + } + numArgs = 0; + continue; + } + numArgs++; + } + if (state === 2) { + break; + } + } else { + state = 0; + } + } + } + if (ch === -1) { + warn("findDefaultInlineStreamEnd: " + "Reached the end of the stream without finding a valid EI marker"); + if (maybeEIPos) { + warn('... trying to recover by using the last "EI" occurrence.'); + stream.skip(-(stream.pos - maybeEIPos)); + } + } + let endOffset = 4; + stream.skip(-endOffset); + ch = stream.peekByte(); + stream.skip(endOffset); + if (!isWhiteSpace(ch)) { + endOffset--; + } + return stream.pos - endOffset - startPos; + } + findDCTDecodeInlineStreamEnd(stream) { + const startPos = stream.pos; + let foundEOI = false, + b, + markerLength; + while ((b = stream.getByte()) !== -1) { + if (b !== 0xff) { + continue; + } + switch (stream.getByte()) { + case 0x00: + break; + case 0xff: + stream.skip(-1); + break; + case 0xd9: + foundEOI = true; + break; + case 0xc0: + case 0xc1: + case 0xc2: + case 0xc3: + case 0xc5: + case 0xc6: + case 0xc7: + case 0xc9: + case 0xca: + case 0xcb: + case 0xcd: + case 0xce: + case 0xcf: + case 0xc4: + case 0xcc: + case 0xda: + case 0xdb: + case 0xdc: + case 0xdd: + case 0xde: + case 0xdf: + case 0xe0: + case 0xe1: + case 0xe2: + case 0xe3: + case 0xe4: + case 0xe5: + case 0xe6: + case 0xe7: + case 0xe8: + case 0xe9: + case 0xea: + case 0xeb: + case 0xec: + case 0xed: + case 0xee: + case 0xef: + case 0xfe: + markerLength = stream.getUint16(); + if (markerLength > 2) { + stream.skip(markerLength - 2); + } else { + stream.skip(-2); + } + break; + } + if (foundEOI) { + break; + } + } + const length = stream.pos - startPos; + if (b === -1) { + warn("Inline DCTDecode image stream: " + "EOI marker not found, searching for /EI/ instead."); + stream.skip(-length); + return this.findDefaultInlineStreamEnd(stream); + } + this.inlineStreamSkipEI(stream); + return length; + } + findASCII85DecodeInlineStreamEnd(stream) { + const TILDE = 0x7e, + GT = 0x3e; + const startPos = stream.pos; + let ch; + while ((ch = stream.getByte()) !== -1) { + if (ch === TILDE) { + const tildePos = stream.pos; + ch = stream.peekByte(); + while (isWhiteSpace(ch)) { + stream.skip(); + ch = stream.peekByte(); + } + if (ch === GT) { + stream.skip(); + break; + } + if (stream.pos > tildePos) { + const maybeEI = stream.peekBytes(2); + if (maybeEI[0] === 0x45 && maybeEI[1] === 0x49) { + break; + } + } + } + } + const length = stream.pos - startPos; + if (ch === -1) { + warn("Inline ASCII85Decode image stream: " + "EOD marker not found, searching for /EI/ instead."); + stream.skip(-length); + return this.findDefaultInlineStreamEnd(stream); + } + this.inlineStreamSkipEI(stream); + return length; + } + findASCIIHexDecodeInlineStreamEnd(stream) { + const GT = 0x3e; + const startPos = stream.pos; + let ch; + while ((ch = stream.getByte()) !== -1) { + if (ch === GT) { + break; + } + } + const length = stream.pos - startPos; + if (ch === -1) { + warn("Inline ASCIIHexDecode image stream: " + "EOD marker not found, searching for /EI/ instead."); + stream.skip(-length); + return this.findDefaultInlineStreamEnd(stream); + } + this.inlineStreamSkipEI(stream); + return length; + } + inlineStreamSkipEI(stream) { + const E = 0x45, + I = 0x49; + let state = 0, + ch; + while ((ch = stream.getByte()) !== -1) { + if (state === 0) { + state = ch === E ? 1 : 0; + } else if (state === 1) { + state = ch === I ? 2 : 0; + } else if (state === 2) { + break; + } + } + } + makeInlineImage(cipherTransform) { + const lexer = this.lexer; + const stream = lexer.stream; + const dictMap = Object.create(null); + let dictLength; + while (!isCmd(this.buf1, "ID") && this.buf1 !== EOF) { + if (!(this.buf1 instanceof Name)) { + throw new FormatError("Dictionary key must be a name object"); + } + const key = this.buf1.name; + this.shift(); + if (this.buf1 === EOF) { + break; + } + dictMap[key] = this.getObj(cipherTransform); + } + if (lexer.beginInlineImagePos !== -1) { + dictLength = stream.pos - lexer.beginInlineImagePos; + } + const filter = this.xref.fetchIfRef(dictMap.F || dictMap.Filter); + let filterName; + if (filter instanceof Name) { + filterName = filter.name; + } else if (Array.isArray(filter)) { + const filterZero = this.xref.fetchIfRef(filter[0]); + if (filterZero instanceof Name) { + filterName = filterZero.name; + } + } + const startPos = stream.pos; + let length; + switch (filterName) { + case "DCT": + case "DCTDecode": + length = this.findDCTDecodeInlineStreamEnd(stream); + break; + case "A85": + case "ASCII85Decode": + length = this.findASCII85DecodeInlineStreamEnd(stream); + break; + case "AHx": + case "ASCIIHexDecode": + length = this.findASCIIHexDecodeInlineStreamEnd(stream); + break; + default: + length = this.findDefaultInlineStreamEnd(stream); + } + let cacheKey; + if (length < MAX_LENGTH_TO_CACHE && dictLength > 0) { + const initialStreamPos = stream.pos; + stream.pos = lexer.beginInlineImagePos; + cacheKey = getInlineImageCacheKey(stream.getBytes(dictLength + length)); + stream.pos = initialStreamPos; + const cacheEntry = this.imageCache[cacheKey]; + if (cacheEntry !== undefined) { + this.buf2 = Cmd.get("EI"); + this.shift(); + cacheEntry.reset(); + return cacheEntry; + } + } + const dict = new Dict(this.xref); + for (const key in dictMap) { + dict.set(key, dictMap[key]); + } + let imageStream = stream.makeSubStream(startPos, length, dict); + if (cipherTransform) { + imageStream = cipherTransform.createStream(imageStream, length); + } + imageStream = this.filter(imageStream, dict, length); + imageStream.dict = dict; + if (cacheKey !== undefined) { + imageStream.cacheKey = `inline_img_${++this._imageId}`; + this.imageCache[cacheKey] = imageStream; + } + this.buf2 = Cmd.get("EI"); + this.shift(); + return imageStream; + } + #findStreamLength(startPos) { + const { + stream + } = this.lexer; + stream.pos = startPos; + const SCAN_BLOCK_LENGTH = 2048; + const signatureLength = "endstream".length; + const END_SIGNATURE = new Uint8Array([0x65, 0x6e, 0x64]); + const endLength = END_SIGNATURE.length; + const PARTIAL_SIGNATURE = [new Uint8Array([0x73, 0x74, 0x72, 0x65, 0x61, 0x6d]), new Uint8Array([0x73, 0x74, 0x65, 0x61, 0x6d]), new Uint8Array([0x73, 0x74, 0x72, 0x65, 0x61])]; + const normalLength = signatureLength - endLength; + while (stream.pos < stream.end) { + const scanBytes = stream.peekBytes(SCAN_BLOCK_LENGTH); + const scanLength = scanBytes.length - signatureLength; + if (scanLength <= 0) { + break; + } + let pos = 0; + while (pos < scanLength) { + let j = 0; + while (j < endLength && scanBytes[pos + j] === END_SIGNATURE[j]) { + j++; + } + if (j >= endLength) { + let found = false; + for (const part of PARTIAL_SIGNATURE) { + const partLen = part.length; + let k = 0; + while (k < partLen && scanBytes[pos + j + k] === part[k]) { + k++; + } + if (k >= normalLength) { + found = true; + break; + } + if (k >= partLen) { + const lastByte = scanBytes[pos + j + k]; + if (isWhiteSpace(lastByte)) { + info(`Found "${bytesToString([...END_SIGNATURE, ...part])}" when ` + "searching for endstream command."); + found = true; + } + break; + } + } + if (found) { + stream.pos += pos; + return stream.pos - startPos; + } + } + pos++; + } + stream.pos += scanLength; + } + return -1; + } + makeStream(dict, cipherTransform) { + const lexer = this.lexer; + let stream = lexer.stream; + lexer.skipToNextLine(); + const startPos = stream.pos - 1; + let length = dict.get("Length"); + if (!Number.isInteger(length)) { + info(`Bad length "${length && length.toString()}" in stream.`); + length = 0; + } + stream.pos = startPos + length; + lexer.nextChar(); + if (this.tryShift() && isCmd(this.buf2, "endstream")) { + this.shift(); + } else { + length = this.#findStreamLength(startPos); + if (length < 0) { + throw new FormatError("Missing endstream command."); + } + lexer.nextChar(); + this.shift(); + this.shift(); + } + this.shift(); + stream = stream.makeSubStream(startPos, length, dict); + if (cipherTransform) { + stream = cipherTransform.createStream(stream, length); + } + stream = this.filter(stream, dict, length); + stream.dict = dict; + return stream; + } + filter(stream, dict, length) { + let filter = dict.get("F", "Filter"); + let params = dict.get("DP", "DecodeParms"); + if (filter instanceof Name) { + if (Array.isArray(params)) { + warn("/DecodeParms should not be an Array, when /Filter is a Name."); + } + return this.makeFilter(stream, filter.name, length, params); + } + let maybeLength = length; + if (Array.isArray(filter)) { + const filterArray = filter; + const paramsArray = params; + for (let i = 0, ii = filterArray.length; i < ii; ++i) { + filter = this.xref.fetchIfRef(filterArray[i]); + if (!(filter instanceof Name)) { + throw new FormatError(`Bad filter name "${filter}"`); + } + params = null; + if (Array.isArray(paramsArray) && i in paramsArray) { + params = this.xref.fetchIfRef(paramsArray[i]); + } + stream = this.makeFilter(stream, filter.name, maybeLength, params); + maybeLength = null; + } + } + return stream; + } + makeFilter(stream, name, maybeLength, params) { + if (maybeLength === 0) { + warn(`Empty "${name}" stream.`); + return new NullStream(); + } + try { + switch (name) { + case "Fl": + case "FlateDecode": + if (params) { + return new PredictorStream(new FlateStream(stream, maybeLength), maybeLength, params); + } + return new FlateStream(stream, maybeLength); + case "LZW": + case "LZWDecode": + let earlyChange = 1; + if (params) { + if (params.has("EarlyChange")) { + earlyChange = params.get("EarlyChange"); + } + return new PredictorStream(new LZWStream(stream, maybeLength, earlyChange), maybeLength, params); + } + return new LZWStream(stream, maybeLength, earlyChange); + case "DCT": + case "DCTDecode": + return new JpegStream(stream, maybeLength, params); + case "JPX": + case "JPXDecode": + return new JpxStream(stream, maybeLength, params); + case "A85": + case "ASCII85Decode": + return new Ascii85Stream(stream, maybeLength); + case "AHx": + case "ASCIIHexDecode": + return new AsciiHexStream(stream, maybeLength); + case "CCF": + case "CCITTFaxDecode": + return new CCITTFaxStream(stream, maybeLength, params); + case "RL": + case "RunLengthDecode": + return new RunLengthStream(stream, maybeLength); + case "JBIG2Decode": + return new Jbig2Stream(stream, maybeLength, params); + } + warn(`Filter "${name}" is not supported.`); + return stream; + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn(`Invalid stream: "${ex}"`); + return new NullStream(); + } + } +} +const specialChars = [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; +function toHexDigit(ch) { + if (ch >= 0x30 && ch <= 0x39) { + return ch & 0x0f; + } + if (ch >= 0x41 && ch <= 0x46 || ch >= 0x61 && ch <= 0x66) { + return (ch & 0x0f) + 9; + } + return -1; +} +class Lexer { + constructor(stream, knownCommands = null) { + this.stream = stream; + this.nextChar(); + this.strBuf = []; + this.knownCommands = knownCommands; + this._hexStringNumWarn = 0; + this.beginInlineImagePos = -1; + } + nextChar() { + return this.currentChar = this.stream.getByte(); + } + peekChar() { + return this.stream.peekByte(); + } + getNumber() { + let ch = this.currentChar; + let eNotation = false; + let divideBy = 0; + let sign = 1; + if (ch === 0x2d) { + sign = -1; + ch = this.nextChar(); + if (ch === 0x2d) { + ch = this.nextChar(); + } + } else if (ch === 0x2b) { + ch = this.nextChar(); + } + if (ch === 0x0a || ch === 0x0d) { + do { + ch = this.nextChar(); + } while (ch === 0x0a || ch === 0x0d); + } + if (ch === 0x2e) { + divideBy = 10; + ch = this.nextChar(); + } + if (ch < 0x30 || ch > 0x39) { + const msg = `Invalid number: ${String.fromCharCode(ch)} (charCode ${ch})`; + if (isWhiteSpace(ch) || ch === -1) { + info(`Lexer.getNumber - "${msg}".`); + return 0; + } + throw new FormatError(msg); + } + let baseValue = ch - 0x30; + let powerValue = 0; + let powerValueSign = 1; + while ((ch = this.nextChar()) >= 0) { + if (ch >= 0x30 && ch <= 0x39) { + const currentDigit = ch - 0x30; + if (eNotation) { + powerValue = powerValue * 10 + currentDigit; + } else { + if (divideBy !== 0) { + divideBy *= 10; + } + baseValue = baseValue * 10 + currentDigit; + } + } else if (ch === 0x2e) { + if (divideBy === 0) { + divideBy = 1; + } else { + break; + } + } else if (ch === 0x2d) { + warn("Badly formatted number: minus sign in the middle"); + } else if (ch === 0x45 || ch === 0x65) { + ch = this.peekChar(); + if (ch === 0x2b || ch === 0x2d) { + powerValueSign = ch === 0x2d ? -1 : 1; + this.nextChar(); + } else if (ch < 0x30 || ch > 0x39) { + break; + } + eNotation = true; + } else { + break; + } + } + if (divideBy !== 0) { + baseValue /= divideBy; + } + if (eNotation) { + baseValue *= 10 ** (powerValueSign * powerValue); + } + return sign * baseValue; + } + getString() { + let numParen = 1; + let done = false; + const strBuf = this.strBuf; + strBuf.length = 0; + let ch = this.nextChar(); + while (true) { + let charBuffered = false; + switch (ch | 0) { + case -1: + warn("Unterminated string"); + done = true; + break; + case 0x28: + ++numParen; + strBuf.push("("); + break; + case 0x29: + if (--numParen === 0) { + this.nextChar(); + done = true; + } else { + strBuf.push(")"); + } + break; + case 0x5c: + ch = this.nextChar(); + switch (ch) { + case -1: + warn("Unterminated string"); + done = true; + break; + case 0x6e: + strBuf.push("\n"); + break; + case 0x72: + strBuf.push("\r"); + break; + case 0x74: + strBuf.push("\t"); + break; + case 0x62: + strBuf.push("\b"); + break; + case 0x66: + strBuf.push("\f"); + break; + case 0x5c: + case 0x28: + case 0x29: + strBuf.push(String.fromCharCode(ch)); + break; + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + let x = ch & 0x0f; + ch = this.nextChar(); + charBuffered = true; + if (ch >= 0x30 && ch <= 0x37) { + x = (x << 3) + (ch & 0x0f); + ch = this.nextChar(); + if (ch >= 0x30 && ch <= 0x37) { + charBuffered = false; + x = (x << 3) + (ch & 0x0f); + } + } + strBuf.push(String.fromCharCode(x)); + break; + case 0x0d: + if (this.peekChar() === 0x0a) { + this.nextChar(); + } + break; + case 0x0a: + break; + default: + strBuf.push(String.fromCharCode(ch)); + break; + } + break; + default: + strBuf.push(String.fromCharCode(ch)); + break; + } + if (done) { + break; + } + if (!charBuffered) { + ch = this.nextChar(); + } + } + return strBuf.join(""); + } + getName() { + let ch, previousCh; + const strBuf = this.strBuf; + strBuf.length = 0; + while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) { + if (ch === 0x23) { + ch = this.nextChar(); + if (specialChars[ch]) { + warn("Lexer_getName: " + "NUMBER SIGN (#) should be followed by a hexadecimal number."); + strBuf.push("#"); + break; + } + const x = toHexDigit(ch); + if (x !== -1) { + previousCh = ch; + ch = this.nextChar(); + const x2 = toHexDigit(ch); + if (x2 === -1) { + warn(`Lexer_getName: Illegal digit (${String.fromCharCode(ch)}) ` + "in hexadecimal number."); + strBuf.push("#", String.fromCharCode(previousCh)); + if (specialChars[ch]) { + break; + } + strBuf.push(String.fromCharCode(ch)); + continue; + } + strBuf.push(String.fromCharCode(x << 4 | x2)); + } else { + strBuf.push("#", String.fromCharCode(ch)); + } + } else { + strBuf.push(String.fromCharCode(ch)); + } + } + if (strBuf.length > 127) { + warn(`Name token is longer than allowed by the spec: ${strBuf.length}`); + } + return Name.get(strBuf.join("")); + } + _hexStringWarn(ch) { + const MAX_HEX_STRING_NUM_WARN = 5; + if (this._hexStringNumWarn++ === MAX_HEX_STRING_NUM_WARN) { + warn("getHexString - ignoring additional invalid characters."); + return; + } + if (this._hexStringNumWarn > MAX_HEX_STRING_NUM_WARN) { + return; + } + warn(`getHexString - ignoring invalid character: ${ch}`); + } + getHexString() { + const strBuf = this.strBuf; + strBuf.length = 0; + let ch = this.currentChar; + let firstDigit = -1, + digit = -1; + this._hexStringNumWarn = 0; + while (true) { + if (ch < 0) { + warn("Unterminated hex string"); + break; + } else if (ch === 0x3e) { + this.nextChar(); + break; + } else if (specialChars[ch] === 1) { + ch = this.nextChar(); + continue; + } else { + digit = toHexDigit(ch); + if (digit === -1) { + this._hexStringWarn(ch); + } else if (firstDigit === -1) { + firstDigit = digit; + } else { + strBuf.push(String.fromCharCode(firstDigit << 4 | digit)); + firstDigit = -1; + } + ch = this.nextChar(); + } + } + if (firstDigit !== -1) { + strBuf.push(String.fromCharCode(firstDigit << 4)); + } + return strBuf.join(""); + } + getObj() { + let comment = false; + let ch = this.currentChar; + while (true) { + if (ch < 0) { + return EOF; + } + if (comment) { + if (ch === 0x0a || ch === 0x0d) { + comment = false; + } + } else if (ch === 0x25) { + comment = true; + } else if (specialChars[ch] !== 1) { + break; + } + ch = this.nextChar(); + } + switch (ch | 0) { + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x2b: + case 0x2d: + case 0x2e: + return this.getNumber(); + case 0x28: + return this.getString(); + case 0x2f: + return this.getName(); + case 0x5b: + this.nextChar(); + return Cmd.get("["); + case 0x5d: + this.nextChar(); + return Cmd.get("]"); + case 0x3c: + ch = this.nextChar(); + if (ch === 0x3c) { + this.nextChar(); + return Cmd.get("<<"); + } + return this.getHexString(); + case 0x3e: + ch = this.nextChar(); + if (ch === 0x3e) { + this.nextChar(); + return Cmd.get(">>"); + } + return Cmd.get(">"); + case 0x7b: + this.nextChar(); + return Cmd.get("{"); + case 0x7d: + this.nextChar(); + return Cmd.get("}"); + case 0x29: + this.nextChar(); + throw new FormatError(`Illegal character: ${ch}`); + } + let str = String.fromCharCode(ch); + if (ch < 0x20 || ch > 0x7f) { + const nextCh = this.peekChar(); + if (nextCh >= 0x20 && nextCh <= 0x7f) { + this.nextChar(); + return Cmd.get(str); + } + } + const knownCommands = this.knownCommands; + let knownCommandFound = knownCommands?.[str] !== undefined; + while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) { + const possibleCommand = str + String.fromCharCode(ch); + if (knownCommandFound && knownCommands[possibleCommand] === undefined) { + break; + } + if (str.length === 128) { + throw new FormatError(`Command token too long: ${str.length}`); + } + str = possibleCommand; + knownCommandFound = knownCommands?.[str] !== undefined; + } + if (str === "true") { + return true; + } + if (str === "false") { + return false; + } + if (str === "null") { + return null; + } + if (str === "BI") { + this.beginInlineImagePos = this.stream.pos; + } + return Cmd.get(str); + } + skipToNextLine() { + let ch = this.currentChar; + while (ch >= 0) { + if (ch === 0x0d) { + ch = this.nextChar(); + if (ch === 0x0a) { + this.nextChar(); + } + break; + } else if (ch === 0x0a) { + this.nextChar(); + break; + } + ch = this.nextChar(); + } + } +} +class Linearization { + static create(stream) { + function getInt(linDict, name, allowZeroValue = false) { + const obj = linDict.get(name); + if (Number.isInteger(obj) && (allowZeroValue ? obj >= 0 : obj > 0)) { + return obj; + } + throw new Error(`The "${name}" parameter in the linearization ` + "dictionary is invalid."); + } + function getHints(linDict) { + const hints = linDict.get("H"); + let hintsLength; + if (Array.isArray(hints) && ((hintsLength = hints.length) === 2 || hintsLength === 4)) { + for (let index = 0; index < hintsLength; index++) { + const hint = hints[index]; + if (!(Number.isInteger(hint) && hint > 0)) { + throw new Error(`Hint (${index}) in the linearization dictionary is invalid.`); + } + } + return hints; + } + throw new Error("Hint array in the linearization dictionary is invalid."); + } + const parser = new Parser({ + lexer: new Lexer(stream), + xref: null + }); + const obj1 = parser.getObj(); + const obj2 = parser.getObj(); + const obj3 = parser.getObj(); + const linDict = parser.getObj(); + let obj, length; + if (!(Number.isInteger(obj1) && Number.isInteger(obj2) && isCmd(obj3, "obj") && linDict instanceof Dict && typeof (obj = linDict.get("Linearized")) === "number" && obj > 0)) { + return null; + } else if ((length = getInt(linDict, "L")) !== stream.length) { + throw new Error('The "L" parameter in the linearization dictionary ' + "does not equal the stream length."); + } + return { + length, + hints: getHints(linDict), + objectNumberFirst: getInt(linDict, "O"), + endFirst: getInt(linDict, "E"), + numPages: getInt(linDict, "N"), + mainXRefEntriesOffset: getInt(linDict, "T"), + pageFirst: linDict.has("P") ? getInt(linDict, "P", true) : 0 + }; + } +} + +;// ./src/core/cmap.js + + + + + + + +const BUILT_IN_CMAPS = ["Adobe-GB1-UCS2", "Adobe-CNS1-UCS2", "Adobe-Japan1-UCS2", "Adobe-Korea1-UCS2", "78-EUC-H", "78-EUC-V", "78-H", "78-RKSJ-H", "78-RKSJ-V", "78-V", "78ms-RKSJ-H", "78ms-RKSJ-V", "83pv-RKSJ-H", "90ms-RKSJ-H", "90ms-RKSJ-V", "90msp-RKSJ-H", "90msp-RKSJ-V", "90pv-RKSJ-H", "90pv-RKSJ-V", "Add-H", "Add-RKSJ-H", "Add-RKSJ-V", "Add-V", "Adobe-CNS1-0", "Adobe-CNS1-1", "Adobe-CNS1-2", "Adobe-CNS1-3", "Adobe-CNS1-4", "Adobe-CNS1-5", "Adobe-CNS1-6", "Adobe-GB1-0", "Adobe-GB1-1", "Adobe-GB1-2", "Adobe-GB1-3", "Adobe-GB1-4", "Adobe-GB1-5", "Adobe-Japan1-0", "Adobe-Japan1-1", "Adobe-Japan1-2", "Adobe-Japan1-3", "Adobe-Japan1-4", "Adobe-Japan1-5", "Adobe-Japan1-6", "Adobe-Korea1-0", "Adobe-Korea1-1", "Adobe-Korea1-2", "B5-H", "B5-V", "B5pc-H", "B5pc-V", "CNS-EUC-H", "CNS-EUC-V", "CNS1-H", "CNS1-V", "CNS2-H", "CNS2-V", "ETHK-B5-H", "ETHK-B5-V", "ETen-B5-H", "ETen-B5-V", "ETenms-B5-H", "ETenms-B5-V", "EUC-H", "EUC-V", "Ext-H", "Ext-RKSJ-H", "Ext-RKSJ-V", "Ext-V", "GB-EUC-H", "GB-EUC-V", "GB-H", "GB-V", "GBK-EUC-H", "GBK-EUC-V", "GBK2K-H", "GBK2K-V", "GBKp-EUC-H", "GBKp-EUC-V", "GBT-EUC-H", "GBT-EUC-V", "GBT-H", "GBT-V", "GBTpc-EUC-H", "GBTpc-EUC-V", "GBpc-EUC-H", "GBpc-EUC-V", "H", "HKdla-B5-H", "HKdla-B5-V", "HKdlb-B5-H", "HKdlb-B5-V", "HKgccs-B5-H", "HKgccs-B5-V", "HKm314-B5-H", "HKm314-B5-V", "HKm471-B5-H", "HKm471-B5-V", "HKscs-B5-H", "HKscs-B5-V", "Hankaku", "Hiragana", "KSC-EUC-H", "KSC-EUC-V", "KSC-H", "KSC-Johab-H", "KSC-Johab-V", "KSC-V", "KSCms-UHC-H", "KSCms-UHC-HW-H", "KSCms-UHC-HW-V", "KSCms-UHC-V", "KSCpc-EUC-H", "KSCpc-EUC-V", "Katakana", "NWP-H", "NWP-V", "RKSJ-H", "RKSJ-V", "Roman", "UniCNS-UCS2-H", "UniCNS-UCS2-V", "UniCNS-UTF16-H", "UniCNS-UTF16-V", "UniCNS-UTF32-H", "UniCNS-UTF32-V", "UniCNS-UTF8-H", "UniCNS-UTF8-V", "UniGB-UCS2-H", "UniGB-UCS2-V", "UniGB-UTF16-H", "UniGB-UTF16-V", "UniGB-UTF32-H", "UniGB-UTF32-V", "UniGB-UTF8-H", "UniGB-UTF8-V", "UniJIS-UCS2-H", "UniJIS-UCS2-HW-H", "UniJIS-UCS2-HW-V", "UniJIS-UCS2-V", "UniJIS-UTF16-H", "UniJIS-UTF16-V", "UniJIS-UTF32-H", "UniJIS-UTF32-V", "UniJIS-UTF8-H", "UniJIS-UTF8-V", "UniJIS2004-UTF16-H", "UniJIS2004-UTF16-V", "UniJIS2004-UTF32-H", "UniJIS2004-UTF32-V", "UniJIS2004-UTF8-H", "UniJIS2004-UTF8-V", "UniJISPro-UCS2-HW-V", "UniJISPro-UCS2-V", "UniJISPro-UTF8-V", "UniJISX0213-UTF32-H", "UniJISX0213-UTF32-V", "UniJISX02132004-UTF32-H", "UniJISX02132004-UTF32-V", "UniKS-UCS2-H", "UniKS-UCS2-V", "UniKS-UTF16-H", "UniKS-UTF16-V", "UniKS-UTF32-H", "UniKS-UTF32-V", "UniKS-UTF8-H", "UniKS-UTF8-V", "V", "WP-Symbol"]; +const MAX_MAP_RANGE = 2 ** 24 - 1; +class CMap { + constructor(builtInCMap = false) { + this.codespaceRanges = [[], [], [], []]; + this.numCodespaceRanges = 0; + this._map = []; + this.name = ""; + this.vertical = false; + this.useCMap = null; + this.builtInCMap = builtInCMap; + } + addCodespaceRange(n, low, high) { + this.codespaceRanges[n - 1].push(low, high); + this.numCodespaceRanges++; + } + mapCidRange(low, high, dstLow) { + if (high - low > MAX_MAP_RANGE) { + throw new Error("mapCidRange - ignoring data above MAX_MAP_RANGE."); + } + while (low <= high) { + this._map[low++] = dstLow++; + } + } + mapBfRange(low, high, dstLow) { + if (high - low > MAX_MAP_RANGE) { + throw new Error("mapBfRange - ignoring data above MAX_MAP_RANGE."); + } + const lastByte = dstLow.length - 1; + while (low <= high) { + this._map[low++] = dstLow; + const nextCharCode = dstLow.charCodeAt(lastByte) + 1; + if (nextCharCode > 0xff) { + dstLow = dstLow.substring(0, lastByte - 1) + String.fromCharCode(dstLow.charCodeAt(lastByte - 1) + 1) + "\x00"; + continue; + } + dstLow = dstLow.substring(0, lastByte) + String.fromCharCode(nextCharCode); + } + } + mapBfRangeToArray(low, high, array) { + if (high - low > MAX_MAP_RANGE) { + throw new Error("mapBfRangeToArray - ignoring data above MAX_MAP_RANGE."); + } + const ii = array.length; + let i = 0; + while (low <= high && i < ii) { + this._map[low] = array[i++]; + ++low; + } + } + mapOne(src, dst) { + this._map[src] = dst; + } + lookup(code) { + return this._map[code]; + } + contains(code) { + return this._map[code] !== undefined; + } + forEach(callback) { + const map = this._map; + const length = map.length; + if (length <= 0x10000) { + for (let i = 0; i < length; i++) { + if (map[i] !== undefined) { + callback(i, map[i]); + } + } + } else { + for (const i in map) { + callback(i, map[i]); + } + } + } + charCodeOf(value) { + const map = this._map; + if (map.length <= 0x10000) { + return map.indexOf(value); + } + for (const charCode in map) { + if (map[charCode] === value) { + return charCode | 0; + } + } + return -1; + } + getMap() { + return this._map; + } + readCharCode(str, offset, out) { + let c = 0; + const codespaceRanges = this.codespaceRanges; + for (let n = 0, nn = codespaceRanges.length; n < nn; n++) { + c = (c << 8 | str.charCodeAt(offset + n)) >>> 0; + const codespaceRange = codespaceRanges[n]; + for (let k = 0, kk = codespaceRange.length; k < kk;) { + const low = codespaceRange[k++]; + const high = codespaceRange[k++]; + if (c >= low && c <= high) { + out.charcode = c; + out.length = n + 1; + return; + } + } + } + out.charcode = 0; + out.length = 1; + } + getCharCodeLength(charCode) { + const codespaceRanges = this.codespaceRanges; + for (let n = 0, nn = codespaceRanges.length; n < nn; n++) { + const codespaceRange = codespaceRanges[n]; + for (let k = 0, kk = codespaceRange.length; k < kk;) { + const low = codespaceRange[k++]; + const high = codespaceRange[k++]; + if (charCode >= low && charCode <= high) { + return n + 1; + } + } + } + return 1; + } + get length() { + return this._map.length; + } + get isIdentityCMap() { + if (!(this.name === "Identity-H" || this.name === "Identity-V")) { + return false; + } + if (this._map.length !== 0x10000) { + return false; + } + for (let i = 0; i < 0x10000; i++) { + if (this._map[i] !== i) { + return false; + } + } + return true; + } +} +class IdentityCMap extends CMap { + constructor(vertical, n) { + super(); + this.vertical = vertical; + this.addCodespaceRange(n, 0, 0xffff); + } + mapCidRange(low, high, dstLow) { + unreachable("should not call mapCidRange"); + } + mapBfRange(low, high, dstLow) { + unreachable("should not call mapBfRange"); + } + mapBfRangeToArray(low, high, array) { + unreachable("should not call mapBfRangeToArray"); + } + mapOne(src, dst) { + unreachable("should not call mapCidOne"); + } + lookup(code) { + return Number.isInteger(code) && code <= 0xffff ? code : undefined; + } + contains(code) { + return Number.isInteger(code) && code <= 0xffff; + } + forEach(callback) { + for (let i = 0; i <= 0xffff; i++) { + callback(i, i); + } + } + charCodeOf(value) { + return Number.isInteger(value) && value <= 0xffff ? value : -1; + } + getMap() { + const map = new Array(0x10000); + for (let i = 0; i <= 0xffff; i++) { + map[i] = i; + } + return map; + } + get length() { + return 0x10000; + } + get isIdentityCMap() { + unreachable("should not access .isIdentityCMap"); + } +} +function strToInt(str) { + let a = 0; + for (let i = 0; i < str.length; i++) { + a = a << 8 | str.charCodeAt(i); + } + return a >>> 0; +} +function expectString(obj) { + if (typeof obj !== "string") { + throw new FormatError("Malformed CMap: expected string."); + } +} +function expectInt(obj) { + if (!Number.isInteger(obj)) { + throw new FormatError("Malformed CMap: expected int."); + } +} +function parseBfChar(cMap, lexer) { + while (true) { + let obj = lexer.getObj(); + if (obj === EOF) { + break; + } + if (isCmd(obj, "endbfchar")) { + return; + } + expectString(obj); + const src = strToInt(obj); + obj = lexer.getObj(); + expectString(obj); + const dst = obj; + cMap.mapOne(src, dst); + } +} +function parseBfRange(cMap, lexer) { + while (true) { + let obj = lexer.getObj(); + if (obj === EOF) { + break; + } + if (isCmd(obj, "endbfrange")) { + return; + } + expectString(obj); + const low = strToInt(obj); + obj = lexer.getObj(); + expectString(obj); + const high = strToInt(obj); + obj = lexer.getObj(); + if (Number.isInteger(obj) || typeof obj === "string") { + const dstLow = Number.isInteger(obj) ? String.fromCharCode(obj) : obj; + cMap.mapBfRange(low, high, dstLow); + } else if (isCmd(obj, "[")) { + obj = lexer.getObj(); + const array = []; + while (!isCmd(obj, "]") && obj !== EOF) { + array.push(obj); + obj = lexer.getObj(); + } + cMap.mapBfRangeToArray(low, high, array); + } else { + break; + } + } + throw new FormatError("Invalid bf range."); +} +function parseCidChar(cMap, lexer) { + while (true) { + let obj = lexer.getObj(); + if (obj === EOF) { + break; + } + if (isCmd(obj, "endcidchar")) { + return; + } + expectString(obj); + const src = strToInt(obj); + obj = lexer.getObj(); + expectInt(obj); + const dst = obj; + cMap.mapOne(src, dst); + } +} +function parseCidRange(cMap, lexer) { + while (true) { + let obj = lexer.getObj(); + if (obj === EOF) { + break; + } + if (isCmd(obj, "endcidrange")) { + return; + } + expectString(obj); + const low = strToInt(obj); + obj = lexer.getObj(); + expectString(obj); + const high = strToInt(obj); + obj = lexer.getObj(); + expectInt(obj); + const dstLow = obj; + cMap.mapCidRange(low, high, dstLow); + } +} +function parseCodespaceRange(cMap, lexer) { + while (true) { + let obj = lexer.getObj(); + if (obj === EOF) { + break; + } + if (isCmd(obj, "endcodespacerange")) { + return; + } + if (typeof obj !== "string") { + break; + } + const low = strToInt(obj); + obj = lexer.getObj(); + if (typeof obj !== "string") { + break; + } + const high = strToInt(obj); + cMap.addCodespaceRange(obj.length, low, high); + } + throw new FormatError("Invalid codespace range."); +} +function parseWMode(cMap, lexer) { + const obj = lexer.getObj(); + if (Number.isInteger(obj)) { + cMap.vertical = !!obj; + } +} +function parseCMapName(cMap, lexer) { + const obj = lexer.getObj(); + if (obj instanceof Name) { + cMap.name = obj.name; + } +} +async function parseCMap(cMap, lexer, fetchBuiltInCMap, useCMap) { + let previous, embeddedUseCMap; + objLoop: while (true) { + try { + const obj = lexer.getObj(); + if (obj === EOF) { + break; + } else if (obj instanceof Name) { + if (obj.name === "WMode") { + parseWMode(cMap, lexer); + } else if (obj.name === "CMapName") { + parseCMapName(cMap, lexer); + } + previous = obj; + } else if (obj instanceof Cmd) { + switch (obj.cmd) { + case "endcmap": + break objLoop; + case "usecmap": + if (previous instanceof Name) { + embeddedUseCMap = previous.name; + } + break; + case "begincodespacerange": + parseCodespaceRange(cMap, lexer); + break; + case "beginbfchar": + parseBfChar(cMap, lexer); + break; + case "begincidchar": + parseCidChar(cMap, lexer); + break; + case "beginbfrange": + parseBfRange(cMap, lexer); + break; + case "begincidrange": + parseCidRange(cMap, lexer); + break; + } + } + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn("Invalid cMap data: " + ex); + continue; + } + } + if (!useCMap && embeddedUseCMap) { + useCMap = embeddedUseCMap; + } + if (useCMap) { + return extendCMap(cMap, fetchBuiltInCMap, useCMap); + } + return cMap; +} +async function extendCMap(cMap, fetchBuiltInCMap, useCMap) { + cMap.useCMap = await createBuiltInCMap(useCMap, fetchBuiltInCMap); + if (cMap.numCodespaceRanges === 0) { + const useCodespaceRanges = cMap.useCMap.codespaceRanges; + for (let i = 0; i < useCodespaceRanges.length; i++) { + cMap.codespaceRanges[i] = useCodespaceRanges[i].slice(); + } + cMap.numCodespaceRanges = cMap.useCMap.numCodespaceRanges; + } + cMap.useCMap.forEach(function (key, value) { + if (!cMap.contains(key)) { + cMap.mapOne(key, value); + } + }); + return cMap; +} +async function createBuiltInCMap(name, fetchBuiltInCMap) { + if (name === "Identity-H") { + return new IdentityCMap(false, 2); + } else if (name === "Identity-V") { + return new IdentityCMap(true, 2); + } + if (!BUILT_IN_CMAPS.includes(name)) { + throw new Error("Unknown CMap name: " + name); + } + if (!fetchBuiltInCMap) { + throw new Error("Built-in CMap parameters are not provided."); + } + const { + cMapData, + isCompressed + } = await fetchBuiltInCMap(name); + const cMap = new CMap(true); + if (isCompressed) { + return new BinaryCMapReader().process(cMapData, cMap, useCMap => extendCMap(cMap, fetchBuiltInCMap, useCMap)); + } + const lexer = new Lexer(new Stream(cMapData)); + return parseCMap(cMap, lexer, fetchBuiltInCMap, null); +} +class CMapFactory { + static async create({ + encoding, + fetchBuiltInCMap, + useCMap + }) { + if (encoding instanceof Name) { + return createBuiltInCMap(encoding.name, fetchBuiltInCMap); + } else if (encoding instanceof BaseStream) { + const parsedCMap = await parseCMap(new CMap(), new Lexer(encoding), fetchBuiltInCMap, useCMap); + if (parsedCMap.isIdentityCMap) { + return createBuiltInCMap(parsedCMap.name, fetchBuiltInCMap); + } + return parsedCMap; + } + throw new Error("Encoding required."); + } +} + +;// ./src/core/charsets.js +const ISOAdobeCharset = [".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", "endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis", "perthousand", "questiondown", "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE", "ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls", "onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus", "Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn", "threequarters", "twosuperior", "registered", "minus", "eth", "multiply", "threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave", "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute", "acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis", "igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde", "scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis", "zcaron"]; +const ExpertCharset = [".notdef", "space", "exclamsmall", "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon", "commasuperior", "threequartersemdash", "periodsuperior", "questionsmall", "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior", "tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior", "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall", "onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall"]; +const ExpertSubsetCharset = [".notdef", "space", "dollaroldstyle", "dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon", "commasuperior", "threequartersemdash", "periodsuperior", "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior", "tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior", "parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah", "centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf", "threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior"]; + +;// ./src/core/encodings.js +const ExpertEncoding = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "space", "exclamsmall", "Hungarumlautsmall", "", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon", "commasuperior", "threequartersemdash", "periodsuperior", "questionsmall", "", "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "", "", "", "isuperior", "", "", "lsuperior", "msuperior", "nsuperior", "osuperior", "", "", "rsuperior", "ssuperior", "tsuperior", "", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior", "", "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "exclamdownsmall", "centoldstyle", "Lslashsmall", "", "", "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", "", "Dotaccentsmall", "", "", "Macronsmall", "", "", "figuredash", "hypheninferior", "", "", "Ogoneksmall", "Ringsmall", "Cedillasmall", "", "", "", "onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", "", "", "zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall"]; +const MacExpertEncoding = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "space", "exclamsmall", "Hungarumlautsmall", "centoldstyle", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon", "", "threequartersemdash", "", "questionsmall", "", "", "", "", "Ethsmall", "", "", "onequarter", "onehalf", "threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", "", "", "", "", "", "", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior", "", "parenrightinferior", "Circumflexsmall", "hypheninferior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", "", "", "asuperior", "centsuperior", "", "", "", "", "Aacutesmall", "Agravesmall", "Acircumflexsmall", "Adieresissmall", "Atildesmall", "Aringsmall", "Ccedillasmall", "Eacutesmall", "Egravesmall", "Ecircumflexsmall", "Edieresissmall", "Iacutesmall", "Igravesmall", "Icircumflexsmall", "Idieresissmall", "Ntildesmall", "Oacutesmall", "Ogravesmall", "Ocircumflexsmall", "Odieresissmall", "Otildesmall", "Uacutesmall", "Ugravesmall", "Ucircumflexsmall", "Udieresissmall", "", "eightsuperior", "fourinferior", "threeinferior", "sixinferior", "eightinferior", "seveninferior", "Scaronsmall", "", "centinferior", "twoinferior", "", "Dieresissmall", "", "Caronsmall", "osuperior", "fiveinferior", "", "commainferior", "periodinferior", "Yacutesmall", "", "dollarinferior", "", "", "Thornsmall", "", "nineinferior", "zeroinferior", "Zcaronsmall", "AEsmall", "Oslashsmall", "questiondownsmall", "oneinferior", "Lslashsmall", "", "", "", "", "", "", "Cedillasmall", "", "", "", "", "", "OEsmall", "figuredash", "hyphensuperior", "", "", "", "", "exclamdownsmall", "", "Ydieresissmall", "", "onesuperior", "twosuperior", "threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "ninesuperior", "zerosuperior", "", "esuperior", "rsuperior", "tsuperior", "", "", "isuperior", "ssuperior", "dsuperior", "", "", "", "", "", "lsuperior", "Ogoneksmall", "Brevesmall", "Macronsmall", "bsuperior", "nsuperior", "msuperior", "commasuperior", "periodsuperior", "Dotaccentsmall", "Ringsmall", "", "", "", ""]; +const MacRomanEncoding = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "", "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde", "aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute", "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling", "section", "bullet", "paragraph", "germandbls", "registered", "copyright", "trademark", "acute", "dieresis", "notequal", "AE", "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen", "mu", "partialdiff", "summation", "product", "pi", "integral", "ordfeminine", "ordmasculine", "Omega", "ae", "oslash", "questiondown", "exclamdown", "logicalnot", "radical", "florin", "approxequal", "Delta", "guillemotleft", "guillemotright", "ellipsis", "space", "Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", "fraction", "currency", "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve", "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron"]; +const StandardEncoding = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", "", "endash", "dagger", "daggerdbl", "periodcentered", "", "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis", "perthousand", "", "questiondown", "", "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", "dieresis", "", "ring", "cedilla", "", "hungarumlaut", "ogonek", "caron", "emdash", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "AE", "", "ordfeminine", "", "", "", "", "Lslash", "Oslash", "OE", "ordmasculine", "", "", "", "", "", "ae", "", "", "", "dotlessi", "", "", "lslash", "oslash", "oe", "germandbls", "", "", "", ""]; +const WinAnsiEncoding = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "bullet", "Euro", "bullet", "quotesinglbase", "florin", "quotedblbase", "ellipsis", "dagger", "daggerdbl", "circumflex", "perthousand", "Scaron", "guilsinglleft", "OE", "bullet", "Zcaron", "bullet", "bullet", "quoteleft", "quoteright", "quotedblleft", "quotedblright", "bullet", "endash", "emdash", "tilde", "trademark", "scaron", "guilsinglright", "oe", "bullet", "zcaron", "Ydieresis", "space", "exclamdown", "cent", "sterling", "currency", "yen", "brokenbar", "section", "dieresis", "copyright", "ordfeminine", "guillemotleft", "logicalnot", "hyphen", "registered", "macron", "degree", "plusminus", "twosuperior", "threesuperior", "acute", "mu", "paragraph", "periodcentered", "cedilla", "onesuperior", "ordmasculine", "guillemotright", "onequarter", "onehalf", "threequarters", "questiondown", "Agrave", "Aacute", "Acircumflex", "Atilde", "Adieresis", "Aring", "AE", "Ccedilla", "Egrave", "Eacute", "Ecircumflex", "Edieresis", "Igrave", "Iacute", "Icircumflex", "Idieresis", "Eth", "Ntilde", "Ograve", "Oacute", "Ocircumflex", "Otilde", "Odieresis", "multiply", "Oslash", "Ugrave", "Uacute", "Ucircumflex", "Udieresis", "Yacute", "Thorn", "germandbls", "agrave", "aacute", "acircumflex", "atilde", "adieresis", "aring", "ae", "ccedilla", "egrave", "eacute", "ecircumflex", "edieresis", "igrave", "iacute", "icircumflex", "idieresis", "eth", "ntilde", "ograve", "oacute", "ocircumflex", "otilde", "odieresis", "divide", "oslash", "ugrave", "uacute", "ucircumflex", "udieresis", "yacute", "thorn", "ydieresis"]; +const SymbolSetEncoding = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "space", "exclam", "universal", "numbersign", "existential", "percent", "ampersand", "suchthat", "parenleft", "parenright", "asteriskmath", "plus", "comma", "minus", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "congruent", "Alpha", "Beta", "Chi", "Delta", "Epsilon", "Phi", "Gamma", "Eta", "Iota", "theta1", "Kappa", "Lambda", "Mu", "Nu", "Omicron", "Pi", "Theta", "Rho", "Sigma", "Tau", "Upsilon", "sigma1", "Omega", "Xi", "Psi", "Zeta", "bracketleft", "therefore", "bracketright", "perpendicular", "underscore", "radicalex", "alpha", "beta", "chi", "delta", "epsilon", "phi", "gamma", "eta", "iota", "phi1", "kappa", "lambda", "mu", "nu", "omicron", "pi", "theta", "rho", "sigma", "tau", "upsilon", "omega1", "omega", "xi", "psi", "zeta", "braceleft", "bar", "braceright", "similar", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Euro", "Upsilon1", "minute", "lessequal", "fraction", "infinity", "florin", "club", "diamond", "heart", "spade", "arrowboth", "arrowleft", "arrowup", "arrowright", "arrowdown", "degree", "plusminus", "second", "greaterequal", "multiply", "proportional", "partialdiff", "bullet", "divide", "notequal", "equivalence", "approxequal", "ellipsis", "arrowvertex", "arrowhorizex", "carriagereturn", "aleph", "Ifraktur", "Rfraktur", "weierstrass", "circlemultiply", "circleplus", "emptyset", "intersection", "union", "propersuperset", "reflexsuperset", "notsubset", "propersubset", "reflexsubset", "element", "notelement", "angle", "gradient", "registerserif", "copyrightserif", "trademarkserif", "product", "radical", "dotmath", "logicalnot", "logicaland", "logicalor", "arrowdblboth", "arrowdblleft", "arrowdblup", "arrowdblright", "arrowdbldown", "lozenge", "angleleft", "registersans", "copyrightsans", "trademarksans", "summation", "parenlefttp", "parenleftex", "parenleftbt", "bracketlefttp", "bracketleftex", "bracketleftbt", "bracelefttp", "braceleftmid", "braceleftbt", "braceex", "", "angleright", "integral", "integraltp", "integralex", "integralbt", "parenrighttp", "parenrightex", "parenrightbt", "bracketrighttp", "bracketrightex", "bracketrightbt", "bracerighttp", "bracerightmid", "bracerightbt", ""]; +const ZapfDingbatsEncoding = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "space", "a1", "a2", "a202", "a3", "a4", "a5", "a119", "a118", "a117", "a11", "a12", "a13", "a14", "a15", "a16", "a105", "a17", "a18", "a19", "a20", "a21", "a22", "a23", "a24", "a25", "a26", "a27", "a28", "a6", "a7", "a8", "a9", "a10", "a29", "a30", "a31", "a32", "a33", "a34", "a35", "a36", "a37", "a38", "a39", "a40", "a41", "a42", "a43", "a44", "a45", "a46", "a47", "a48", "a49", "a50", "a51", "a52", "a53", "a54", "a55", "a56", "a57", "a58", "a59", "a60", "a61", "a62", "a63", "a64", "a65", "a66", "a67", "a68", "a69", "a70", "a71", "a72", "a73", "a74", "a203", "a75", "a204", "a76", "a77", "a78", "a79", "a81", "a82", "a83", "a84", "a97", "a98", "a99", "a100", "", "a89", "a90", "a93", "a94", "a91", "a92", "a205", "a85", "a206", "a86", "a87", "a88", "a95", "a96", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "a101", "a102", "a103", "a104", "a106", "a107", "a108", "a112", "a111", "a110", "a109", "a120", "a121", "a122", "a123", "a124", "a125", "a126", "a127", "a128", "a129", "a130", "a131", "a132", "a133", "a134", "a135", "a136", "a137", "a138", "a139", "a140", "a141", "a142", "a143", "a144", "a145", "a146", "a147", "a148", "a149", "a150", "a151", "a152", "a153", "a154", "a155", "a156", "a157", "a158", "a159", "a160", "a161", "a163", "a164", "a196", "a165", "a192", "a166", "a167", "a168", "a169", "a170", "a171", "a172", "a173", "a162", "a174", "a175", "a176", "a177", "a178", "a179", "a193", "a180", "a199", "a181", "a200", "a182", "", "a201", "a183", "a184", "a197", "a185", "a194", "a198", "a186", "a195", "a187", "a188", "a189", "a190", "a191", ""]; +function getEncoding(encodingName) { + switch (encodingName) { + case "WinAnsiEncoding": + return WinAnsiEncoding; + case "StandardEncoding": + return StandardEncoding; + case "MacRomanEncoding": + return MacRomanEncoding; + case "SymbolSetEncoding": + return SymbolSetEncoding; + case "ZapfDingbatsEncoding": + return ZapfDingbatsEncoding; + case "ExpertEncoding": + return ExpertEncoding; + case "MacExpertEncoding": + return MacExpertEncoding; + default: + return null; + } +} + +;// ./src/core/cff_parser.js + + + +const MAX_SUBR_NESTING = 10; +const CFFStandardStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", "endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis", "perthousand", "questiondown", "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE", "ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls", "onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus", "Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn", "threequarters", "twosuperior", "registered", "minus", "eth", "multiply", "threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave", "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute", "acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis", "igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde", "scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall", "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "commasuperior", "threequartersemdash", "periodsuperior", "questionsmall", "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior", "tsuperior", "ff", "ffi", "ffl", "parenleftinferior", "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall", "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", "zerosuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall", "001.000", "001.001", "001.002", "001.003", "Black", "Bold", "Book", "Light", "Medium", "Regular", "Roman", "Semibold"]; +const NUM_STANDARD_CFF_STRINGS = 391; +const CharstringValidationData = [null, { + id: "hstem", + min: 2, + stackClearing: true, + stem: true +}, null, { + id: "vstem", + min: 2, + stackClearing: true, + stem: true +}, { + id: "vmoveto", + min: 1, + stackClearing: true +}, { + id: "rlineto", + min: 2, + resetStack: true +}, { + id: "hlineto", + min: 1, + resetStack: true +}, { + id: "vlineto", + min: 1, + resetStack: true +}, { + id: "rrcurveto", + min: 6, + resetStack: true +}, null, { + id: "callsubr", + min: 1, + undefStack: true +}, { + id: "return", + min: 0, + undefStack: true +}, null, null, { + id: "endchar", + min: 0, + stackClearing: true +}, null, null, null, { + id: "hstemhm", + min: 2, + stackClearing: true, + stem: true +}, { + id: "hintmask", + min: 0, + stackClearing: true +}, { + id: "cntrmask", + min: 0, + stackClearing: true +}, { + id: "rmoveto", + min: 2, + stackClearing: true +}, { + id: "hmoveto", + min: 1, + stackClearing: true +}, { + id: "vstemhm", + min: 2, + stackClearing: true, + stem: true +}, { + id: "rcurveline", + min: 8, + resetStack: true +}, { + id: "rlinecurve", + min: 8, + resetStack: true +}, { + id: "vvcurveto", + min: 4, + resetStack: true +}, { + id: "hhcurveto", + min: 4, + resetStack: true +}, null, { + id: "callgsubr", + min: 1, + undefStack: true +}, { + id: "vhcurveto", + min: 4, + resetStack: true +}, { + id: "hvcurveto", + min: 4, + resetStack: true +}]; +const CharstringValidationData12 = [null, null, null, { + id: "and", + min: 2, + stackDelta: -1 +}, { + id: "or", + min: 2, + stackDelta: -1 +}, { + id: "not", + min: 1, + stackDelta: 0 +}, null, null, null, { + id: "abs", + min: 1, + stackDelta: 0 +}, { + id: "add", + min: 2, + stackDelta: -1, + stackFn(stack, index) { + stack[index - 2] = stack[index - 2] + stack[index - 1]; + } +}, { + id: "sub", + min: 2, + stackDelta: -1, + stackFn(stack, index) { + stack[index - 2] = stack[index - 2] - stack[index - 1]; + } +}, { + id: "div", + min: 2, + stackDelta: -1, + stackFn(stack, index) { + stack[index - 2] = stack[index - 2] / stack[index - 1]; + } +}, null, { + id: "neg", + min: 1, + stackDelta: 0, + stackFn(stack, index) { + stack[index - 1] = -stack[index - 1]; + } +}, { + id: "eq", + min: 2, + stackDelta: -1 +}, null, null, { + id: "drop", + min: 1, + stackDelta: -1 +}, null, { + id: "put", + min: 2, + stackDelta: -2 +}, { + id: "get", + min: 1, + stackDelta: 0 +}, { + id: "ifelse", + min: 4, + stackDelta: -3 +}, { + id: "random", + min: 0, + stackDelta: 1 +}, { + id: "mul", + min: 2, + stackDelta: -1, + stackFn(stack, index) { + stack[index - 2] = stack[index - 2] * stack[index - 1]; + } +}, null, { + id: "sqrt", + min: 1, + stackDelta: 0 +}, { + id: "dup", + min: 1, + stackDelta: 1 +}, { + id: "exch", + min: 2, + stackDelta: 0 +}, { + id: "index", + min: 2, + stackDelta: 0 +}, { + id: "roll", + min: 3, + stackDelta: -2 +}, null, null, null, { + id: "hflex", + min: 7, + resetStack: true +}, { + id: "flex", + min: 13, + resetStack: true +}, { + id: "hflex1", + min: 9, + resetStack: true +}, { + id: "flex1", + min: 11, + resetStack: true +}]; +class CFFParser { + constructor(file, properties, seacAnalysisEnabled) { + this.bytes = file.getBytes(); + this.properties = properties; + this.seacAnalysisEnabled = !!seacAnalysisEnabled; + } + parse() { + const properties = this.properties; + const cff = new CFF(); + this.cff = cff; + const header = this.parseHeader(); + const nameIndex = this.parseIndex(header.endPos); + const topDictIndex = this.parseIndex(nameIndex.endPos); + const stringIndex = this.parseIndex(topDictIndex.endPos); + const globalSubrIndex = this.parseIndex(stringIndex.endPos); + const topDictParsed = this.parseDict(topDictIndex.obj.get(0)); + const topDict = this.createDict(CFFTopDict, topDictParsed, cff.strings); + cff.header = header.obj; + cff.names = this.parseNameIndex(nameIndex.obj); + cff.strings = this.parseStringIndex(stringIndex.obj); + cff.topDict = topDict; + cff.globalSubrIndex = globalSubrIndex.obj; + this.parsePrivateDict(cff.topDict); + cff.isCIDFont = topDict.hasName("ROS"); + const charStringOffset = topDict.getByName("CharStrings"); + const charStringIndex = this.parseIndex(charStringOffset).obj; + const fontMatrix = topDict.getByName("FontMatrix"); + if (fontMatrix) { + properties.fontMatrix = fontMatrix; + } + const fontBBox = topDict.getByName("FontBBox"); + if (fontBBox) { + properties.ascent = Math.max(fontBBox[3], fontBBox[1]); + properties.descent = Math.min(fontBBox[1], fontBBox[3]); + properties.ascentScaled = true; + } + let charset, encoding; + if (cff.isCIDFont) { + const fdArrayIndex = this.parseIndex(topDict.getByName("FDArray")).obj; + for (let i = 0, ii = fdArrayIndex.count; i < ii; ++i) { + const dictRaw = fdArrayIndex.get(i); + const fontDict = this.createDict(CFFTopDict, this.parseDict(dictRaw), cff.strings); + this.parsePrivateDict(fontDict); + cff.fdArray.push(fontDict); + } + encoding = null; + charset = this.parseCharsets(topDict.getByName("charset"), charStringIndex.count, cff.strings, true); + cff.fdSelect = this.parseFDSelect(topDict.getByName("FDSelect"), charStringIndex.count); + } else { + charset = this.parseCharsets(topDict.getByName("charset"), charStringIndex.count, cff.strings, false); + encoding = this.parseEncoding(topDict.getByName("Encoding"), properties, cff.strings, charset.charset); + } + cff.charset = charset; + cff.encoding = encoding; + const charStringsAndSeacs = this.parseCharStrings({ + charStrings: charStringIndex, + localSubrIndex: topDict.privateDict.subrsIndex, + globalSubrIndex: globalSubrIndex.obj, + fdSelect: cff.fdSelect, + fdArray: cff.fdArray, + privateDict: topDict.privateDict + }); + cff.charStrings = charStringsAndSeacs.charStrings; + cff.seacs = charStringsAndSeacs.seacs; + cff.widths = charStringsAndSeacs.widths; + return cff; + } + parseHeader() { + let bytes = this.bytes; + const bytesLength = bytes.length; + let offset = 0; + while (offset < bytesLength && bytes[offset] !== 1) { + ++offset; + } + if (offset >= bytesLength) { + throw new FormatError("Invalid CFF header"); + } + if (offset !== 0) { + info("cff data is shifted"); + bytes = bytes.subarray(offset); + this.bytes = bytes; + } + const major = bytes[0]; + const minor = bytes[1]; + const hdrSize = bytes[2]; + const offSize = bytes[3]; + const header = new CFFHeader(major, minor, hdrSize, offSize); + return { + obj: header, + endPos: hdrSize + }; + } + parseDict(dict) { + let pos = 0; + function parseOperand() { + let value = dict[pos++]; + if (value === 30) { + return parseFloatOperand(); + } else if (value === 28) { + value = dict[pos++]; + value = (value << 24 | dict[pos++] << 16) >> 16; + return value; + } else if (value === 29) { + value = dict[pos++]; + value = value << 8 | dict[pos++]; + value = value << 8 | dict[pos++]; + value = value << 8 | dict[pos++]; + return value; + } else if (value >= 32 && value <= 246) { + return value - 139; + } else if (value >= 247 && value <= 250) { + return (value - 247) * 256 + dict[pos++] + 108; + } else if (value >= 251 && value <= 254) { + return -((value - 251) * 256) - dict[pos++] - 108; + } + warn('CFFParser_parseDict: "' + value + '" is a reserved command.'); + return NaN; + } + function parseFloatOperand() { + let str = ""; + const eof = 15; + const lookup = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "E", "E-", null, "-"]; + const length = dict.length; + while (pos < length) { + const b = dict[pos++]; + const b1 = b >> 4; + const b2 = b & 15; + if (b1 === eof) { + break; + } + str += lookup[b1]; + if (b2 === eof) { + break; + } + str += lookup[b2]; + } + return parseFloat(str); + } + let operands = []; + const entries = []; + pos = 0; + const end = dict.length; + while (pos < end) { + let b = dict[pos]; + if (b <= 21) { + if (b === 12) { + b = b << 8 | dict[++pos]; + } + entries.push([b, operands]); + operands = []; + ++pos; + } else { + operands.push(parseOperand()); + } + } + return entries; + } + parseIndex(pos) { + const cffIndex = new CFFIndex(); + const bytes = this.bytes; + const count = bytes[pos++] << 8 | bytes[pos++]; + const offsets = []; + let end = pos; + let i, ii; + if (count !== 0) { + const offsetSize = bytes[pos++]; + const startPos = pos + (count + 1) * offsetSize - 1; + for (i = 0, ii = count + 1; i < ii; ++i) { + let offset = 0; + for (let j = 0; j < offsetSize; ++j) { + offset <<= 8; + offset += bytes[pos++]; + } + offsets.push(startPos + offset); + } + end = offsets[count]; + } + for (i = 0, ii = offsets.length - 1; i < ii; ++i) { + const offsetStart = offsets[i]; + const offsetEnd = offsets[i + 1]; + cffIndex.add(bytes.subarray(offsetStart, offsetEnd)); + } + return { + obj: cffIndex, + endPos: end + }; + } + parseNameIndex(index) { + const names = []; + for (let i = 0, ii = index.count; i < ii; ++i) { + const name = index.get(i); + names.push(bytesToString(name)); + } + return names; + } + parseStringIndex(index) { + const strings = new CFFStrings(); + for (let i = 0, ii = index.count; i < ii; ++i) { + const data = index.get(i); + strings.add(bytesToString(data)); + } + return strings; + } + createDict(Type, dict, strings) { + const cffDict = new Type(strings); + for (const [key, value] of dict) { + cffDict.setByKey(key, value); + } + return cffDict; + } + parseCharString(state, data, localSubrIndex, globalSubrIndex) { + if (!data || state.callDepth > MAX_SUBR_NESTING) { + return false; + } + let stackSize = state.stackSize; + const stack = state.stack; + let length = data.length; + for (let j = 0; j < length;) { + const value = data[j++]; + let validationCommand = null; + if (value === 12) { + const q = data[j++]; + if (q === 0) { + data[j - 2] = 139; + data[j - 1] = 22; + stackSize = 0; + } else { + validationCommand = CharstringValidationData12[q]; + } + } else if (value === 28) { + stack[stackSize] = (data[j] << 24 | data[j + 1] << 16) >> 16; + j += 2; + stackSize++; + } else if (value === 14) { + if (stackSize >= 4) { + stackSize -= 4; + if (this.seacAnalysisEnabled) { + state.seac = stack.slice(stackSize, stackSize + 4); + return false; + } + } + validationCommand = CharstringValidationData[value]; + } else if (value >= 32 && value <= 246) { + stack[stackSize] = value - 139; + stackSize++; + } else if (value >= 247 && value <= 254) { + stack[stackSize] = value < 251 ? (value - 247 << 8) + data[j] + 108 : -(value - 251 << 8) - data[j] - 108; + j++; + stackSize++; + } else if (value === 255) { + stack[stackSize] = (data[j] << 24 | data[j + 1] << 16 | data[j + 2] << 8 | data[j + 3]) / 65536; + j += 4; + stackSize++; + } else if (value === 19 || value === 20) { + state.hints += stackSize >> 1; + if (state.hints === 0) { + data.copyWithin(j - 1, j, -1); + j -= 1; + length -= 1; + continue; + } + j += state.hints + 7 >> 3; + stackSize %= 2; + validationCommand = CharstringValidationData[value]; + } else if (value === 10 || value === 29) { + const subrsIndex = value === 10 ? localSubrIndex : globalSubrIndex; + if (!subrsIndex) { + validationCommand = CharstringValidationData[value]; + warn("Missing subrsIndex for " + validationCommand.id); + return false; + } + let bias = 32768; + if (subrsIndex.count < 1240) { + bias = 107; + } else if (subrsIndex.count < 33900) { + bias = 1131; + } + const subrNumber = stack[--stackSize] + bias; + if (subrNumber < 0 || subrNumber >= subrsIndex.count || isNaN(subrNumber)) { + validationCommand = CharstringValidationData[value]; + warn("Out of bounds subrIndex for " + validationCommand.id); + return false; + } + state.stackSize = stackSize; + state.callDepth++; + const valid = this.parseCharString(state, subrsIndex.get(subrNumber), localSubrIndex, globalSubrIndex); + if (!valid) { + return false; + } + state.callDepth--; + stackSize = state.stackSize; + continue; + } else if (value === 11) { + state.stackSize = stackSize; + return true; + } else if (value === 0 && j === data.length) { + data[j - 1] = 14; + validationCommand = CharstringValidationData[14]; + } else if (value === 9) { + data.copyWithin(j - 1, j, -1); + j -= 1; + length -= 1; + continue; + } else { + validationCommand = CharstringValidationData[value]; + } + if (validationCommand) { + if (validationCommand.stem) { + state.hints += stackSize >> 1; + if (value === 3 || value === 23) { + state.hasVStems = true; + } else if (state.hasVStems && (value === 1 || value === 18)) { + warn("CFF stem hints are in wrong order"); + data[j - 1] = value === 1 ? 3 : 23; + } + } + if ("min" in validationCommand) { + if (!state.undefStack && stackSize < validationCommand.min) { + warn("Not enough parameters for " + validationCommand.id + "; actual: " + stackSize + ", expected: " + validationCommand.min); + if (stackSize === 0) { + data[j - 1] = 14; + return true; + } + return false; + } + } + if (state.firstStackClearing && validationCommand.stackClearing) { + state.firstStackClearing = false; + stackSize -= validationCommand.min; + if (stackSize >= 2 && validationCommand.stem) { + stackSize %= 2; + } else if (stackSize > 1) { + warn("Found too many parameters for stack-clearing command"); + } + if (stackSize > 0) { + state.width = stack[stackSize - 1]; + } + } + if ("stackDelta" in validationCommand) { + if ("stackFn" in validationCommand) { + validationCommand.stackFn(stack, stackSize); + } + stackSize += validationCommand.stackDelta; + } else if (validationCommand.stackClearing) { + stackSize = 0; + } else if (validationCommand.resetStack) { + stackSize = 0; + state.undefStack = false; + } else if (validationCommand.undefStack) { + stackSize = 0; + state.undefStack = true; + state.firstStackClearing = false; + } + } + } + if (length < data.length) { + data.fill(14, length); + } + state.stackSize = stackSize; + return true; + } + parseCharStrings({ + charStrings, + localSubrIndex, + globalSubrIndex, + fdSelect, + fdArray, + privateDict + }) { + const seacs = []; + const widths = []; + const count = charStrings.count; + for (let i = 0; i < count; i++) { + const charstring = charStrings.get(i); + const state = { + callDepth: 0, + stackSize: 0, + stack: [], + undefStack: true, + hints: 0, + firstStackClearing: true, + seac: null, + width: null, + hasVStems: false + }; + let valid = true; + let localSubrToUse = null; + let privateDictToUse = privateDict; + if (fdSelect && fdArray.length) { + const fdIndex = fdSelect.getFDIndex(i); + if (fdIndex === -1) { + warn("Glyph index is not in fd select."); + valid = false; + } + if (fdIndex >= fdArray.length) { + warn("Invalid fd index for glyph index."); + valid = false; + } + if (valid) { + privateDictToUse = fdArray[fdIndex].privateDict; + localSubrToUse = privateDictToUse.subrsIndex; + } + } else if (localSubrIndex) { + localSubrToUse = localSubrIndex; + } + if (valid) { + valid = this.parseCharString(state, charstring, localSubrToUse, globalSubrIndex); + } + if (state.width !== null) { + const nominalWidth = privateDictToUse.getByName("nominalWidthX"); + widths[i] = nominalWidth + state.width; + } else { + const defaultWidth = privateDictToUse.getByName("defaultWidthX"); + widths[i] = defaultWidth; + } + if (state.seac !== null) { + seacs[i] = state.seac; + } + if (!valid) { + charStrings.set(i, new Uint8Array([14])); + } + } + return { + charStrings, + seacs, + widths + }; + } + emptyPrivateDictionary(parentDict) { + const privateDict = this.createDict(CFFPrivateDict, [], parentDict.strings); + parentDict.setByKey(18, [0, 0]); + parentDict.privateDict = privateDict; + } + parsePrivateDict(parentDict) { + if (!parentDict.hasName("Private")) { + this.emptyPrivateDictionary(parentDict); + return; + } + const privateOffset = parentDict.getByName("Private"); + if (!Array.isArray(privateOffset) || privateOffset.length !== 2) { + parentDict.removeByName("Private"); + return; + } + const size = privateOffset[0]; + const offset = privateOffset[1]; + if (size === 0 || offset >= this.bytes.length) { + this.emptyPrivateDictionary(parentDict); + return; + } + const privateDictEnd = offset + size; + const dictData = this.bytes.subarray(offset, privateDictEnd); + const dict = this.parseDict(dictData); + const privateDict = this.createDict(CFFPrivateDict, dict, parentDict.strings); + parentDict.privateDict = privateDict; + if (privateDict.getByName("ExpansionFactor") === 0) { + privateDict.setByName("ExpansionFactor", 0.06); + } + if (!privateDict.getByName("Subrs")) { + return; + } + const subrsOffset = privateDict.getByName("Subrs"); + const relativeOffset = offset + subrsOffset; + if (subrsOffset === 0 || relativeOffset >= this.bytes.length) { + this.emptyPrivateDictionary(parentDict); + return; + } + const subrsIndex = this.parseIndex(relativeOffset); + privateDict.subrsIndex = subrsIndex.obj; + } + parseCharsets(pos, length, strings, cid) { + if (pos === 0) { + return new CFFCharset(true, CFFCharsetPredefinedTypes.ISO_ADOBE, ISOAdobeCharset); + } else if (pos === 1) { + return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT, ExpertCharset); + } else if (pos === 2) { + return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT_SUBSET, ExpertSubsetCharset); + } + const bytes = this.bytes; + const start = pos; + const format = bytes[pos++]; + const charset = [cid ? 0 : ".notdef"]; + let id, count, i; + length -= 1; + switch (format) { + case 0: + for (i = 0; i < length; i++) { + id = bytes[pos++] << 8 | bytes[pos++]; + charset.push(cid ? id : strings.get(id)); + } + break; + case 1: + while (charset.length <= length) { + id = bytes[pos++] << 8 | bytes[pos++]; + count = bytes[pos++]; + for (i = 0; i <= count; i++) { + charset.push(cid ? id++ : strings.get(id++)); + } + } + break; + case 2: + while (charset.length <= length) { + id = bytes[pos++] << 8 | bytes[pos++]; + count = bytes[pos++] << 8 | bytes[pos++]; + for (i = 0; i <= count; i++) { + charset.push(cid ? id++ : strings.get(id++)); + } + } + break; + default: + throw new FormatError("Unknown charset format"); + } + const end = pos; + const raw = bytes.subarray(start, end); + return new CFFCharset(false, format, charset, raw); + } + parseEncoding(pos, properties, strings, charset) { + const encoding = Object.create(null); + const bytes = this.bytes; + let predefined = false; + let format, i, ii; + let raw = null; + function readSupplement() { + const supplementsCount = bytes[pos++]; + for (i = 0; i < supplementsCount; i++) { + const code = bytes[pos++]; + const sid = (bytes[pos++] << 8) + (bytes[pos++] & 0xff); + encoding[code] = charset.indexOf(strings.get(sid)); + } + } + if (pos === 0 || pos === 1) { + predefined = true; + format = pos; + const baseEncoding = pos ? ExpertEncoding : StandardEncoding; + for (i = 0, ii = charset.length; i < ii; i++) { + const index = baseEncoding.indexOf(charset[i]); + if (index !== -1) { + encoding[index] = i; + } + } + } else { + const dataStart = pos; + format = bytes[pos++]; + switch (format & 0x7f) { + case 0: + const glyphsCount = bytes[pos++]; + for (i = 1; i <= glyphsCount; i++) { + encoding[bytes[pos++]] = i; + } + break; + case 1: + const rangesCount = bytes[pos++]; + let gid = 1; + for (i = 0; i < rangesCount; i++) { + const start = bytes[pos++]; + const left = bytes[pos++]; + for (let j = start; j <= start + left; j++) { + encoding[j] = gid++; + } + } + break; + default: + throw new FormatError(`Unknown encoding format: ${format} in CFF`); + } + const dataEnd = pos; + if (format & 0x80) { + bytes[dataStart] &= 0x7f; + readSupplement(); + } + raw = bytes.subarray(dataStart, dataEnd); + } + format &= 0x7f; + return new CFFEncoding(predefined, format, encoding, raw); + } + parseFDSelect(pos, length) { + const bytes = this.bytes; + const format = bytes[pos++]; + const fdSelect = []; + let i; + switch (format) { + case 0: + for (i = 0; i < length; ++i) { + const id = bytes[pos++]; + fdSelect.push(id); + } + break; + case 3: + const rangesCount = bytes[pos++] << 8 | bytes[pos++]; + for (i = 0; i < rangesCount; ++i) { + let first = bytes[pos++] << 8 | bytes[pos++]; + if (i === 0 && first !== 0) { + warn("parseFDSelect: The first range must have a first GID of 0" + " -- trying to recover."); + first = 0; + } + const fdIndex = bytes[pos++]; + const next = bytes[pos] << 8 | bytes[pos + 1]; + for (let j = first; j < next; ++j) { + fdSelect.push(fdIndex); + } + } + pos += 2; + break; + default: + throw new FormatError(`parseFDSelect: Unknown format "${format}".`); + } + if (fdSelect.length !== length) { + throw new FormatError("parseFDSelect: Invalid font data."); + } + return new CFFFDSelect(format, fdSelect); + } +} +class CFF { + constructor() { + this.header = null; + this.names = []; + this.topDict = null; + this.strings = new CFFStrings(); + this.globalSubrIndex = null; + this.encoding = null; + this.charset = null; + this.charStrings = null; + this.fdArray = []; + this.fdSelect = null; + this.isCIDFont = false; + } + duplicateFirstGlyph() { + if (this.charStrings.count >= 65535) { + warn("Not enough space in charstrings to duplicate first glyph."); + return; + } + const glyphZero = this.charStrings.get(0); + this.charStrings.add(glyphZero); + if (this.isCIDFont) { + this.fdSelect.fdSelect.push(this.fdSelect.fdSelect[0]); + } + } + hasGlyphId(id) { + if (id < 0 || id >= this.charStrings.count) { + return false; + } + const glyph = this.charStrings.get(id); + return glyph.length > 0; + } +} +class CFFHeader { + constructor(major, minor, hdrSize, offSize) { + this.major = major; + this.minor = minor; + this.hdrSize = hdrSize; + this.offSize = offSize; + } +} +class CFFStrings { + constructor() { + this.strings = []; + } + get(index) { + if (index >= 0 && index <= NUM_STANDARD_CFF_STRINGS - 1) { + return CFFStandardStrings[index]; + } + if (index - NUM_STANDARD_CFF_STRINGS <= this.strings.length) { + return this.strings[index - NUM_STANDARD_CFF_STRINGS]; + } + return CFFStandardStrings[0]; + } + getSID(str) { + let index = CFFStandardStrings.indexOf(str); + if (index !== -1) { + return index; + } + index = this.strings.indexOf(str); + if (index !== -1) { + return index + NUM_STANDARD_CFF_STRINGS; + } + return -1; + } + add(value) { + this.strings.push(value); + } + get count() { + return this.strings.length; + } +} +class CFFIndex { + constructor() { + this.objects = []; + this.length = 0; + } + add(data) { + this.length += data.length; + this.objects.push(data); + } + set(index, data) { + this.length += data.length - this.objects[index].length; + this.objects[index] = data; + } + get(index) { + return this.objects[index]; + } + get count() { + return this.objects.length; + } +} +class CFFDict { + constructor(tables, strings) { + this.keyToNameMap = tables.keyToNameMap; + this.nameToKeyMap = tables.nameToKeyMap; + this.defaults = tables.defaults; + this.types = tables.types; + this.opcodes = tables.opcodes; + this.order = tables.order; + this.strings = strings; + this.values = Object.create(null); + } + setByKey(key, value) { + if (!(key in this.keyToNameMap)) { + return false; + } + if (value.length === 0) { + return true; + } + for (const val of value) { + if (isNaN(val)) { + warn(`Invalid CFFDict value: "${value}" for key "${key}".`); + return true; + } + } + const type = this.types[key]; + if (type === "num" || type === "sid" || type === "offset") { + value = value[0]; + } + this.values[key] = value; + return true; + } + setByName(name, value) { + if (!(name in this.nameToKeyMap)) { + throw new FormatError(`Invalid dictionary name "${name}"`); + } + this.values[this.nameToKeyMap[name]] = value; + } + hasName(name) { + return this.nameToKeyMap[name] in this.values; + } + getByName(name) { + if (!(name in this.nameToKeyMap)) { + throw new FormatError(`Invalid dictionary name ${name}"`); + } + const key = this.nameToKeyMap[name]; + if (!(key in this.values)) { + return this.defaults[key]; + } + return this.values[key]; + } + removeByName(name) { + delete this.values[this.nameToKeyMap[name]]; + } + static createTables(layout) { + const tables = { + keyToNameMap: {}, + nameToKeyMap: {}, + defaults: {}, + types: {}, + opcodes: {}, + order: [] + }; + for (const entry of layout) { + const key = Array.isArray(entry[0]) ? (entry[0][0] << 8) + entry[0][1] : entry[0]; + tables.keyToNameMap[key] = entry[1]; + tables.nameToKeyMap[entry[1]] = key; + tables.types[key] = entry[2]; + tables.defaults[key] = entry[3]; + tables.opcodes[key] = Array.isArray(entry[0]) ? entry[0] : [entry[0]]; + tables.order.push(key); + } + return tables; + } +} +const CFFTopDictLayout = [[[12, 30], "ROS", ["sid", "sid", "num"], null], [[12, 20], "SyntheticBase", "num", null], [0, "version", "sid", null], [1, "Notice", "sid", null], [[12, 0], "Copyright", "sid", null], [2, "FullName", "sid", null], [3, "FamilyName", "sid", null], [4, "Weight", "sid", null], [[12, 1], "isFixedPitch", "num", 0], [[12, 2], "ItalicAngle", "num", 0], [[12, 3], "UnderlinePosition", "num", -100], [[12, 4], "UnderlineThickness", "num", 50], [[12, 5], "PaintType", "num", 0], [[12, 6], "CharstringType", "num", 2], [[12, 7], "FontMatrix", ["num", "num", "num", "num", "num", "num"], [0.001, 0, 0, 0.001, 0, 0]], [13, "UniqueID", "num", null], [5, "FontBBox", ["num", "num", "num", "num"], [0, 0, 0, 0]], [[12, 8], "StrokeWidth", "num", 0], [14, "XUID", "array", null], [15, "charset", "offset", 0], [16, "Encoding", "offset", 0], [17, "CharStrings", "offset", 0], [18, "Private", ["offset", "offset"], null], [[12, 21], "PostScript", "sid", null], [[12, 22], "BaseFontName", "sid", null], [[12, 23], "BaseFontBlend", "delta", null], [[12, 31], "CIDFontVersion", "num", 0], [[12, 32], "CIDFontRevision", "num", 0], [[12, 33], "CIDFontType", "num", 0], [[12, 34], "CIDCount", "num", 8720], [[12, 35], "UIDBase", "num", null], [[12, 37], "FDSelect", "offset", null], [[12, 36], "FDArray", "offset", null], [[12, 38], "FontName", "sid", null]]; +class CFFTopDict extends CFFDict { + static get tables() { + return shadow(this, "tables", this.createTables(CFFTopDictLayout)); + } + constructor(strings) { + super(CFFTopDict.tables, strings); + this.privateDict = null; + } +} +const CFFPrivateDictLayout = [[6, "BlueValues", "delta", null], [7, "OtherBlues", "delta", null], [8, "FamilyBlues", "delta", null], [9, "FamilyOtherBlues", "delta", null], [[12, 9], "BlueScale", "num", 0.039625], [[12, 10], "BlueShift", "num", 7], [[12, 11], "BlueFuzz", "num", 1], [10, "StdHW", "num", null], [11, "StdVW", "num", null], [[12, 12], "StemSnapH", "delta", null], [[12, 13], "StemSnapV", "delta", null], [[12, 14], "ForceBold", "num", 0], [[12, 17], "LanguageGroup", "num", 0], [[12, 18], "ExpansionFactor", "num", 0.06], [[12, 19], "initialRandomSeed", "num", 0], [20, "defaultWidthX", "num", 0], [21, "nominalWidthX", "num", 0], [19, "Subrs", "offset", null]]; +class CFFPrivateDict extends CFFDict { + static get tables() { + return shadow(this, "tables", this.createTables(CFFPrivateDictLayout)); + } + constructor(strings) { + super(CFFPrivateDict.tables, strings); + this.subrsIndex = null; + } +} +const CFFCharsetPredefinedTypes = { + ISO_ADOBE: 0, + EXPERT: 1, + EXPERT_SUBSET: 2 +}; +class CFFCharset { + constructor(predefined, format, charset, raw) { + this.predefined = predefined; + this.format = format; + this.charset = charset; + this.raw = raw; + } +} +class CFFEncoding { + constructor(predefined, format, encoding, raw) { + this.predefined = predefined; + this.format = format; + this.encoding = encoding; + this.raw = raw; + } +} +class CFFFDSelect { + constructor(format, fdSelect) { + this.format = format; + this.fdSelect = fdSelect; + } + getFDIndex(glyphIndex) { + if (glyphIndex < 0 || glyphIndex >= this.fdSelect.length) { + return -1; + } + return this.fdSelect[glyphIndex]; + } +} +class CFFOffsetTracker { + constructor() { + this.offsets = Object.create(null); + } + isTracking(key) { + return key in this.offsets; + } + track(key, location) { + if (key in this.offsets) { + throw new FormatError(`Already tracking location of ${key}`); + } + this.offsets[key] = location; + } + offset(value) { + for (const key in this.offsets) { + this.offsets[key] += value; + } + } + setEntryLocation(key, values, output) { + if (!(key in this.offsets)) { + throw new FormatError(`Not tracking location of ${key}`); + } + const data = output.data; + const dataOffset = this.offsets[key]; + const size = 5; + for (let i = 0, ii = values.length; i < ii; ++i) { + const offset0 = i * size + dataOffset; + const offset1 = offset0 + 1; + const offset2 = offset0 + 2; + const offset3 = offset0 + 3; + const offset4 = offset0 + 4; + if (data[offset0] !== 0x1d || data[offset1] !== 0 || data[offset2] !== 0 || data[offset3] !== 0 || data[offset4] !== 0) { + throw new FormatError("writing to an offset that is not empty"); + } + const value = values[i]; + data[offset0] = 0x1d; + data[offset1] = value >> 24 & 0xff; + data[offset2] = value >> 16 & 0xff; + data[offset3] = value >> 8 & 0xff; + data[offset4] = value & 0xff; + } + } +} +class CFFCompiler { + constructor(cff) { + this.cff = cff; + } + compile() { + const cff = this.cff; + const output = { + data: [], + length: 0, + add(data) { + try { + this.data.push(...data); + } catch { + this.data = this.data.concat(data); + } + this.length = this.data.length; + } + }; + const header = this.compileHeader(cff.header); + output.add(header); + const nameIndex = this.compileNameIndex(cff.names); + output.add(nameIndex); + if (cff.isCIDFont) { + if (cff.topDict.hasName("FontMatrix")) { + const base = cff.topDict.getByName("FontMatrix"); + cff.topDict.removeByName("FontMatrix"); + for (const subDict of cff.fdArray) { + let matrix = base.slice(0); + if (subDict.hasName("FontMatrix")) { + matrix = Util.transform(matrix, subDict.getByName("FontMatrix")); + } + subDict.setByName("FontMatrix", matrix); + } + } + } + const xuid = cff.topDict.getByName("XUID"); + if (xuid?.length > 16) { + cff.topDict.removeByName("XUID"); + } + cff.topDict.setByName("charset", 0); + let compiled = this.compileTopDicts([cff.topDict], output.length, cff.isCIDFont); + output.add(compiled.output); + const topDictTracker = compiled.trackers[0]; + const stringIndex = this.compileStringIndex(cff.strings.strings); + output.add(stringIndex); + const globalSubrIndex = this.compileIndex(cff.globalSubrIndex); + output.add(globalSubrIndex); + if (cff.encoding && cff.topDict.hasName("Encoding")) { + if (cff.encoding.predefined) { + topDictTracker.setEntryLocation("Encoding", [cff.encoding.format], output); + } else { + const encoding = this.compileEncoding(cff.encoding); + topDictTracker.setEntryLocation("Encoding", [output.length], output); + output.add(encoding); + } + } + const charset = this.compileCharset(cff.charset, cff.charStrings.count, cff.strings, cff.isCIDFont); + topDictTracker.setEntryLocation("charset", [output.length], output); + output.add(charset); + const charStrings = this.compileCharStrings(cff.charStrings); + topDictTracker.setEntryLocation("CharStrings", [output.length], output); + output.add(charStrings); + if (cff.isCIDFont) { + topDictTracker.setEntryLocation("FDSelect", [output.length], output); + const fdSelect = this.compileFDSelect(cff.fdSelect); + output.add(fdSelect); + compiled = this.compileTopDicts(cff.fdArray, output.length, true); + topDictTracker.setEntryLocation("FDArray", [output.length], output); + output.add(compiled.output); + const fontDictTrackers = compiled.trackers; + this.compilePrivateDicts(cff.fdArray, fontDictTrackers, output); + } + this.compilePrivateDicts([cff.topDict], [topDictTracker], output); + output.add([0]); + return output.data; + } + encodeNumber(value) { + if (Number.isInteger(value)) { + return this.encodeInteger(value); + } + return this.encodeFloat(value); + } + static get EncodeFloatRegExp() { + return shadow(this, "EncodeFloatRegExp", /\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/); + } + encodeFloat(num) { + let value = num.toString(); + const m = CFFCompiler.EncodeFloatRegExp.exec(value); + if (m) { + const epsilon = parseFloat("1e" + ((m[2] ? +m[2] : 0) + m[1].length)); + value = (Math.round(num * epsilon) / epsilon).toString(); + } + let nibbles = ""; + let i, ii; + for (i = 0, ii = value.length; i < ii; ++i) { + const a = value[i]; + if (a === "e") { + nibbles += value[++i] === "-" ? "c" : "b"; + } else if (a === ".") { + nibbles += "a"; + } else if (a === "-") { + nibbles += "e"; + } else { + nibbles += a; + } + } + nibbles += nibbles.length & 1 ? "f" : "ff"; + const out = [30]; + for (i = 0, ii = nibbles.length; i < ii; i += 2) { + out.push(parseInt(nibbles.substring(i, i + 2), 16)); + } + return out; + } + encodeInteger(value) { + let code; + if (value >= -107 && value <= 107) { + code = [value + 139]; + } else if (value >= 108 && value <= 1131) { + value -= 108; + code = [(value >> 8) + 247, value & 0xff]; + } else if (value >= -1131 && value <= -108) { + value = -value - 108; + code = [(value >> 8) + 251, value & 0xff]; + } else if (value >= -32768 && value <= 32767) { + code = [0x1c, value >> 8 & 0xff, value & 0xff]; + } else { + code = [0x1d, value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff]; + } + return code; + } + compileHeader(header) { + return [header.major, header.minor, 4, header.offSize]; + } + compileNameIndex(names) { + const nameIndex = new CFFIndex(); + for (const name of names) { + const length = Math.min(name.length, 127); + let sanitizedName = new Array(length); + for (let j = 0; j < length; j++) { + let char = name[j]; + if (char < "!" || char > "~" || char === "[" || char === "]" || char === "(" || char === ")" || char === "{" || char === "}" || char === "<" || char === ">" || char === "/" || char === "%") { + char = "_"; + } + sanitizedName[j] = char; + } + sanitizedName = sanitizedName.join(""); + if (sanitizedName === "") { + sanitizedName = "Bad_Font_Name"; + } + nameIndex.add(stringToBytes(sanitizedName)); + } + return this.compileIndex(nameIndex); + } + compileTopDicts(dicts, length, removeCidKeys) { + const fontDictTrackers = []; + let fdArrayIndex = new CFFIndex(); + for (const fontDict of dicts) { + if (removeCidKeys) { + fontDict.removeByName("CIDFontVersion"); + fontDict.removeByName("CIDFontRevision"); + fontDict.removeByName("CIDFontType"); + fontDict.removeByName("CIDCount"); + fontDict.removeByName("UIDBase"); + } + const fontDictTracker = new CFFOffsetTracker(); + const fontDictData = this.compileDict(fontDict, fontDictTracker); + fontDictTrackers.push(fontDictTracker); + fdArrayIndex.add(fontDictData); + fontDictTracker.offset(length); + } + fdArrayIndex = this.compileIndex(fdArrayIndex, fontDictTrackers); + return { + trackers: fontDictTrackers, + output: fdArrayIndex + }; + } + compilePrivateDicts(dicts, trackers, output) { + for (let i = 0, ii = dicts.length; i < ii; ++i) { + const fontDict = dicts[i]; + const privateDict = fontDict.privateDict; + if (!privateDict || !fontDict.hasName("Private")) { + throw new FormatError("There must be a private dictionary."); + } + const privateDictTracker = new CFFOffsetTracker(); + const privateDictData = this.compileDict(privateDict, privateDictTracker); + let outputLength = output.length; + privateDictTracker.offset(outputLength); + if (!privateDictData.length) { + outputLength = 0; + } + trackers[i].setEntryLocation("Private", [privateDictData.length, outputLength], output); + output.add(privateDictData); + if (privateDict.subrsIndex && privateDict.hasName("Subrs")) { + const subrs = this.compileIndex(privateDict.subrsIndex); + privateDictTracker.setEntryLocation("Subrs", [privateDictData.length], output); + output.add(subrs); + } + } + } + compileDict(dict, offsetTracker) { + const out = []; + for (const key of dict.order) { + if (!(key in dict.values)) { + continue; + } + let values = dict.values[key]; + let types = dict.types[key]; + if (!Array.isArray(types)) { + types = [types]; + } + if (!Array.isArray(values)) { + values = [values]; + } + if (values.length === 0) { + continue; + } + for (let j = 0, jj = types.length; j < jj; ++j) { + const type = types[j]; + const value = values[j]; + switch (type) { + case "num": + case "sid": + out.push(...this.encodeNumber(value)); + break; + case "offset": + const name = dict.keyToNameMap[key]; + if (!offsetTracker.isTracking(name)) { + offsetTracker.track(name, out.length); + } + out.push(0x1d, 0, 0, 0, 0); + break; + case "array": + case "delta": + out.push(...this.encodeNumber(value)); + for (let k = 1, kk = values.length; k < kk; ++k) { + out.push(...this.encodeNumber(values[k])); + } + break; + default: + throw new FormatError(`Unknown data type of ${type}`); + } + } + out.push(...dict.opcodes[key]); + } + return out; + } + compileStringIndex(strings) { + const stringIndex = new CFFIndex(); + for (const string of strings) { + stringIndex.add(stringToBytes(string)); + } + return this.compileIndex(stringIndex); + } + compileCharStrings(charStrings) { + const charStringsIndex = new CFFIndex(); + for (let i = 0; i < charStrings.count; i++) { + const glyph = charStrings.get(i); + if (glyph.length === 0) { + charStringsIndex.add(new Uint8Array([0x8b, 0x0e])); + continue; + } + charStringsIndex.add(glyph); + } + return this.compileIndex(charStringsIndex); + } + compileCharset(charset, numGlyphs, strings, isCIDFont) { + let out; + const numGlyphsLessNotDef = numGlyphs - 1; + if (isCIDFont) { + out = new Uint8Array([2, 0, 0, numGlyphsLessNotDef >> 8 & 0xff, numGlyphsLessNotDef & 0xff]); + } else { + const length = 1 + numGlyphsLessNotDef * 2; + out = new Uint8Array(length); + out[0] = 0; + let charsetIndex = 0; + const numCharsets = charset.charset.length; + let warned = false; + for (let i = 1; i < out.length; i += 2) { + let sid = 0; + if (charsetIndex < numCharsets) { + const name = charset.charset[charsetIndex++]; + sid = strings.getSID(name); + if (sid === -1) { + sid = 0; + if (!warned) { + warned = true; + warn(`Couldn't find ${name} in CFF strings`); + } + } + } + out[i] = sid >> 8 & 0xff; + out[i + 1] = sid & 0xff; + } + } + return this.compileTypedArray(out); + } + compileEncoding(encoding) { + return this.compileTypedArray(encoding.raw); + } + compileFDSelect(fdSelect) { + const format = fdSelect.format; + let out, i; + switch (format) { + case 0: + out = new Uint8Array(1 + fdSelect.fdSelect.length); + out[0] = format; + for (i = 0; i < fdSelect.fdSelect.length; i++) { + out[i + 1] = fdSelect.fdSelect[i]; + } + break; + case 3: + const start = 0; + let lastFD = fdSelect.fdSelect[0]; + const ranges = [format, 0, 0, start >> 8 & 0xff, start & 0xff, lastFD]; + for (i = 1; i < fdSelect.fdSelect.length; i++) { + const currentFD = fdSelect.fdSelect[i]; + if (currentFD !== lastFD) { + ranges.push(i >> 8 & 0xff, i & 0xff, currentFD); + lastFD = currentFD; + } + } + const numRanges = (ranges.length - 3) / 3; + ranges[1] = numRanges >> 8 & 0xff; + ranges[2] = numRanges & 0xff; + ranges.push(i >> 8 & 0xff, i & 0xff); + out = new Uint8Array(ranges); + break; + } + return this.compileTypedArray(out); + } + compileTypedArray(data) { + return Array.from(data); + } + compileIndex(index, trackers = []) { + const objects = index.objects; + const count = objects.length; + if (count === 0) { + return [0, 0]; + } + const data = [count >> 8 & 0xff, count & 0xff]; + let lastOffset = 1, + i; + for (i = 0; i < count; ++i) { + lastOffset += objects[i].length; + } + let offsetSize; + if (lastOffset < 0x100) { + offsetSize = 1; + } else if (lastOffset < 0x10000) { + offsetSize = 2; + } else if (lastOffset < 0x1000000) { + offsetSize = 3; + } else { + offsetSize = 4; + } + data.push(offsetSize); + let relativeOffset = 1; + for (i = 0; i < count + 1; i++) { + if (offsetSize === 1) { + data.push(relativeOffset & 0xff); + } else if (offsetSize === 2) { + data.push(relativeOffset >> 8 & 0xff, relativeOffset & 0xff); + } else if (offsetSize === 3) { + data.push(relativeOffset >> 16 & 0xff, relativeOffset >> 8 & 0xff, relativeOffset & 0xff); + } else { + data.push(relativeOffset >>> 24 & 0xff, relativeOffset >> 16 & 0xff, relativeOffset >> 8 & 0xff, relativeOffset & 0xff); + } + if (objects[i]) { + relativeOffset += objects[i].length; + } + } + for (i = 0; i < count; i++) { + if (trackers[i]) { + trackers[i].offset(data.length); + } + data.push(...objects[i]); + } + return data; + } +} + +;// ./src/core/glyphlist.js + +const getGlyphsUnicode = getLookupTableFactory(function (t) { + t.A = 0x0041; + t.AE = 0x00c6; + t.AEacute = 0x01fc; + t.AEmacron = 0x01e2; + t.AEsmall = 0xf7e6; + t.Aacute = 0x00c1; + t.Aacutesmall = 0xf7e1; + t.Abreve = 0x0102; + t.Abreveacute = 0x1eae; + t.Abrevecyrillic = 0x04d0; + t.Abrevedotbelow = 0x1eb6; + t.Abrevegrave = 0x1eb0; + t.Abrevehookabove = 0x1eb2; + t.Abrevetilde = 0x1eb4; + t.Acaron = 0x01cd; + t.Acircle = 0x24b6; + t.Acircumflex = 0x00c2; + t.Acircumflexacute = 0x1ea4; + t.Acircumflexdotbelow = 0x1eac; + t.Acircumflexgrave = 0x1ea6; + t.Acircumflexhookabove = 0x1ea8; + t.Acircumflexsmall = 0xf7e2; + t.Acircumflextilde = 0x1eaa; + t.Acute = 0xf6c9; + t.Acutesmall = 0xf7b4; + t.Acyrillic = 0x0410; + t.Adblgrave = 0x0200; + t.Adieresis = 0x00c4; + t.Adieresiscyrillic = 0x04d2; + t.Adieresismacron = 0x01de; + t.Adieresissmall = 0xf7e4; + t.Adotbelow = 0x1ea0; + t.Adotmacron = 0x01e0; + t.Agrave = 0x00c0; + t.Agravesmall = 0xf7e0; + t.Ahookabove = 0x1ea2; + t.Aiecyrillic = 0x04d4; + t.Ainvertedbreve = 0x0202; + t.Alpha = 0x0391; + t.Alphatonos = 0x0386; + t.Amacron = 0x0100; + t.Amonospace = 0xff21; + t.Aogonek = 0x0104; + t.Aring = 0x00c5; + t.Aringacute = 0x01fa; + t.Aringbelow = 0x1e00; + t.Aringsmall = 0xf7e5; + t.Asmall = 0xf761; + t.Atilde = 0x00c3; + t.Atildesmall = 0xf7e3; + t.Aybarmenian = 0x0531; + t.B = 0x0042; + t.Bcircle = 0x24b7; + t.Bdotaccent = 0x1e02; + t.Bdotbelow = 0x1e04; + t.Becyrillic = 0x0411; + t.Benarmenian = 0x0532; + t.Beta = 0x0392; + t.Bhook = 0x0181; + t.Blinebelow = 0x1e06; + t.Bmonospace = 0xff22; + t.Brevesmall = 0xf6f4; + t.Bsmall = 0xf762; + t.Btopbar = 0x0182; + t.C = 0x0043; + t.Caarmenian = 0x053e; + t.Cacute = 0x0106; + t.Caron = 0xf6ca; + t.Caronsmall = 0xf6f5; + t.Ccaron = 0x010c; + t.Ccedilla = 0x00c7; + t.Ccedillaacute = 0x1e08; + t.Ccedillasmall = 0xf7e7; + t.Ccircle = 0x24b8; + t.Ccircumflex = 0x0108; + t.Cdot = 0x010a; + t.Cdotaccent = 0x010a; + t.Cedillasmall = 0xf7b8; + t.Chaarmenian = 0x0549; + t.Cheabkhasiancyrillic = 0x04bc; + t.Checyrillic = 0x0427; + t.Chedescenderabkhasiancyrillic = 0x04be; + t.Chedescendercyrillic = 0x04b6; + t.Chedieresiscyrillic = 0x04f4; + t.Cheharmenian = 0x0543; + t.Chekhakassiancyrillic = 0x04cb; + t.Cheverticalstrokecyrillic = 0x04b8; + t.Chi = 0x03a7; + t.Chook = 0x0187; + t.Circumflexsmall = 0xf6f6; + t.Cmonospace = 0xff23; + t.Coarmenian = 0x0551; + t.Csmall = 0xf763; + t.D = 0x0044; + t.DZ = 0x01f1; + t.DZcaron = 0x01c4; + t.Daarmenian = 0x0534; + t.Dafrican = 0x0189; + t.Dcaron = 0x010e; + t.Dcedilla = 0x1e10; + t.Dcircle = 0x24b9; + t.Dcircumflexbelow = 0x1e12; + t.Dcroat = 0x0110; + t.Ddotaccent = 0x1e0a; + t.Ddotbelow = 0x1e0c; + t.Decyrillic = 0x0414; + t.Deicoptic = 0x03ee; + t.Delta = 0x2206; + t.Deltagreek = 0x0394; + t.Dhook = 0x018a; + t.Dieresis = 0xf6cb; + t.DieresisAcute = 0xf6cc; + t.DieresisGrave = 0xf6cd; + t.Dieresissmall = 0xf7a8; + t.Digammagreek = 0x03dc; + t.Djecyrillic = 0x0402; + t.Dlinebelow = 0x1e0e; + t.Dmonospace = 0xff24; + t.Dotaccentsmall = 0xf6f7; + t.Dslash = 0x0110; + t.Dsmall = 0xf764; + t.Dtopbar = 0x018b; + t.Dz = 0x01f2; + t.Dzcaron = 0x01c5; + t.Dzeabkhasiancyrillic = 0x04e0; + t.Dzecyrillic = 0x0405; + t.Dzhecyrillic = 0x040f; + t.E = 0x0045; + t.Eacute = 0x00c9; + t.Eacutesmall = 0xf7e9; + t.Ebreve = 0x0114; + t.Ecaron = 0x011a; + t.Ecedillabreve = 0x1e1c; + t.Echarmenian = 0x0535; + t.Ecircle = 0x24ba; + t.Ecircumflex = 0x00ca; + t.Ecircumflexacute = 0x1ebe; + t.Ecircumflexbelow = 0x1e18; + t.Ecircumflexdotbelow = 0x1ec6; + t.Ecircumflexgrave = 0x1ec0; + t.Ecircumflexhookabove = 0x1ec2; + t.Ecircumflexsmall = 0xf7ea; + t.Ecircumflextilde = 0x1ec4; + t.Ecyrillic = 0x0404; + t.Edblgrave = 0x0204; + t.Edieresis = 0x00cb; + t.Edieresissmall = 0xf7eb; + t.Edot = 0x0116; + t.Edotaccent = 0x0116; + t.Edotbelow = 0x1eb8; + t.Efcyrillic = 0x0424; + t.Egrave = 0x00c8; + t.Egravesmall = 0xf7e8; + t.Eharmenian = 0x0537; + t.Ehookabove = 0x1eba; + t.Eightroman = 0x2167; + t.Einvertedbreve = 0x0206; + t.Eiotifiedcyrillic = 0x0464; + t.Elcyrillic = 0x041b; + t.Elevenroman = 0x216a; + t.Emacron = 0x0112; + t.Emacronacute = 0x1e16; + t.Emacrongrave = 0x1e14; + t.Emcyrillic = 0x041c; + t.Emonospace = 0xff25; + t.Encyrillic = 0x041d; + t.Endescendercyrillic = 0x04a2; + t.Eng = 0x014a; + t.Enghecyrillic = 0x04a4; + t.Enhookcyrillic = 0x04c7; + t.Eogonek = 0x0118; + t.Eopen = 0x0190; + t.Epsilon = 0x0395; + t.Epsilontonos = 0x0388; + t.Ercyrillic = 0x0420; + t.Ereversed = 0x018e; + t.Ereversedcyrillic = 0x042d; + t.Escyrillic = 0x0421; + t.Esdescendercyrillic = 0x04aa; + t.Esh = 0x01a9; + t.Esmall = 0xf765; + t.Eta = 0x0397; + t.Etarmenian = 0x0538; + t.Etatonos = 0x0389; + t.Eth = 0x00d0; + t.Ethsmall = 0xf7f0; + t.Etilde = 0x1ebc; + t.Etildebelow = 0x1e1a; + t.Euro = 0x20ac; + t.Ezh = 0x01b7; + t.Ezhcaron = 0x01ee; + t.Ezhreversed = 0x01b8; + t.F = 0x0046; + t.Fcircle = 0x24bb; + t.Fdotaccent = 0x1e1e; + t.Feharmenian = 0x0556; + t.Feicoptic = 0x03e4; + t.Fhook = 0x0191; + t.Fitacyrillic = 0x0472; + t.Fiveroman = 0x2164; + t.Fmonospace = 0xff26; + t.Fourroman = 0x2163; + t.Fsmall = 0xf766; + t.G = 0x0047; + t.GBsquare = 0x3387; + t.Gacute = 0x01f4; + t.Gamma = 0x0393; + t.Gammaafrican = 0x0194; + t.Gangiacoptic = 0x03ea; + t.Gbreve = 0x011e; + t.Gcaron = 0x01e6; + t.Gcedilla = 0x0122; + t.Gcircle = 0x24bc; + t.Gcircumflex = 0x011c; + t.Gcommaaccent = 0x0122; + t.Gdot = 0x0120; + t.Gdotaccent = 0x0120; + t.Gecyrillic = 0x0413; + t.Ghadarmenian = 0x0542; + t.Ghemiddlehookcyrillic = 0x0494; + t.Ghestrokecyrillic = 0x0492; + t.Gheupturncyrillic = 0x0490; + t.Ghook = 0x0193; + t.Gimarmenian = 0x0533; + t.Gjecyrillic = 0x0403; + t.Gmacron = 0x1e20; + t.Gmonospace = 0xff27; + t.Grave = 0xf6ce; + t.Gravesmall = 0xf760; + t.Gsmall = 0xf767; + t.Gsmallhook = 0x029b; + t.Gstroke = 0x01e4; + t.H = 0x0048; + t.H18533 = 0x25cf; + t.H18543 = 0x25aa; + t.H18551 = 0x25ab; + t.H22073 = 0x25a1; + t.HPsquare = 0x33cb; + t.Haabkhasiancyrillic = 0x04a8; + t.Hadescendercyrillic = 0x04b2; + t.Hardsigncyrillic = 0x042a; + t.Hbar = 0x0126; + t.Hbrevebelow = 0x1e2a; + t.Hcedilla = 0x1e28; + t.Hcircle = 0x24bd; + t.Hcircumflex = 0x0124; + t.Hdieresis = 0x1e26; + t.Hdotaccent = 0x1e22; + t.Hdotbelow = 0x1e24; + t.Hmonospace = 0xff28; + t.Hoarmenian = 0x0540; + t.Horicoptic = 0x03e8; + t.Hsmall = 0xf768; + t.Hungarumlaut = 0xf6cf; + t.Hungarumlautsmall = 0xf6f8; + t.Hzsquare = 0x3390; + t.I = 0x0049; + t.IAcyrillic = 0x042f; + t.IJ = 0x0132; + t.IUcyrillic = 0x042e; + t.Iacute = 0x00cd; + t.Iacutesmall = 0xf7ed; + t.Ibreve = 0x012c; + t.Icaron = 0x01cf; + t.Icircle = 0x24be; + t.Icircumflex = 0x00ce; + t.Icircumflexsmall = 0xf7ee; + t.Icyrillic = 0x0406; + t.Idblgrave = 0x0208; + t.Idieresis = 0x00cf; + t.Idieresisacute = 0x1e2e; + t.Idieresiscyrillic = 0x04e4; + t.Idieresissmall = 0xf7ef; + t.Idot = 0x0130; + t.Idotaccent = 0x0130; + t.Idotbelow = 0x1eca; + t.Iebrevecyrillic = 0x04d6; + t.Iecyrillic = 0x0415; + t.Ifraktur = 0x2111; + t.Igrave = 0x00cc; + t.Igravesmall = 0xf7ec; + t.Ihookabove = 0x1ec8; + t.Iicyrillic = 0x0418; + t.Iinvertedbreve = 0x020a; + t.Iishortcyrillic = 0x0419; + t.Imacron = 0x012a; + t.Imacroncyrillic = 0x04e2; + t.Imonospace = 0xff29; + t.Iniarmenian = 0x053b; + t.Iocyrillic = 0x0401; + t.Iogonek = 0x012e; + t.Iota = 0x0399; + t.Iotaafrican = 0x0196; + t.Iotadieresis = 0x03aa; + t.Iotatonos = 0x038a; + t.Ismall = 0xf769; + t.Istroke = 0x0197; + t.Itilde = 0x0128; + t.Itildebelow = 0x1e2c; + t.Izhitsacyrillic = 0x0474; + t.Izhitsadblgravecyrillic = 0x0476; + t.J = 0x004a; + t.Jaarmenian = 0x0541; + t.Jcircle = 0x24bf; + t.Jcircumflex = 0x0134; + t.Jecyrillic = 0x0408; + t.Jheharmenian = 0x054b; + t.Jmonospace = 0xff2a; + t.Jsmall = 0xf76a; + t.K = 0x004b; + t.KBsquare = 0x3385; + t.KKsquare = 0x33cd; + t.Kabashkircyrillic = 0x04a0; + t.Kacute = 0x1e30; + t.Kacyrillic = 0x041a; + t.Kadescendercyrillic = 0x049a; + t.Kahookcyrillic = 0x04c3; + t.Kappa = 0x039a; + t.Kastrokecyrillic = 0x049e; + t.Kaverticalstrokecyrillic = 0x049c; + t.Kcaron = 0x01e8; + t.Kcedilla = 0x0136; + t.Kcircle = 0x24c0; + t.Kcommaaccent = 0x0136; + t.Kdotbelow = 0x1e32; + t.Keharmenian = 0x0554; + t.Kenarmenian = 0x053f; + t.Khacyrillic = 0x0425; + t.Kheicoptic = 0x03e6; + t.Khook = 0x0198; + t.Kjecyrillic = 0x040c; + t.Klinebelow = 0x1e34; + t.Kmonospace = 0xff2b; + t.Koppacyrillic = 0x0480; + t.Koppagreek = 0x03de; + t.Ksicyrillic = 0x046e; + t.Ksmall = 0xf76b; + t.L = 0x004c; + t.LJ = 0x01c7; + t.LL = 0xf6bf; + t.Lacute = 0x0139; + t.Lambda = 0x039b; + t.Lcaron = 0x013d; + t.Lcedilla = 0x013b; + t.Lcircle = 0x24c1; + t.Lcircumflexbelow = 0x1e3c; + t.Lcommaaccent = 0x013b; + t.Ldot = 0x013f; + t.Ldotaccent = 0x013f; + t.Ldotbelow = 0x1e36; + t.Ldotbelowmacron = 0x1e38; + t.Liwnarmenian = 0x053c; + t.Lj = 0x01c8; + t.Ljecyrillic = 0x0409; + t.Llinebelow = 0x1e3a; + t.Lmonospace = 0xff2c; + t.Lslash = 0x0141; + t.Lslashsmall = 0xf6f9; + t.Lsmall = 0xf76c; + t.M = 0x004d; + t.MBsquare = 0x3386; + t.Macron = 0xf6d0; + t.Macronsmall = 0xf7af; + t.Macute = 0x1e3e; + t.Mcircle = 0x24c2; + t.Mdotaccent = 0x1e40; + t.Mdotbelow = 0x1e42; + t.Menarmenian = 0x0544; + t.Mmonospace = 0xff2d; + t.Msmall = 0xf76d; + t.Mturned = 0x019c; + t.Mu = 0x039c; + t.N = 0x004e; + t.NJ = 0x01ca; + t.Nacute = 0x0143; + t.Ncaron = 0x0147; + t.Ncedilla = 0x0145; + t.Ncircle = 0x24c3; + t.Ncircumflexbelow = 0x1e4a; + t.Ncommaaccent = 0x0145; + t.Ndotaccent = 0x1e44; + t.Ndotbelow = 0x1e46; + t.Nhookleft = 0x019d; + t.Nineroman = 0x2168; + t.Nj = 0x01cb; + t.Njecyrillic = 0x040a; + t.Nlinebelow = 0x1e48; + t.Nmonospace = 0xff2e; + t.Nowarmenian = 0x0546; + t.Nsmall = 0xf76e; + t.Ntilde = 0x00d1; + t.Ntildesmall = 0xf7f1; + t.Nu = 0x039d; + t.O = 0x004f; + t.OE = 0x0152; + t.OEsmall = 0xf6fa; + t.Oacute = 0x00d3; + t.Oacutesmall = 0xf7f3; + t.Obarredcyrillic = 0x04e8; + t.Obarreddieresiscyrillic = 0x04ea; + t.Obreve = 0x014e; + t.Ocaron = 0x01d1; + t.Ocenteredtilde = 0x019f; + t.Ocircle = 0x24c4; + t.Ocircumflex = 0x00d4; + t.Ocircumflexacute = 0x1ed0; + t.Ocircumflexdotbelow = 0x1ed8; + t.Ocircumflexgrave = 0x1ed2; + t.Ocircumflexhookabove = 0x1ed4; + t.Ocircumflexsmall = 0xf7f4; + t.Ocircumflextilde = 0x1ed6; + t.Ocyrillic = 0x041e; + t.Odblacute = 0x0150; + t.Odblgrave = 0x020c; + t.Odieresis = 0x00d6; + t.Odieresiscyrillic = 0x04e6; + t.Odieresissmall = 0xf7f6; + t.Odotbelow = 0x1ecc; + t.Ogoneksmall = 0xf6fb; + t.Ograve = 0x00d2; + t.Ogravesmall = 0xf7f2; + t.Oharmenian = 0x0555; + t.Ohm = 0x2126; + t.Ohookabove = 0x1ece; + t.Ohorn = 0x01a0; + t.Ohornacute = 0x1eda; + t.Ohorndotbelow = 0x1ee2; + t.Ohorngrave = 0x1edc; + t.Ohornhookabove = 0x1ede; + t.Ohorntilde = 0x1ee0; + t.Ohungarumlaut = 0x0150; + t.Oi = 0x01a2; + t.Oinvertedbreve = 0x020e; + t.Omacron = 0x014c; + t.Omacronacute = 0x1e52; + t.Omacrongrave = 0x1e50; + t.Omega = 0x2126; + t.Omegacyrillic = 0x0460; + t.Omegagreek = 0x03a9; + t.Omegaroundcyrillic = 0x047a; + t.Omegatitlocyrillic = 0x047c; + t.Omegatonos = 0x038f; + t.Omicron = 0x039f; + t.Omicrontonos = 0x038c; + t.Omonospace = 0xff2f; + t.Oneroman = 0x2160; + t.Oogonek = 0x01ea; + t.Oogonekmacron = 0x01ec; + t.Oopen = 0x0186; + t.Oslash = 0x00d8; + t.Oslashacute = 0x01fe; + t.Oslashsmall = 0xf7f8; + t.Osmall = 0xf76f; + t.Ostrokeacute = 0x01fe; + t.Otcyrillic = 0x047e; + t.Otilde = 0x00d5; + t.Otildeacute = 0x1e4c; + t.Otildedieresis = 0x1e4e; + t.Otildesmall = 0xf7f5; + t.P = 0x0050; + t.Pacute = 0x1e54; + t.Pcircle = 0x24c5; + t.Pdotaccent = 0x1e56; + t.Pecyrillic = 0x041f; + t.Peharmenian = 0x054a; + t.Pemiddlehookcyrillic = 0x04a6; + t.Phi = 0x03a6; + t.Phook = 0x01a4; + t.Pi = 0x03a0; + t.Piwrarmenian = 0x0553; + t.Pmonospace = 0xff30; + t.Psi = 0x03a8; + t.Psicyrillic = 0x0470; + t.Psmall = 0xf770; + t.Q = 0x0051; + t.Qcircle = 0x24c6; + t.Qmonospace = 0xff31; + t.Qsmall = 0xf771; + t.R = 0x0052; + t.Raarmenian = 0x054c; + t.Racute = 0x0154; + t.Rcaron = 0x0158; + t.Rcedilla = 0x0156; + t.Rcircle = 0x24c7; + t.Rcommaaccent = 0x0156; + t.Rdblgrave = 0x0210; + t.Rdotaccent = 0x1e58; + t.Rdotbelow = 0x1e5a; + t.Rdotbelowmacron = 0x1e5c; + t.Reharmenian = 0x0550; + t.Rfraktur = 0x211c; + t.Rho = 0x03a1; + t.Ringsmall = 0xf6fc; + t.Rinvertedbreve = 0x0212; + t.Rlinebelow = 0x1e5e; + t.Rmonospace = 0xff32; + t.Rsmall = 0xf772; + t.Rsmallinverted = 0x0281; + t.Rsmallinvertedsuperior = 0x02b6; + t.S = 0x0053; + t.SF010000 = 0x250c; + t.SF020000 = 0x2514; + t.SF030000 = 0x2510; + t.SF040000 = 0x2518; + t.SF050000 = 0x253c; + t.SF060000 = 0x252c; + t.SF070000 = 0x2534; + t.SF080000 = 0x251c; + t.SF090000 = 0x2524; + t.SF100000 = 0x2500; + t.SF110000 = 0x2502; + t.SF190000 = 0x2561; + t.SF200000 = 0x2562; + t.SF210000 = 0x2556; + t.SF220000 = 0x2555; + t.SF230000 = 0x2563; + t.SF240000 = 0x2551; + t.SF250000 = 0x2557; + t.SF260000 = 0x255d; + t.SF270000 = 0x255c; + t.SF280000 = 0x255b; + t.SF360000 = 0x255e; + t.SF370000 = 0x255f; + t.SF380000 = 0x255a; + t.SF390000 = 0x2554; + t.SF400000 = 0x2569; + t.SF410000 = 0x2566; + t.SF420000 = 0x2560; + t.SF430000 = 0x2550; + t.SF440000 = 0x256c; + t.SF450000 = 0x2567; + t.SF460000 = 0x2568; + t.SF470000 = 0x2564; + t.SF480000 = 0x2565; + t.SF490000 = 0x2559; + t.SF500000 = 0x2558; + t.SF510000 = 0x2552; + t.SF520000 = 0x2553; + t.SF530000 = 0x256b; + t.SF540000 = 0x256a; + t.Sacute = 0x015a; + t.Sacutedotaccent = 0x1e64; + t.Sampigreek = 0x03e0; + t.Scaron = 0x0160; + t.Scarondotaccent = 0x1e66; + t.Scaronsmall = 0xf6fd; + t.Scedilla = 0x015e; + t.Schwa = 0x018f; + t.Schwacyrillic = 0x04d8; + t.Schwadieresiscyrillic = 0x04da; + t.Scircle = 0x24c8; + t.Scircumflex = 0x015c; + t.Scommaaccent = 0x0218; + t.Sdotaccent = 0x1e60; + t.Sdotbelow = 0x1e62; + t.Sdotbelowdotaccent = 0x1e68; + t.Seharmenian = 0x054d; + t.Sevenroman = 0x2166; + t.Shaarmenian = 0x0547; + t.Shacyrillic = 0x0428; + t.Shchacyrillic = 0x0429; + t.Sheicoptic = 0x03e2; + t.Shhacyrillic = 0x04ba; + t.Shimacoptic = 0x03ec; + t.Sigma = 0x03a3; + t.Sixroman = 0x2165; + t.Smonospace = 0xff33; + t.Softsigncyrillic = 0x042c; + t.Ssmall = 0xf773; + t.Stigmagreek = 0x03da; + t.T = 0x0054; + t.Tau = 0x03a4; + t.Tbar = 0x0166; + t.Tcaron = 0x0164; + t.Tcedilla = 0x0162; + t.Tcircle = 0x24c9; + t.Tcircumflexbelow = 0x1e70; + t.Tcommaaccent = 0x0162; + t.Tdotaccent = 0x1e6a; + t.Tdotbelow = 0x1e6c; + t.Tecyrillic = 0x0422; + t.Tedescendercyrillic = 0x04ac; + t.Tenroman = 0x2169; + t.Tetsecyrillic = 0x04b4; + t.Theta = 0x0398; + t.Thook = 0x01ac; + t.Thorn = 0x00de; + t.Thornsmall = 0xf7fe; + t.Threeroman = 0x2162; + t.Tildesmall = 0xf6fe; + t.Tiwnarmenian = 0x054f; + t.Tlinebelow = 0x1e6e; + t.Tmonospace = 0xff34; + t.Toarmenian = 0x0539; + t.Tonefive = 0x01bc; + t.Tonesix = 0x0184; + t.Tonetwo = 0x01a7; + t.Tretroflexhook = 0x01ae; + t.Tsecyrillic = 0x0426; + t.Tshecyrillic = 0x040b; + t.Tsmall = 0xf774; + t.Twelveroman = 0x216b; + t.Tworoman = 0x2161; + t.U = 0x0055; + t.Uacute = 0x00da; + t.Uacutesmall = 0xf7fa; + t.Ubreve = 0x016c; + t.Ucaron = 0x01d3; + t.Ucircle = 0x24ca; + t.Ucircumflex = 0x00db; + t.Ucircumflexbelow = 0x1e76; + t.Ucircumflexsmall = 0xf7fb; + t.Ucyrillic = 0x0423; + t.Udblacute = 0x0170; + t.Udblgrave = 0x0214; + t.Udieresis = 0x00dc; + t.Udieresisacute = 0x01d7; + t.Udieresisbelow = 0x1e72; + t.Udieresiscaron = 0x01d9; + t.Udieresiscyrillic = 0x04f0; + t.Udieresisgrave = 0x01db; + t.Udieresismacron = 0x01d5; + t.Udieresissmall = 0xf7fc; + t.Udotbelow = 0x1ee4; + t.Ugrave = 0x00d9; + t.Ugravesmall = 0xf7f9; + t.Uhookabove = 0x1ee6; + t.Uhorn = 0x01af; + t.Uhornacute = 0x1ee8; + t.Uhorndotbelow = 0x1ef0; + t.Uhorngrave = 0x1eea; + t.Uhornhookabove = 0x1eec; + t.Uhorntilde = 0x1eee; + t.Uhungarumlaut = 0x0170; + t.Uhungarumlautcyrillic = 0x04f2; + t.Uinvertedbreve = 0x0216; + t.Ukcyrillic = 0x0478; + t.Umacron = 0x016a; + t.Umacroncyrillic = 0x04ee; + t.Umacrondieresis = 0x1e7a; + t.Umonospace = 0xff35; + t.Uogonek = 0x0172; + t.Upsilon = 0x03a5; + t.Upsilon1 = 0x03d2; + t.Upsilonacutehooksymbolgreek = 0x03d3; + t.Upsilonafrican = 0x01b1; + t.Upsilondieresis = 0x03ab; + t.Upsilondieresishooksymbolgreek = 0x03d4; + t.Upsilonhooksymbol = 0x03d2; + t.Upsilontonos = 0x038e; + t.Uring = 0x016e; + t.Ushortcyrillic = 0x040e; + t.Usmall = 0xf775; + t.Ustraightcyrillic = 0x04ae; + t.Ustraightstrokecyrillic = 0x04b0; + t.Utilde = 0x0168; + t.Utildeacute = 0x1e78; + t.Utildebelow = 0x1e74; + t.V = 0x0056; + t.Vcircle = 0x24cb; + t.Vdotbelow = 0x1e7e; + t.Vecyrillic = 0x0412; + t.Vewarmenian = 0x054e; + t.Vhook = 0x01b2; + t.Vmonospace = 0xff36; + t.Voarmenian = 0x0548; + t.Vsmall = 0xf776; + t.Vtilde = 0x1e7c; + t.W = 0x0057; + t.Wacute = 0x1e82; + t.Wcircle = 0x24cc; + t.Wcircumflex = 0x0174; + t.Wdieresis = 0x1e84; + t.Wdotaccent = 0x1e86; + t.Wdotbelow = 0x1e88; + t.Wgrave = 0x1e80; + t.Wmonospace = 0xff37; + t.Wsmall = 0xf777; + t.X = 0x0058; + t.Xcircle = 0x24cd; + t.Xdieresis = 0x1e8c; + t.Xdotaccent = 0x1e8a; + t.Xeharmenian = 0x053d; + t.Xi = 0x039e; + t.Xmonospace = 0xff38; + t.Xsmall = 0xf778; + t.Y = 0x0059; + t.Yacute = 0x00dd; + t.Yacutesmall = 0xf7fd; + t.Yatcyrillic = 0x0462; + t.Ycircle = 0x24ce; + t.Ycircumflex = 0x0176; + t.Ydieresis = 0x0178; + t.Ydieresissmall = 0xf7ff; + t.Ydotaccent = 0x1e8e; + t.Ydotbelow = 0x1ef4; + t.Yericyrillic = 0x042b; + t.Yerudieresiscyrillic = 0x04f8; + t.Ygrave = 0x1ef2; + t.Yhook = 0x01b3; + t.Yhookabove = 0x1ef6; + t.Yiarmenian = 0x0545; + t.Yicyrillic = 0x0407; + t.Yiwnarmenian = 0x0552; + t.Ymonospace = 0xff39; + t.Ysmall = 0xf779; + t.Ytilde = 0x1ef8; + t.Yusbigcyrillic = 0x046a; + t.Yusbigiotifiedcyrillic = 0x046c; + t.Yuslittlecyrillic = 0x0466; + t.Yuslittleiotifiedcyrillic = 0x0468; + t.Z = 0x005a; + t.Zaarmenian = 0x0536; + t.Zacute = 0x0179; + t.Zcaron = 0x017d; + t.Zcaronsmall = 0xf6ff; + t.Zcircle = 0x24cf; + t.Zcircumflex = 0x1e90; + t.Zdot = 0x017b; + t.Zdotaccent = 0x017b; + t.Zdotbelow = 0x1e92; + t.Zecyrillic = 0x0417; + t.Zedescendercyrillic = 0x0498; + t.Zedieresiscyrillic = 0x04de; + t.Zeta = 0x0396; + t.Zhearmenian = 0x053a; + t.Zhebrevecyrillic = 0x04c1; + t.Zhecyrillic = 0x0416; + t.Zhedescendercyrillic = 0x0496; + t.Zhedieresiscyrillic = 0x04dc; + t.Zlinebelow = 0x1e94; + t.Zmonospace = 0xff3a; + t.Zsmall = 0xf77a; + t.Zstroke = 0x01b5; + t.a = 0x0061; + t.aabengali = 0x0986; + t.aacute = 0x00e1; + t.aadeva = 0x0906; + t.aagujarati = 0x0a86; + t.aagurmukhi = 0x0a06; + t.aamatragurmukhi = 0x0a3e; + t.aarusquare = 0x3303; + t.aavowelsignbengali = 0x09be; + t.aavowelsigndeva = 0x093e; + t.aavowelsigngujarati = 0x0abe; + t.abbreviationmarkarmenian = 0x055f; + t.abbreviationsigndeva = 0x0970; + t.abengali = 0x0985; + t.abopomofo = 0x311a; + t.abreve = 0x0103; + t.abreveacute = 0x1eaf; + t.abrevecyrillic = 0x04d1; + t.abrevedotbelow = 0x1eb7; + t.abrevegrave = 0x1eb1; + t.abrevehookabove = 0x1eb3; + t.abrevetilde = 0x1eb5; + t.acaron = 0x01ce; + t.acircle = 0x24d0; + t.acircumflex = 0x00e2; + t.acircumflexacute = 0x1ea5; + t.acircumflexdotbelow = 0x1ead; + t.acircumflexgrave = 0x1ea7; + t.acircumflexhookabove = 0x1ea9; + t.acircumflextilde = 0x1eab; + t.acute = 0x00b4; + t.acutebelowcmb = 0x0317; + t.acutecmb = 0x0301; + t.acutecomb = 0x0301; + t.acutedeva = 0x0954; + t.acutelowmod = 0x02cf; + t.acutetonecmb = 0x0341; + t.acyrillic = 0x0430; + t.adblgrave = 0x0201; + t.addakgurmukhi = 0x0a71; + t.adeva = 0x0905; + t.adieresis = 0x00e4; + t.adieresiscyrillic = 0x04d3; + t.adieresismacron = 0x01df; + t.adotbelow = 0x1ea1; + t.adotmacron = 0x01e1; + t.ae = 0x00e6; + t.aeacute = 0x01fd; + t.aekorean = 0x3150; + t.aemacron = 0x01e3; + t.afii00208 = 0x2015; + t.afii08941 = 0x20a4; + t.afii10017 = 0x0410; + t.afii10018 = 0x0411; + t.afii10019 = 0x0412; + t.afii10020 = 0x0413; + t.afii10021 = 0x0414; + t.afii10022 = 0x0415; + t.afii10023 = 0x0401; + t.afii10024 = 0x0416; + t.afii10025 = 0x0417; + t.afii10026 = 0x0418; + t.afii10027 = 0x0419; + t.afii10028 = 0x041a; + t.afii10029 = 0x041b; + t.afii10030 = 0x041c; + t.afii10031 = 0x041d; + t.afii10032 = 0x041e; + t.afii10033 = 0x041f; + t.afii10034 = 0x0420; + t.afii10035 = 0x0421; + t.afii10036 = 0x0422; + t.afii10037 = 0x0423; + t.afii10038 = 0x0424; + t.afii10039 = 0x0425; + t.afii10040 = 0x0426; + t.afii10041 = 0x0427; + t.afii10042 = 0x0428; + t.afii10043 = 0x0429; + t.afii10044 = 0x042a; + t.afii10045 = 0x042b; + t.afii10046 = 0x042c; + t.afii10047 = 0x042d; + t.afii10048 = 0x042e; + t.afii10049 = 0x042f; + t.afii10050 = 0x0490; + t.afii10051 = 0x0402; + t.afii10052 = 0x0403; + t.afii10053 = 0x0404; + t.afii10054 = 0x0405; + t.afii10055 = 0x0406; + t.afii10056 = 0x0407; + t.afii10057 = 0x0408; + t.afii10058 = 0x0409; + t.afii10059 = 0x040a; + t.afii10060 = 0x040b; + t.afii10061 = 0x040c; + t.afii10062 = 0x040e; + t.afii10063 = 0xf6c4; + t.afii10064 = 0xf6c5; + t.afii10065 = 0x0430; + t.afii10066 = 0x0431; + t.afii10067 = 0x0432; + t.afii10068 = 0x0433; + t.afii10069 = 0x0434; + t.afii10070 = 0x0435; + t.afii10071 = 0x0451; + t.afii10072 = 0x0436; + t.afii10073 = 0x0437; + t.afii10074 = 0x0438; + t.afii10075 = 0x0439; + t.afii10076 = 0x043a; + t.afii10077 = 0x043b; + t.afii10078 = 0x043c; + t.afii10079 = 0x043d; + t.afii10080 = 0x043e; + t.afii10081 = 0x043f; + t.afii10082 = 0x0440; + t.afii10083 = 0x0441; + t.afii10084 = 0x0442; + t.afii10085 = 0x0443; + t.afii10086 = 0x0444; + t.afii10087 = 0x0445; + t.afii10088 = 0x0446; + t.afii10089 = 0x0447; + t.afii10090 = 0x0448; + t.afii10091 = 0x0449; + t.afii10092 = 0x044a; + t.afii10093 = 0x044b; + t.afii10094 = 0x044c; + t.afii10095 = 0x044d; + t.afii10096 = 0x044e; + t.afii10097 = 0x044f; + t.afii10098 = 0x0491; + t.afii10099 = 0x0452; + t.afii10100 = 0x0453; + t.afii10101 = 0x0454; + t.afii10102 = 0x0455; + t.afii10103 = 0x0456; + t.afii10104 = 0x0457; + t.afii10105 = 0x0458; + t.afii10106 = 0x0459; + t.afii10107 = 0x045a; + t.afii10108 = 0x045b; + t.afii10109 = 0x045c; + t.afii10110 = 0x045e; + t.afii10145 = 0x040f; + t.afii10146 = 0x0462; + t.afii10147 = 0x0472; + t.afii10148 = 0x0474; + t.afii10192 = 0xf6c6; + t.afii10193 = 0x045f; + t.afii10194 = 0x0463; + t.afii10195 = 0x0473; + t.afii10196 = 0x0475; + t.afii10831 = 0xf6c7; + t.afii10832 = 0xf6c8; + t.afii10846 = 0x04d9; + t.afii299 = 0x200e; + t.afii300 = 0x200f; + t.afii301 = 0x200d; + t.afii57381 = 0x066a; + t.afii57388 = 0x060c; + t.afii57392 = 0x0660; + t.afii57393 = 0x0661; + t.afii57394 = 0x0662; + t.afii57395 = 0x0663; + t.afii57396 = 0x0664; + t.afii57397 = 0x0665; + t.afii57398 = 0x0666; + t.afii57399 = 0x0667; + t.afii57400 = 0x0668; + t.afii57401 = 0x0669; + t.afii57403 = 0x061b; + t.afii57407 = 0x061f; + t.afii57409 = 0x0621; + t.afii57410 = 0x0622; + t.afii57411 = 0x0623; + t.afii57412 = 0x0624; + t.afii57413 = 0x0625; + t.afii57414 = 0x0626; + t.afii57415 = 0x0627; + t.afii57416 = 0x0628; + t.afii57417 = 0x0629; + t.afii57418 = 0x062a; + t.afii57419 = 0x062b; + t.afii57420 = 0x062c; + t.afii57421 = 0x062d; + t.afii57422 = 0x062e; + t.afii57423 = 0x062f; + t.afii57424 = 0x0630; + t.afii57425 = 0x0631; + t.afii57426 = 0x0632; + t.afii57427 = 0x0633; + t.afii57428 = 0x0634; + t.afii57429 = 0x0635; + t.afii57430 = 0x0636; + t.afii57431 = 0x0637; + t.afii57432 = 0x0638; + t.afii57433 = 0x0639; + t.afii57434 = 0x063a; + t.afii57440 = 0x0640; + t.afii57441 = 0x0641; + t.afii57442 = 0x0642; + t.afii57443 = 0x0643; + t.afii57444 = 0x0644; + t.afii57445 = 0x0645; + t.afii57446 = 0x0646; + t.afii57448 = 0x0648; + t.afii57449 = 0x0649; + t.afii57450 = 0x064a; + t.afii57451 = 0x064b; + t.afii57452 = 0x064c; + t.afii57453 = 0x064d; + t.afii57454 = 0x064e; + t.afii57455 = 0x064f; + t.afii57456 = 0x0650; + t.afii57457 = 0x0651; + t.afii57458 = 0x0652; + t.afii57470 = 0x0647; + t.afii57505 = 0x06a4; + t.afii57506 = 0x067e; + t.afii57507 = 0x0686; + t.afii57508 = 0x0698; + t.afii57509 = 0x06af; + t.afii57511 = 0x0679; + t.afii57512 = 0x0688; + t.afii57513 = 0x0691; + t.afii57514 = 0x06ba; + t.afii57519 = 0x06d2; + t.afii57534 = 0x06d5; + t.afii57636 = 0x20aa; + t.afii57645 = 0x05be; + t.afii57658 = 0x05c3; + t.afii57664 = 0x05d0; + t.afii57665 = 0x05d1; + t.afii57666 = 0x05d2; + t.afii57667 = 0x05d3; + t.afii57668 = 0x05d4; + t.afii57669 = 0x05d5; + t.afii57670 = 0x05d6; + t.afii57671 = 0x05d7; + t.afii57672 = 0x05d8; + t.afii57673 = 0x05d9; + t.afii57674 = 0x05da; + t.afii57675 = 0x05db; + t.afii57676 = 0x05dc; + t.afii57677 = 0x05dd; + t.afii57678 = 0x05de; + t.afii57679 = 0x05df; + t.afii57680 = 0x05e0; + t.afii57681 = 0x05e1; + t.afii57682 = 0x05e2; + t.afii57683 = 0x05e3; + t.afii57684 = 0x05e4; + t.afii57685 = 0x05e5; + t.afii57686 = 0x05e6; + t.afii57687 = 0x05e7; + t.afii57688 = 0x05e8; + t.afii57689 = 0x05e9; + t.afii57690 = 0x05ea; + t.afii57694 = 0xfb2a; + t.afii57695 = 0xfb2b; + t.afii57700 = 0xfb4b; + t.afii57705 = 0xfb1f; + t.afii57716 = 0x05f0; + t.afii57717 = 0x05f1; + t.afii57718 = 0x05f2; + t.afii57723 = 0xfb35; + t.afii57793 = 0x05b4; + t.afii57794 = 0x05b5; + t.afii57795 = 0x05b6; + t.afii57796 = 0x05bb; + t.afii57797 = 0x05b8; + t.afii57798 = 0x05b7; + t.afii57799 = 0x05b0; + t.afii57800 = 0x05b2; + t.afii57801 = 0x05b1; + t.afii57802 = 0x05b3; + t.afii57803 = 0x05c2; + t.afii57804 = 0x05c1; + t.afii57806 = 0x05b9; + t.afii57807 = 0x05bc; + t.afii57839 = 0x05bd; + t.afii57841 = 0x05bf; + t.afii57842 = 0x05c0; + t.afii57929 = 0x02bc; + t.afii61248 = 0x2105; + t.afii61289 = 0x2113; + t.afii61352 = 0x2116; + t.afii61573 = 0x202c; + t.afii61574 = 0x202d; + t.afii61575 = 0x202e; + t.afii61664 = 0x200c; + t.afii63167 = 0x066d; + t.afii64937 = 0x02bd; + t.agrave = 0x00e0; + t.agujarati = 0x0a85; + t.agurmukhi = 0x0a05; + t.ahiragana = 0x3042; + t.ahookabove = 0x1ea3; + t.aibengali = 0x0990; + t.aibopomofo = 0x311e; + t.aideva = 0x0910; + t.aiecyrillic = 0x04d5; + t.aigujarati = 0x0a90; + t.aigurmukhi = 0x0a10; + t.aimatragurmukhi = 0x0a48; + t.ainarabic = 0x0639; + t.ainfinalarabic = 0xfeca; + t.aininitialarabic = 0xfecb; + t.ainmedialarabic = 0xfecc; + t.ainvertedbreve = 0x0203; + t.aivowelsignbengali = 0x09c8; + t.aivowelsigndeva = 0x0948; + t.aivowelsigngujarati = 0x0ac8; + t.akatakana = 0x30a2; + t.akatakanahalfwidth = 0xff71; + t.akorean = 0x314f; + t.alef = 0x05d0; + t.alefarabic = 0x0627; + t.alefdageshhebrew = 0xfb30; + t.aleffinalarabic = 0xfe8e; + t.alefhamzaabovearabic = 0x0623; + t.alefhamzaabovefinalarabic = 0xfe84; + t.alefhamzabelowarabic = 0x0625; + t.alefhamzabelowfinalarabic = 0xfe88; + t.alefhebrew = 0x05d0; + t.aleflamedhebrew = 0xfb4f; + t.alefmaddaabovearabic = 0x0622; + t.alefmaddaabovefinalarabic = 0xfe82; + t.alefmaksuraarabic = 0x0649; + t.alefmaksurafinalarabic = 0xfef0; + t.alefmaksurainitialarabic = 0xfef3; + t.alefmaksuramedialarabic = 0xfef4; + t.alefpatahhebrew = 0xfb2e; + t.alefqamatshebrew = 0xfb2f; + t.aleph = 0x2135; + t.allequal = 0x224c; + t.alpha = 0x03b1; + t.alphatonos = 0x03ac; + t.amacron = 0x0101; + t.amonospace = 0xff41; + t.ampersand = 0x0026; + t.ampersandmonospace = 0xff06; + t.ampersandsmall = 0xf726; + t.amsquare = 0x33c2; + t.anbopomofo = 0x3122; + t.angbopomofo = 0x3124; + t.angbracketleft = 0x3008; + t.angbracketright = 0x3009; + t.angkhankhuthai = 0x0e5a; + t.angle = 0x2220; + t.anglebracketleft = 0x3008; + t.anglebracketleftvertical = 0xfe3f; + t.anglebracketright = 0x3009; + t.anglebracketrightvertical = 0xfe40; + t.angleleft = 0x2329; + t.angleright = 0x232a; + t.angstrom = 0x212b; + t.anoteleia = 0x0387; + t.anudattadeva = 0x0952; + t.anusvarabengali = 0x0982; + t.anusvaradeva = 0x0902; + t.anusvaragujarati = 0x0a82; + t.aogonek = 0x0105; + t.apaatosquare = 0x3300; + t.aparen = 0x249c; + t.apostrophearmenian = 0x055a; + t.apostrophemod = 0x02bc; + t.apple = 0xf8ff; + t.approaches = 0x2250; + t.approxequal = 0x2248; + t.approxequalorimage = 0x2252; + t.approximatelyequal = 0x2245; + t.araeaekorean = 0x318e; + t.araeakorean = 0x318d; + t.arc = 0x2312; + t.arighthalfring = 0x1e9a; + t.aring = 0x00e5; + t.aringacute = 0x01fb; + t.aringbelow = 0x1e01; + t.arrowboth = 0x2194; + t.arrowdashdown = 0x21e3; + t.arrowdashleft = 0x21e0; + t.arrowdashright = 0x21e2; + t.arrowdashup = 0x21e1; + t.arrowdblboth = 0x21d4; + t.arrowdbldown = 0x21d3; + t.arrowdblleft = 0x21d0; + t.arrowdblright = 0x21d2; + t.arrowdblup = 0x21d1; + t.arrowdown = 0x2193; + t.arrowdownleft = 0x2199; + t.arrowdownright = 0x2198; + t.arrowdownwhite = 0x21e9; + t.arrowheaddownmod = 0x02c5; + t.arrowheadleftmod = 0x02c2; + t.arrowheadrightmod = 0x02c3; + t.arrowheadupmod = 0x02c4; + t.arrowhorizex = 0xf8e7; + t.arrowleft = 0x2190; + t.arrowleftdbl = 0x21d0; + t.arrowleftdblstroke = 0x21cd; + t.arrowleftoverright = 0x21c6; + t.arrowleftwhite = 0x21e6; + t.arrowright = 0x2192; + t.arrowrightdblstroke = 0x21cf; + t.arrowrightheavy = 0x279e; + t.arrowrightoverleft = 0x21c4; + t.arrowrightwhite = 0x21e8; + t.arrowtableft = 0x21e4; + t.arrowtabright = 0x21e5; + t.arrowup = 0x2191; + t.arrowupdn = 0x2195; + t.arrowupdnbse = 0x21a8; + t.arrowupdownbase = 0x21a8; + t.arrowupleft = 0x2196; + t.arrowupleftofdown = 0x21c5; + t.arrowupright = 0x2197; + t.arrowupwhite = 0x21e7; + t.arrowvertex = 0xf8e6; + t.asciicircum = 0x005e; + t.asciicircummonospace = 0xff3e; + t.asciitilde = 0x007e; + t.asciitildemonospace = 0xff5e; + t.ascript = 0x0251; + t.ascriptturned = 0x0252; + t.asmallhiragana = 0x3041; + t.asmallkatakana = 0x30a1; + t.asmallkatakanahalfwidth = 0xff67; + t.asterisk = 0x002a; + t.asteriskaltonearabic = 0x066d; + t.asteriskarabic = 0x066d; + t.asteriskmath = 0x2217; + t.asteriskmonospace = 0xff0a; + t.asterisksmall = 0xfe61; + t.asterism = 0x2042; + t.asuperior = 0xf6e9; + t.asymptoticallyequal = 0x2243; + t.at = 0x0040; + t.atilde = 0x00e3; + t.atmonospace = 0xff20; + t.atsmall = 0xfe6b; + t.aturned = 0x0250; + t.aubengali = 0x0994; + t.aubopomofo = 0x3120; + t.audeva = 0x0914; + t.augujarati = 0x0a94; + t.augurmukhi = 0x0a14; + t.aulengthmarkbengali = 0x09d7; + t.aumatragurmukhi = 0x0a4c; + t.auvowelsignbengali = 0x09cc; + t.auvowelsigndeva = 0x094c; + t.auvowelsigngujarati = 0x0acc; + t.avagrahadeva = 0x093d; + t.aybarmenian = 0x0561; + t.ayin = 0x05e2; + t.ayinaltonehebrew = 0xfb20; + t.ayinhebrew = 0x05e2; + t.b = 0x0062; + t.babengali = 0x09ac; + t.backslash = 0x005c; + t.backslashmonospace = 0xff3c; + t.badeva = 0x092c; + t.bagujarati = 0x0aac; + t.bagurmukhi = 0x0a2c; + t.bahiragana = 0x3070; + t.bahtthai = 0x0e3f; + t.bakatakana = 0x30d0; + t.bar = 0x007c; + t.barmonospace = 0xff5c; + t.bbopomofo = 0x3105; + t.bcircle = 0x24d1; + t.bdotaccent = 0x1e03; + t.bdotbelow = 0x1e05; + t.beamedsixteenthnotes = 0x266c; + t.because = 0x2235; + t.becyrillic = 0x0431; + t.beharabic = 0x0628; + t.behfinalarabic = 0xfe90; + t.behinitialarabic = 0xfe91; + t.behiragana = 0x3079; + t.behmedialarabic = 0xfe92; + t.behmeeminitialarabic = 0xfc9f; + t.behmeemisolatedarabic = 0xfc08; + t.behnoonfinalarabic = 0xfc6d; + t.bekatakana = 0x30d9; + t.benarmenian = 0x0562; + t.bet = 0x05d1; + t.beta = 0x03b2; + t.betasymbolgreek = 0x03d0; + t.betdagesh = 0xfb31; + t.betdageshhebrew = 0xfb31; + t.bethebrew = 0x05d1; + t.betrafehebrew = 0xfb4c; + t.bhabengali = 0x09ad; + t.bhadeva = 0x092d; + t.bhagujarati = 0x0aad; + t.bhagurmukhi = 0x0a2d; + t.bhook = 0x0253; + t.bihiragana = 0x3073; + t.bikatakana = 0x30d3; + t.bilabialclick = 0x0298; + t.bindigurmukhi = 0x0a02; + t.birusquare = 0x3331; + t.blackcircle = 0x25cf; + t.blackdiamond = 0x25c6; + t.blackdownpointingtriangle = 0x25bc; + t.blackleftpointingpointer = 0x25c4; + t.blackleftpointingtriangle = 0x25c0; + t.blacklenticularbracketleft = 0x3010; + t.blacklenticularbracketleftvertical = 0xfe3b; + t.blacklenticularbracketright = 0x3011; + t.blacklenticularbracketrightvertical = 0xfe3c; + t.blacklowerlefttriangle = 0x25e3; + t.blacklowerrighttriangle = 0x25e2; + t.blackrectangle = 0x25ac; + t.blackrightpointingpointer = 0x25ba; + t.blackrightpointingtriangle = 0x25b6; + t.blacksmallsquare = 0x25aa; + t.blacksmilingface = 0x263b; + t.blacksquare = 0x25a0; + t.blackstar = 0x2605; + t.blackupperlefttriangle = 0x25e4; + t.blackupperrighttriangle = 0x25e5; + t.blackuppointingsmalltriangle = 0x25b4; + t.blackuppointingtriangle = 0x25b2; + t.blank = 0x2423; + t.blinebelow = 0x1e07; + t.block = 0x2588; + t.bmonospace = 0xff42; + t.bobaimaithai = 0x0e1a; + t.bohiragana = 0x307c; + t.bokatakana = 0x30dc; + t.bparen = 0x249d; + t.bqsquare = 0x33c3; + t.braceex = 0xf8f4; + t.braceleft = 0x007b; + t.braceleftbt = 0xf8f3; + t.braceleftmid = 0xf8f2; + t.braceleftmonospace = 0xff5b; + t.braceleftsmall = 0xfe5b; + t.bracelefttp = 0xf8f1; + t.braceleftvertical = 0xfe37; + t.braceright = 0x007d; + t.bracerightbt = 0xf8fe; + t.bracerightmid = 0xf8fd; + t.bracerightmonospace = 0xff5d; + t.bracerightsmall = 0xfe5c; + t.bracerighttp = 0xf8fc; + t.bracerightvertical = 0xfe38; + t.bracketleft = 0x005b; + t.bracketleftbt = 0xf8f0; + t.bracketleftex = 0xf8ef; + t.bracketleftmonospace = 0xff3b; + t.bracketlefttp = 0xf8ee; + t.bracketright = 0x005d; + t.bracketrightbt = 0xf8fb; + t.bracketrightex = 0xf8fa; + t.bracketrightmonospace = 0xff3d; + t.bracketrighttp = 0xf8f9; + t.breve = 0x02d8; + t.brevebelowcmb = 0x032e; + t.brevecmb = 0x0306; + t.breveinvertedbelowcmb = 0x032f; + t.breveinvertedcmb = 0x0311; + t.breveinverteddoublecmb = 0x0361; + t.bridgebelowcmb = 0x032a; + t.bridgeinvertedbelowcmb = 0x033a; + t.brokenbar = 0x00a6; + t.bstroke = 0x0180; + t.bsuperior = 0xf6ea; + t.btopbar = 0x0183; + t.buhiragana = 0x3076; + t.bukatakana = 0x30d6; + t.bullet = 0x2022; + t.bulletinverse = 0x25d8; + t.bulletoperator = 0x2219; + t.bullseye = 0x25ce; + t.c = 0x0063; + t.caarmenian = 0x056e; + t.cabengali = 0x099a; + t.cacute = 0x0107; + t.cadeva = 0x091a; + t.cagujarati = 0x0a9a; + t.cagurmukhi = 0x0a1a; + t.calsquare = 0x3388; + t.candrabindubengali = 0x0981; + t.candrabinducmb = 0x0310; + t.candrabindudeva = 0x0901; + t.candrabindugujarati = 0x0a81; + t.capslock = 0x21ea; + t.careof = 0x2105; + t.caron = 0x02c7; + t.caronbelowcmb = 0x032c; + t.caroncmb = 0x030c; + t.carriagereturn = 0x21b5; + t.cbopomofo = 0x3118; + t.ccaron = 0x010d; + t.ccedilla = 0x00e7; + t.ccedillaacute = 0x1e09; + t.ccircle = 0x24d2; + t.ccircumflex = 0x0109; + t.ccurl = 0x0255; + t.cdot = 0x010b; + t.cdotaccent = 0x010b; + t.cdsquare = 0x33c5; + t.cedilla = 0x00b8; + t.cedillacmb = 0x0327; + t.cent = 0x00a2; + t.centigrade = 0x2103; + t.centinferior = 0xf6df; + t.centmonospace = 0xffe0; + t.centoldstyle = 0xf7a2; + t.centsuperior = 0xf6e0; + t.chaarmenian = 0x0579; + t.chabengali = 0x099b; + t.chadeva = 0x091b; + t.chagujarati = 0x0a9b; + t.chagurmukhi = 0x0a1b; + t.chbopomofo = 0x3114; + t.cheabkhasiancyrillic = 0x04bd; + t.checkmark = 0x2713; + t.checyrillic = 0x0447; + t.chedescenderabkhasiancyrillic = 0x04bf; + t.chedescendercyrillic = 0x04b7; + t.chedieresiscyrillic = 0x04f5; + t.cheharmenian = 0x0573; + t.chekhakassiancyrillic = 0x04cc; + t.cheverticalstrokecyrillic = 0x04b9; + t.chi = 0x03c7; + t.chieuchacirclekorean = 0x3277; + t.chieuchaparenkorean = 0x3217; + t.chieuchcirclekorean = 0x3269; + t.chieuchkorean = 0x314a; + t.chieuchparenkorean = 0x3209; + t.chochangthai = 0x0e0a; + t.chochanthai = 0x0e08; + t.chochingthai = 0x0e09; + t.chochoethai = 0x0e0c; + t.chook = 0x0188; + t.cieucacirclekorean = 0x3276; + t.cieucaparenkorean = 0x3216; + t.cieuccirclekorean = 0x3268; + t.cieuckorean = 0x3148; + t.cieucparenkorean = 0x3208; + t.cieucuparenkorean = 0x321c; + t.circle = 0x25cb; + t.circlecopyrt = 0x00a9; + t.circlemultiply = 0x2297; + t.circleot = 0x2299; + t.circleplus = 0x2295; + t.circlepostalmark = 0x3036; + t.circlewithlefthalfblack = 0x25d0; + t.circlewithrighthalfblack = 0x25d1; + t.circumflex = 0x02c6; + t.circumflexbelowcmb = 0x032d; + t.circumflexcmb = 0x0302; + t.clear = 0x2327; + t.clickalveolar = 0x01c2; + t.clickdental = 0x01c0; + t.clicklateral = 0x01c1; + t.clickretroflex = 0x01c3; + t.club = 0x2663; + t.clubsuitblack = 0x2663; + t.clubsuitwhite = 0x2667; + t.cmcubedsquare = 0x33a4; + t.cmonospace = 0xff43; + t.cmsquaredsquare = 0x33a0; + t.coarmenian = 0x0581; + t.colon = 0x003a; + t.colonmonetary = 0x20a1; + t.colonmonospace = 0xff1a; + t.colonsign = 0x20a1; + t.colonsmall = 0xfe55; + t.colontriangularhalfmod = 0x02d1; + t.colontriangularmod = 0x02d0; + t.comma = 0x002c; + t.commaabovecmb = 0x0313; + t.commaaboverightcmb = 0x0315; + t.commaaccent = 0xf6c3; + t.commaarabic = 0x060c; + t.commaarmenian = 0x055d; + t.commainferior = 0xf6e1; + t.commamonospace = 0xff0c; + t.commareversedabovecmb = 0x0314; + t.commareversedmod = 0x02bd; + t.commasmall = 0xfe50; + t.commasuperior = 0xf6e2; + t.commaturnedabovecmb = 0x0312; + t.commaturnedmod = 0x02bb; + t.compass = 0x263c; + t.congruent = 0x2245; + t.contourintegral = 0x222e; + t.control = 0x2303; + t.controlACK = 0x0006; + t.controlBEL = 0x0007; + t.controlBS = 0x0008; + t.controlCAN = 0x0018; + t.controlCR = 0x000d; + t.controlDC1 = 0x0011; + t.controlDC2 = 0x0012; + t.controlDC3 = 0x0013; + t.controlDC4 = 0x0014; + t.controlDEL = 0x007f; + t.controlDLE = 0x0010; + t.controlEM = 0x0019; + t.controlENQ = 0x0005; + t.controlEOT = 0x0004; + t.controlESC = 0x001b; + t.controlETB = 0x0017; + t.controlETX = 0x0003; + t.controlFF = 0x000c; + t.controlFS = 0x001c; + t.controlGS = 0x001d; + t.controlHT = 0x0009; + t.controlLF = 0x000a; + t.controlNAK = 0x0015; + t.controlNULL = 0x0000; + t.controlRS = 0x001e; + t.controlSI = 0x000f; + t.controlSO = 0x000e; + t.controlSOT = 0x0002; + t.controlSTX = 0x0001; + t.controlSUB = 0x001a; + t.controlSYN = 0x0016; + t.controlUS = 0x001f; + t.controlVT = 0x000b; + t.copyright = 0x00a9; + t.copyrightsans = 0xf8e9; + t.copyrightserif = 0xf6d9; + t.cornerbracketleft = 0x300c; + t.cornerbracketlefthalfwidth = 0xff62; + t.cornerbracketleftvertical = 0xfe41; + t.cornerbracketright = 0x300d; + t.cornerbracketrighthalfwidth = 0xff63; + t.cornerbracketrightvertical = 0xfe42; + t.corporationsquare = 0x337f; + t.cosquare = 0x33c7; + t.coverkgsquare = 0x33c6; + t.cparen = 0x249e; + t.cruzeiro = 0x20a2; + t.cstretched = 0x0297; + t.curlyand = 0x22cf; + t.curlyor = 0x22ce; + t.currency = 0x00a4; + t.cyrBreve = 0xf6d1; + t.cyrFlex = 0xf6d2; + t.cyrbreve = 0xf6d4; + t.cyrflex = 0xf6d5; + t.d = 0x0064; + t.daarmenian = 0x0564; + t.dabengali = 0x09a6; + t.dadarabic = 0x0636; + t.dadeva = 0x0926; + t.dadfinalarabic = 0xfebe; + t.dadinitialarabic = 0xfebf; + t.dadmedialarabic = 0xfec0; + t.dagesh = 0x05bc; + t.dageshhebrew = 0x05bc; + t.dagger = 0x2020; + t.daggerdbl = 0x2021; + t.dagujarati = 0x0aa6; + t.dagurmukhi = 0x0a26; + t.dahiragana = 0x3060; + t.dakatakana = 0x30c0; + t.dalarabic = 0x062f; + t.dalet = 0x05d3; + t.daletdagesh = 0xfb33; + t.daletdageshhebrew = 0xfb33; + t.dalethebrew = 0x05d3; + t.dalfinalarabic = 0xfeaa; + t.dammaarabic = 0x064f; + t.dammalowarabic = 0x064f; + t.dammatanaltonearabic = 0x064c; + t.dammatanarabic = 0x064c; + t.danda = 0x0964; + t.dargahebrew = 0x05a7; + t.dargalefthebrew = 0x05a7; + t.dasiapneumatacyrilliccmb = 0x0485; + t.dblGrave = 0xf6d3; + t.dblanglebracketleft = 0x300a; + t.dblanglebracketleftvertical = 0xfe3d; + t.dblanglebracketright = 0x300b; + t.dblanglebracketrightvertical = 0xfe3e; + t.dblarchinvertedbelowcmb = 0x032b; + t.dblarrowleft = 0x21d4; + t.dblarrowright = 0x21d2; + t.dbldanda = 0x0965; + t.dblgrave = 0xf6d6; + t.dblgravecmb = 0x030f; + t.dblintegral = 0x222c; + t.dbllowline = 0x2017; + t.dbllowlinecmb = 0x0333; + t.dbloverlinecmb = 0x033f; + t.dblprimemod = 0x02ba; + t.dblverticalbar = 0x2016; + t.dblverticallineabovecmb = 0x030e; + t.dbopomofo = 0x3109; + t.dbsquare = 0x33c8; + t.dcaron = 0x010f; + t.dcedilla = 0x1e11; + t.dcircle = 0x24d3; + t.dcircumflexbelow = 0x1e13; + t.dcroat = 0x0111; + t.ddabengali = 0x09a1; + t.ddadeva = 0x0921; + t.ddagujarati = 0x0aa1; + t.ddagurmukhi = 0x0a21; + t.ddalarabic = 0x0688; + t.ddalfinalarabic = 0xfb89; + t.dddhadeva = 0x095c; + t.ddhabengali = 0x09a2; + t.ddhadeva = 0x0922; + t.ddhagujarati = 0x0aa2; + t.ddhagurmukhi = 0x0a22; + t.ddotaccent = 0x1e0b; + t.ddotbelow = 0x1e0d; + t.decimalseparatorarabic = 0x066b; + t.decimalseparatorpersian = 0x066b; + t.decyrillic = 0x0434; + t.degree = 0x00b0; + t.dehihebrew = 0x05ad; + t.dehiragana = 0x3067; + t.deicoptic = 0x03ef; + t.dekatakana = 0x30c7; + t.deleteleft = 0x232b; + t.deleteright = 0x2326; + t.delta = 0x03b4; + t.deltaturned = 0x018d; + t.denominatorminusonenumeratorbengali = 0x09f8; + t.dezh = 0x02a4; + t.dhabengali = 0x09a7; + t.dhadeva = 0x0927; + t.dhagujarati = 0x0aa7; + t.dhagurmukhi = 0x0a27; + t.dhook = 0x0257; + t.dialytikatonos = 0x0385; + t.dialytikatonoscmb = 0x0344; + t.diamond = 0x2666; + t.diamondsuitwhite = 0x2662; + t.dieresis = 0x00a8; + t.dieresisacute = 0xf6d7; + t.dieresisbelowcmb = 0x0324; + t.dieresiscmb = 0x0308; + t.dieresisgrave = 0xf6d8; + t.dieresistonos = 0x0385; + t.dihiragana = 0x3062; + t.dikatakana = 0x30c2; + t.dittomark = 0x3003; + t.divide = 0x00f7; + t.divides = 0x2223; + t.divisionslash = 0x2215; + t.djecyrillic = 0x0452; + t.dkshade = 0x2593; + t.dlinebelow = 0x1e0f; + t.dlsquare = 0x3397; + t.dmacron = 0x0111; + t.dmonospace = 0xff44; + t.dnblock = 0x2584; + t.dochadathai = 0x0e0e; + t.dodekthai = 0x0e14; + t.dohiragana = 0x3069; + t.dokatakana = 0x30c9; + t.dollar = 0x0024; + t.dollarinferior = 0xf6e3; + t.dollarmonospace = 0xff04; + t.dollaroldstyle = 0xf724; + t.dollarsmall = 0xfe69; + t.dollarsuperior = 0xf6e4; + t.dong = 0x20ab; + t.dorusquare = 0x3326; + t.dotaccent = 0x02d9; + t.dotaccentcmb = 0x0307; + t.dotbelowcmb = 0x0323; + t.dotbelowcomb = 0x0323; + t.dotkatakana = 0x30fb; + t.dotlessi = 0x0131; + t.dotlessj = 0xf6be; + t.dotlessjstrokehook = 0x0284; + t.dotmath = 0x22c5; + t.dottedcircle = 0x25cc; + t.doubleyodpatah = 0xfb1f; + t.doubleyodpatahhebrew = 0xfb1f; + t.downtackbelowcmb = 0x031e; + t.downtackmod = 0x02d5; + t.dparen = 0x249f; + t.dsuperior = 0xf6eb; + t.dtail = 0x0256; + t.dtopbar = 0x018c; + t.duhiragana = 0x3065; + t.dukatakana = 0x30c5; + t.dz = 0x01f3; + t.dzaltone = 0x02a3; + t.dzcaron = 0x01c6; + t.dzcurl = 0x02a5; + t.dzeabkhasiancyrillic = 0x04e1; + t.dzecyrillic = 0x0455; + t.dzhecyrillic = 0x045f; + t.e = 0x0065; + t.eacute = 0x00e9; + t.earth = 0x2641; + t.ebengali = 0x098f; + t.ebopomofo = 0x311c; + t.ebreve = 0x0115; + t.ecandradeva = 0x090d; + t.ecandragujarati = 0x0a8d; + t.ecandravowelsigndeva = 0x0945; + t.ecandravowelsigngujarati = 0x0ac5; + t.ecaron = 0x011b; + t.ecedillabreve = 0x1e1d; + t.echarmenian = 0x0565; + t.echyiwnarmenian = 0x0587; + t.ecircle = 0x24d4; + t.ecircumflex = 0x00ea; + t.ecircumflexacute = 0x1ebf; + t.ecircumflexbelow = 0x1e19; + t.ecircumflexdotbelow = 0x1ec7; + t.ecircumflexgrave = 0x1ec1; + t.ecircumflexhookabove = 0x1ec3; + t.ecircumflextilde = 0x1ec5; + t.ecyrillic = 0x0454; + t.edblgrave = 0x0205; + t.edeva = 0x090f; + t.edieresis = 0x00eb; + t.edot = 0x0117; + t.edotaccent = 0x0117; + t.edotbelow = 0x1eb9; + t.eegurmukhi = 0x0a0f; + t.eematragurmukhi = 0x0a47; + t.efcyrillic = 0x0444; + t.egrave = 0x00e8; + t.egujarati = 0x0a8f; + t.eharmenian = 0x0567; + t.ehbopomofo = 0x311d; + t.ehiragana = 0x3048; + t.ehookabove = 0x1ebb; + t.eibopomofo = 0x311f; + t.eight = 0x0038; + t.eightarabic = 0x0668; + t.eightbengali = 0x09ee; + t.eightcircle = 0x2467; + t.eightcircleinversesansserif = 0x2791; + t.eightdeva = 0x096e; + t.eighteencircle = 0x2471; + t.eighteenparen = 0x2485; + t.eighteenperiod = 0x2499; + t.eightgujarati = 0x0aee; + t.eightgurmukhi = 0x0a6e; + t.eighthackarabic = 0x0668; + t.eighthangzhou = 0x3028; + t.eighthnotebeamed = 0x266b; + t.eightideographicparen = 0x3227; + t.eightinferior = 0x2088; + t.eightmonospace = 0xff18; + t.eightoldstyle = 0xf738; + t.eightparen = 0x247b; + t.eightperiod = 0x248f; + t.eightpersian = 0x06f8; + t.eightroman = 0x2177; + t.eightsuperior = 0x2078; + t.eightthai = 0x0e58; + t.einvertedbreve = 0x0207; + t.eiotifiedcyrillic = 0x0465; + t.ekatakana = 0x30a8; + t.ekatakanahalfwidth = 0xff74; + t.ekonkargurmukhi = 0x0a74; + t.ekorean = 0x3154; + t.elcyrillic = 0x043b; + t.element = 0x2208; + t.elevencircle = 0x246a; + t.elevenparen = 0x247e; + t.elevenperiod = 0x2492; + t.elevenroman = 0x217a; + t.ellipsis = 0x2026; + t.ellipsisvertical = 0x22ee; + t.emacron = 0x0113; + t.emacronacute = 0x1e17; + t.emacrongrave = 0x1e15; + t.emcyrillic = 0x043c; + t.emdash = 0x2014; + t.emdashvertical = 0xfe31; + t.emonospace = 0xff45; + t.emphasismarkarmenian = 0x055b; + t.emptyset = 0x2205; + t.enbopomofo = 0x3123; + t.encyrillic = 0x043d; + t.endash = 0x2013; + t.endashvertical = 0xfe32; + t.endescendercyrillic = 0x04a3; + t.eng = 0x014b; + t.engbopomofo = 0x3125; + t.enghecyrillic = 0x04a5; + t.enhookcyrillic = 0x04c8; + t.enspace = 0x2002; + t.eogonek = 0x0119; + t.eokorean = 0x3153; + t.eopen = 0x025b; + t.eopenclosed = 0x029a; + t.eopenreversed = 0x025c; + t.eopenreversedclosed = 0x025e; + t.eopenreversedhook = 0x025d; + t.eparen = 0x24a0; + t.epsilon = 0x03b5; + t.epsilontonos = 0x03ad; + t.equal = 0x003d; + t.equalmonospace = 0xff1d; + t.equalsmall = 0xfe66; + t.equalsuperior = 0x207c; + t.equivalence = 0x2261; + t.erbopomofo = 0x3126; + t.ercyrillic = 0x0440; + t.ereversed = 0x0258; + t.ereversedcyrillic = 0x044d; + t.escyrillic = 0x0441; + t.esdescendercyrillic = 0x04ab; + t.esh = 0x0283; + t.eshcurl = 0x0286; + t.eshortdeva = 0x090e; + t.eshortvowelsigndeva = 0x0946; + t.eshreversedloop = 0x01aa; + t.eshsquatreversed = 0x0285; + t.esmallhiragana = 0x3047; + t.esmallkatakana = 0x30a7; + t.esmallkatakanahalfwidth = 0xff6a; + t.estimated = 0x212e; + t.esuperior = 0xf6ec; + t.eta = 0x03b7; + t.etarmenian = 0x0568; + t.etatonos = 0x03ae; + t.eth = 0x00f0; + t.etilde = 0x1ebd; + t.etildebelow = 0x1e1b; + t.etnahtafoukhhebrew = 0x0591; + t.etnahtafoukhlefthebrew = 0x0591; + t.etnahtahebrew = 0x0591; + t.etnahtalefthebrew = 0x0591; + t.eturned = 0x01dd; + t.eukorean = 0x3161; + t.euro = 0x20ac; + t.evowelsignbengali = 0x09c7; + t.evowelsigndeva = 0x0947; + t.evowelsigngujarati = 0x0ac7; + t.exclam = 0x0021; + t.exclamarmenian = 0x055c; + t.exclamdbl = 0x203c; + t.exclamdown = 0x00a1; + t.exclamdownsmall = 0xf7a1; + t.exclammonospace = 0xff01; + t.exclamsmall = 0xf721; + t.existential = 0x2203; + t.ezh = 0x0292; + t.ezhcaron = 0x01ef; + t.ezhcurl = 0x0293; + t.ezhreversed = 0x01b9; + t.ezhtail = 0x01ba; + t.f = 0x0066; + t.fadeva = 0x095e; + t.fagurmukhi = 0x0a5e; + t.fahrenheit = 0x2109; + t.fathaarabic = 0x064e; + t.fathalowarabic = 0x064e; + t.fathatanarabic = 0x064b; + t.fbopomofo = 0x3108; + t.fcircle = 0x24d5; + t.fdotaccent = 0x1e1f; + t.feharabic = 0x0641; + t.feharmenian = 0x0586; + t.fehfinalarabic = 0xfed2; + t.fehinitialarabic = 0xfed3; + t.fehmedialarabic = 0xfed4; + t.feicoptic = 0x03e5; + t.female = 0x2640; + t.ff = 0xfb00; + t.f_f = 0xfb00; + t.ffi = 0xfb03; + t.f_f_i = 0xfb03; + t.ffl = 0xfb04; + t.f_f_l = 0xfb04; + t.fi = 0xfb01; + t.f_i = 0xfb01; + t.fifteencircle = 0x246e; + t.fifteenparen = 0x2482; + t.fifteenperiod = 0x2496; + t.figuredash = 0x2012; + t.filledbox = 0x25a0; + t.filledrect = 0x25ac; + t.finalkaf = 0x05da; + t.finalkafdagesh = 0xfb3a; + t.finalkafdageshhebrew = 0xfb3a; + t.finalkafhebrew = 0x05da; + t.finalmem = 0x05dd; + t.finalmemhebrew = 0x05dd; + t.finalnun = 0x05df; + t.finalnunhebrew = 0x05df; + t.finalpe = 0x05e3; + t.finalpehebrew = 0x05e3; + t.finaltsadi = 0x05e5; + t.finaltsadihebrew = 0x05e5; + t.firsttonechinese = 0x02c9; + t.fisheye = 0x25c9; + t.fitacyrillic = 0x0473; + t.five = 0x0035; + t.fivearabic = 0x0665; + t.fivebengali = 0x09eb; + t.fivecircle = 0x2464; + t.fivecircleinversesansserif = 0x278e; + t.fivedeva = 0x096b; + t.fiveeighths = 0x215d; + t.fivegujarati = 0x0aeb; + t.fivegurmukhi = 0x0a6b; + t.fivehackarabic = 0x0665; + t.fivehangzhou = 0x3025; + t.fiveideographicparen = 0x3224; + t.fiveinferior = 0x2085; + t.fivemonospace = 0xff15; + t.fiveoldstyle = 0xf735; + t.fiveparen = 0x2478; + t.fiveperiod = 0x248c; + t.fivepersian = 0x06f5; + t.fiveroman = 0x2174; + t.fivesuperior = 0x2075; + t.fivethai = 0x0e55; + t.fl = 0xfb02; + t.f_l = 0xfb02; + t.florin = 0x0192; + t.fmonospace = 0xff46; + t.fmsquare = 0x3399; + t.fofanthai = 0x0e1f; + t.fofathai = 0x0e1d; + t.fongmanthai = 0x0e4f; + t.forall = 0x2200; + t.four = 0x0034; + t.fourarabic = 0x0664; + t.fourbengali = 0x09ea; + t.fourcircle = 0x2463; + t.fourcircleinversesansserif = 0x278d; + t.fourdeva = 0x096a; + t.fourgujarati = 0x0aea; + t.fourgurmukhi = 0x0a6a; + t.fourhackarabic = 0x0664; + t.fourhangzhou = 0x3024; + t.fourideographicparen = 0x3223; + t.fourinferior = 0x2084; + t.fourmonospace = 0xff14; + t.fournumeratorbengali = 0x09f7; + t.fouroldstyle = 0xf734; + t.fourparen = 0x2477; + t.fourperiod = 0x248b; + t.fourpersian = 0x06f4; + t.fourroman = 0x2173; + t.foursuperior = 0x2074; + t.fourteencircle = 0x246d; + t.fourteenparen = 0x2481; + t.fourteenperiod = 0x2495; + t.fourthai = 0x0e54; + t.fourthtonechinese = 0x02cb; + t.fparen = 0x24a1; + t.fraction = 0x2044; + t.franc = 0x20a3; + t.g = 0x0067; + t.gabengali = 0x0997; + t.gacute = 0x01f5; + t.gadeva = 0x0917; + t.gafarabic = 0x06af; + t.gaffinalarabic = 0xfb93; + t.gafinitialarabic = 0xfb94; + t.gafmedialarabic = 0xfb95; + t.gagujarati = 0x0a97; + t.gagurmukhi = 0x0a17; + t.gahiragana = 0x304c; + t.gakatakana = 0x30ac; + t.gamma = 0x03b3; + t.gammalatinsmall = 0x0263; + t.gammasuperior = 0x02e0; + t.gangiacoptic = 0x03eb; + t.gbopomofo = 0x310d; + t.gbreve = 0x011f; + t.gcaron = 0x01e7; + t.gcedilla = 0x0123; + t.gcircle = 0x24d6; + t.gcircumflex = 0x011d; + t.gcommaaccent = 0x0123; + t.gdot = 0x0121; + t.gdotaccent = 0x0121; + t.gecyrillic = 0x0433; + t.gehiragana = 0x3052; + t.gekatakana = 0x30b2; + t.geometricallyequal = 0x2251; + t.gereshaccenthebrew = 0x059c; + t.gereshhebrew = 0x05f3; + t.gereshmuqdamhebrew = 0x059d; + t.germandbls = 0x00df; + t.gershayimaccenthebrew = 0x059e; + t.gershayimhebrew = 0x05f4; + t.getamark = 0x3013; + t.ghabengali = 0x0998; + t.ghadarmenian = 0x0572; + t.ghadeva = 0x0918; + t.ghagujarati = 0x0a98; + t.ghagurmukhi = 0x0a18; + t.ghainarabic = 0x063a; + t.ghainfinalarabic = 0xfece; + t.ghaininitialarabic = 0xfecf; + t.ghainmedialarabic = 0xfed0; + t.ghemiddlehookcyrillic = 0x0495; + t.ghestrokecyrillic = 0x0493; + t.gheupturncyrillic = 0x0491; + t.ghhadeva = 0x095a; + t.ghhagurmukhi = 0x0a5a; + t.ghook = 0x0260; + t.ghzsquare = 0x3393; + t.gihiragana = 0x304e; + t.gikatakana = 0x30ae; + t.gimarmenian = 0x0563; + t.gimel = 0x05d2; + t.gimeldagesh = 0xfb32; + t.gimeldageshhebrew = 0xfb32; + t.gimelhebrew = 0x05d2; + t.gjecyrillic = 0x0453; + t.glottalinvertedstroke = 0x01be; + t.glottalstop = 0x0294; + t.glottalstopinverted = 0x0296; + t.glottalstopmod = 0x02c0; + t.glottalstopreversed = 0x0295; + t.glottalstopreversedmod = 0x02c1; + t.glottalstopreversedsuperior = 0x02e4; + t.glottalstopstroke = 0x02a1; + t.glottalstopstrokereversed = 0x02a2; + t.gmacron = 0x1e21; + t.gmonospace = 0xff47; + t.gohiragana = 0x3054; + t.gokatakana = 0x30b4; + t.gparen = 0x24a2; + t.gpasquare = 0x33ac; + t.gradient = 0x2207; + t.grave = 0x0060; + t.gravebelowcmb = 0x0316; + t.gravecmb = 0x0300; + t.gravecomb = 0x0300; + t.gravedeva = 0x0953; + t.gravelowmod = 0x02ce; + t.gravemonospace = 0xff40; + t.gravetonecmb = 0x0340; + t.greater = 0x003e; + t.greaterequal = 0x2265; + t.greaterequalorless = 0x22db; + t.greatermonospace = 0xff1e; + t.greaterorequivalent = 0x2273; + t.greaterorless = 0x2277; + t.greateroverequal = 0x2267; + t.greatersmall = 0xfe65; + t.gscript = 0x0261; + t.gstroke = 0x01e5; + t.guhiragana = 0x3050; + t.guillemotleft = 0x00ab; + t.guillemotright = 0x00bb; + t.guilsinglleft = 0x2039; + t.guilsinglright = 0x203a; + t.gukatakana = 0x30b0; + t.guramusquare = 0x3318; + t.gysquare = 0x33c9; + t.h = 0x0068; + t.haabkhasiancyrillic = 0x04a9; + t.haaltonearabic = 0x06c1; + t.habengali = 0x09b9; + t.hadescendercyrillic = 0x04b3; + t.hadeva = 0x0939; + t.hagujarati = 0x0ab9; + t.hagurmukhi = 0x0a39; + t.haharabic = 0x062d; + t.hahfinalarabic = 0xfea2; + t.hahinitialarabic = 0xfea3; + t.hahiragana = 0x306f; + t.hahmedialarabic = 0xfea4; + t.haitusquare = 0x332a; + t.hakatakana = 0x30cf; + t.hakatakanahalfwidth = 0xff8a; + t.halantgurmukhi = 0x0a4d; + t.hamzaarabic = 0x0621; + t.hamzalowarabic = 0x0621; + t.hangulfiller = 0x3164; + t.hardsigncyrillic = 0x044a; + t.harpoonleftbarbup = 0x21bc; + t.harpoonrightbarbup = 0x21c0; + t.hasquare = 0x33ca; + t.hatafpatah = 0x05b2; + t.hatafpatah16 = 0x05b2; + t.hatafpatah23 = 0x05b2; + t.hatafpatah2f = 0x05b2; + t.hatafpatahhebrew = 0x05b2; + t.hatafpatahnarrowhebrew = 0x05b2; + t.hatafpatahquarterhebrew = 0x05b2; + t.hatafpatahwidehebrew = 0x05b2; + t.hatafqamats = 0x05b3; + t.hatafqamats1b = 0x05b3; + t.hatafqamats28 = 0x05b3; + t.hatafqamats34 = 0x05b3; + t.hatafqamatshebrew = 0x05b3; + t.hatafqamatsnarrowhebrew = 0x05b3; + t.hatafqamatsquarterhebrew = 0x05b3; + t.hatafqamatswidehebrew = 0x05b3; + t.hatafsegol = 0x05b1; + t.hatafsegol17 = 0x05b1; + t.hatafsegol24 = 0x05b1; + t.hatafsegol30 = 0x05b1; + t.hatafsegolhebrew = 0x05b1; + t.hatafsegolnarrowhebrew = 0x05b1; + t.hatafsegolquarterhebrew = 0x05b1; + t.hatafsegolwidehebrew = 0x05b1; + t.hbar = 0x0127; + t.hbopomofo = 0x310f; + t.hbrevebelow = 0x1e2b; + t.hcedilla = 0x1e29; + t.hcircle = 0x24d7; + t.hcircumflex = 0x0125; + t.hdieresis = 0x1e27; + t.hdotaccent = 0x1e23; + t.hdotbelow = 0x1e25; + t.he = 0x05d4; + t.heart = 0x2665; + t.heartsuitblack = 0x2665; + t.heartsuitwhite = 0x2661; + t.hedagesh = 0xfb34; + t.hedageshhebrew = 0xfb34; + t.hehaltonearabic = 0x06c1; + t.heharabic = 0x0647; + t.hehebrew = 0x05d4; + t.hehfinalaltonearabic = 0xfba7; + t.hehfinalalttwoarabic = 0xfeea; + t.hehfinalarabic = 0xfeea; + t.hehhamzaabovefinalarabic = 0xfba5; + t.hehhamzaaboveisolatedarabic = 0xfba4; + t.hehinitialaltonearabic = 0xfba8; + t.hehinitialarabic = 0xfeeb; + t.hehiragana = 0x3078; + t.hehmedialaltonearabic = 0xfba9; + t.hehmedialarabic = 0xfeec; + t.heiseierasquare = 0x337b; + t.hekatakana = 0x30d8; + t.hekatakanahalfwidth = 0xff8d; + t.hekutaarusquare = 0x3336; + t.henghook = 0x0267; + t.herutusquare = 0x3339; + t.het = 0x05d7; + t.hethebrew = 0x05d7; + t.hhook = 0x0266; + t.hhooksuperior = 0x02b1; + t.hieuhacirclekorean = 0x327b; + t.hieuhaparenkorean = 0x321b; + t.hieuhcirclekorean = 0x326d; + t.hieuhkorean = 0x314e; + t.hieuhparenkorean = 0x320d; + t.hihiragana = 0x3072; + t.hikatakana = 0x30d2; + t.hikatakanahalfwidth = 0xff8b; + t.hiriq = 0x05b4; + t.hiriq14 = 0x05b4; + t.hiriq21 = 0x05b4; + t.hiriq2d = 0x05b4; + t.hiriqhebrew = 0x05b4; + t.hiriqnarrowhebrew = 0x05b4; + t.hiriqquarterhebrew = 0x05b4; + t.hiriqwidehebrew = 0x05b4; + t.hlinebelow = 0x1e96; + t.hmonospace = 0xff48; + t.hoarmenian = 0x0570; + t.hohipthai = 0x0e2b; + t.hohiragana = 0x307b; + t.hokatakana = 0x30db; + t.hokatakanahalfwidth = 0xff8e; + t.holam = 0x05b9; + t.holam19 = 0x05b9; + t.holam26 = 0x05b9; + t.holam32 = 0x05b9; + t.holamhebrew = 0x05b9; + t.holamnarrowhebrew = 0x05b9; + t.holamquarterhebrew = 0x05b9; + t.holamwidehebrew = 0x05b9; + t.honokhukthai = 0x0e2e; + t.hookabovecomb = 0x0309; + t.hookcmb = 0x0309; + t.hookpalatalizedbelowcmb = 0x0321; + t.hookretroflexbelowcmb = 0x0322; + t.hoonsquare = 0x3342; + t.horicoptic = 0x03e9; + t.horizontalbar = 0x2015; + t.horncmb = 0x031b; + t.hotsprings = 0x2668; + t.house = 0x2302; + t.hparen = 0x24a3; + t.hsuperior = 0x02b0; + t.hturned = 0x0265; + t.huhiragana = 0x3075; + t.huiitosquare = 0x3333; + t.hukatakana = 0x30d5; + t.hukatakanahalfwidth = 0xff8c; + t.hungarumlaut = 0x02dd; + t.hungarumlautcmb = 0x030b; + t.hv = 0x0195; + t.hyphen = 0x002d; + t.hypheninferior = 0xf6e5; + t.hyphenmonospace = 0xff0d; + t.hyphensmall = 0xfe63; + t.hyphensuperior = 0xf6e6; + t.hyphentwo = 0x2010; + t.i = 0x0069; + t.iacute = 0x00ed; + t.iacyrillic = 0x044f; + t.ibengali = 0x0987; + t.ibopomofo = 0x3127; + t.ibreve = 0x012d; + t.icaron = 0x01d0; + t.icircle = 0x24d8; + t.icircumflex = 0x00ee; + t.icyrillic = 0x0456; + t.idblgrave = 0x0209; + t.ideographearthcircle = 0x328f; + t.ideographfirecircle = 0x328b; + t.ideographicallianceparen = 0x323f; + t.ideographiccallparen = 0x323a; + t.ideographiccentrecircle = 0x32a5; + t.ideographicclose = 0x3006; + t.ideographiccomma = 0x3001; + t.ideographiccommaleft = 0xff64; + t.ideographiccongratulationparen = 0x3237; + t.ideographiccorrectcircle = 0x32a3; + t.ideographicearthparen = 0x322f; + t.ideographicenterpriseparen = 0x323d; + t.ideographicexcellentcircle = 0x329d; + t.ideographicfestivalparen = 0x3240; + t.ideographicfinancialcircle = 0x3296; + t.ideographicfinancialparen = 0x3236; + t.ideographicfireparen = 0x322b; + t.ideographichaveparen = 0x3232; + t.ideographichighcircle = 0x32a4; + t.ideographiciterationmark = 0x3005; + t.ideographiclaborcircle = 0x3298; + t.ideographiclaborparen = 0x3238; + t.ideographicleftcircle = 0x32a7; + t.ideographiclowcircle = 0x32a6; + t.ideographicmedicinecircle = 0x32a9; + t.ideographicmetalparen = 0x322e; + t.ideographicmoonparen = 0x322a; + t.ideographicnameparen = 0x3234; + t.ideographicperiod = 0x3002; + t.ideographicprintcircle = 0x329e; + t.ideographicreachparen = 0x3243; + t.ideographicrepresentparen = 0x3239; + t.ideographicresourceparen = 0x323e; + t.ideographicrightcircle = 0x32a8; + t.ideographicsecretcircle = 0x3299; + t.ideographicselfparen = 0x3242; + t.ideographicsocietyparen = 0x3233; + t.ideographicspace = 0x3000; + t.ideographicspecialparen = 0x3235; + t.ideographicstockparen = 0x3231; + t.ideographicstudyparen = 0x323b; + t.ideographicsunparen = 0x3230; + t.ideographicsuperviseparen = 0x323c; + t.ideographicwaterparen = 0x322c; + t.ideographicwoodparen = 0x322d; + t.ideographiczero = 0x3007; + t.ideographmetalcircle = 0x328e; + t.ideographmooncircle = 0x328a; + t.ideographnamecircle = 0x3294; + t.ideographsuncircle = 0x3290; + t.ideographwatercircle = 0x328c; + t.ideographwoodcircle = 0x328d; + t.ideva = 0x0907; + t.idieresis = 0x00ef; + t.idieresisacute = 0x1e2f; + t.idieresiscyrillic = 0x04e5; + t.idotbelow = 0x1ecb; + t.iebrevecyrillic = 0x04d7; + t.iecyrillic = 0x0435; + t.ieungacirclekorean = 0x3275; + t.ieungaparenkorean = 0x3215; + t.ieungcirclekorean = 0x3267; + t.ieungkorean = 0x3147; + t.ieungparenkorean = 0x3207; + t.igrave = 0x00ec; + t.igujarati = 0x0a87; + t.igurmukhi = 0x0a07; + t.ihiragana = 0x3044; + t.ihookabove = 0x1ec9; + t.iibengali = 0x0988; + t.iicyrillic = 0x0438; + t.iideva = 0x0908; + t.iigujarati = 0x0a88; + t.iigurmukhi = 0x0a08; + t.iimatragurmukhi = 0x0a40; + t.iinvertedbreve = 0x020b; + t.iishortcyrillic = 0x0439; + t.iivowelsignbengali = 0x09c0; + t.iivowelsigndeva = 0x0940; + t.iivowelsigngujarati = 0x0ac0; + t.ij = 0x0133; + t.ikatakana = 0x30a4; + t.ikatakanahalfwidth = 0xff72; + t.ikorean = 0x3163; + t.ilde = 0x02dc; + t.iluyhebrew = 0x05ac; + t.imacron = 0x012b; + t.imacroncyrillic = 0x04e3; + t.imageorapproximatelyequal = 0x2253; + t.imatragurmukhi = 0x0a3f; + t.imonospace = 0xff49; + t.increment = 0x2206; + t.infinity = 0x221e; + t.iniarmenian = 0x056b; + t.integral = 0x222b; + t.integralbottom = 0x2321; + t.integralbt = 0x2321; + t.integralex = 0xf8f5; + t.integraltop = 0x2320; + t.integraltp = 0x2320; + t.intersection = 0x2229; + t.intisquare = 0x3305; + t.invbullet = 0x25d8; + t.invcircle = 0x25d9; + t.invsmileface = 0x263b; + t.iocyrillic = 0x0451; + t.iogonek = 0x012f; + t.iota = 0x03b9; + t.iotadieresis = 0x03ca; + t.iotadieresistonos = 0x0390; + t.iotalatin = 0x0269; + t.iotatonos = 0x03af; + t.iparen = 0x24a4; + t.irigurmukhi = 0x0a72; + t.ismallhiragana = 0x3043; + t.ismallkatakana = 0x30a3; + t.ismallkatakanahalfwidth = 0xff68; + t.issharbengali = 0x09fa; + t.istroke = 0x0268; + t.isuperior = 0xf6ed; + t.iterationhiragana = 0x309d; + t.iterationkatakana = 0x30fd; + t.itilde = 0x0129; + t.itildebelow = 0x1e2d; + t.iubopomofo = 0x3129; + t.iucyrillic = 0x044e; + t.ivowelsignbengali = 0x09bf; + t.ivowelsigndeva = 0x093f; + t.ivowelsigngujarati = 0x0abf; + t.izhitsacyrillic = 0x0475; + t.izhitsadblgravecyrillic = 0x0477; + t.j = 0x006a; + t.jaarmenian = 0x0571; + t.jabengali = 0x099c; + t.jadeva = 0x091c; + t.jagujarati = 0x0a9c; + t.jagurmukhi = 0x0a1c; + t.jbopomofo = 0x3110; + t.jcaron = 0x01f0; + t.jcircle = 0x24d9; + t.jcircumflex = 0x0135; + t.jcrossedtail = 0x029d; + t.jdotlessstroke = 0x025f; + t.jecyrillic = 0x0458; + t.jeemarabic = 0x062c; + t.jeemfinalarabic = 0xfe9e; + t.jeeminitialarabic = 0xfe9f; + t.jeemmedialarabic = 0xfea0; + t.jeharabic = 0x0698; + t.jehfinalarabic = 0xfb8b; + t.jhabengali = 0x099d; + t.jhadeva = 0x091d; + t.jhagujarati = 0x0a9d; + t.jhagurmukhi = 0x0a1d; + t.jheharmenian = 0x057b; + t.jis = 0x3004; + t.jmonospace = 0xff4a; + t.jparen = 0x24a5; + t.jsuperior = 0x02b2; + t.k = 0x006b; + t.kabashkircyrillic = 0x04a1; + t.kabengali = 0x0995; + t.kacute = 0x1e31; + t.kacyrillic = 0x043a; + t.kadescendercyrillic = 0x049b; + t.kadeva = 0x0915; + t.kaf = 0x05db; + t.kafarabic = 0x0643; + t.kafdagesh = 0xfb3b; + t.kafdageshhebrew = 0xfb3b; + t.kaffinalarabic = 0xfeda; + t.kafhebrew = 0x05db; + t.kafinitialarabic = 0xfedb; + t.kafmedialarabic = 0xfedc; + t.kafrafehebrew = 0xfb4d; + t.kagujarati = 0x0a95; + t.kagurmukhi = 0x0a15; + t.kahiragana = 0x304b; + t.kahookcyrillic = 0x04c4; + t.kakatakana = 0x30ab; + t.kakatakanahalfwidth = 0xff76; + t.kappa = 0x03ba; + t.kappasymbolgreek = 0x03f0; + t.kapyeounmieumkorean = 0x3171; + t.kapyeounphieuphkorean = 0x3184; + t.kapyeounpieupkorean = 0x3178; + t.kapyeounssangpieupkorean = 0x3179; + t.karoriisquare = 0x330d; + t.kashidaautoarabic = 0x0640; + t.kashidaautonosidebearingarabic = 0x0640; + t.kasmallkatakana = 0x30f5; + t.kasquare = 0x3384; + t.kasraarabic = 0x0650; + t.kasratanarabic = 0x064d; + t.kastrokecyrillic = 0x049f; + t.katahiraprolongmarkhalfwidth = 0xff70; + t.kaverticalstrokecyrillic = 0x049d; + t.kbopomofo = 0x310e; + t.kcalsquare = 0x3389; + t.kcaron = 0x01e9; + t.kcedilla = 0x0137; + t.kcircle = 0x24da; + t.kcommaaccent = 0x0137; + t.kdotbelow = 0x1e33; + t.keharmenian = 0x0584; + t.kehiragana = 0x3051; + t.kekatakana = 0x30b1; + t.kekatakanahalfwidth = 0xff79; + t.kenarmenian = 0x056f; + t.kesmallkatakana = 0x30f6; + t.kgreenlandic = 0x0138; + t.khabengali = 0x0996; + t.khacyrillic = 0x0445; + t.khadeva = 0x0916; + t.khagujarati = 0x0a96; + t.khagurmukhi = 0x0a16; + t.khaharabic = 0x062e; + t.khahfinalarabic = 0xfea6; + t.khahinitialarabic = 0xfea7; + t.khahmedialarabic = 0xfea8; + t.kheicoptic = 0x03e7; + t.khhadeva = 0x0959; + t.khhagurmukhi = 0x0a59; + t.khieukhacirclekorean = 0x3278; + t.khieukhaparenkorean = 0x3218; + t.khieukhcirclekorean = 0x326a; + t.khieukhkorean = 0x314b; + t.khieukhparenkorean = 0x320a; + t.khokhaithai = 0x0e02; + t.khokhonthai = 0x0e05; + t.khokhuatthai = 0x0e03; + t.khokhwaithai = 0x0e04; + t.khomutthai = 0x0e5b; + t.khook = 0x0199; + t.khorakhangthai = 0x0e06; + t.khzsquare = 0x3391; + t.kihiragana = 0x304d; + t.kikatakana = 0x30ad; + t.kikatakanahalfwidth = 0xff77; + t.kiroguramusquare = 0x3315; + t.kiromeetorusquare = 0x3316; + t.kirosquare = 0x3314; + t.kiyeokacirclekorean = 0x326e; + t.kiyeokaparenkorean = 0x320e; + t.kiyeokcirclekorean = 0x3260; + t.kiyeokkorean = 0x3131; + t.kiyeokparenkorean = 0x3200; + t.kiyeoksioskorean = 0x3133; + t.kjecyrillic = 0x045c; + t.klinebelow = 0x1e35; + t.klsquare = 0x3398; + t.kmcubedsquare = 0x33a6; + t.kmonospace = 0xff4b; + t.kmsquaredsquare = 0x33a2; + t.kohiragana = 0x3053; + t.kohmsquare = 0x33c0; + t.kokaithai = 0x0e01; + t.kokatakana = 0x30b3; + t.kokatakanahalfwidth = 0xff7a; + t.kooposquare = 0x331e; + t.koppacyrillic = 0x0481; + t.koreanstandardsymbol = 0x327f; + t.koroniscmb = 0x0343; + t.kparen = 0x24a6; + t.kpasquare = 0x33aa; + t.ksicyrillic = 0x046f; + t.ktsquare = 0x33cf; + t.kturned = 0x029e; + t.kuhiragana = 0x304f; + t.kukatakana = 0x30af; + t.kukatakanahalfwidth = 0xff78; + t.kvsquare = 0x33b8; + t.kwsquare = 0x33be; + t.l = 0x006c; + t.labengali = 0x09b2; + t.lacute = 0x013a; + t.ladeva = 0x0932; + t.lagujarati = 0x0ab2; + t.lagurmukhi = 0x0a32; + t.lakkhangyaothai = 0x0e45; + t.lamaleffinalarabic = 0xfefc; + t.lamalefhamzaabovefinalarabic = 0xfef8; + t.lamalefhamzaaboveisolatedarabic = 0xfef7; + t.lamalefhamzabelowfinalarabic = 0xfefa; + t.lamalefhamzabelowisolatedarabic = 0xfef9; + t.lamalefisolatedarabic = 0xfefb; + t.lamalefmaddaabovefinalarabic = 0xfef6; + t.lamalefmaddaaboveisolatedarabic = 0xfef5; + t.lamarabic = 0x0644; + t.lambda = 0x03bb; + t.lambdastroke = 0x019b; + t.lamed = 0x05dc; + t.lameddagesh = 0xfb3c; + t.lameddageshhebrew = 0xfb3c; + t.lamedhebrew = 0x05dc; + t.lamfinalarabic = 0xfede; + t.lamhahinitialarabic = 0xfcca; + t.laminitialarabic = 0xfedf; + t.lamjeeminitialarabic = 0xfcc9; + t.lamkhahinitialarabic = 0xfccb; + t.lamlamhehisolatedarabic = 0xfdf2; + t.lammedialarabic = 0xfee0; + t.lammeemhahinitialarabic = 0xfd88; + t.lammeeminitialarabic = 0xfccc; + t.largecircle = 0x25ef; + t.lbar = 0x019a; + t.lbelt = 0x026c; + t.lbopomofo = 0x310c; + t.lcaron = 0x013e; + t.lcedilla = 0x013c; + t.lcircle = 0x24db; + t.lcircumflexbelow = 0x1e3d; + t.lcommaaccent = 0x013c; + t.ldot = 0x0140; + t.ldotaccent = 0x0140; + t.ldotbelow = 0x1e37; + t.ldotbelowmacron = 0x1e39; + t.leftangleabovecmb = 0x031a; + t.lefttackbelowcmb = 0x0318; + t.less = 0x003c; + t.lessequal = 0x2264; + t.lessequalorgreater = 0x22da; + t.lessmonospace = 0xff1c; + t.lessorequivalent = 0x2272; + t.lessorgreater = 0x2276; + t.lessoverequal = 0x2266; + t.lesssmall = 0xfe64; + t.lezh = 0x026e; + t.lfblock = 0x258c; + t.lhookretroflex = 0x026d; + t.lira = 0x20a4; + t.liwnarmenian = 0x056c; + t.lj = 0x01c9; + t.ljecyrillic = 0x0459; + t.ll = 0xf6c0; + t.lladeva = 0x0933; + t.llagujarati = 0x0ab3; + t.llinebelow = 0x1e3b; + t.llladeva = 0x0934; + t.llvocalicbengali = 0x09e1; + t.llvocalicdeva = 0x0961; + t.llvocalicvowelsignbengali = 0x09e3; + t.llvocalicvowelsigndeva = 0x0963; + t.lmiddletilde = 0x026b; + t.lmonospace = 0xff4c; + t.lmsquare = 0x33d0; + t.lochulathai = 0x0e2c; + t.logicaland = 0x2227; + t.logicalnot = 0x00ac; + t.logicalnotreversed = 0x2310; + t.logicalor = 0x2228; + t.lolingthai = 0x0e25; + t.longs = 0x017f; + t.lowlinecenterline = 0xfe4e; + t.lowlinecmb = 0x0332; + t.lowlinedashed = 0xfe4d; + t.lozenge = 0x25ca; + t.lparen = 0x24a7; + t.lslash = 0x0142; + t.lsquare = 0x2113; + t.lsuperior = 0xf6ee; + t.ltshade = 0x2591; + t.luthai = 0x0e26; + t.lvocalicbengali = 0x098c; + t.lvocalicdeva = 0x090c; + t.lvocalicvowelsignbengali = 0x09e2; + t.lvocalicvowelsigndeva = 0x0962; + t.lxsquare = 0x33d3; + t.m = 0x006d; + t.mabengali = 0x09ae; + t.macron = 0x00af; + t.macronbelowcmb = 0x0331; + t.macroncmb = 0x0304; + t.macronlowmod = 0x02cd; + t.macronmonospace = 0xffe3; + t.macute = 0x1e3f; + t.madeva = 0x092e; + t.magujarati = 0x0aae; + t.magurmukhi = 0x0a2e; + t.mahapakhhebrew = 0x05a4; + t.mahapakhlefthebrew = 0x05a4; + t.mahiragana = 0x307e; + t.maichattawalowleftthai = 0xf895; + t.maichattawalowrightthai = 0xf894; + t.maichattawathai = 0x0e4b; + t.maichattawaupperleftthai = 0xf893; + t.maieklowleftthai = 0xf88c; + t.maieklowrightthai = 0xf88b; + t.maiekthai = 0x0e48; + t.maiekupperleftthai = 0xf88a; + t.maihanakatleftthai = 0xf884; + t.maihanakatthai = 0x0e31; + t.maitaikhuleftthai = 0xf889; + t.maitaikhuthai = 0x0e47; + t.maitholowleftthai = 0xf88f; + t.maitholowrightthai = 0xf88e; + t.maithothai = 0x0e49; + t.maithoupperleftthai = 0xf88d; + t.maitrilowleftthai = 0xf892; + t.maitrilowrightthai = 0xf891; + t.maitrithai = 0x0e4a; + t.maitriupperleftthai = 0xf890; + t.maiyamokthai = 0x0e46; + t.makatakana = 0x30de; + t.makatakanahalfwidth = 0xff8f; + t.male = 0x2642; + t.mansyonsquare = 0x3347; + t.maqafhebrew = 0x05be; + t.mars = 0x2642; + t.masoracirclehebrew = 0x05af; + t.masquare = 0x3383; + t.mbopomofo = 0x3107; + t.mbsquare = 0x33d4; + t.mcircle = 0x24dc; + t.mcubedsquare = 0x33a5; + t.mdotaccent = 0x1e41; + t.mdotbelow = 0x1e43; + t.meemarabic = 0x0645; + t.meemfinalarabic = 0xfee2; + t.meeminitialarabic = 0xfee3; + t.meemmedialarabic = 0xfee4; + t.meemmeeminitialarabic = 0xfcd1; + t.meemmeemisolatedarabic = 0xfc48; + t.meetorusquare = 0x334d; + t.mehiragana = 0x3081; + t.meizierasquare = 0x337e; + t.mekatakana = 0x30e1; + t.mekatakanahalfwidth = 0xff92; + t.mem = 0x05de; + t.memdagesh = 0xfb3e; + t.memdageshhebrew = 0xfb3e; + t.memhebrew = 0x05de; + t.menarmenian = 0x0574; + t.merkhahebrew = 0x05a5; + t.merkhakefulahebrew = 0x05a6; + t.merkhakefulalefthebrew = 0x05a6; + t.merkhalefthebrew = 0x05a5; + t.mhook = 0x0271; + t.mhzsquare = 0x3392; + t.middledotkatakanahalfwidth = 0xff65; + t.middot = 0x00b7; + t.mieumacirclekorean = 0x3272; + t.mieumaparenkorean = 0x3212; + t.mieumcirclekorean = 0x3264; + t.mieumkorean = 0x3141; + t.mieumpansioskorean = 0x3170; + t.mieumparenkorean = 0x3204; + t.mieumpieupkorean = 0x316e; + t.mieumsioskorean = 0x316f; + t.mihiragana = 0x307f; + t.mikatakana = 0x30df; + t.mikatakanahalfwidth = 0xff90; + t.minus = 0x2212; + t.minusbelowcmb = 0x0320; + t.minuscircle = 0x2296; + t.minusmod = 0x02d7; + t.minusplus = 0x2213; + t.minute = 0x2032; + t.miribaarusquare = 0x334a; + t.mirisquare = 0x3349; + t.mlonglegturned = 0x0270; + t.mlsquare = 0x3396; + t.mmcubedsquare = 0x33a3; + t.mmonospace = 0xff4d; + t.mmsquaredsquare = 0x339f; + t.mohiragana = 0x3082; + t.mohmsquare = 0x33c1; + t.mokatakana = 0x30e2; + t.mokatakanahalfwidth = 0xff93; + t.molsquare = 0x33d6; + t.momathai = 0x0e21; + t.moverssquare = 0x33a7; + t.moverssquaredsquare = 0x33a8; + t.mparen = 0x24a8; + t.mpasquare = 0x33ab; + t.mssquare = 0x33b3; + t.msuperior = 0xf6ef; + t.mturned = 0x026f; + t.mu = 0x00b5; + t.mu1 = 0x00b5; + t.muasquare = 0x3382; + t.muchgreater = 0x226b; + t.muchless = 0x226a; + t.mufsquare = 0x338c; + t.mugreek = 0x03bc; + t.mugsquare = 0x338d; + t.muhiragana = 0x3080; + t.mukatakana = 0x30e0; + t.mukatakanahalfwidth = 0xff91; + t.mulsquare = 0x3395; + t.multiply = 0x00d7; + t.mumsquare = 0x339b; + t.munahhebrew = 0x05a3; + t.munahlefthebrew = 0x05a3; + t.musicalnote = 0x266a; + t.musicalnotedbl = 0x266b; + t.musicflatsign = 0x266d; + t.musicsharpsign = 0x266f; + t.mussquare = 0x33b2; + t.muvsquare = 0x33b6; + t.muwsquare = 0x33bc; + t.mvmegasquare = 0x33b9; + t.mvsquare = 0x33b7; + t.mwmegasquare = 0x33bf; + t.mwsquare = 0x33bd; + t.n = 0x006e; + t.nabengali = 0x09a8; + t.nabla = 0x2207; + t.nacute = 0x0144; + t.nadeva = 0x0928; + t.nagujarati = 0x0aa8; + t.nagurmukhi = 0x0a28; + t.nahiragana = 0x306a; + t.nakatakana = 0x30ca; + t.nakatakanahalfwidth = 0xff85; + t.napostrophe = 0x0149; + t.nasquare = 0x3381; + t.nbopomofo = 0x310b; + t.nbspace = 0x00a0; + t.ncaron = 0x0148; + t.ncedilla = 0x0146; + t.ncircle = 0x24dd; + t.ncircumflexbelow = 0x1e4b; + t.ncommaaccent = 0x0146; + t.ndotaccent = 0x1e45; + t.ndotbelow = 0x1e47; + t.nehiragana = 0x306d; + t.nekatakana = 0x30cd; + t.nekatakanahalfwidth = 0xff88; + t.newsheqelsign = 0x20aa; + t.nfsquare = 0x338b; + t.ngabengali = 0x0999; + t.ngadeva = 0x0919; + t.ngagujarati = 0x0a99; + t.ngagurmukhi = 0x0a19; + t.ngonguthai = 0x0e07; + t.nhiragana = 0x3093; + t.nhookleft = 0x0272; + t.nhookretroflex = 0x0273; + t.nieunacirclekorean = 0x326f; + t.nieunaparenkorean = 0x320f; + t.nieuncieuckorean = 0x3135; + t.nieuncirclekorean = 0x3261; + t.nieunhieuhkorean = 0x3136; + t.nieunkorean = 0x3134; + t.nieunpansioskorean = 0x3168; + t.nieunparenkorean = 0x3201; + t.nieunsioskorean = 0x3167; + t.nieuntikeutkorean = 0x3166; + t.nihiragana = 0x306b; + t.nikatakana = 0x30cb; + t.nikatakanahalfwidth = 0xff86; + t.nikhahitleftthai = 0xf899; + t.nikhahitthai = 0x0e4d; + t.nine = 0x0039; + t.ninearabic = 0x0669; + t.ninebengali = 0x09ef; + t.ninecircle = 0x2468; + t.ninecircleinversesansserif = 0x2792; + t.ninedeva = 0x096f; + t.ninegujarati = 0x0aef; + t.ninegurmukhi = 0x0a6f; + t.ninehackarabic = 0x0669; + t.ninehangzhou = 0x3029; + t.nineideographicparen = 0x3228; + t.nineinferior = 0x2089; + t.ninemonospace = 0xff19; + t.nineoldstyle = 0xf739; + t.nineparen = 0x247c; + t.nineperiod = 0x2490; + t.ninepersian = 0x06f9; + t.nineroman = 0x2178; + t.ninesuperior = 0x2079; + t.nineteencircle = 0x2472; + t.nineteenparen = 0x2486; + t.nineteenperiod = 0x249a; + t.ninethai = 0x0e59; + t.nj = 0x01cc; + t.njecyrillic = 0x045a; + t.nkatakana = 0x30f3; + t.nkatakanahalfwidth = 0xff9d; + t.nlegrightlong = 0x019e; + t.nlinebelow = 0x1e49; + t.nmonospace = 0xff4e; + t.nmsquare = 0x339a; + t.nnabengali = 0x09a3; + t.nnadeva = 0x0923; + t.nnagujarati = 0x0aa3; + t.nnagurmukhi = 0x0a23; + t.nnnadeva = 0x0929; + t.nohiragana = 0x306e; + t.nokatakana = 0x30ce; + t.nokatakanahalfwidth = 0xff89; + t.nonbreakingspace = 0x00a0; + t.nonenthai = 0x0e13; + t.nonuthai = 0x0e19; + t.noonarabic = 0x0646; + t.noonfinalarabic = 0xfee6; + t.noonghunnaarabic = 0x06ba; + t.noonghunnafinalarabic = 0xfb9f; + t.nooninitialarabic = 0xfee7; + t.noonjeeminitialarabic = 0xfcd2; + t.noonjeemisolatedarabic = 0xfc4b; + t.noonmedialarabic = 0xfee8; + t.noonmeeminitialarabic = 0xfcd5; + t.noonmeemisolatedarabic = 0xfc4e; + t.noonnoonfinalarabic = 0xfc8d; + t.notcontains = 0x220c; + t.notelement = 0x2209; + t.notelementof = 0x2209; + t.notequal = 0x2260; + t.notgreater = 0x226f; + t.notgreaternorequal = 0x2271; + t.notgreaternorless = 0x2279; + t.notidentical = 0x2262; + t.notless = 0x226e; + t.notlessnorequal = 0x2270; + t.notparallel = 0x2226; + t.notprecedes = 0x2280; + t.notsubset = 0x2284; + t.notsucceeds = 0x2281; + t.notsuperset = 0x2285; + t.nowarmenian = 0x0576; + t.nparen = 0x24a9; + t.nssquare = 0x33b1; + t.nsuperior = 0x207f; + t.ntilde = 0x00f1; + t.nu = 0x03bd; + t.nuhiragana = 0x306c; + t.nukatakana = 0x30cc; + t.nukatakanahalfwidth = 0xff87; + t.nuktabengali = 0x09bc; + t.nuktadeva = 0x093c; + t.nuktagujarati = 0x0abc; + t.nuktagurmukhi = 0x0a3c; + t.numbersign = 0x0023; + t.numbersignmonospace = 0xff03; + t.numbersignsmall = 0xfe5f; + t.numeralsigngreek = 0x0374; + t.numeralsignlowergreek = 0x0375; + t.numero = 0x2116; + t.nun = 0x05e0; + t.nundagesh = 0xfb40; + t.nundageshhebrew = 0xfb40; + t.nunhebrew = 0x05e0; + t.nvsquare = 0x33b5; + t.nwsquare = 0x33bb; + t.nyabengali = 0x099e; + t.nyadeva = 0x091e; + t.nyagujarati = 0x0a9e; + t.nyagurmukhi = 0x0a1e; + t.o = 0x006f; + t.oacute = 0x00f3; + t.oangthai = 0x0e2d; + t.obarred = 0x0275; + t.obarredcyrillic = 0x04e9; + t.obarreddieresiscyrillic = 0x04eb; + t.obengali = 0x0993; + t.obopomofo = 0x311b; + t.obreve = 0x014f; + t.ocandradeva = 0x0911; + t.ocandragujarati = 0x0a91; + t.ocandravowelsigndeva = 0x0949; + t.ocandravowelsigngujarati = 0x0ac9; + t.ocaron = 0x01d2; + t.ocircle = 0x24de; + t.ocircumflex = 0x00f4; + t.ocircumflexacute = 0x1ed1; + t.ocircumflexdotbelow = 0x1ed9; + t.ocircumflexgrave = 0x1ed3; + t.ocircumflexhookabove = 0x1ed5; + t.ocircumflextilde = 0x1ed7; + t.ocyrillic = 0x043e; + t.odblacute = 0x0151; + t.odblgrave = 0x020d; + t.odeva = 0x0913; + t.odieresis = 0x00f6; + t.odieresiscyrillic = 0x04e7; + t.odotbelow = 0x1ecd; + t.oe = 0x0153; + t.oekorean = 0x315a; + t.ogonek = 0x02db; + t.ogonekcmb = 0x0328; + t.ograve = 0x00f2; + t.ogujarati = 0x0a93; + t.oharmenian = 0x0585; + t.ohiragana = 0x304a; + t.ohookabove = 0x1ecf; + t.ohorn = 0x01a1; + t.ohornacute = 0x1edb; + t.ohorndotbelow = 0x1ee3; + t.ohorngrave = 0x1edd; + t.ohornhookabove = 0x1edf; + t.ohorntilde = 0x1ee1; + t.ohungarumlaut = 0x0151; + t.oi = 0x01a3; + t.oinvertedbreve = 0x020f; + t.okatakana = 0x30aa; + t.okatakanahalfwidth = 0xff75; + t.okorean = 0x3157; + t.olehebrew = 0x05ab; + t.omacron = 0x014d; + t.omacronacute = 0x1e53; + t.omacrongrave = 0x1e51; + t.omdeva = 0x0950; + t.omega = 0x03c9; + t.omega1 = 0x03d6; + t.omegacyrillic = 0x0461; + t.omegalatinclosed = 0x0277; + t.omegaroundcyrillic = 0x047b; + t.omegatitlocyrillic = 0x047d; + t.omegatonos = 0x03ce; + t.omgujarati = 0x0ad0; + t.omicron = 0x03bf; + t.omicrontonos = 0x03cc; + t.omonospace = 0xff4f; + t.one = 0x0031; + t.onearabic = 0x0661; + t.onebengali = 0x09e7; + t.onecircle = 0x2460; + t.onecircleinversesansserif = 0x278a; + t.onedeva = 0x0967; + t.onedotenleader = 0x2024; + t.oneeighth = 0x215b; + t.onefitted = 0xf6dc; + t.onegujarati = 0x0ae7; + t.onegurmukhi = 0x0a67; + t.onehackarabic = 0x0661; + t.onehalf = 0x00bd; + t.onehangzhou = 0x3021; + t.oneideographicparen = 0x3220; + t.oneinferior = 0x2081; + t.onemonospace = 0xff11; + t.onenumeratorbengali = 0x09f4; + t.oneoldstyle = 0xf731; + t.oneparen = 0x2474; + t.oneperiod = 0x2488; + t.onepersian = 0x06f1; + t.onequarter = 0x00bc; + t.oneroman = 0x2170; + t.onesuperior = 0x00b9; + t.onethai = 0x0e51; + t.onethird = 0x2153; + t.oogonek = 0x01eb; + t.oogonekmacron = 0x01ed; + t.oogurmukhi = 0x0a13; + t.oomatragurmukhi = 0x0a4b; + t.oopen = 0x0254; + t.oparen = 0x24aa; + t.openbullet = 0x25e6; + t.option = 0x2325; + t.ordfeminine = 0x00aa; + t.ordmasculine = 0x00ba; + t.orthogonal = 0x221f; + t.oshortdeva = 0x0912; + t.oshortvowelsigndeva = 0x094a; + t.oslash = 0x00f8; + t.oslashacute = 0x01ff; + t.osmallhiragana = 0x3049; + t.osmallkatakana = 0x30a9; + t.osmallkatakanahalfwidth = 0xff6b; + t.ostrokeacute = 0x01ff; + t.osuperior = 0xf6f0; + t.otcyrillic = 0x047f; + t.otilde = 0x00f5; + t.otildeacute = 0x1e4d; + t.otildedieresis = 0x1e4f; + t.oubopomofo = 0x3121; + t.overline = 0x203e; + t.overlinecenterline = 0xfe4a; + t.overlinecmb = 0x0305; + t.overlinedashed = 0xfe49; + t.overlinedblwavy = 0xfe4c; + t.overlinewavy = 0xfe4b; + t.overscore = 0x00af; + t.ovowelsignbengali = 0x09cb; + t.ovowelsigndeva = 0x094b; + t.ovowelsigngujarati = 0x0acb; + t.p = 0x0070; + t.paampssquare = 0x3380; + t.paasentosquare = 0x332b; + t.pabengali = 0x09aa; + t.pacute = 0x1e55; + t.padeva = 0x092a; + t.pagedown = 0x21df; + t.pageup = 0x21de; + t.pagujarati = 0x0aaa; + t.pagurmukhi = 0x0a2a; + t.pahiragana = 0x3071; + t.paiyannoithai = 0x0e2f; + t.pakatakana = 0x30d1; + t.palatalizationcyrilliccmb = 0x0484; + t.palochkacyrillic = 0x04c0; + t.pansioskorean = 0x317f; + t.paragraph = 0x00b6; + t.parallel = 0x2225; + t.parenleft = 0x0028; + t.parenleftaltonearabic = 0xfd3e; + t.parenleftbt = 0xf8ed; + t.parenleftex = 0xf8ec; + t.parenleftinferior = 0x208d; + t.parenleftmonospace = 0xff08; + t.parenleftsmall = 0xfe59; + t.parenleftsuperior = 0x207d; + t.parenlefttp = 0xf8eb; + t.parenleftvertical = 0xfe35; + t.parenright = 0x0029; + t.parenrightaltonearabic = 0xfd3f; + t.parenrightbt = 0xf8f8; + t.parenrightex = 0xf8f7; + t.parenrightinferior = 0x208e; + t.parenrightmonospace = 0xff09; + t.parenrightsmall = 0xfe5a; + t.parenrightsuperior = 0x207e; + t.parenrighttp = 0xf8f6; + t.parenrightvertical = 0xfe36; + t.partialdiff = 0x2202; + t.paseqhebrew = 0x05c0; + t.pashtahebrew = 0x0599; + t.pasquare = 0x33a9; + t.patah = 0x05b7; + t.patah11 = 0x05b7; + t.patah1d = 0x05b7; + t.patah2a = 0x05b7; + t.patahhebrew = 0x05b7; + t.patahnarrowhebrew = 0x05b7; + t.patahquarterhebrew = 0x05b7; + t.patahwidehebrew = 0x05b7; + t.pazerhebrew = 0x05a1; + t.pbopomofo = 0x3106; + t.pcircle = 0x24df; + t.pdotaccent = 0x1e57; + t.pe = 0x05e4; + t.pecyrillic = 0x043f; + t.pedagesh = 0xfb44; + t.pedageshhebrew = 0xfb44; + t.peezisquare = 0x333b; + t.pefinaldageshhebrew = 0xfb43; + t.peharabic = 0x067e; + t.peharmenian = 0x057a; + t.pehebrew = 0x05e4; + t.pehfinalarabic = 0xfb57; + t.pehinitialarabic = 0xfb58; + t.pehiragana = 0x307a; + t.pehmedialarabic = 0xfb59; + t.pekatakana = 0x30da; + t.pemiddlehookcyrillic = 0x04a7; + t.perafehebrew = 0xfb4e; + t.percent = 0x0025; + t.percentarabic = 0x066a; + t.percentmonospace = 0xff05; + t.percentsmall = 0xfe6a; + t.period = 0x002e; + t.periodarmenian = 0x0589; + t.periodcentered = 0x00b7; + t.periodhalfwidth = 0xff61; + t.periodinferior = 0xf6e7; + t.periodmonospace = 0xff0e; + t.periodsmall = 0xfe52; + t.periodsuperior = 0xf6e8; + t.perispomenigreekcmb = 0x0342; + t.perpendicular = 0x22a5; + t.perthousand = 0x2030; + t.peseta = 0x20a7; + t.pfsquare = 0x338a; + t.phabengali = 0x09ab; + t.phadeva = 0x092b; + t.phagujarati = 0x0aab; + t.phagurmukhi = 0x0a2b; + t.phi = 0x03c6; + t.phi1 = 0x03d5; + t.phieuphacirclekorean = 0x327a; + t.phieuphaparenkorean = 0x321a; + t.phieuphcirclekorean = 0x326c; + t.phieuphkorean = 0x314d; + t.phieuphparenkorean = 0x320c; + t.philatin = 0x0278; + t.phinthuthai = 0x0e3a; + t.phisymbolgreek = 0x03d5; + t.phook = 0x01a5; + t.phophanthai = 0x0e1e; + t.phophungthai = 0x0e1c; + t.phosamphaothai = 0x0e20; + t.pi = 0x03c0; + t.pieupacirclekorean = 0x3273; + t.pieupaparenkorean = 0x3213; + t.pieupcieuckorean = 0x3176; + t.pieupcirclekorean = 0x3265; + t.pieupkiyeokkorean = 0x3172; + t.pieupkorean = 0x3142; + t.pieupparenkorean = 0x3205; + t.pieupsioskiyeokkorean = 0x3174; + t.pieupsioskorean = 0x3144; + t.pieupsiostikeutkorean = 0x3175; + t.pieupthieuthkorean = 0x3177; + t.pieuptikeutkorean = 0x3173; + t.pihiragana = 0x3074; + t.pikatakana = 0x30d4; + t.pisymbolgreek = 0x03d6; + t.piwrarmenian = 0x0583; + t.planckover2pi = 0x210f; + t.planckover2pi1 = 0x210f; + t.plus = 0x002b; + t.plusbelowcmb = 0x031f; + t.pluscircle = 0x2295; + t.plusminus = 0x00b1; + t.plusmod = 0x02d6; + t.plusmonospace = 0xff0b; + t.plussmall = 0xfe62; + t.plussuperior = 0x207a; + t.pmonospace = 0xff50; + t.pmsquare = 0x33d8; + t.pohiragana = 0x307d; + t.pointingindexdownwhite = 0x261f; + t.pointingindexleftwhite = 0x261c; + t.pointingindexrightwhite = 0x261e; + t.pointingindexupwhite = 0x261d; + t.pokatakana = 0x30dd; + t.poplathai = 0x0e1b; + t.postalmark = 0x3012; + t.postalmarkface = 0x3020; + t.pparen = 0x24ab; + t.precedes = 0x227a; + t.prescription = 0x211e; + t.primemod = 0x02b9; + t.primereversed = 0x2035; + t.product = 0x220f; + t.projective = 0x2305; + t.prolongedkana = 0x30fc; + t.propellor = 0x2318; + t.propersubset = 0x2282; + t.propersuperset = 0x2283; + t.proportion = 0x2237; + t.proportional = 0x221d; + t.psi = 0x03c8; + t.psicyrillic = 0x0471; + t.psilipneumatacyrilliccmb = 0x0486; + t.pssquare = 0x33b0; + t.puhiragana = 0x3077; + t.pukatakana = 0x30d7; + t.pvsquare = 0x33b4; + t.pwsquare = 0x33ba; + t.q = 0x0071; + t.qadeva = 0x0958; + t.qadmahebrew = 0x05a8; + t.qafarabic = 0x0642; + t.qaffinalarabic = 0xfed6; + t.qafinitialarabic = 0xfed7; + t.qafmedialarabic = 0xfed8; + t.qamats = 0x05b8; + t.qamats10 = 0x05b8; + t.qamats1a = 0x05b8; + t.qamats1c = 0x05b8; + t.qamats27 = 0x05b8; + t.qamats29 = 0x05b8; + t.qamats33 = 0x05b8; + t.qamatsde = 0x05b8; + t.qamatshebrew = 0x05b8; + t.qamatsnarrowhebrew = 0x05b8; + t.qamatsqatanhebrew = 0x05b8; + t.qamatsqatannarrowhebrew = 0x05b8; + t.qamatsqatanquarterhebrew = 0x05b8; + t.qamatsqatanwidehebrew = 0x05b8; + t.qamatsquarterhebrew = 0x05b8; + t.qamatswidehebrew = 0x05b8; + t.qarneyparahebrew = 0x059f; + t.qbopomofo = 0x3111; + t.qcircle = 0x24e0; + t.qhook = 0x02a0; + t.qmonospace = 0xff51; + t.qof = 0x05e7; + t.qofdagesh = 0xfb47; + t.qofdageshhebrew = 0xfb47; + t.qofhebrew = 0x05e7; + t.qparen = 0x24ac; + t.quarternote = 0x2669; + t.qubuts = 0x05bb; + t.qubuts18 = 0x05bb; + t.qubuts25 = 0x05bb; + t.qubuts31 = 0x05bb; + t.qubutshebrew = 0x05bb; + t.qubutsnarrowhebrew = 0x05bb; + t.qubutsquarterhebrew = 0x05bb; + t.qubutswidehebrew = 0x05bb; + t.question = 0x003f; + t.questionarabic = 0x061f; + t.questionarmenian = 0x055e; + t.questiondown = 0x00bf; + t.questiondownsmall = 0xf7bf; + t.questiongreek = 0x037e; + t.questionmonospace = 0xff1f; + t.questionsmall = 0xf73f; + t.quotedbl = 0x0022; + t.quotedblbase = 0x201e; + t.quotedblleft = 0x201c; + t.quotedblmonospace = 0xff02; + t.quotedblprime = 0x301e; + t.quotedblprimereversed = 0x301d; + t.quotedblright = 0x201d; + t.quoteleft = 0x2018; + t.quoteleftreversed = 0x201b; + t.quotereversed = 0x201b; + t.quoteright = 0x2019; + t.quoterightn = 0x0149; + t.quotesinglbase = 0x201a; + t.quotesingle = 0x0027; + t.quotesinglemonospace = 0xff07; + t.r = 0x0072; + t.raarmenian = 0x057c; + t.rabengali = 0x09b0; + t.racute = 0x0155; + t.radeva = 0x0930; + t.radical = 0x221a; + t.radicalex = 0xf8e5; + t.radoverssquare = 0x33ae; + t.radoverssquaredsquare = 0x33af; + t.radsquare = 0x33ad; + t.rafe = 0x05bf; + t.rafehebrew = 0x05bf; + t.ragujarati = 0x0ab0; + t.ragurmukhi = 0x0a30; + t.rahiragana = 0x3089; + t.rakatakana = 0x30e9; + t.rakatakanahalfwidth = 0xff97; + t.ralowerdiagonalbengali = 0x09f1; + t.ramiddlediagonalbengali = 0x09f0; + t.ramshorn = 0x0264; + t.ratio = 0x2236; + t.rbopomofo = 0x3116; + t.rcaron = 0x0159; + t.rcedilla = 0x0157; + t.rcircle = 0x24e1; + t.rcommaaccent = 0x0157; + t.rdblgrave = 0x0211; + t.rdotaccent = 0x1e59; + t.rdotbelow = 0x1e5b; + t.rdotbelowmacron = 0x1e5d; + t.referencemark = 0x203b; + t.reflexsubset = 0x2286; + t.reflexsuperset = 0x2287; + t.registered = 0x00ae; + t.registersans = 0xf8e8; + t.registerserif = 0xf6da; + t.reharabic = 0x0631; + t.reharmenian = 0x0580; + t.rehfinalarabic = 0xfeae; + t.rehiragana = 0x308c; + t.rekatakana = 0x30ec; + t.rekatakanahalfwidth = 0xff9a; + t.resh = 0x05e8; + t.reshdageshhebrew = 0xfb48; + t.reshhebrew = 0x05e8; + t.reversedtilde = 0x223d; + t.reviahebrew = 0x0597; + t.reviamugrashhebrew = 0x0597; + t.revlogicalnot = 0x2310; + t.rfishhook = 0x027e; + t.rfishhookreversed = 0x027f; + t.rhabengali = 0x09dd; + t.rhadeva = 0x095d; + t.rho = 0x03c1; + t.rhook = 0x027d; + t.rhookturned = 0x027b; + t.rhookturnedsuperior = 0x02b5; + t.rhosymbolgreek = 0x03f1; + t.rhotichookmod = 0x02de; + t.rieulacirclekorean = 0x3271; + t.rieulaparenkorean = 0x3211; + t.rieulcirclekorean = 0x3263; + t.rieulhieuhkorean = 0x3140; + t.rieulkiyeokkorean = 0x313a; + t.rieulkiyeoksioskorean = 0x3169; + t.rieulkorean = 0x3139; + t.rieulmieumkorean = 0x313b; + t.rieulpansioskorean = 0x316c; + t.rieulparenkorean = 0x3203; + t.rieulphieuphkorean = 0x313f; + t.rieulpieupkorean = 0x313c; + t.rieulpieupsioskorean = 0x316b; + t.rieulsioskorean = 0x313d; + t.rieulthieuthkorean = 0x313e; + t.rieultikeutkorean = 0x316a; + t.rieulyeorinhieuhkorean = 0x316d; + t.rightangle = 0x221f; + t.righttackbelowcmb = 0x0319; + t.righttriangle = 0x22bf; + t.rihiragana = 0x308a; + t.rikatakana = 0x30ea; + t.rikatakanahalfwidth = 0xff98; + t.ring = 0x02da; + t.ringbelowcmb = 0x0325; + t.ringcmb = 0x030a; + t.ringhalfleft = 0x02bf; + t.ringhalfleftarmenian = 0x0559; + t.ringhalfleftbelowcmb = 0x031c; + t.ringhalfleftcentered = 0x02d3; + t.ringhalfright = 0x02be; + t.ringhalfrightbelowcmb = 0x0339; + t.ringhalfrightcentered = 0x02d2; + t.rinvertedbreve = 0x0213; + t.rittorusquare = 0x3351; + t.rlinebelow = 0x1e5f; + t.rlongleg = 0x027c; + t.rlonglegturned = 0x027a; + t.rmonospace = 0xff52; + t.rohiragana = 0x308d; + t.rokatakana = 0x30ed; + t.rokatakanahalfwidth = 0xff9b; + t.roruathai = 0x0e23; + t.rparen = 0x24ad; + t.rrabengali = 0x09dc; + t.rradeva = 0x0931; + t.rragurmukhi = 0x0a5c; + t.rreharabic = 0x0691; + t.rrehfinalarabic = 0xfb8d; + t.rrvocalicbengali = 0x09e0; + t.rrvocalicdeva = 0x0960; + t.rrvocalicgujarati = 0x0ae0; + t.rrvocalicvowelsignbengali = 0x09c4; + t.rrvocalicvowelsigndeva = 0x0944; + t.rrvocalicvowelsigngujarati = 0x0ac4; + t.rsuperior = 0xf6f1; + t.rtblock = 0x2590; + t.rturned = 0x0279; + t.rturnedsuperior = 0x02b4; + t.ruhiragana = 0x308b; + t.rukatakana = 0x30eb; + t.rukatakanahalfwidth = 0xff99; + t.rupeemarkbengali = 0x09f2; + t.rupeesignbengali = 0x09f3; + t.rupiah = 0xf6dd; + t.ruthai = 0x0e24; + t.rvocalicbengali = 0x098b; + t.rvocalicdeva = 0x090b; + t.rvocalicgujarati = 0x0a8b; + t.rvocalicvowelsignbengali = 0x09c3; + t.rvocalicvowelsigndeva = 0x0943; + t.rvocalicvowelsigngujarati = 0x0ac3; + t.s = 0x0073; + t.sabengali = 0x09b8; + t.sacute = 0x015b; + t.sacutedotaccent = 0x1e65; + t.sadarabic = 0x0635; + t.sadeva = 0x0938; + t.sadfinalarabic = 0xfeba; + t.sadinitialarabic = 0xfebb; + t.sadmedialarabic = 0xfebc; + t.sagujarati = 0x0ab8; + t.sagurmukhi = 0x0a38; + t.sahiragana = 0x3055; + t.sakatakana = 0x30b5; + t.sakatakanahalfwidth = 0xff7b; + t.sallallahoualayhewasallamarabic = 0xfdfa; + t.samekh = 0x05e1; + t.samekhdagesh = 0xfb41; + t.samekhdageshhebrew = 0xfb41; + t.samekhhebrew = 0x05e1; + t.saraaathai = 0x0e32; + t.saraaethai = 0x0e41; + t.saraaimaimalaithai = 0x0e44; + t.saraaimaimuanthai = 0x0e43; + t.saraamthai = 0x0e33; + t.saraathai = 0x0e30; + t.saraethai = 0x0e40; + t.saraiileftthai = 0xf886; + t.saraiithai = 0x0e35; + t.saraileftthai = 0xf885; + t.saraithai = 0x0e34; + t.saraothai = 0x0e42; + t.saraueeleftthai = 0xf888; + t.saraueethai = 0x0e37; + t.saraueleftthai = 0xf887; + t.sarauethai = 0x0e36; + t.sarauthai = 0x0e38; + t.sarauuthai = 0x0e39; + t.sbopomofo = 0x3119; + t.scaron = 0x0161; + t.scarondotaccent = 0x1e67; + t.scedilla = 0x015f; + t.schwa = 0x0259; + t.schwacyrillic = 0x04d9; + t.schwadieresiscyrillic = 0x04db; + t.schwahook = 0x025a; + t.scircle = 0x24e2; + t.scircumflex = 0x015d; + t.scommaaccent = 0x0219; + t.sdotaccent = 0x1e61; + t.sdotbelow = 0x1e63; + t.sdotbelowdotaccent = 0x1e69; + t.seagullbelowcmb = 0x033c; + t.second = 0x2033; + t.secondtonechinese = 0x02ca; + t.section = 0x00a7; + t.seenarabic = 0x0633; + t.seenfinalarabic = 0xfeb2; + t.seeninitialarabic = 0xfeb3; + t.seenmedialarabic = 0xfeb4; + t.segol = 0x05b6; + t.segol13 = 0x05b6; + t.segol1f = 0x05b6; + t.segol2c = 0x05b6; + t.segolhebrew = 0x05b6; + t.segolnarrowhebrew = 0x05b6; + t.segolquarterhebrew = 0x05b6; + t.segoltahebrew = 0x0592; + t.segolwidehebrew = 0x05b6; + t.seharmenian = 0x057d; + t.sehiragana = 0x305b; + t.sekatakana = 0x30bb; + t.sekatakanahalfwidth = 0xff7e; + t.semicolon = 0x003b; + t.semicolonarabic = 0x061b; + t.semicolonmonospace = 0xff1b; + t.semicolonsmall = 0xfe54; + t.semivoicedmarkkana = 0x309c; + t.semivoicedmarkkanahalfwidth = 0xff9f; + t.sentisquare = 0x3322; + t.sentosquare = 0x3323; + t.seven = 0x0037; + t.sevenarabic = 0x0667; + t.sevenbengali = 0x09ed; + t.sevencircle = 0x2466; + t.sevencircleinversesansserif = 0x2790; + t.sevendeva = 0x096d; + t.seveneighths = 0x215e; + t.sevengujarati = 0x0aed; + t.sevengurmukhi = 0x0a6d; + t.sevenhackarabic = 0x0667; + t.sevenhangzhou = 0x3027; + t.sevenideographicparen = 0x3226; + t.seveninferior = 0x2087; + t.sevenmonospace = 0xff17; + t.sevenoldstyle = 0xf737; + t.sevenparen = 0x247a; + t.sevenperiod = 0x248e; + t.sevenpersian = 0x06f7; + t.sevenroman = 0x2176; + t.sevensuperior = 0x2077; + t.seventeencircle = 0x2470; + t.seventeenparen = 0x2484; + t.seventeenperiod = 0x2498; + t.seventhai = 0x0e57; + t.sfthyphen = 0x00ad; + t.shaarmenian = 0x0577; + t.shabengali = 0x09b6; + t.shacyrillic = 0x0448; + t.shaddaarabic = 0x0651; + t.shaddadammaarabic = 0xfc61; + t.shaddadammatanarabic = 0xfc5e; + t.shaddafathaarabic = 0xfc60; + t.shaddakasraarabic = 0xfc62; + t.shaddakasratanarabic = 0xfc5f; + t.shade = 0x2592; + t.shadedark = 0x2593; + t.shadelight = 0x2591; + t.shademedium = 0x2592; + t.shadeva = 0x0936; + t.shagujarati = 0x0ab6; + t.shagurmukhi = 0x0a36; + t.shalshelethebrew = 0x0593; + t.shbopomofo = 0x3115; + t.shchacyrillic = 0x0449; + t.sheenarabic = 0x0634; + t.sheenfinalarabic = 0xfeb6; + t.sheeninitialarabic = 0xfeb7; + t.sheenmedialarabic = 0xfeb8; + t.sheicoptic = 0x03e3; + t.sheqel = 0x20aa; + t.sheqelhebrew = 0x20aa; + t.sheva = 0x05b0; + t.sheva115 = 0x05b0; + t.sheva15 = 0x05b0; + t.sheva22 = 0x05b0; + t.sheva2e = 0x05b0; + t.shevahebrew = 0x05b0; + t.shevanarrowhebrew = 0x05b0; + t.shevaquarterhebrew = 0x05b0; + t.shevawidehebrew = 0x05b0; + t.shhacyrillic = 0x04bb; + t.shimacoptic = 0x03ed; + t.shin = 0x05e9; + t.shindagesh = 0xfb49; + t.shindageshhebrew = 0xfb49; + t.shindageshshindot = 0xfb2c; + t.shindageshshindothebrew = 0xfb2c; + t.shindageshsindot = 0xfb2d; + t.shindageshsindothebrew = 0xfb2d; + t.shindothebrew = 0x05c1; + t.shinhebrew = 0x05e9; + t.shinshindot = 0xfb2a; + t.shinshindothebrew = 0xfb2a; + t.shinsindot = 0xfb2b; + t.shinsindothebrew = 0xfb2b; + t.shook = 0x0282; + t.sigma = 0x03c3; + t.sigma1 = 0x03c2; + t.sigmafinal = 0x03c2; + t.sigmalunatesymbolgreek = 0x03f2; + t.sihiragana = 0x3057; + t.sikatakana = 0x30b7; + t.sikatakanahalfwidth = 0xff7c; + t.siluqhebrew = 0x05bd; + t.siluqlefthebrew = 0x05bd; + t.similar = 0x223c; + t.sindothebrew = 0x05c2; + t.siosacirclekorean = 0x3274; + t.siosaparenkorean = 0x3214; + t.sioscieuckorean = 0x317e; + t.sioscirclekorean = 0x3266; + t.sioskiyeokkorean = 0x317a; + t.sioskorean = 0x3145; + t.siosnieunkorean = 0x317b; + t.siosparenkorean = 0x3206; + t.siospieupkorean = 0x317d; + t.siostikeutkorean = 0x317c; + t.six = 0x0036; + t.sixarabic = 0x0666; + t.sixbengali = 0x09ec; + t.sixcircle = 0x2465; + t.sixcircleinversesansserif = 0x278f; + t.sixdeva = 0x096c; + t.sixgujarati = 0x0aec; + t.sixgurmukhi = 0x0a6c; + t.sixhackarabic = 0x0666; + t.sixhangzhou = 0x3026; + t.sixideographicparen = 0x3225; + t.sixinferior = 0x2086; + t.sixmonospace = 0xff16; + t.sixoldstyle = 0xf736; + t.sixparen = 0x2479; + t.sixperiod = 0x248d; + t.sixpersian = 0x06f6; + t.sixroman = 0x2175; + t.sixsuperior = 0x2076; + t.sixteencircle = 0x246f; + t.sixteencurrencydenominatorbengali = 0x09f9; + t.sixteenparen = 0x2483; + t.sixteenperiod = 0x2497; + t.sixthai = 0x0e56; + t.slash = 0x002f; + t.slashmonospace = 0xff0f; + t.slong = 0x017f; + t.slongdotaccent = 0x1e9b; + t.smileface = 0x263a; + t.smonospace = 0xff53; + t.sofpasuqhebrew = 0x05c3; + t.softhyphen = 0x00ad; + t.softsigncyrillic = 0x044c; + t.sohiragana = 0x305d; + t.sokatakana = 0x30bd; + t.sokatakanahalfwidth = 0xff7f; + t.soliduslongoverlaycmb = 0x0338; + t.solidusshortoverlaycmb = 0x0337; + t.sorusithai = 0x0e29; + t.sosalathai = 0x0e28; + t.sosothai = 0x0e0b; + t.sosuathai = 0x0e2a; + t.space = 0x0020; + t.spacehackarabic = 0x0020; + t.spade = 0x2660; + t.spadesuitblack = 0x2660; + t.spadesuitwhite = 0x2664; + t.sparen = 0x24ae; + t.squarebelowcmb = 0x033b; + t.squarecc = 0x33c4; + t.squarecm = 0x339d; + t.squarediagonalcrosshatchfill = 0x25a9; + t.squarehorizontalfill = 0x25a4; + t.squarekg = 0x338f; + t.squarekm = 0x339e; + t.squarekmcapital = 0x33ce; + t.squareln = 0x33d1; + t.squarelog = 0x33d2; + t.squaremg = 0x338e; + t.squaremil = 0x33d5; + t.squaremm = 0x339c; + t.squaremsquared = 0x33a1; + t.squareorthogonalcrosshatchfill = 0x25a6; + t.squareupperlefttolowerrightfill = 0x25a7; + t.squareupperrighttolowerleftfill = 0x25a8; + t.squareverticalfill = 0x25a5; + t.squarewhitewithsmallblack = 0x25a3; + t.srsquare = 0x33db; + t.ssabengali = 0x09b7; + t.ssadeva = 0x0937; + t.ssagujarati = 0x0ab7; + t.ssangcieuckorean = 0x3149; + t.ssanghieuhkorean = 0x3185; + t.ssangieungkorean = 0x3180; + t.ssangkiyeokkorean = 0x3132; + t.ssangnieunkorean = 0x3165; + t.ssangpieupkorean = 0x3143; + t.ssangsioskorean = 0x3146; + t.ssangtikeutkorean = 0x3138; + t.ssuperior = 0xf6f2; + t.sterling = 0x00a3; + t.sterlingmonospace = 0xffe1; + t.strokelongoverlaycmb = 0x0336; + t.strokeshortoverlaycmb = 0x0335; + t.subset = 0x2282; + t.subsetnotequal = 0x228a; + t.subsetorequal = 0x2286; + t.succeeds = 0x227b; + t.suchthat = 0x220b; + t.suhiragana = 0x3059; + t.sukatakana = 0x30b9; + t.sukatakanahalfwidth = 0xff7d; + t.sukunarabic = 0x0652; + t.summation = 0x2211; + t.sun = 0x263c; + t.superset = 0x2283; + t.supersetnotequal = 0x228b; + t.supersetorequal = 0x2287; + t.svsquare = 0x33dc; + t.syouwaerasquare = 0x337c; + t.t = 0x0074; + t.tabengali = 0x09a4; + t.tackdown = 0x22a4; + t.tackleft = 0x22a3; + t.tadeva = 0x0924; + t.tagujarati = 0x0aa4; + t.tagurmukhi = 0x0a24; + t.taharabic = 0x0637; + t.tahfinalarabic = 0xfec2; + t.tahinitialarabic = 0xfec3; + t.tahiragana = 0x305f; + t.tahmedialarabic = 0xfec4; + t.taisyouerasquare = 0x337d; + t.takatakana = 0x30bf; + t.takatakanahalfwidth = 0xff80; + t.tatweelarabic = 0x0640; + t.tau = 0x03c4; + t.tav = 0x05ea; + t.tavdages = 0xfb4a; + t.tavdagesh = 0xfb4a; + t.tavdageshhebrew = 0xfb4a; + t.tavhebrew = 0x05ea; + t.tbar = 0x0167; + t.tbopomofo = 0x310a; + t.tcaron = 0x0165; + t.tccurl = 0x02a8; + t.tcedilla = 0x0163; + t.tcheharabic = 0x0686; + t.tchehfinalarabic = 0xfb7b; + t.tchehinitialarabic = 0xfb7c; + t.tchehmedialarabic = 0xfb7d; + t.tcircle = 0x24e3; + t.tcircumflexbelow = 0x1e71; + t.tcommaaccent = 0x0163; + t.tdieresis = 0x1e97; + t.tdotaccent = 0x1e6b; + t.tdotbelow = 0x1e6d; + t.tecyrillic = 0x0442; + t.tedescendercyrillic = 0x04ad; + t.teharabic = 0x062a; + t.tehfinalarabic = 0xfe96; + t.tehhahinitialarabic = 0xfca2; + t.tehhahisolatedarabic = 0xfc0c; + t.tehinitialarabic = 0xfe97; + t.tehiragana = 0x3066; + t.tehjeeminitialarabic = 0xfca1; + t.tehjeemisolatedarabic = 0xfc0b; + t.tehmarbutaarabic = 0x0629; + t.tehmarbutafinalarabic = 0xfe94; + t.tehmedialarabic = 0xfe98; + t.tehmeeminitialarabic = 0xfca4; + t.tehmeemisolatedarabic = 0xfc0e; + t.tehnoonfinalarabic = 0xfc73; + t.tekatakana = 0x30c6; + t.tekatakanahalfwidth = 0xff83; + t.telephone = 0x2121; + t.telephoneblack = 0x260e; + t.telishagedolahebrew = 0x05a0; + t.telishaqetanahebrew = 0x05a9; + t.tencircle = 0x2469; + t.tenideographicparen = 0x3229; + t.tenparen = 0x247d; + t.tenperiod = 0x2491; + t.tenroman = 0x2179; + t.tesh = 0x02a7; + t.tet = 0x05d8; + t.tetdagesh = 0xfb38; + t.tetdageshhebrew = 0xfb38; + t.tethebrew = 0x05d8; + t.tetsecyrillic = 0x04b5; + t.tevirhebrew = 0x059b; + t.tevirlefthebrew = 0x059b; + t.thabengali = 0x09a5; + t.thadeva = 0x0925; + t.thagujarati = 0x0aa5; + t.thagurmukhi = 0x0a25; + t.thalarabic = 0x0630; + t.thalfinalarabic = 0xfeac; + t.thanthakhatlowleftthai = 0xf898; + t.thanthakhatlowrightthai = 0xf897; + t.thanthakhatthai = 0x0e4c; + t.thanthakhatupperleftthai = 0xf896; + t.theharabic = 0x062b; + t.thehfinalarabic = 0xfe9a; + t.thehinitialarabic = 0xfe9b; + t.thehmedialarabic = 0xfe9c; + t.thereexists = 0x2203; + t.therefore = 0x2234; + t.theta = 0x03b8; + t.theta1 = 0x03d1; + t.thetasymbolgreek = 0x03d1; + t.thieuthacirclekorean = 0x3279; + t.thieuthaparenkorean = 0x3219; + t.thieuthcirclekorean = 0x326b; + t.thieuthkorean = 0x314c; + t.thieuthparenkorean = 0x320b; + t.thirteencircle = 0x246c; + t.thirteenparen = 0x2480; + t.thirteenperiod = 0x2494; + t.thonangmonthothai = 0x0e11; + t.thook = 0x01ad; + t.thophuthaothai = 0x0e12; + t.thorn = 0x00fe; + t.thothahanthai = 0x0e17; + t.thothanthai = 0x0e10; + t.thothongthai = 0x0e18; + t.thothungthai = 0x0e16; + t.thousandcyrillic = 0x0482; + t.thousandsseparatorarabic = 0x066c; + t.thousandsseparatorpersian = 0x066c; + t.three = 0x0033; + t.threearabic = 0x0663; + t.threebengali = 0x09e9; + t.threecircle = 0x2462; + t.threecircleinversesansserif = 0x278c; + t.threedeva = 0x0969; + t.threeeighths = 0x215c; + t.threegujarati = 0x0ae9; + t.threegurmukhi = 0x0a69; + t.threehackarabic = 0x0663; + t.threehangzhou = 0x3023; + t.threeideographicparen = 0x3222; + t.threeinferior = 0x2083; + t.threemonospace = 0xff13; + t.threenumeratorbengali = 0x09f6; + t.threeoldstyle = 0xf733; + t.threeparen = 0x2476; + t.threeperiod = 0x248a; + t.threepersian = 0x06f3; + t.threequarters = 0x00be; + t.threequartersemdash = 0xf6de; + t.threeroman = 0x2172; + t.threesuperior = 0x00b3; + t.threethai = 0x0e53; + t.thzsquare = 0x3394; + t.tihiragana = 0x3061; + t.tikatakana = 0x30c1; + t.tikatakanahalfwidth = 0xff81; + t.tikeutacirclekorean = 0x3270; + t.tikeutaparenkorean = 0x3210; + t.tikeutcirclekorean = 0x3262; + t.tikeutkorean = 0x3137; + t.tikeutparenkorean = 0x3202; + t.tilde = 0x02dc; + t.tildebelowcmb = 0x0330; + t.tildecmb = 0x0303; + t.tildecomb = 0x0303; + t.tildedoublecmb = 0x0360; + t.tildeoperator = 0x223c; + t.tildeoverlaycmb = 0x0334; + t.tildeverticalcmb = 0x033e; + t.timescircle = 0x2297; + t.tipehahebrew = 0x0596; + t.tipehalefthebrew = 0x0596; + t.tippigurmukhi = 0x0a70; + t.titlocyrilliccmb = 0x0483; + t.tiwnarmenian = 0x057f; + t.tlinebelow = 0x1e6f; + t.tmonospace = 0xff54; + t.toarmenian = 0x0569; + t.tohiragana = 0x3068; + t.tokatakana = 0x30c8; + t.tokatakanahalfwidth = 0xff84; + t.tonebarextrahighmod = 0x02e5; + t.tonebarextralowmod = 0x02e9; + t.tonebarhighmod = 0x02e6; + t.tonebarlowmod = 0x02e8; + t.tonebarmidmod = 0x02e7; + t.tonefive = 0x01bd; + t.tonesix = 0x0185; + t.tonetwo = 0x01a8; + t.tonos = 0x0384; + t.tonsquare = 0x3327; + t.topatakthai = 0x0e0f; + t.tortoiseshellbracketleft = 0x3014; + t.tortoiseshellbracketleftsmall = 0xfe5d; + t.tortoiseshellbracketleftvertical = 0xfe39; + t.tortoiseshellbracketright = 0x3015; + t.tortoiseshellbracketrightsmall = 0xfe5e; + t.tortoiseshellbracketrightvertical = 0xfe3a; + t.totaothai = 0x0e15; + t.tpalatalhook = 0x01ab; + t.tparen = 0x24af; + t.trademark = 0x2122; + t.trademarksans = 0xf8ea; + t.trademarkserif = 0xf6db; + t.tretroflexhook = 0x0288; + t.triagdn = 0x25bc; + t.triaglf = 0x25c4; + t.triagrt = 0x25ba; + t.triagup = 0x25b2; + t.ts = 0x02a6; + t.tsadi = 0x05e6; + t.tsadidagesh = 0xfb46; + t.tsadidageshhebrew = 0xfb46; + t.tsadihebrew = 0x05e6; + t.tsecyrillic = 0x0446; + t.tsere = 0x05b5; + t.tsere12 = 0x05b5; + t.tsere1e = 0x05b5; + t.tsere2b = 0x05b5; + t.tserehebrew = 0x05b5; + t.tserenarrowhebrew = 0x05b5; + t.tserequarterhebrew = 0x05b5; + t.tserewidehebrew = 0x05b5; + t.tshecyrillic = 0x045b; + t.tsuperior = 0xf6f3; + t.ttabengali = 0x099f; + t.ttadeva = 0x091f; + t.ttagujarati = 0x0a9f; + t.ttagurmukhi = 0x0a1f; + t.tteharabic = 0x0679; + t.ttehfinalarabic = 0xfb67; + t.ttehinitialarabic = 0xfb68; + t.ttehmedialarabic = 0xfb69; + t.tthabengali = 0x09a0; + t.tthadeva = 0x0920; + t.tthagujarati = 0x0aa0; + t.tthagurmukhi = 0x0a20; + t.tturned = 0x0287; + t.tuhiragana = 0x3064; + t.tukatakana = 0x30c4; + t.tukatakanahalfwidth = 0xff82; + t.tusmallhiragana = 0x3063; + t.tusmallkatakana = 0x30c3; + t.tusmallkatakanahalfwidth = 0xff6f; + t.twelvecircle = 0x246b; + t.twelveparen = 0x247f; + t.twelveperiod = 0x2493; + t.twelveroman = 0x217b; + t.twentycircle = 0x2473; + t.twentyhangzhou = 0x5344; + t.twentyparen = 0x2487; + t.twentyperiod = 0x249b; + t.two = 0x0032; + t.twoarabic = 0x0662; + t.twobengali = 0x09e8; + t.twocircle = 0x2461; + t.twocircleinversesansserif = 0x278b; + t.twodeva = 0x0968; + t.twodotenleader = 0x2025; + t.twodotleader = 0x2025; + t.twodotleadervertical = 0xfe30; + t.twogujarati = 0x0ae8; + t.twogurmukhi = 0x0a68; + t.twohackarabic = 0x0662; + t.twohangzhou = 0x3022; + t.twoideographicparen = 0x3221; + t.twoinferior = 0x2082; + t.twomonospace = 0xff12; + t.twonumeratorbengali = 0x09f5; + t.twooldstyle = 0xf732; + t.twoparen = 0x2475; + t.twoperiod = 0x2489; + t.twopersian = 0x06f2; + t.tworoman = 0x2171; + t.twostroke = 0x01bb; + t.twosuperior = 0x00b2; + t.twothai = 0x0e52; + t.twothirds = 0x2154; + t.u = 0x0075; + t.uacute = 0x00fa; + t.ubar = 0x0289; + t.ubengali = 0x0989; + t.ubopomofo = 0x3128; + t.ubreve = 0x016d; + t.ucaron = 0x01d4; + t.ucircle = 0x24e4; + t.ucircumflex = 0x00fb; + t.ucircumflexbelow = 0x1e77; + t.ucyrillic = 0x0443; + t.udattadeva = 0x0951; + t.udblacute = 0x0171; + t.udblgrave = 0x0215; + t.udeva = 0x0909; + t.udieresis = 0x00fc; + t.udieresisacute = 0x01d8; + t.udieresisbelow = 0x1e73; + t.udieresiscaron = 0x01da; + t.udieresiscyrillic = 0x04f1; + t.udieresisgrave = 0x01dc; + t.udieresismacron = 0x01d6; + t.udotbelow = 0x1ee5; + t.ugrave = 0x00f9; + t.ugujarati = 0x0a89; + t.ugurmukhi = 0x0a09; + t.uhiragana = 0x3046; + t.uhookabove = 0x1ee7; + t.uhorn = 0x01b0; + t.uhornacute = 0x1ee9; + t.uhorndotbelow = 0x1ef1; + t.uhorngrave = 0x1eeb; + t.uhornhookabove = 0x1eed; + t.uhorntilde = 0x1eef; + t.uhungarumlaut = 0x0171; + t.uhungarumlautcyrillic = 0x04f3; + t.uinvertedbreve = 0x0217; + t.ukatakana = 0x30a6; + t.ukatakanahalfwidth = 0xff73; + t.ukcyrillic = 0x0479; + t.ukorean = 0x315c; + t.umacron = 0x016b; + t.umacroncyrillic = 0x04ef; + t.umacrondieresis = 0x1e7b; + t.umatragurmukhi = 0x0a41; + t.umonospace = 0xff55; + t.underscore = 0x005f; + t.underscoredbl = 0x2017; + t.underscoremonospace = 0xff3f; + t.underscorevertical = 0xfe33; + t.underscorewavy = 0xfe4f; + t.union = 0x222a; + t.universal = 0x2200; + t.uogonek = 0x0173; + t.uparen = 0x24b0; + t.upblock = 0x2580; + t.upperdothebrew = 0x05c4; + t.upsilon = 0x03c5; + t.upsilondieresis = 0x03cb; + t.upsilondieresistonos = 0x03b0; + t.upsilonlatin = 0x028a; + t.upsilontonos = 0x03cd; + t.uptackbelowcmb = 0x031d; + t.uptackmod = 0x02d4; + t.uragurmukhi = 0x0a73; + t.uring = 0x016f; + t.ushortcyrillic = 0x045e; + t.usmallhiragana = 0x3045; + t.usmallkatakana = 0x30a5; + t.usmallkatakanahalfwidth = 0xff69; + t.ustraightcyrillic = 0x04af; + t.ustraightstrokecyrillic = 0x04b1; + t.utilde = 0x0169; + t.utildeacute = 0x1e79; + t.utildebelow = 0x1e75; + t.uubengali = 0x098a; + t.uudeva = 0x090a; + t.uugujarati = 0x0a8a; + t.uugurmukhi = 0x0a0a; + t.uumatragurmukhi = 0x0a42; + t.uuvowelsignbengali = 0x09c2; + t.uuvowelsigndeva = 0x0942; + t.uuvowelsigngujarati = 0x0ac2; + t.uvowelsignbengali = 0x09c1; + t.uvowelsigndeva = 0x0941; + t.uvowelsigngujarati = 0x0ac1; + t.v = 0x0076; + t.vadeva = 0x0935; + t.vagujarati = 0x0ab5; + t.vagurmukhi = 0x0a35; + t.vakatakana = 0x30f7; + t.vav = 0x05d5; + t.vavdagesh = 0xfb35; + t.vavdagesh65 = 0xfb35; + t.vavdageshhebrew = 0xfb35; + t.vavhebrew = 0x05d5; + t.vavholam = 0xfb4b; + t.vavholamhebrew = 0xfb4b; + t.vavvavhebrew = 0x05f0; + t.vavyodhebrew = 0x05f1; + t.vcircle = 0x24e5; + t.vdotbelow = 0x1e7f; + t.vecyrillic = 0x0432; + t.veharabic = 0x06a4; + t.vehfinalarabic = 0xfb6b; + t.vehinitialarabic = 0xfb6c; + t.vehmedialarabic = 0xfb6d; + t.vekatakana = 0x30f9; + t.venus = 0x2640; + t.verticalbar = 0x007c; + t.verticallineabovecmb = 0x030d; + t.verticallinebelowcmb = 0x0329; + t.verticallinelowmod = 0x02cc; + t.verticallinemod = 0x02c8; + t.vewarmenian = 0x057e; + t.vhook = 0x028b; + t.vikatakana = 0x30f8; + t.viramabengali = 0x09cd; + t.viramadeva = 0x094d; + t.viramagujarati = 0x0acd; + t.visargabengali = 0x0983; + t.visargadeva = 0x0903; + t.visargagujarati = 0x0a83; + t.vmonospace = 0xff56; + t.voarmenian = 0x0578; + t.voicediterationhiragana = 0x309e; + t.voicediterationkatakana = 0x30fe; + t.voicedmarkkana = 0x309b; + t.voicedmarkkanahalfwidth = 0xff9e; + t.vokatakana = 0x30fa; + t.vparen = 0x24b1; + t.vtilde = 0x1e7d; + t.vturned = 0x028c; + t.vuhiragana = 0x3094; + t.vukatakana = 0x30f4; + t.w = 0x0077; + t.wacute = 0x1e83; + t.waekorean = 0x3159; + t.wahiragana = 0x308f; + t.wakatakana = 0x30ef; + t.wakatakanahalfwidth = 0xff9c; + t.wakorean = 0x3158; + t.wasmallhiragana = 0x308e; + t.wasmallkatakana = 0x30ee; + t.wattosquare = 0x3357; + t.wavedash = 0x301c; + t.wavyunderscorevertical = 0xfe34; + t.wawarabic = 0x0648; + t.wawfinalarabic = 0xfeee; + t.wawhamzaabovearabic = 0x0624; + t.wawhamzaabovefinalarabic = 0xfe86; + t.wbsquare = 0x33dd; + t.wcircle = 0x24e6; + t.wcircumflex = 0x0175; + t.wdieresis = 0x1e85; + t.wdotaccent = 0x1e87; + t.wdotbelow = 0x1e89; + t.wehiragana = 0x3091; + t.weierstrass = 0x2118; + t.wekatakana = 0x30f1; + t.wekorean = 0x315e; + t.weokorean = 0x315d; + t.wgrave = 0x1e81; + t.whitebullet = 0x25e6; + t.whitecircle = 0x25cb; + t.whitecircleinverse = 0x25d9; + t.whitecornerbracketleft = 0x300e; + t.whitecornerbracketleftvertical = 0xfe43; + t.whitecornerbracketright = 0x300f; + t.whitecornerbracketrightvertical = 0xfe44; + t.whitediamond = 0x25c7; + t.whitediamondcontainingblacksmalldiamond = 0x25c8; + t.whitedownpointingsmalltriangle = 0x25bf; + t.whitedownpointingtriangle = 0x25bd; + t.whiteleftpointingsmalltriangle = 0x25c3; + t.whiteleftpointingtriangle = 0x25c1; + t.whitelenticularbracketleft = 0x3016; + t.whitelenticularbracketright = 0x3017; + t.whiterightpointingsmalltriangle = 0x25b9; + t.whiterightpointingtriangle = 0x25b7; + t.whitesmallsquare = 0x25ab; + t.whitesmilingface = 0x263a; + t.whitesquare = 0x25a1; + t.whitestar = 0x2606; + t.whitetelephone = 0x260f; + t.whitetortoiseshellbracketleft = 0x3018; + t.whitetortoiseshellbracketright = 0x3019; + t.whiteuppointingsmalltriangle = 0x25b5; + t.whiteuppointingtriangle = 0x25b3; + t.wihiragana = 0x3090; + t.wikatakana = 0x30f0; + t.wikorean = 0x315f; + t.wmonospace = 0xff57; + t.wohiragana = 0x3092; + t.wokatakana = 0x30f2; + t.wokatakanahalfwidth = 0xff66; + t.won = 0x20a9; + t.wonmonospace = 0xffe6; + t.wowaenthai = 0x0e27; + t.wparen = 0x24b2; + t.wring = 0x1e98; + t.wsuperior = 0x02b7; + t.wturned = 0x028d; + t.wynn = 0x01bf; + t.x = 0x0078; + t.xabovecmb = 0x033d; + t.xbopomofo = 0x3112; + t.xcircle = 0x24e7; + t.xdieresis = 0x1e8d; + t.xdotaccent = 0x1e8b; + t.xeharmenian = 0x056d; + t.xi = 0x03be; + t.xmonospace = 0xff58; + t.xparen = 0x24b3; + t.xsuperior = 0x02e3; + t.y = 0x0079; + t.yaadosquare = 0x334e; + t.yabengali = 0x09af; + t.yacute = 0x00fd; + t.yadeva = 0x092f; + t.yaekorean = 0x3152; + t.yagujarati = 0x0aaf; + t.yagurmukhi = 0x0a2f; + t.yahiragana = 0x3084; + t.yakatakana = 0x30e4; + t.yakatakanahalfwidth = 0xff94; + t.yakorean = 0x3151; + t.yamakkanthai = 0x0e4e; + t.yasmallhiragana = 0x3083; + t.yasmallkatakana = 0x30e3; + t.yasmallkatakanahalfwidth = 0xff6c; + t.yatcyrillic = 0x0463; + t.ycircle = 0x24e8; + t.ycircumflex = 0x0177; + t.ydieresis = 0x00ff; + t.ydotaccent = 0x1e8f; + t.ydotbelow = 0x1ef5; + t.yeharabic = 0x064a; + t.yehbarreearabic = 0x06d2; + t.yehbarreefinalarabic = 0xfbaf; + t.yehfinalarabic = 0xfef2; + t.yehhamzaabovearabic = 0x0626; + t.yehhamzaabovefinalarabic = 0xfe8a; + t.yehhamzaaboveinitialarabic = 0xfe8b; + t.yehhamzaabovemedialarabic = 0xfe8c; + t.yehinitialarabic = 0xfef3; + t.yehmedialarabic = 0xfef4; + t.yehmeeminitialarabic = 0xfcdd; + t.yehmeemisolatedarabic = 0xfc58; + t.yehnoonfinalarabic = 0xfc94; + t.yehthreedotsbelowarabic = 0x06d1; + t.yekorean = 0x3156; + t.yen = 0x00a5; + t.yenmonospace = 0xffe5; + t.yeokorean = 0x3155; + t.yeorinhieuhkorean = 0x3186; + t.yerahbenyomohebrew = 0x05aa; + t.yerahbenyomolefthebrew = 0x05aa; + t.yericyrillic = 0x044b; + t.yerudieresiscyrillic = 0x04f9; + t.yesieungkorean = 0x3181; + t.yesieungpansioskorean = 0x3183; + t.yesieungsioskorean = 0x3182; + t.yetivhebrew = 0x059a; + t.ygrave = 0x1ef3; + t.yhook = 0x01b4; + t.yhookabove = 0x1ef7; + t.yiarmenian = 0x0575; + t.yicyrillic = 0x0457; + t.yikorean = 0x3162; + t.yinyang = 0x262f; + t.yiwnarmenian = 0x0582; + t.ymonospace = 0xff59; + t.yod = 0x05d9; + t.yoddagesh = 0xfb39; + t.yoddageshhebrew = 0xfb39; + t.yodhebrew = 0x05d9; + t.yodyodhebrew = 0x05f2; + t.yodyodpatahhebrew = 0xfb1f; + t.yohiragana = 0x3088; + t.yoikorean = 0x3189; + t.yokatakana = 0x30e8; + t.yokatakanahalfwidth = 0xff96; + t.yokorean = 0x315b; + t.yosmallhiragana = 0x3087; + t.yosmallkatakana = 0x30e7; + t.yosmallkatakanahalfwidth = 0xff6e; + t.yotgreek = 0x03f3; + t.yoyaekorean = 0x3188; + t.yoyakorean = 0x3187; + t.yoyakthai = 0x0e22; + t.yoyingthai = 0x0e0d; + t.yparen = 0x24b4; + t.ypogegrammeni = 0x037a; + t.ypogegrammenigreekcmb = 0x0345; + t.yr = 0x01a6; + t.yring = 0x1e99; + t.ysuperior = 0x02b8; + t.ytilde = 0x1ef9; + t.yturned = 0x028e; + t.yuhiragana = 0x3086; + t.yuikorean = 0x318c; + t.yukatakana = 0x30e6; + t.yukatakanahalfwidth = 0xff95; + t.yukorean = 0x3160; + t.yusbigcyrillic = 0x046b; + t.yusbigiotifiedcyrillic = 0x046d; + t.yuslittlecyrillic = 0x0467; + t.yuslittleiotifiedcyrillic = 0x0469; + t.yusmallhiragana = 0x3085; + t.yusmallkatakana = 0x30e5; + t.yusmallkatakanahalfwidth = 0xff6d; + t.yuyekorean = 0x318b; + t.yuyeokorean = 0x318a; + t.yyabengali = 0x09df; + t.yyadeva = 0x095f; + t.z = 0x007a; + t.zaarmenian = 0x0566; + t.zacute = 0x017a; + t.zadeva = 0x095b; + t.zagurmukhi = 0x0a5b; + t.zaharabic = 0x0638; + t.zahfinalarabic = 0xfec6; + t.zahinitialarabic = 0xfec7; + t.zahiragana = 0x3056; + t.zahmedialarabic = 0xfec8; + t.zainarabic = 0x0632; + t.zainfinalarabic = 0xfeb0; + t.zakatakana = 0x30b6; + t.zaqefgadolhebrew = 0x0595; + t.zaqefqatanhebrew = 0x0594; + t.zarqahebrew = 0x0598; + t.zayin = 0x05d6; + t.zayindagesh = 0xfb36; + t.zayindageshhebrew = 0xfb36; + t.zayinhebrew = 0x05d6; + t.zbopomofo = 0x3117; + t.zcaron = 0x017e; + t.zcircle = 0x24e9; + t.zcircumflex = 0x1e91; + t.zcurl = 0x0291; + t.zdot = 0x017c; + t.zdotaccent = 0x017c; + t.zdotbelow = 0x1e93; + t.zecyrillic = 0x0437; + t.zedescendercyrillic = 0x0499; + t.zedieresiscyrillic = 0x04df; + t.zehiragana = 0x305c; + t.zekatakana = 0x30bc; + t.zero = 0x0030; + t.zeroarabic = 0x0660; + t.zerobengali = 0x09e6; + t.zerodeva = 0x0966; + t.zerogujarati = 0x0ae6; + t.zerogurmukhi = 0x0a66; + t.zerohackarabic = 0x0660; + t.zeroinferior = 0x2080; + t.zeromonospace = 0xff10; + t.zerooldstyle = 0xf730; + t.zeropersian = 0x06f0; + t.zerosuperior = 0x2070; + t.zerothai = 0x0e50; + t.zerowidthjoiner = 0xfeff; + t.zerowidthnonjoiner = 0x200c; + t.zerowidthspace = 0x200b; + t.zeta = 0x03b6; + t.zhbopomofo = 0x3113; + t.zhearmenian = 0x056a; + t.zhebrevecyrillic = 0x04c2; + t.zhecyrillic = 0x0436; + t.zhedescendercyrillic = 0x0497; + t.zhedieresiscyrillic = 0x04dd; + t.zihiragana = 0x3058; + t.zikatakana = 0x30b8; + t.zinorhebrew = 0x05ae; + t.zlinebelow = 0x1e95; + t.zmonospace = 0xff5a; + t.zohiragana = 0x305e; + t.zokatakana = 0x30be; + t.zparen = 0x24b5; + t.zretroflexhook = 0x0290; + t.zstroke = 0x01b6; + t.zuhiragana = 0x305a; + t.zukatakana = 0x30ba; + t[".notdef"] = 0x0000; + t.angbracketleftbig = 0x2329; + t.angbracketleftBig = 0x2329; + t.angbracketleftbigg = 0x2329; + t.angbracketleftBigg = 0x2329; + t.angbracketrightBig = 0x232a; + t.angbracketrightbig = 0x232a; + t.angbracketrightBigg = 0x232a; + t.angbracketrightbigg = 0x232a; + t.arrowhookleft = 0x21aa; + t.arrowhookright = 0x21a9; + t.arrowlefttophalf = 0x21bc; + t.arrowleftbothalf = 0x21bd; + t.arrownortheast = 0x2197; + t.arrownorthwest = 0x2196; + t.arrowrighttophalf = 0x21c0; + t.arrowrightbothalf = 0x21c1; + t.arrowsoutheast = 0x2198; + t.arrowsouthwest = 0x2199; + t.backslashbig = 0x2216; + t.backslashBig = 0x2216; + t.backslashBigg = 0x2216; + t.backslashbigg = 0x2216; + t.bardbl = 0x2016; + t.bracehtipdownleft = 0xfe37; + t.bracehtipdownright = 0xfe37; + t.bracehtipupleft = 0xfe38; + t.bracehtipupright = 0xfe38; + t.braceleftBig = 0x007b; + t.braceleftbig = 0x007b; + t.braceleftbigg = 0x007b; + t.braceleftBigg = 0x007b; + t.bracerightBig = 0x007d; + t.bracerightbig = 0x007d; + t.bracerightbigg = 0x007d; + t.bracerightBigg = 0x007d; + t.bracketleftbig = 0x005b; + t.bracketleftBig = 0x005b; + t.bracketleftbigg = 0x005b; + t.bracketleftBigg = 0x005b; + t.bracketrightBig = 0x005d; + t.bracketrightbig = 0x005d; + t.bracketrightbigg = 0x005d; + t.bracketrightBigg = 0x005d; + t.ceilingleftbig = 0x2308; + t.ceilingleftBig = 0x2308; + t.ceilingleftBigg = 0x2308; + t.ceilingleftbigg = 0x2308; + t.ceilingrightbig = 0x2309; + t.ceilingrightBig = 0x2309; + t.ceilingrightbigg = 0x2309; + t.ceilingrightBigg = 0x2309; + t.circledotdisplay = 0x2299; + t.circledottext = 0x2299; + t.circlemultiplydisplay = 0x2297; + t.circlemultiplytext = 0x2297; + t.circleplusdisplay = 0x2295; + t.circleplustext = 0x2295; + t.contintegraldisplay = 0x222e; + t.contintegraltext = 0x222e; + t.coproductdisplay = 0x2210; + t.coproducttext = 0x2210; + t.floorleftBig = 0x230a; + t.floorleftbig = 0x230a; + t.floorleftbigg = 0x230a; + t.floorleftBigg = 0x230a; + t.floorrightbig = 0x230b; + t.floorrightBig = 0x230b; + t.floorrightBigg = 0x230b; + t.floorrightbigg = 0x230b; + t.hatwide = 0x0302; + t.hatwider = 0x0302; + t.hatwidest = 0x0302; + t.intercal = 0x1d40; + t.integraldisplay = 0x222b; + t.integraltext = 0x222b; + t.intersectiondisplay = 0x22c2; + t.intersectiontext = 0x22c2; + t.logicalanddisplay = 0x2227; + t.logicalandtext = 0x2227; + t.logicalordisplay = 0x2228; + t.logicalortext = 0x2228; + t.parenleftBig = 0x0028; + t.parenleftbig = 0x0028; + t.parenleftBigg = 0x0028; + t.parenleftbigg = 0x0028; + t.parenrightBig = 0x0029; + t.parenrightbig = 0x0029; + t.parenrightBigg = 0x0029; + t.parenrightbigg = 0x0029; + t.prime = 0x2032; + t.productdisplay = 0x220f; + t.producttext = 0x220f; + t.radicalbig = 0x221a; + t.radicalBig = 0x221a; + t.radicalBigg = 0x221a; + t.radicalbigg = 0x221a; + t.radicalbt = 0x221a; + t.radicaltp = 0x221a; + t.radicalvertex = 0x221a; + t.slashbig = 0x002f; + t.slashBig = 0x002f; + t.slashBigg = 0x002f; + t.slashbigg = 0x002f; + t.summationdisplay = 0x2211; + t.summationtext = 0x2211; + t.tildewide = 0x02dc; + t.tildewider = 0x02dc; + t.tildewidest = 0x02dc; + t.uniondisplay = 0x22c3; + t.unionmultidisplay = 0x228e; + t.unionmultitext = 0x228e; + t.unionsqdisplay = 0x2294; + t.unionsqtext = 0x2294; + t.uniontext = 0x22c3; + t.vextenddouble = 0x2225; + t.vextendsingle = 0x2223; +}); +const getDingbatsGlyphsUnicode = getLookupTableFactory(function (t) { + t.space = 0x0020; + t.a1 = 0x2701; + t.a2 = 0x2702; + t.a202 = 0x2703; + t.a3 = 0x2704; + t.a4 = 0x260e; + t.a5 = 0x2706; + t.a119 = 0x2707; + t.a118 = 0x2708; + t.a117 = 0x2709; + t.a11 = 0x261b; + t.a12 = 0x261e; + t.a13 = 0x270c; + t.a14 = 0x270d; + t.a15 = 0x270e; + t.a16 = 0x270f; + t.a105 = 0x2710; + t.a17 = 0x2711; + t.a18 = 0x2712; + t.a19 = 0x2713; + t.a20 = 0x2714; + t.a21 = 0x2715; + t.a22 = 0x2716; + t.a23 = 0x2717; + t.a24 = 0x2718; + t.a25 = 0x2719; + t.a26 = 0x271a; + t.a27 = 0x271b; + t.a28 = 0x271c; + t.a6 = 0x271d; + t.a7 = 0x271e; + t.a8 = 0x271f; + t.a9 = 0x2720; + t.a10 = 0x2721; + t.a29 = 0x2722; + t.a30 = 0x2723; + t.a31 = 0x2724; + t.a32 = 0x2725; + t.a33 = 0x2726; + t.a34 = 0x2727; + t.a35 = 0x2605; + t.a36 = 0x2729; + t.a37 = 0x272a; + t.a38 = 0x272b; + t.a39 = 0x272c; + t.a40 = 0x272d; + t.a41 = 0x272e; + t.a42 = 0x272f; + t.a43 = 0x2730; + t.a44 = 0x2731; + t.a45 = 0x2732; + t.a46 = 0x2733; + t.a47 = 0x2734; + t.a48 = 0x2735; + t.a49 = 0x2736; + t.a50 = 0x2737; + t.a51 = 0x2738; + t.a52 = 0x2739; + t.a53 = 0x273a; + t.a54 = 0x273b; + t.a55 = 0x273c; + t.a56 = 0x273d; + t.a57 = 0x273e; + t.a58 = 0x273f; + t.a59 = 0x2740; + t.a60 = 0x2741; + t.a61 = 0x2742; + t.a62 = 0x2743; + t.a63 = 0x2744; + t.a64 = 0x2745; + t.a65 = 0x2746; + t.a66 = 0x2747; + t.a67 = 0x2748; + t.a68 = 0x2749; + t.a69 = 0x274a; + t.a70 = 0x274b; + t.a71 = 0x25cf; + t.a72 = 0x274d; + t.a73 = 0x25a0; + t.a74 = 0x274f; + t.a203 = 0x2750; + t.a75 = 0x2751; + t.a204 = 0x2752; + t.a76 = 0x25b2; + t.a77 = 0x25bc; + t.a78 = 0x25c6; + t.a79 = 0x2756; + t.a81 = 0x25d7; + t.a82 = 0x2758; + t.a83 = 0x2759; + t.a84 = 0x275a; + t.a97 = 0x275b; + t.a98 = 0x275c; + t.a99 = 0x275d; + t.a100 = 0x275e; + t.a101 = 0x2761; + t.a102 = 0x2762; + t.a103 = 0x2763; + t.a104 = 0x2764; + t.a106 = 0x2765; + t.a107 = 0x2766; + t.a108 = 0x2767; + t.a112 = 0x2663; + t.a111 = 0x2666; + t.a110 = 0x2665; + t.a109 = 0x2660; + t.a120 = 0x2460; + t.a121 = 0x2461; + t.a122 = 0x2462; + t.a123 = 0x2463; + t.a124 = 0x2464; + t.a125 = 0x2465; + t.a126 = 0x2466; + t.a127 = 0x2467; + t.a128 = 0x2468; + t.a129 = 0x2469; + t.a130 = 0x2776; + t.a131 = 0x2777; + t.a132 = 0x2778; + t.a133 = 0x2779; + t.a134 = 0x277a; + t.a135 = 0x277b; + t.a136 = 0x277c; + t.a137 = 0x277d; + t.a138 = 0x277e; + t.a139 = 0x277f; + t.a140 = 0x2780; + t.a141 = 0x2781; + t.a142 = 0x2782; + t.a143 = 0x2783; + t.a144 = 0x2784; + t.a145 = 0x2785; + t.a146 = 0x2786; + t.a147 = 0x2787; + t.a148 = 0x2788; + t.a149 = 0x2789; + t.a150 = 0x278a; + t.a151 = 0x278b; + t.a152 = 0x278c; + t.a153 = 0x278d; + t.a154 = 0x278e; + t.a155 = 0x278f; + t.a156 = 0x2790; + t.a157 = 0x2791; + t.a158 = 0x2792; + t.a159 = 0x2793; + t.a160 = 0x2794; + t.a161 = 0x2192; + t.a163 = 0x2194; + t.a164 = 0x2195; + t.a196 = 0x2798; + t.a165 = 0x2799; + t.a192 = 0x279a; + t.a166 = 0x279b; + t.a167 = 0x279c; + t.a168 = 0x279d; + t.a169 = 0x279e; + t.a170 = 0x279f; + t.a171 = 0x27a0; + t.a172 = 0x27a1; + t.a173 = 0x27a2; + t.a162 = 0x27a3; + t.a174 = 0x27a4; + t.a175 = 0x27a5; + t.a176 = 0x27a6; + t.a177 = 0x27a7; + t.a178 = 0x27a8; + t.a179 = 0x27a9; + t.a193 = 0x27aa; + t.a180 = 0x27ab; + t.a199 = 0x27ac; + t.a181 = 0x27ad; + t.a200 = 0x27ae; + t.a182 = 0x27af; + t.a201 = 0x27b1; + t.a183 = 0x27b2; + t.a184 = 0x27b3; + t.a197 = 0x27b4; + t.a185 = 0x27b5; + t.a194 = 0x27b6; + t.a198 = 0x27b7; + t.a186 = 0x27b8; + t.a195 = 0x27b9; + t.a187 = 0x27ba; + t.a188 = 0x27bb; + t.a189 = 0x27bc; + t.a190 = 0x27bd; + t.a191 = 0x27be; + t.a89 = 0x2768; + t.a90 = 0x2769; + t.a93 = 0x276a; + t.a94 = 0x276b; + t.a91 = 0x276c; + t.a92 = 0x276d; + t.a205 = 0x276e; + t.a85 = 0x276f; + t.a206 = 0x2770; + t.a86 = 0x2771; + t.a87 = 0x2772; + t.a88 = 0x2773; + t.a95 = 0x2774; + t.a96 = 0x2775; + t[".notdef"] = 0x0000; +}); + +;// ./src/core/unicode.js + +const getSpecialPUASymbols = getLookupTableFactory(function (t) { + t[63721] = 0x00a9; + t[63193] = 0x00a9; + t[63720] = 0x00ae; + t[63194] = 0x00ae; + t[63722] = 0x2122; + t[63195] = 0x2122; + t[63729] = 0x23a7; + t[63730] = 0x23a8; + t[63731] = 0x23a9; + t[63740] = 0x23ab; + t[63741] = 0x23ac; + t[63742] = 0x23ad; + t[63726] = 0x23a1; + t[63727] = 0x23a2; + t[63728] = 0x23a3; + t[63737] = 0x23a4; + t[63738] = 0x23a5; + t[63739] = 0x23a6; + t[63723] = 0x239b; + t[63724] = 0x239c; + t[63725] = 0x239d; + t[63734] = 0x239e; + t[63735] = 0x239f; + t[63736] = 0x23a0; +}); +function mapSpecialUnicodeValues(code) { + if (code >= 0xfff0 && code <= 0xffff) { + return 0; + } else if (code >= 0xf600 && code <= 0xf8ff) { + return getSpecialPUASymbols()[code] || code; + } else if (code === 0x00ad) { + return 0x002d; + } + return code; +} +function getUnicodeForGlyph(name, glyphsUnicodeMap) { + let unicode = glyphsUnicodeMap[name]; + if (unicode !== undefined) { + return unicode; + } + if (!name) { + return -1; + } + if (name[0] === "u") { + const nameLen = name.length; + let hexStr; + if (nameLen === 7 && name[1] === "n" && name[2] === "i") { + hexStr = name.substring(3); + } else if (nameLen >= 5 && nameLen <= 7) { + hexStr = name.substring(1); + } else { + return -1; + } + if (hexStr === hexStr.toUpperCase()) { + unicode = parseInt(hexStr, 16); + if (unicode >= 0) { + return unicode; + } + } + } + return -1; +} +const UnicodeRanges = [[0x0000, 0x007f], [0x0080, 0x00ff], [0x0100, 0x017f], [0x0180, 0x024f], [0x0250, 0x02af, 0x1d00, 0x1d7f, 0x1d80, 0x1dbf], [0x02b0, 0x02ff, 0xa700, 0xa71f], [0x0300, 0x036f, 0x1dc0, 0x1dff], [0x0370, 0x03ff], [0x2c80, 0x2cff], [0x0400, 0x04ff, 0x0500, 0x052f, 0x2de0, 0x2dff, 0xa640, 0xa69f], [0x0530, 0x058f], [0x0590, 0x05ff], [0xa500, 0xa63f], [0x0600, 0x06ff, 0x0750, 0x077f], [0x07c0, 0x07ff], [0x0900, 0x097f], [0x0980, 0x09ff], [0x0a00, 0x0a7f], [0x0a80, 0x0aff], [0x0b00, 0x0b7f], [0x0b80, 0x0bff], [0x0c00, 0x0c7f], [0x0c80, 0x0cff], [0x0d00, 0x0d7f], [0x0e00, 0x0e7f], [0x0e80, 0x0eff], [0x10a0, 0x10ff, 0x2d00, 0x2d2f], [0x1b00, 0x1b7f], [0x1100, 0x11ff], [0x1e00, 0x1eff, 0x2c60, 0x2c7f, 0xa720, 0xa7ff], [0x1f00, 0x1fff], [0x2000, 0x206f, 0x2e00, 0x2e7f], [0x2070, 0x209f], [0x20a0, 0x20cf], [0x20d0, 0x20ff], [0x2100, 0x214f], [0x2150, 0x218f], [0x2190, 0x21ff, 0x27f0, 0x27ff, 0x2900, 0x297f, 0x2b00, 0x2bff], [0x2200, 0x22ff, 0x2a00, 0x2aff, 0x27c0, 0x27ef, 0x2980, 0x29ff], [0x2300, 0x23ff], [0x2400, 0x243f], [0x2440, 0x245f], [0x2460, 0x24ff], [0x2500, 0x257f], [0x2580, 0x259f], [0x25a0, 0x25ff], [0x2600, 0x26ff], [0x2700, 0x27bf], [0x3000, 0x303f], [0x3040, 0x309f], [0x30a0, 0x30ff, 0x31f0, 0x31ff], [0x3100, 0x312f, 0x31a0, 0x31bf], [0x3130, 0x318f], [0xa840, 0xa87f], [0x3200, 0x32ff], [0x3300, 0x33ff], [0xac00, 0xd7af], [0xd800, 0xdfff], [0x10900, 0x1091f], [0x4e00, 0x9fff, 0x2e80, 0x2eff, 0x2f00, 0x2fdf, 0x2ff0, 0x2fff, 0x3400, 0x4dbf, 0x20000, 0x2a6df, 0x3190, 0x319f], [0xe000, 0xf8ff], [0x31c0, 0x31ef, 0xf900, 0xfaff, 0x2f800, 0x2fa1f], [0xfb00, 0xfb4f], [0xfb50, 0xfdff], [0xfe20, 0xfe2f], [0xfe10, 0xfe1f], [0xfe50, 0xfe6f], [0xfe70, 0xfeff], [0xff00, 0xffef], [0xfff0, 0xffff], [0x0f00, 0x0fff], [0x0700, 0x074f], [0x0780, 0x07bf], [0x0d80, 0x0dff], [0x1000, 0x109f], [0x1200, 0x137f, 0x1380, 0x139f, 0x2d80, 0x2ddf], [0x13a0, 0x13ff], [0x1400, 0x167f], [0x1680, 0x169f], [0x16a0, 0x16ff], [0x1780, 0x17ff], [0x1800, 0x18af], [0x2800, 0x28ff], [0xa000, 0xa48f], [0x1700, 0x171f, 0x1720, 0x173f, 0x1740, 0x175f, 0x1760, 0x177f], [0x10300, 0x1032f], [0x10330, 0x1034f], [0x10400, 0x1044f], [0x1d000, 0x1d0ff, 0x1d100, 0x1d1ff, 0x1d200, 0x1d24f], [0x1d400, 0x1d7ff], [0xff000, 0xffffd], [0xfe00, 0xfe0f, 0xe0100, 0xe01ef], [0xe0000, 0xe007f], [0x1900, 0x194f], [0x1950, 0x197f], [0x1980, 0x19df], [0x1a00, 0x1a1f], [0x2c00, 0x2c5f], [0x2d30, 0x2d7f], [0x4dc0, 0x4dff], [0xa800, 0xa82f], [0x10000, 0x1007f, 0x10080, 0x100ff, 0x10100, 0x1013f], [0x10140, 0x1018f], [0x10380, 0x1039f], [0x103a0, 0x103df], [0x10450, 0x1047f], [0x10480, 0x104af], [0x10800, 0x1083f], [0x10a00, 0x10a5f], [0x1d300, 0x1d35f], [0x12000, 0x123ff, 0x12400, 0x1247f], [0x1d360, 0x1d37f], [0x1b80, 0x1bbf], [0x1c00, 0x1c4f], [0x1c50, 0x1c7f], [0xa880, 0xa8df], [0xa900, 0xa92f], [0xa930, 0xa95f], [0xaa00, 0xaa5f], [0x10190, 0x101cf], [0x101d0, 0x101ff], [0x102a0, 0x102df, 0x10280, 0x1029f, 0x10920, 0x1093f], [0x1f030, 0x1f09f, 0x1f000, 0x1f02f]]; +function getUnicodeRangeFor(value, lastPosition = -1) { + if (lastPosition !== -1) { + const range = UnicodeRanges[lastPosition]; + for (let i = 0, ii = range.length; i < ii; i += 2) { + if (value >= range[i] && value <= range[i + 1]) { + return lastPosition; + } + } + } + for (let i = 0, ii = UnicodeRanges.length; i < ii; i++) { + const range = UnicodeRanges[i]; + for (let j = 0, jj = range.length; j < jj; j += 2) { + if (value >= range[j] && value <= range[j + 1]) { + return i; + } + } + } + return -1; +} +const SpecialCharRegExp = new RegExp("^(\\s)|(\\p{Mn})|(\\p{Cf})$", "u"); +const CategoryCache = new Map(); +function getCharUnicodeCategory(char) { + const cachedCategory = CategoryCache.get(char); + if (cachedCategory) { + return cachedCategory; + } + const groups = char.match(SpecialCharRegExp); + const category = { + isWhitespace: !!groups?.[1], + isZeroWidthDiacritic: !!groups?.[2], + isInvisibleFormatMark: !!groups?.[3] + }; + CategoryCache.set(char, category); + return category; +} +function clearUnicodeCaches() { + CategoryCache.clear(); +} + +;// ./src/core/fonts_utils.js + + + + + +const SEAC_ANALYSIS_ENABLED = true; +const FontFlags = { + FixedPitch: 1, + Serif: 2, + Symbolic: 4, + Script: 8, + Nonsymbolic: 32, + Italic: 64, + AllCap: 65536, + SmallCap: 131072, + ForceBold: 262144 +}; +const MacStandardGlyphOrdering = [".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde", "aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute", "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling", "section", "bullet", "paragraph", "germandbls", "registered", "copyright", "trademark", "acute", "dieresis", "notequal", "AE", "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen", "mu", "partialdiff", "summation", "product", "pi", "integral", "ordfeminine", "ordmasculine", "Omega", "ae", "oslash", "questiondown", "exclamdown", "logicalnot", "radical", "florin", "approxequal", "Delta", "guillemotleft", "guillemotright", "ellipsis", "nonbreakingspace", "Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", "fraction", "currency", "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve", "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash", "Scaron", "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth", "Yacute", "yacute", "Thorn", "thorn", "minus", "multiply", "onesuperior", "twosuperior", "threesuperior", "onehalf", "onequarter", "threequarters", "franc", "Gbreve", "gbreve", "Idotaccent", "Scedilla", "scedilla", "Cacute", "cacute", "Ccaron", "ccaron", "dcroat"]; +function recoverGlyphName(name, glyphsUnicodeMap) { + if (glyphsUnicodeMap[name] !== undefined) { + return name; + } + const unicode = getUnicodeForGlyph(name, glyphsUnicodeMap); + if (unicode !== -1) { + for (const key in glyphsUnicodeMap) { + if (glyphsUnicodeMap[key] === unicode) { + return key; + } + } + } + info("Unable to recover a standard glyph name for: " + name); + return name; +} +function type1FontGlyphMapping(properties, builtInEncoding, glyphNames) { + const charCodeToGlyphId = Object.create(null); + let glyphId, charCode, baseEncoding; + const isSymbolicFont = !!(properties.flags & FontFlags.Symbolic); + if (properties.isInternalFont) { + baseEncoding = builtInEncoding; + for (charCode = 0; charCode < baseEncoding.length; charCode++) { + glyphId = glyphNames.indexOf(baseEncoding[charCode]); + charCodeToGlyphId[charCode] = glyphId >= 0 ? glyphId : 0; + } + } else if (properties.baseEncodingName) { + baseEncoding = getEncoding(properties.baseEncodingName); + for (charCode = 0; charCode < baseEncoding.length; charCode++) { + glyphId = glyphNames.indexOf(baseEncoding[charCode]); + charCodeToGlyphId[charCode] = glyphId >= 0 ? glyphId : 0; + } + } else if (isSymbolicFont) { + for (charCode in builtInEncoding) { + charCodeToGlyphId[charCode] = builtInEncoding[charCode]; + } + } else { + baseEncoding = StandardEncoding; + for (charCode = 0; charCode < baseEncoding.length; charCode++) { + glyphId = glyphNames.indexOf(baseEncoding[charCode]); + charCodeToGlyphId[charCode] = glyphId >= 0 ? glyphId : 0; + } + } + const differences = properties.differences; + let glyphsUnicodeMap; + if (differences) { + for (charCode in differences) { + const glyphName = differences[charCode]; + glyphId = glyphNames.indexOf(glyphName); + if (glyphId === -1) { + if (!glyphsUnicodeMap) { + glyphsUnicodeMap = getGlyphsUnicode(); + } + const standardGlyphName = recoverGlyphName(glyphName, glyphsUnicodeMap); + if (standardGlyphName !== glyphName) { + glyphId = glyphNames.indexOf(standardGlyphName); + } + } + charCodeToGlyphId[charCode] = glyphId >= 0 ? glyphId : 0; + } + } + return charCodeToGlyphId; +} +function normalizeFontName(name) { + return name.replaceAll(/[,_]/g, "-").replaceAll(/\s/g, ""); +} +const getVerticalPresentationForm = getLookupTableFactory(t => { + t[0x2013] = 0xfe32; + t[0x2014] = 0xfe31; + t[0x2025] = 0xfe30; + t[0x2026] = 0xfe19; + t[0x3001] = 0xfe11; + t[0x3002] = 0xfe12; + t[0x3008] = 0xfe3f; + t[0x3009] = 0xfe40; + t[0x300a] = 0xfe3d; + t[0x300b] = 0xfe3e; + t[0x300c] = 0xfe41; + t[0x300d] = 0xfe42; + t[0x300e] = 0xfe43; + t[0x300f] = 0xfe44; + t[0x3010] = 0xfe3b; + t[0x3011] = 0xfe3c; + t[0x3014] = 0xfe39; + t[0x3015] = 0xfe3a; + t[0x3016] = 0xfe17; + t[0x3017] = 0xfe18; + t[0xfe4f] = 0xfe34; + t[0xff01] = 0xfe15; + t[0xff08] = 0xfe35; + t[0xff09] = 0xfe36; + t[0xff0c] = 0xfe10; + t[0xff1a] = 0xfe13; + t[0xff1b] = 0xfe14; + t[0xff1f] = 0xfe16; + t[0xff3b] = 0xfe47; + t[0xff3d] = 0xfe48; + t[0xff3f] = 0xfe33; + t[0xff5b] = 0xfe37; + t[0xff5d] = 0xfe38; +}); + +;// ./src/core/standard_fonts.js + + +const getStdFontMap = getLookupTableFactory(function (t) { + t["Times-Roman"] = "Times-Roman"; + t.Helvetica = "Helvetica"; + t.Courier = "Courier"; + t.Symbol = "Symbol"; + t["Times-Bold"] = "Times-Bold"; + t["Helvetica-Bold"] = "Helvetica-Bold"; + t["Courier-Bold"] = "Courier-Bold"; + t.ZapfDingbats = "ZapfDingbats"; + t["Times-Italic"] = "Times-Italic"; + t["Helvetica-Oblique"] = "Helvetica-Oblique"; + t["Courier-Oblique"] = "Courier-Oblique"; + t["Times-BoldItalic"] = "Times-BoldItalic"; + t["Helvetica-BoldOblique"] = "Helvetica-BoldOblique"; + t["Courier-BoldOblique"] = "Courier-BoldOblique"; + t.ArialNarrow = "Helvetica"; + t["ArialNarrow-Bold"] = "Helvetica-Bold"; + t["ArialNarrow-BoldItalic"] = "Helvetica-BoldOblique"; + t["ArialNarrow-Italic"] = "Helvetica-Oblique"; + t.ArialBlack = "Helvetica"; + t["ArialBlack-Bold"] = "Helvetica-Bold"; + t["ArialBlack-BoldItalic"] = "Helvetica-BoldOblique"; + t["ArialBlack-Italic"] = "Helvetica-Oblique"; + t["Arial-Black"] = "Helvetica"; + t["Arial-Black-Bold"] = "Helvetica-Bold"; + t["Arial-Black-BoldItalic"] = "Helvetica-BoldOblique"; + t["Arial-Black-Italic"] = "Helvetica-Oblique"; + t.Arial = "Helvetica"; + t["Arial-Bold"] = "Helvetica-Bold"; + t["Arial-BoldItalic"] = "Helvetica-BoldOblique"; + t["Arial-Italic"] = "Helvetica-Oblique"; + t.ArialMT = "Helvetica"; + t["Arial-BoldItalicMT"] = "Helvetica-BoldOblique"; + t["Arial-BoldMT"] = "Helvetica-Bold"; + t["Arial-ItalicMT"] = "Helvetica-Oblique"; + t["Arial-BoldItalicMT-BoldItalic"] = "Helvetica-BoldOblique"; + t["Arial-BoldMT-Bold"] = "Helvetica-Bold"; + t["Arial-ItalicMT-Italic"] = "Helvetica-Oblique"; + t.ArialUnicodeMS = "Helvetica"; + t["ArialUnicodeMS-Bold"] = "Helvetica-Bold"; + t["ArialUnicodeMS-BoldItalic"] = "Helvetica-BoldOblique"; + t["ArialUnicodeMS-Italic"] = "Helvetica-Oblique"; + t["Courier-BoldItalic"] = "Courier-BoldOblique"; + t["Courier-Italic"] = "Courier-Oblique"; + t.CourierNew = "Courier"; + t["CourierNew-Bold"] = "Courier-Bold"; + t["CourierNew-BoldItalic"] = "Courier-BoldOblique"; + t["CourierNew-Italic"] = "Courier-Oblique"; + t["CourierNewPS-BoldItalicMT"] = "Courier-BoldOblique"; + t["CourierNewPS-BoldMT"] = "Courier-Bold"; + t["CourierNewPS-ItalicMT"] = "Courier-Oblique"; + t.CourierNewPSMT = "Courier"; + t["Helvetica-BoldItalic"] = "Helvetica-BoldOblique"; + t["Helvetica-Italic"] = "Helvetica-Oblique"; + t["HelveticaLTStd-Bold"] = "Helvetica-Bold"; + t["Symbol-Bold"] = "Symbol"; + t["Symbol-BoldItalic"] = "Symbol"; + t["Symbol-Italic"] = "Symbol"; + t.TimesNewRoman = "Times-Roman"; + t["TimesNewRoman-Bold"] = "Times-Bold"; + t["TimesNewRoman-BoldItalic"] = "Times-BoldItalic"; + t["TimesNewRoman-Italic"] = "Times-Italic"; + t.TimesNewRomanPS = "Times-Roman"; + t["TimesNewRomanPS-Bold"] = "Times-Bold"; + t["TimesNewRomanPS-BoldItalic"] = "Times-BoldItalic"; + t["TimesNewRomanPS-BoldItalicMT"] = "Times-BoldItalic"; + t["TimesNewRomanPS-BoldMT"] = "Times-Bold"; + t["TimesNewRomanPS-Italic"] = "Times-Italic"; + t["TimesNewRomanPS-ItalicMT"] = "Times-Italic"; + t.TimesNewRomanPSMT = "Times-Roman"; + t["TimesNewRomanPSMT-Bold"] = "Times-Bold"; + t["TimesNewRomanPSMT-BoldItalic"] = "Times-BoldItalic"; + t["TimesNewRomanPSMT-Italic"] = "Times-Italic"; +}); +const getFontNameToFileMap = getLookupTableFactory(function (t) { + t.Courier = "FoxitFixed.pfb"; + t["Courier-Bold"] = "FoxitFixedBold.pfb"; + t["Courier-BoldOblique"] = "FoxitFixedBoldItalic.pfb"; + t["Courier-Oblique"] = "FoxitFixedItalic.pfb"; + t.Helvetica = "LiberationSans-Regular.ttf"; + t["Helvetica-Bold"] = "LiberationSans-Bold.ttf"; + t["Helvetica-BoldOblique"] = "LiberationSans-BoldItalic.ttf"; + t["Helvetica-Oblique"] = "LiberationSans-Italic.ttf"; + t["Times-Roman"] = "FoxitSerif.pfb"; + t["Times-Bold"] = "FoxitSerifBold.pfb"; + t["Times-BoldItalic"] = "FoxitSerifBoldItalic.pfb"; + t["Times-Italic"] = "FoxitSerifItalic.pfb"; + t.Symbol = "FoxitSymbol.pfb"; + t.ZapfDingbats = "FoxitDingbats.pfb"; + t["LiberationSans-Regular"] = "LiberationSans-Regular.ttf"; + t["LiberationSans-Bold"] = "LiberationSans-Bold.ttf"; + t["LiberationSans-Italic"] = "LiberationSans-Italic.ttf"; + t["LiberationSans-BoldItalic"] = "LiberationSans-BoldItalic.ttf"; +}); +const getNonStdFontMap = getLookupTableFactory(function (t) { + t.Calibri = "Helvetica"; + t["Calibri-Bold"] = "Helvetica-Bold"; + t["Calibri-BoldItalic"] = "Helvetica-BoldOblique"; + t["Calibri-Italic"] = "Helvetica-Oblique"; + t.CenturyGothic = "Helvetica"; + t["CenturyGothic-Bold"] = "Helvetica-Bold"; + t["CenturyGothic-BoldItalic"] = "Helvetica-BoldOblique"; + t["CenturyGothic-Italic"] = "Helvetica-Oblique"; + t.ComicSansMS = "Comic Sans MS"; + t["ComicSansMS-Bold"] = "Comic Sans MS-Bold"; + t["ComicSansMS-BoldItalic"] = "Comic Sans MS-BoldItalic"; + t["ComicSansMS-Italic"] = "Comic Sans MS-Italic"; + t.GillSansMT = "Helvetica"; + t["GillSansMT-Bold"] = "Helvetica-Bold"; + t["GillSansMT-BoldItalic"] = "Helvetica-BoldOblique"; + t["GillSansMT-Italic"] = "Helvetica-Oblique"; + t.Impact = "Helvetica"; + t["ItcSymbol-Bold"] = "Helvetica-Bold"; + t["ItcSymbol-BoldItalic"] = "Helvetica-BoldOblique"; + t["ItcSymbol-Book"] = "Helvetica"; + t["ItcSymbol-BookItalic"] = "Helvetica-Oblique"; + t["ItcSymbol-Medium"] = "Helvetica"; + t["ItcSymbol-MediumItalic"] = "Helvetica-Oblique"; + t.LucidaConsole = "Courier"; + t["LucidaConsole-Bold"] = "Courier-Bold"; + t["LucidaConsole-BoldItalic"] = "Courier-BoldOblique"; + t["LucidaConsole-Italic"] = "Courier-Oblique"; + t["LucidaSans-Demi"] = "Helvetica-Bold"; + t["MS-Gothic"] = "MS Gothic"; + t["MS-Gothic-Bold"] = "MS Gothic-Bold"; + t["MS-Gothic-BoldItalic"] = "MS Gothic-BoldItalic"; + t["MS-Gothic-Italic"] = "MS Gothic-Italic"; + t["MS-Mincho"] = "MS Mincho"; + t["MS-Mincho-Bold"] = "MS Mincho-Bold"; + t["MS-Mincho-BoldItalic"] = "MS Mincho-BoldItalic"; + t["MS-Mincho-Italic"] = "MS Mincho-Italic"; + t["MS-PGothic"] = "MS PGothic"; + t["MS-PGothic-Bold"] = "MS PGothic-Bold"; + t["MS-PGothic-BoldItalic"] = "MS PGothic-BoldItalic"; + t["MS-PGothic-Italic"] = "MS PGothic-Italic"; + t["MS-PMincho"] = "MS PMincho"; + t["MS-PMincho-Bold"] = "MS PMincho-Bold"; + t["MS-PMincho-BoldItalic"] = "MS PMincho-BoldItalic"; + t["MS-PMincho-Italic"] = "MS PMincho-Italic"; + t.NuptialScript = "Times-Italic"; + t.SegoeUISymbol = "Helvetica"; +}); +const getSerifFonts = getLookupTableFactory(function (t) { + t["Adobe Jenson"] = true; + t["Adobe Text"] = true; + t.Albertus = true; + t.Aldus = true; + t.Alexandria = true; + t.Algerian = true; + t["American Typewriter"] = true; + t.Antiqua = true; + t.Apex = true; + t.Arno = true; + t.Aster = true; + t.Aurora = true; + t.Baskerville = true; + t.Bell = true; + t.Bembo = true; + t["Bembo Schoolbook"] = true; + t.Benguiat = true; + t["Berkeley Old Style"] = true; + t["Bernhard Modern"] = true; + t["Berthold City"] = true; + t.Bodoni = true; + t["Bauer Bodoni"] = true; + t["Book Antiqua"] = true; + t.Bookman = true; + t["Bordeaux Roman"] = true; + t["Californian FB"] = true; + t.Calisto = true; + t.Calvert = true; + t.Capitals = true; + t.Cambria = true; + t.Cartier = true; + t.Caslon = true; + t.Catull = true; + t.Centaur = true; + t["Century Old Style"] = true; + t["Century Schoolbook"] = true; + t.Chaparral = true; + t["Charis SIL"] = true; + t.Cheltenham = true; + t["Cholla Slab"] = true; + t.Clarendon = true; + t.Clearface = true; + t.Cochin = true; + t.Colonna = true; + t["Computer Modern"] = true; + t["Concrete Roman"] = true; + t.Constantia = true; + t["Cooper Black"] = true; + t.Corona = true; + t.Ecotype = true; + t.Egyptienne = true; + t.Elephant = true; + t.Excelsior = true; + t.Fairfield = true; + t["FF Scala"] = true; + t.Folkard = true; + t.Footlight = true; + t.FreeSerif = true; + t["Friz Quadrata"] = true; + t.Garamond = true; + t.Gentium = true; + t.Georgia = true; + t.Gloucester = true; + t["Goudy Old Style"] = true; + t["Goudy Schoolbook"] = true; + t["Goudy Pro Font"] = true; + t.Granjon = true; + t["Guardian Egyptian"] = true; + t.Heather = true; + t.Hercules = true; + t["High Tower Text"] = true; + t.Hiroshige = true; + t["Hoefler Text"] = true; + t["Humana Serif"] = true; + t.Imprint = true; + t["Ionic No. 5"] = true; + t.Janson = true; + t.Joanna = true; + t.Korinna = true; + t.Lexicon = true; + t.LiberationSerif = true; + t["Liberation Serif"] = true; + t["Linux Libertine"] = true; + t.Literaturnaya = true; + t.Lucida = true; + t["Lucida Bright"] = true; + t.Melior = true; + t.Memphis = true; + t.Miller = true; + t.Minion = true; + t.Modern = true; + t["Mona Lisa"] = true; + t["Mrs Eaves"] = true; + t["MS Serif"] = true; + t["Museo Slab"] = true; + t["New York"] = true; + t["Nimbus Roman"] = true; + t["NPS Rawlinson Roadway"] = true; + t.NuptialScript = true; + t.Palatino = true; + t.Perpetua = true; + t.Plantin = true; + t["Plantin Schoolbook"] = true; + t.Playbill = true; + t["Poor Richard"] = true; + t["Rawlinson Roadway"] = true; + t.Renault = true; + t.Requiem = true; + t.Rockwell = true; + t.Roman = true; + t["Rotis Serif"] = true; + t.Sabon = true; + t.Scala = true; + t.Seagull = true; + t.Sistina = true; + t.Souvenir = true; + t.STIX = true; + t["Stone Informal"] = true; + t["Stone Serif"] = true; + t.Sylfaen = true; + t.Times = true; + t.Trajan = true; + t["Trinité"] = true; + t["Trump Mediaeval"] = true; + t.Utopia = true; + t["Vale Type"] = true; + t["Bitstream Vera"] = true; + t["Vera Serif"] = true; + t.Versailles = true; + t.Wanted = true; + t.Weiss = true; + t["Wide Latin"] = true; + t.Windsor = true; + t.XITS = true; +}); +const getSymbolsFonts = getLookupTableFactory(function (t) { + t.Dingbats = true; + t.Symbol = true; + t.ZapfDingbats = true; + t.Wingdings = true; + t["Wingdings-Bold"] = true; + t["Wingdings-Regular"] = true; +}); +const getGlyphMapForStandardFonts = getLookupTableFactory(function (t) { + t[2] = 10; + t[3] = 32; + t[4] = 33; + t[5] = 34; + t[6] = 35; + t[7] = 36; + t[8] = 37; + t[9] = 38; + t[10] = 39; + t[11] = 40; + t[12] = 41; + t[13] = 42; + t[14] = 43; + t[15] = 44; + t[16] = 45; + t[17] = 46; + t[18] = 47; + t[19] = 48; + t[20] = 49; + t[21] = 50; + t[22] = 51; + t[23] = 52; + t[24] = 53; + t[25] = 54; + t[26] = 55; + t[27] = 56; + t[28] = 57; + t[29] = 58; + t[30] = 894; + t[31] = 60; + t[32] = 61; + t[33] = 62; + t[34] = 63; + t[35] = 64; + t[36] = 65; + t[37] = 66; + t[38] = 67; + t[39] = 68; + t[40] = 69; + t[41] = 70; + t[42] = 71; + t[43] = 72; + t[44] = 73; + t[45] = 74; + t[46] = 75; + t[47] = 76; + t[48] = 77; + t[49] = 78; + t[50] = 79; + t[51] = 80; + t[52] = 81; + t[53] = 82; + t[54] = 83; + t[55] = 84; + t[56] = 85; + t[57] = 86; + t[58] = 87; + t[59] = 88; + t[60] = 89; + t[61] = 90; + t[62] = 91; + t[63] = 92; + t[64] = 93; + t[65] = 94; + t[66] = 95; + t[67] = 96; + t[68] = 97; + t[69] = 98; + t[70] = 99; + t[71] = 100; + t[72] = 101; + t[73] = 102; + t[74] = 103; + t[75] = 104; + t[76] = 105; + t[77] = 106; + t[78] = 107; + t[79] = 108; + t[80] = 109; + t[81] = 110; + t[82] = 111; + t[83] = 112; + t[84] = 113; + t[85] = 114; + t[86] = 115; + t[87] = 116; + t[88] = 117; + t[89] = 118; + t[90] = 119; + t[91] = 120; + t[92] = 121; + t[93] = 122; + t[94] = 123; + t[95] = 124; + t[96] = 125; + t[97] = 126; + t[98] = 196; + t[99] = 197; + t[100] = 199; + t[101] = 201; + t[102] = 209; + t[103] = 214; + t[104] = 220; + t[105] = 225; + t[106] = 224; + t[107] = 226; + t[108] = 228; + t[109] = 227; + t[110] = 229; + t[111] = 231; + t[112] = 233; + t[113] = 232; + t[114] = 234; + t[115] = 235; + t[116] = 237; + t[117] = 236; + t[118] = 238; + t[119] = 239; + t[120] = 241; + t[121] = 243; + t[122] = 242; + t[123] = 244; + t[124] = 246; + t[125] = 245; + t[126] = 250; + t[127] = 249; + t[128] = 251; + t[129] = 252; + t[130] = 8224; + t[131] = 176; + t[132] = 162; + t[133] = 163; + t[134] = 167; + t[135] = 8226; + t[136] = 182; + t[137] = 223; + t[138] = 174; + t[139] = 169; + t[140] = 8482; + t[141] = 180; + t[142] = 168; + t[143] = 8800; + t[144] = 198; + t[145] = 216; + t[146] = 8734; + t[147] = 177; + t[148] = 8804; + t[149] = 8805; + t[150] = 165; + t[151] = 181; + t[152] = 8706; + t[153] = 8721; + t[154] = 8719; + t[156] = 8747; + t[157] = 170; + t[158] = 186; + t[159] = 8486; + t[160] = 230; + t[161] = 248; + t[162] = 191; + t[163] = 161; + t[164] = 172; + t[165] = 8730; + t[166] = 402; + t[167] = 8776; + t[168] = 8710; + t[169] = 171; + t[170] = 187; + t[171] = 8230; + t[179] = 8220; + t[180] = 8221; + t[181] = 8216; + t[182] = 8217; + t[200] = 193; + t[203] = 205; + t[207] = 211; + t[210] = 218; + t[223] = 711; + t[224] = 321; + t[225] = 322; + t[226] = 352; + t[227] = 353; + t[228] = 381; + t[229] = 382; + t[233] = 221; + t[234] = 253; + t[252] = 263; + t[253] = 268; + t[254] = 269; + t[258] = 258; + t[260] = 260; + t[261] = 261; + t[265] = 280; + t[266] = 281; + t[267] = 282; + t[268] = 283; + t[269] = 313; + t[275] = 323; + t[276] = 324; + t[278] = 328; + t[283] = 344; + t[284] = 345; + t[285] = 346; + t[286] = 347; + t[292] = 367; + t[295] = 377; + t[296] = 378; + t[298] = 380; + t[305] = 963; + t[306] = 964; + t[307] = 966; + t[308] = 8215; + t[309] = 8252; + t[310] = 8319; + t[311] = 8359; + t[312] = 8592; + t[313] = 8593; + t[337] = 9552; + t[493] = 1039; + t[494] = 1040; + t[672] = 1488; + t[673] = 1489; + t[674] = 1490; + t[675] = 1491; + t[676] = 1492; + t[677] = 1493; + t[678] = 1494; + t[679] = 1495; + t[680] = 1496; + t[681] = 1497; + t[682] = 1498; + t[683] = 1499; + t[684] = 1500; + t[685] = 1501; + t[686] = 1502; + t[687] = 1503; + t[688] = 1504; + t[689] = 1505; + t[690] = 1506; + t[691] = 1507; + t[692] = 1508; + t[693] = 1509; + t[694] = 1510; + t[695] = 1511; + t[696] = 1512; + t[697] = 1513; + t[698] = 1514; + t[705] = 1524; + t[706] = 8362; + t[710] = 64288; + t[711] = 64298; + t[759] = 1617; + t[761] = 1776; + t[763] = 1778; + t[775] = 1652; + t[777] = 1764; + t[778] = 1780; + t[779] = 1781; + t[780] = 1782; + t[782] = 771; + t[783] = 64726; + t[786] = 8363; + t[788] = 8532; + t[790] = 768; + t[791] = 769; + t[792] = 768; + t[795] = 803; + t[797] = 64336; + t[798] = 64337; + t[799] = 64342; + t[800] = 64343; + t[801] = 64344; + t[802] = 64345; + t[803] = 64362; + t[804] = 64363; + t[805] = 64364; + t[2424] = 7821; + t[2425] = 7822; + t[2426] = 7823; + t[2427] = 7824; + t[2428] = 7825; + t[2429] = 7826; + t[2430] = 7827; + t[2433] = 7682; + t[2678] = 8045; + t[2679] = 8046; + t[2830] = 1552; + t[2838] = 686; + t[2840] = 751; + t[2842] = 753; + t[2843] = 754; + t[2844] = 755; + t[2846] = 757; + t[2856] = 767; + t[2857] = 848; + t[2858] = 849; + t[2862] = 853; + t[2863] = 854; + t[2864] = 855; + t[2865] = 861; + t[2866] = 862; + t[2906] = 7460; + t[2908] = 7462; + t[2909] = 7463; + t[2910] = 7464; + t[2912] = 7466; + t[2913] = 7467; + t[2914] = 7468; + t[2916] = 7470; + t[2917] = 7471; + t[2918] = 7472; + t[2920] = 7474; + t[2921] = 7475; + t[2922] = 7476; + t[2924] = 7478; + t[2925] = 7479; + t[2926] = 7480; + t[2928] = 7482; + t[2929] = 7483; + t[2930] = 7484; + t[2932] = 7486; + t[2933] = 7487; + t[2934] = 7488; + t[2936] = 7490; + t[2937] = 7491; + t[2938] = 7492; + t[2940] = 7494; + t[2941] = 7495; + t[2942] = 7496; + t[2944] = 7498; + t[2946] = 7500; + t[2948] = 7502; + t[2950] = 7504; + t[2951] = 7505; + t[2952] = 7506; + t[2954] = 7508; + t[2955] = 7509; + t[2956] = 7510; + t[2958] = 7512; + t[2959] = 7513; + t[2960] = 7514; + t[2962] = 7516; + t[2963] = 7517; + t[2964] = 7518; + t[2966] = 7520; + t[2967] = 7521; + t[2968] = 7522; + t[2970] = 7524; + t[2971] = 7525; + t[2972] = 7526; + t[2974] = 7528; + t[2975] = 7529; + t[2976] = 7530; + t[2978] = 1537; + t[2979] = 1538; + t[2980] = 1539; + t[2982] = 1549; + t[2983] = 1551; + t[2984] = 1552; + t[2986] = 1554; + t[2987] = 1555; + t[2988] = 1556; + t[2990] = 1623; + t[2991] = 1624; + t[2995] = 1775; + t[2999] = 1791; + t[3002] = 64290; + t[3003] = 64291; + t[3004] = 64292; + t[3006] = 64294; + t[3007] = 64295; + t[3008] = 64296; + t[3011] = 1900; + t[3014] = 8223; + t[3015] = 8244; + t[3017] = 7532; + t[3018] = 7533; + t[3019] = 7534; + t[3075] = 7590; + t[3076] = 7591; + t[3079] = 7594; + t[3080] = 7595; + t[3083] = 7598; + t[3084] = 7599; + t[3087] = 7602; + t[3088] = 7603; + t[3091] = 7606; + t[3092] = 7607; + t[3095] = 7610; + t[3096] = 7611; + t[3099] = 7614; + t[3100] = 7615; + t[3103] = 7618; + t[3104] = 7619; + t[3107] = 8337; + t[3108] = 8338; + t[3116] = 1884; + t[3119] = 1885; + t[3120] = 1885; + t[3123] = 1886; + t[3124] = 1886; + t[3127] = 1887; + t[3128] = 1887; + t[3131] = 1888; + t[3132] = 1888; + t[3135] = 1889; + t[3136] = 1889; + t[3139] = 1890; + t[3140] = 1890; + t[3143] = 1891; + t[3144] = 1891; + t[3147] = 1892; + t[3148] = 1892; + t[3153] = 580; + t[3154] = 581; + t[3157] = 584; + t[3158] = 585; + t[3161] = 588; + t[3162] = 589; + t[3165] = 891; + t[3166] = 892; + t[3169] = 1274; + t[3170] = 1275; + t[3173] = 1278; + t[3174] = 1279; + t[3181] = 7622; + t[3182] = 7623; + t[3282] = 11799; + t[3316] = 578; + t[3379] = 42785; + t[3393] = 1159; + t[3416] = 8377; +}); +const getSupplementalGlyphMapForArialBlack = getLookupTableFactory(function (t) { + t[227] = 322; + t[264] = 261; + t[291] = 346; +}); +const getSupplementalGlyphMapForCalibri = getLookupTableFactory(function (t) { + t[1] = 32; + t[4] = 65; + t[5] = 192; + t[6] = 193; + t[9] = 196; + t[17] = 66; + t[18] = 67; + t[21] = 268; + t[24] = 68; + t[28] = 69; + t[29] = 200; + t[30] = 201; + t[32] = 282; + t[38] = 70; + t[39] = 71; + t[44] = 72; + t[47] = 73; + t[48] = 204; + t[49] = 205; + t[58] = 74; + t[60] = 75; + t[62] = 76; + t[68] = 77; + t[69] = 78; + t[75] = 79; + t[76] = 210; + t[80] = 214; + t[87] = 80; + t[89] = 81; + t[90] = 82; + t[92] = 344; + t[94] = 83; + t[97] = 352; + t[100] = 84; + t[104] = 85; + t[109] = 220; + t[115] = 86; + t[116] = 87; + t[121] = 88; + t[122] = 89; + t[124] = 221; + t[127] = 90; + t[129] = 381; + t[258] = 97; + t[259] = 224; + t[260] = 225; + t[263] = 228; + t[268] = 261; + t[271] = 98; + t[272] = 99; + t[273] = 263; + t[275] = 269; + t[282] = 100; + t[286] = 101; + t[287] = 232; + t[288] = 233; + t[290] = 283; + t[295] = 281; + t[296] = 102; + t[336] = 103; + t[346] = 104; + t[349] = 105; + t[350] = 236; + t[351] = 237; + t[361] = 106; + t[364] = 107; + t[367] = 108; + t[371] = 322; + t[373] = 109; + t[374] = 110; + t[381] = 111; + t[382] = 242; + t[383] = 243; + t[386] = 246; + t[393] = 112; + t[395] = 113; + t[396] = 114; + t[398] = 345; + t[400] = 115; + t[401] = 347; + t[403] = 353; + t[410] = 116; + t[437] = 117; + t[442] = 252; + t[448] = 118; + t[449] = 119; + t[454] = 120; + t[455] = 121; + t[457] = 253; + t[460] = 122; + t[462] = 382; + t[463] = 380; + t[853] = 44; + t[855] = 58; + t[856] = 46; + t[876] = 47; + t[878] = 45; + t[882] = 45; + t[894] = 40; + t[895] = 41; + t[896] = 91; + t[897] = 93; + t[923] = 64; + t[1004] = 48; + t[1005] = 49; + t[1006] = 50; + t[1007] = 51; + t[1008] = 52; + t[1009] = 53; + t[1010] = 54; + t[1011] = 55; + t[1012] = 56; + t[1013] = 57; + t[1081] = 37; + t[1085] = 43; + t[1086] = 45; +}); +function getStandardFontName(name) { + const fontName = normalizeFontName(name); + const stdFontMap = getStdFontMap(); + return stdFontMap[fontName]; +} +function isKnownFontName(name) { + const fontName = normalizeFontName(name); + return !!(getStdFontMap()[fontName] || getNonStdFontMap()[fontName] || getSerifFonts()[fontName] || getSymbolsFonts()[fontName]); +} + +;// ./src/core/to_unicode_map.js + +class ToUnicodeMap { + constructor(cmap = []) { + this._map = cmap; + } + get length() { + return this._map.length; + } + forEach(callback) { + for (const charCode in this._map) { + callback(charCode, this._map[charCode].codePointAt(0)); + } + } + has(i) { + return this._map[i] !== undefined; + } + get(i) { + return this._map[i]; + } + charCodeOf(value) { + const map = this._map; + if (map.length <= 0x10000) { + return map.indexOf(value); + } + for (const charCode in map) { + if (map[charCode] === value) { + return charCode | 0; + } + } + return -1; + } + amend(map) { + for (const charCode in map) { + this._map[charCode] = map[charCode]; + } + } +} +class IdentityToUnicodeMap { + constructor(firstChar, lastChar) { + this.firstChar = firstChar; + this.lastChar = lastChar; + } + get length() { + return this.lastChar + 1 - this.firstChar; + } + forEach(callback) { + for (let i = this.firstChar, ii = this.lastChar; i <= ii; i++) { + callback(i, i); + } + } + has(i) { + return this.firstChar <= i && i <= this.lastChar; + } + get(i) { + if (this.firstChar <= i && i <= this.lastChar) { + return String.fromCharCode(i); + } + return undefined; + } + charCodeOf(v) { + return Number.isInteger(v) && v >= this.firstChar && v <= this.lastChar ? v : -1; + } + amend(map) { + unreachable("Should not call amend()"); + } +} + +;// ./src/core/cff_font.js + + + +class CFFFont { + constructor(file, properties) { + this.properties = properties; + const parser = new CFFParser(file, properties, SEAC_ANALYSIS_ENABLED); + this.cff = parser.parse(); + this.cff.duplicateFirstGlyph(); + const compiler = new CFFCompiler(this.cff); + this.seacs = this.cff.seacs; + try { + this.data = compiler.compile(); + } catch { + warn("Failed to compile font " + properties.loadedName); + this.data = file; + } + this._createBuiltInEncoding(); + } + get numGlyphs() { + return this.cff.charStrings.count; + } + getCharset() { + return this.cff.charset.charset; + } + getGlyphMapping() { + const cff = this.cff; + const properties = this.properties; + const { + cidToGidMap, + cMap + } = properties; + const charsets = cff.charset.charset; + let charCodeToGlyphId; + let glyphId; + if (properties.composite) { + let invCidToGidMap; + if (cidToGidMap?.length > 0) { + invCidToGidMap = Object.create(null); + for (let i = 0, ii = cidToGidMap.length; i < ii; i++) { + const gid = cidToGidMap[i]; + if (gid !== undefined) { + invCidToGidMap[gid] = i; + } + } + } + charCodeToGlyphId = Object.create(null); + let charCode; + if (cff.isCIDFont) { + for (glyphId = 0; glyphId < charsets.length; glyphId++) { + const cid = charsets[glyphId]; + charCode = cMap.charCodeOf(cid); + if (invCidToGidMap?.[charCode] !== undefined) { + charCode = invCidToGidMap[charCode]; + } + charCodeToGlyphId[charCode] = glyphId; + } + } else { + for (glyphId = 0; glyphId < cff.charStrings.count; glyphId++) { + charCode = cMap.charCodeOf(glyphId); + charCodeToGlyphId[charCode] = glyphId; + } + } + return charCodeToGlyphId; + } + let encoding = cff.encoding ? cff.encoding.encoding : null; + if (properties.isInternalFont) { + encoding = properties.defaultEncoding; + } + charCodeToGlyphId = type1FontGlyphMapping(properties, encoding, charsets); + return charCodeToGlyphId; + } + hasGlyphId(id) { + return this.cff.hasGlyphId(id); + } + _createBuiltInEncoding() { + const { + charset, + encoding + } = this.cff; + if (!charset || !encoding) { + return; + } + const charsets = charset.charset, + encodings = encoding.encoding; + const map = []; + for (const charCode in encodings) { + const glyphId = encodings[charCode]; + if (glyphId >= 0) { + const glyphName = charsets[glyphId]; + if (glyphName) { + map[charCode] = glyphName; + } + } + } + if (map.length > 0) { + this.properties.builtInEncoding = map; + } + } +} + +;// ./src/core/font_renderer.js + + + + + + +function getUint32(data, offset) { + return (data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3]) >>> 0; +} +function getUint16(data, offset) { + return data[offset] << 8 | data[offset + 1]; +} +function getInt16(data, offset) { + return (data[offset] << 24 | data[offset + 1] << 16) >> 16; +} +function getInt8(data, offset) { + return data[offset] << 24 >> 24; +} +function getFloat214(data, offset) { + return getInt16(data, offset) / 16384; +} +function getSubroutineBias(subrs) { + const numSubrs = subrs.length; + let bias = 32768; + if (numSubrs < 1240) { + bias = 107; + } else if (numSubrs < 33900) { + bias = 1131; + } + return bias; +} +function parseCmap(data, start, end) { + const offset = getUint16(data, start + 2) === 1 ? getUint32(data, start + 8) : getUint32(data, start + 16); + const format = getUint16(data, start + offset); + let ranges, p, i; + if (format === 4) { + getUint16(data, start + offset + 2); + const segCount = getUint16(data, start + offset + 6) >> 1; + p = start + offset + 14; + ranges = []; + for (i = 0; i < segCount; i++, p += 2) { + ranges[i] = { + end: getUint16(data, p) + }; + } + p += 2; + for (i = 0; i < segCount; i++, p += 2) { + ranges[i].start = getUint16(data, p); + } + for (i = 0; i < segCount; i++, p += 2) { + ranges[i].idDelta = getUint16(data, p); + } + for (i = 0; i < segCount; i++, p += 2) { + let idOffset = getUint16(data, p); + if (idOffset === 0) { + continue; + } + ranges[i].ids = []; + for (let j = 0, jj = ranges[i].end - ranges[i].start + 1; j < jj; j++) { + ranges[i].ids[j] = getUint16(data, p + idOffset); + idOffset += 2; + } + } + return ranges; + } else if (format === 12) { + const groups = getUint32(data, start + offset + 12); + p = start + offset + 16; + ranges = []; + for (i = 0; i < groups; i++) { + start = getUint32(data, p); + ranges.push({ + start, + end: getUint32(data, p + 4), + idDelta: getUint32(data, p + 8) - start + }); + p += 12; + } + return ranges; + } + throw new FormatError(`unsupported cmap: ${format}`); +} +function parseCff(data, start, end, seacAnalysisEnabled) { + const properties = {}; + const parser = new CFFParser(new Stream(data, start, end - start), properties, seacAnalysisEnabled); + const cff = parser.parse(); + return { + glyphs: cff.charStrings.objects, + subrs: cff.topDict.privateDict?.subrsIndex?.objects, + gsubrs: cff.globalSubrIndex?.objects, + isCFFCIDFont: cff.isCIDFont, + fdSelect: cff.fdSelect, + fdArray: cff.fdArray + }; +} +function parseGlyfTable(glyf, loca, isGlyphLocationsLong) { + let itemSize, itemDecode; + if (isGlyphLocationsLong) { + itemSize = 4; + itemDecode = getUint32; + } else { + itemSize = 2; + itemDecode = (data, offset) => 2 * getUint16(data, offset); + } + const glyphs = []; + let startOffset = itemDecode(loca, 0); + for (let j = itemSize; j < loca.length; j += itemSize) { + const endOffset = itemDecode(loca, j); + glyphs.push(glyf.subarray(startOffset, endOffset)); + startOffset = endOffset; + } + return glyphs; +} +function lookupCmap(ranges, unicode) { + const code = unicode.codePointAt(0); + let gid = 0, + l = 0, + r = ranges.length - 1; + while (l < r) { + const c = l + r + 1 >> 1; + if (code < ranges[c].start) { + r = c - 1; + } else { + l = c; + } + } + if (ranges[l].start <= code && code <= ranges[l].end) { + gid = ranges[l].idDelta + (ranges[l].ids ? ranges[l].ids[code - ranges[l].start] : code) & 0xffff; + } + return { + charCode: code, + glyphId: gid + }; +} +function compileGlyf(code, cmds, font) { + function moveTo(x, y) { + cmds.add("M", [x, y]); + } + function lineTo(x, y) { + cmds.add("L", [x, y]); + } + function quadraticCurveTo(xa, ya, x, y) { + cmds.add("Q", [xa, ya, x, y]); + } + let i = 0; + const numberOfContours = getInt16(code, i); + let flags; + let x = 0, + y = 0; + i += 10; + if (numberOfContours < 0) { + do { + flags = getUint16(code, i); + const glyphIndex = getUint16(code, i + 2); + i += 4; + let arg1, arg2; + if (flags & 0x01) { + if (flags & 0x02) { + arg1 = getInt16(code, i); + arg2 = getInt16(code, i + 2); + } else { + arg1 = getUint16(code, i); + arg2 = getUint16(code, i + 2); + } + i += 4; + } else if (flags & 0x02) { + arg1 = getInt8(code, i++); + arg2 = getInt8(code, i++); + } else { + arg1 = code[i++]; + arg2 = code[i++]; + } + if (flags & 0x02) { + x = arg1; + y = arg2; + } else { + x = 0; + y = 0; + } + let scaleX = 1, + scaleY = 1, + scale01 = 0, + scale10 = 0; + if (flags & 0x08) { + scaleX = scaleY = getFloat214(code, i); + i += 2; + } else if (flags & 0x40) { + scaleX = getFloat214(code, i); + scaleY = getFloat214(code, i + 2); + i += 4; + } else if (flags & 0x80) { + scaleX = getFloat214(code, i); + scale01 = getFloat214(code, i + 2); + scale10 = getFloat214(code, i + 4); + scaleY = getFloat214(code, i + 6); + i += 8; + } + const subglyph = font.glyphs[glyphIndex]; + if (subglyph) { + cmds.save(); + cmds.transform([scaleX, scale01, scale10, scaleY, x, y]); + if (!(flags & 0x02)) {} + compileGlyf(subglyph, cmds, font); + cmds.restore(); + } + } while (flags & 0x20); + } else { + const endPtsOfContours = []; + let j, jj; + for (j = 0; j < numberOfContours; j++) { + endPtsOfContours.push(getUint16(code, i)); + i += 2; + } + const instructionLength = getUint16(code, i); + i += 2 + instructionLength; + const numberOfPoints = endPtsOfContours.at(-1) + 1; + const points = []; + while (points.length < numberOfPoints) { + flags = code[i++]; + let repeat = 1; + if (flags & 0x08) { + repeat += code[i++]; + } + while (repeat-- > 0) { + points.push({ + flags + }); + } + } + for (j = 0; j < numberOfPoints; j++) { + switch (points[j].flags & 0x12) { + case 0x00: + x += getInt16(code, i); + i += 2; + break; + case 0x02: + x -= code[i++]; + break; + case 0x12: + x += code[i++]; + break; + } + points[j].x = x; + } + for (j = 0; j < numberOfPoints; j++) { + switch (points[j].flags & 0x24) { + case 0x00: + y += getInt16(code, i); + i += 2; + break; + case 0x04: + y -= code[i++]; + break; + case 0x24: + y += code[i++]; + break; + } + points[j].y = y; + } + let startPoint = 0; + for (i = 0; i < numberOfContours; i++) { + const endPoint = endPtsOfContours[i]; + const contour = points.slice(startPoint, endPoint + 1); + if (contour[0].flags & 1) { + contour.push(contour[0]); + } else if (contour.at(-1).flags & 1) { + contour.unshift(contour.at(-1)); + } else { + const p = { + flags: 1, + x: (contour[0].x + contour.at(-1).x) / 2, + y: (contour[0].y + contour.at(-1).y) / 2 + }; + contour.unshift(p); + contour.push(p); + } + moveTo(contour[0].x, contour[0].y); + for (j = 1, jj = contour.length; j < jj; j++) { + if (contour[j].flags & 1) { + lineTo(contour[j].x, contour[j].y); + } else if (contour[j + 1].flags & 1) { + quadraticCurveTo(contour[j].x, contour[j].y, contour[j + 1].x, contour[j + 1].y); + j++; + } else { + quadraticCurveTo(contour[j].x, contour[j].y, (contour[j].x + contour[j + 1].x) / 2, (contour[j].y + contour[j + 1].y) / 2); + } + } + startPoint = endPoint + 1; + } + } +} +function compileCharString(charStringCode, cmds, font, glyphId) { + function moveTo(x, y) { + cmds.add("M", [x, y]); + } + function lineTo(x, y) { + cmds.add("L", [x, y]); + } + function bezierCurveTo(x1, y1, x2, y2, x, y) { + cmds.add("C", [x1, y1, x2, y2, x, y]); + } + const stack = []; + let x = 0, + y = 0; + let stems = 0; + function parse(code) { + let i = 0; + while (i < code.length) { + let stackClean = false; + let v = code[i++]; + let xa, xb, ya, yb, y1, y2, y3, n, subrCode; + switch (v) { + case 1: + stems += stack.length >> 1; + stackClean = true; + break; + case 3: + stems += stack.length >> 1; + stackClean = true; + break; + case 4: + y += stack.pop(); + moveTo(x, y); + stackClean = true; + break; + case 5: + while (stack.length > 0) { + x += stack.shift(); + y += stack.shift(); + lineTo(x, y); + } + break; + case 6: + while (stack.length > 0) { + x += stack.shift(); + lineTo(x, y); + if (stack.length === 0) { + break; + } + y += stack.shift(); + lineTo(x, y); + } + break; + case 7: + while (stack.length > 0) { + y += stack.shift(); + lineTo(x, y); + if (stack.length === 0) { + break; + } + x += stack.shift(); + lineTo(x, y); + } + break; + case 8: + while (stack.length > 0) { + xa = x + stack.shift(); + ya = y + stack.shift(); + xb = xa + stack.shift(); + yb = ya + stack.shift(); + x = xb + stack.shift(); + y = yb + stack.shift(); + bezierCurveTo(xa, ya, xb, yb, x, y); + } + break; + case 10: + n = stack.pop(); + subrCode = null; + if (font.isCFFCIDFont) { + const fdIndex = font.fdSelect.getFDIndex(glyphId); + if (fdIndex >= 0 && fdIndex < font.fdArray.length) { + const fontDict = font.fdArray[fdIndex]; + let subrs; + if (fontDict.privateDict?.subrsIndex) { + subrs = fontDict.privateDict.subrsIndex.objects; + } + if (subrs) { + n += getSubroutineBias(subrs); + subrCode = subrs[n]; + } + } else { + warn("Invalid fd index for glyph index."); + } + } else { + subrCode = font.subrs[n + font.subrsBias]; + } + if (subrCode) { + parse(subrCode); + } + break; + case 11: + return; + case 12: + v = code[i++]; + switch (v) { + case 34: + xa = x + stack.shift(); + xb = xa + stack.shift(); + y1 = y + stack.shift(); + x = xb + stack.shift(); + bezierCurveTo(xa, y, xb, y1, x, y1); + xa = x + stack.shift(); + xb = xa + stack.shift(); + x = xb + stack.shift(); + bezierCurveTo(xa, y1, xb, y, x, y); + break; + case 35: + xa = x + stack.shift(); + ya = y + stack.shift(); + xb = xa + stack.shift(); + yb = ya + stack.shift(); + x = xb + stack.shift(); + y = yb + stack.shift(); + bezierCurveTo(xa, ya, xb, yb, x, y); + xa = x + stack.shift(); + ya = y + stack.shift(); + xb = xa + stack.shift(); + yb = ya + stack.shift(); + x = xb + stack.shift(); + y = yb + stack.shift(); + bezierCurveTo(xa, ya, xb, yb, x, y); + stack.pop(); + break; + case 36: + xa = x + stack.shift(); + y1 = y + stack.shift(); + xb = xa + stack.shift(); + y2 = y1 + stack.shift(); + x = xb + stack.shift(); + bezierCurveTo(xa, y1, xb, y2, x, y2); + xa = x + stack.shift(); + xb = xa + stack.shift(); + y3 = y2 + stack.shift(); + x = xb + stack.shift(); + bezierCurveTo(xa, y2, xb, y3, x, y); + break; + case 37: + const x0 = x, + y0 = y; + xa = x + stack.shift(); + ya = y + stack.shift(); + xb = xa + stack.shift(); + yb = ya + stack.shift(); + x = xb + stack.shift(); + y = yb + stack.shift(); + bezierCurveTo(xa, ya, xb, yb, x, y); + xa = x + stack.shift(); + ya = y + stack.shift(); + xb = xa + stack.shift(); + yb = ya + stack.shift(); + x = xb; + y = yb; + if (Math.abs(x - x0) > Math.abs(y - y0)) { + x += stack.shift(); + } else { + y += stack.shift(); + } + bezierCurveTo(xa, ya, xb, yb, x, y); + break; + default: + throw new FormatError(`unknown operator: 12 ${v}`); + } + break; + case 14: + if (stack.length >= 4) { + const achar = stack.pop(); + const bchar = stack.pop(); + y = stack.pop(); + x = stack.pop(); + cmds.save(); + cmds.translate(x, y); + let cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[StandardEncoding[achar]])); + compileCharString(font.glyphs[cmap.glyphId], cmds, font, cmap.glyphId); + cmds.restore(); + cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[StandardEncoding[bchar]])); + compileCharString(font.glyphs[cmap.glyphId], cmds, font, cmap.glyphId); + } + return; + case 18: + stems += stack.length >> 1; + stackClean = true; + break; + case 19: + stems += stack.length >> 1; + i += stems + 7 >> 3; + stackClean = true; + break; + case 20: + stems += stack.length >> 1; + i += stems + 7 >> 3; + stackClean = true; + break; + case 21: + y += stack.pop(); + x += stack.pop(); + moveTo(x, y); + stackClean = true; + break; + case 22: + x += stack.pop(); + moveTo(x, y); + stackClean = true; + break; + case 23: + stems += stack.length >> 1; + stackClean = true; + break; + case 24: + while (stack.length > 2) { + xa = x + stack.shift(); + ya = y + stack.shift(); + xb = xa + stack.shift(); + yb = ya + stack.shift(); + x = xb + stack.shift(); + y = yb + stack.shift(); + bezierCurveTo(xa, ya, xb, yb, x, y); + } + x += stack.shift(); + y += stack.shift(); + lineTo(x, y); + break; + case 25: + while (stack.length > 6) { + x += stack.shift(); + y += stack.shift(); + lineTo(x, y); + } + xa = x + stack.shift(); + ya = y + stack.shift(); + xb = xa + stack.shift(); + yb = ya + stack.shift(); + x = xb + stack.shift(); + y = yb + stack.shift(); + bezierCurveTo(xa, ya, xb, yb, x, y); + break; + case 26: + if (stack.length % 2) { + x += stack.shift(); + } + while (stack.length > 0) { + xa = x; + ya = y + stack.shift(); + xb = xa + stack.shift(); + yb = ya + stack.shift(); + x = xb; + y = yb + stack.shift(); + bezierCurveTo(xa, ya, xb, yb, x, y); + } + break; + case 27: + if (stack.length % 2) { + y += stack.shift(); + } + while (stack.length > 0) { + xa = x + stack.shift(); + ya = y; + xb = xa + stack.shift(); + yb = ya + stack.shift(); + x = xb + stack.shift(); + y = yb; + bezierCurveTo(xa, ya, xb, yb, x, y); + } + break; + case 28: + stack.push((code[i] << 24 | code[i + 1] << 16) >> 16); + i += 2; + break; + case 29: + n = stack.pop() + font.gsubrsBias; + subrCode = font.gsubrs[n]; + if (subrCode) { + parse(subrCode); + } + break; + case 30: + while (stack.length > 0) { + xa = x; + ya = y + stack.shift(); + xb = xa + stack.shift(); + yb = ya + stack.shift(); + x = xb + stack.shift(); + y = yb + (stack.length === 1 ? stack.shift() : 0); + bezierCurveTo(xa, ya, xb, yb, x, y); + if (stack.length === 0) { + break; + } + xa = x + stack.shift(); + ya = y; + xb = xa + stack.shift(); + yb = ya + stack.shift(); + y = yb + stack.shift(); + x = xb + (stack.length === 1 ? stack.shift() : 0); + bezierCurveTo(xa, ya, xb, yb, x, y); + } + break; + case 31: + while (stack.length > 0) { + xa = x + stack.shift(); + ya = y; + xb = xa + stack.shift(); + yb = ya + stack.shift(); + y = yb + stack.shift(); + x = xb + (stack.length === 1 ? stack.shift() : 0); + bezierCurveTo(xa, ya, xb, yb, x, y); + if (stack.length === 0) { + break; + } + xa = x; + ya = y + stack.shift(); + xb = xa + stack.shift(); + yb = ya + stack.shift(); + x = xb + stack.shift(); + y = yb + (stack.length === 1 ? stack.shift() : 0); + bezierCurveTo(xa, ya, xb, yb, x, y); + } + break; + default: + if (v < 32) { + throw new FormatError(`unknown operator: ${v}`); + } + if (v < 247) { + stack.push(v - 139); + } else if (v < 251) { + stack.push((v - 247) * 256 + code[i++] + 108); + } else if (v < 255) { + stack.push(-(v - 251) * 256 - code[i++] - 108); + } else { + stack.push((code[i] << 24 | code[i + 1] << 16 | code[i + 2] << 8 | code[i + 3]) / 65536); + i += 4; + } + break; + } + if (stackClean) { + stack.length = 0; + } + } + } + parse(charStringCode); +} +const NOOP = ""; +class Commands { + cmds = []; + transformStack = []; + currentTransform = [1, 0, 0, 1, 0, 0]; + add(cmd, args) { + if (args) { + const [a, b, c, d, e, f] = this.currentTransform; + for (let i = 0, ii = args.length; i < ii; i += 2) { + const x = args[i]; + const y = args[i + 1]; + args[i] = a * x + c * y + e; + args[i + 1] = b * x + d * y + f; + } + this.cmds.push(`${cmd}${args.join(" ")}`); + } else { + this.cmds.push(cmd); + } + } + transform(transf) { + this.currentTransform = Util.transform(this.currentTransform, transf); + } + translate(x, y) { + this.transform([1, 0, 0, 1, x, y]); + } + save() { + this.transformStack.push(this.currentTransform.slice()); + } + restore() { + this.currentTransform = this.transformStack.pop() || [1, 0, 0, 1, 0, 0]; + } + getSVG() { + return this.cmds.join(""); + } +} +class CompiledFont { + constructor(fontMatrix) { + this.fontMatrix = fontMatrix; + this.compiledGlyphs = Object.create(null); + this.compiledCharCodeToGlyphId = Object.create(null); + } + getPathJs(unicode) { + const { + charCode, + glyphId + } = lookupCmap(this.cmap, unicode); + let fn = this.compiledGlyphs[glyphId], + compileEx; + if (fn === undefined) { + try { + fn = this.compileGlyph(this.glyphs[glyphId], glyphId); + } catch (ex) { + fn = NOOP; + compileEx = ex; + } + this.compiledGlyphs[glyphId] = fn; + } + this.compiledCharCodeToGlyphId[charCode] ??= glyphId; + if (compileEx) { + throw compileEx; + } + return fn; + } + compileGlyph(code, glyphId) { + if (!code?.length || code[0] === 14) { + return NOOP; + } + let fontMatrix = this.fontMatrix; + if (this.isCFFCIDFont) { + const fdIndex = this.fdSelect.getFDIndex(glyphId); + if (fdIndex >= 0 && fdIndex < this.fdArray.length) { + const fontDict = this.fdArray[fdIndex]; + fontMatrix = fontDict.getByName("FontMatrix") || FONT_IDENTITY_MATRIX; + } else { + warn("Invalid fd index for glyph index."); + } + } + assert(isNumberArray(fontMatrix, 6), "Expected a valid fontMatrix."); + const cmds = new Commands(); + cmds.transform(fontMatrix.slice()); + this.compileGlyphImpl(code, cmds, glyphId); + cmds.add("Z"); + return cmds.getSVG(); + } + compileGlyphImpl() { + unreachable("Children classes should implement this."); + } + hasBuiltPath(unicode) { + const { + charCode, + glyphId + } = lookupCmap(this.cmap, unicode); + return this.compiledGlyphs[glyphId] !== undefined && this.compiledCharCodeToGlyphId[charCode] !== undefined; + } +} +class TrueTypeCompiled extends CompiledFont { + constructor(glyphs, cmap, fontMatrix) { + super(fontMatrix || [0.000488, 0, 0, 0.000488, 0, 0]); + this.glyphs = glyphs; + this.cmap = cmap; + } + compileGlyphImpl(code, cmds) { + compileGlyf(code, cmds, this); + } +} +class Type2Compiled extends CompiledFont { + constructor(cffInfo, cmap, fontMatrix) { + super(fontMatrix || [0.001, 0, 0, 0.001, 0, 0]); + this.glyphs = cffInfo.glyphs; + this.gsubrs = cffInfo.gsubrs || []; + this.subrs = cffInfo.subrs || []; + this.cmap = cmap; + this.glyphNameMap = getGlyphsUnicode(); + this.gsubrsBias = getSubroutineBias(this.gsubrs); + this.subrsBias = getSubroutineBias(this.subrs); + this.isCFFCIDFont = cffInfo.isCFFCIDFont; + this.fdSelect = cffInfo.fdSelect; + this.fdArray = cffInfo.fdArray; + } + compileGlyphImpl(code, cmds, glyphId) { + compileCharString(code, cmds, this, glyphId); + } +} +class FontRendererFactory { + static create(font, seacAnalysisEnabled) { + const data = new Uint8Array(font.data); + let cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm; + const numTables = getUint16(data, 4); + for (let i = 0, p = 12; i < numTables; i++, p += 16) { + const tag = bytesToString(data.subarray(p, p + 4)); + const offset = getUint32(data, p + 8); + const length = getUint32(data, p + 12); + switch (tag) { + case "cmap": + cmap = parseCmap(data, offset, offset + length); + break; + case "glyf": + glyf = data.subarray(offset, offset + length); + break; + case "loca": + loca = data.subarray(offset, offset + length); + break; + case "head": + unitsPerEm = getUint16(data, offset + 18); + indexToLocFormat = getUint16(data, offset + 50); + break; + case "CFF ": + cff = parseCff(data, offset, offset + length, seacAnalysisEnabled); + break; + } + } + if (glyf) { + const fontMatrix = !unitsPerEm ? font.fontMatrix : [1 / unitsPerEm, 0, 0, 1 / unitsPerEm, 0, 0]; + return new TrueTypeCompiled(parseGlyfTable(glyf, loca, indexToLocFormat), cmap, fontMatrix); + } + return new Type2Compiled(cff, cmap, font.fontMatrix); + } +} + +;// ./src/core/metrics.js + +const getMetrics = getLookupTableFactory(function (t) { + t.Courier = 600; + t["Courier-Bold"] = 600; + t["Courier-BoldOblique"] = 600; + t["Courier-Oblique"] = 600; + t.Helvetica = getLookupTableFactory(function (t) { + t.space = 278; + t.exclam = 278; + t.quotedbl = 355; + t.numbersign = 556; + t.dollar = 556; + t.percent = 889; + t.ampersand = 667; + t.quoteright = 222; + t.parenleft = 333; + t.parenright = 333; + t.asterisk = 389; + t.plus = 584; + t.comma = 278; + t.hyphen = 333; + t.period = 278; + t.slash = 278; + t.zero = 556; + t.one = 556; + t.two = 556; + t.three = 556; + t.four = 556; + t.five = 556; + t.six = 556; + t.seven = 556; + t.eight = 556; + t.nine = 556; + t.colon = 278; + t.semicolon = 278; + t.less = 584; + t.equal = 584; + t.greater = 584; + t.question = 556; + t.at = 1015; + t.A = 667; + t.B = 667; + t.C = 722; + t.D = 722; + t.E = 667; + t.F = 611; + t.G = 778; + t.H = 722; + t.I = 278; + t.J = 500; + t.K = 667; + t.L = 556; + t.M = 833; + t.N = 722; + t.O = 778; + t.P = 667; + t.Q = 778; + t.R = 722; + t.S = 667; + t.T = 611; + t.U = 722; + t.V = 667; + t.W = 944; + t.X = 667; + t.Y = 667; + t.Z = 611; + t.bracketleft = 278; + t.backslash = 278; + t.bracketright = 278; + t.asciicircum = 469; + t.underscore = 556; + t.quoteleft = 222; + t.a = 556; + t.b = 556; + t.c = 500; + t.d = 556; + t.e = 556; + t.f = 278; + t.g = 556; + t.h = 556; + t.i = 222; + t.j = 222; + t.k = 500; + t.l = 222; + t.m = 833; + t.n = 556; + t.o = 556; + t.p = 556; + t.q = 556; + t.r = 333; + t.s = 500; + t.t = 278; + t.u = 556; + t.v = 500; + t.w = 722; + t.x = 500; + t.y = 500; + t.z = 500; + t.braceleft = 334; + t.bar = 260; + t.braceright = 334; + t.asciitilde = 584; + t.exclamdown = 333; + t.cent = 556; + t.sterling = 556; + t.fraction = 167; + t.yen = 556; + t.florin = 556; + t.section = 556; + t.currency = 556; + t.quotesingle = 191; + t.quotedblleft = 333; + t.guillemotleft = 556; + t.guilsinglleft = 333; + t.guilsinglright = 333; + t.fi = 500; + t.fl = 500; + t.endash = 556; + t.dagger = 556; + t.daggerdbl = 556; + t.periodcentered = 278; + t.paragraph = 537; + t.bullet = 350; + t.quotesinglbase = 222; + t.quotedblbase = 333; + t.quotedblright = 333; + t.guillemotright = 556; + t.ellipsis = 1000; + t.perthousand = 1000; + t.questiondown = 611; + t.grave = 333; + t.acute = 333; + t.circumflex = 333; + t.tilde = 333; + t.macron = 333; + t.breve = 333; + t.dotaccent = 333; + t.dieresis = 333; + t.ring = 333; + t.cedilla = 333; + t.hungarumlaut = 333; + t.ogonek = 333; + t.caron = 333; + t.emdash = 1000; + t.AE = 1000; + t.ordfeminine = 370; + t.Lslash = 556; + t.Oslash = 778; + t.OE = 1000; + t.ordmasculine = 365; + t.ae = 889; + t.dotlessi = 278; + t.lslash = 222; + t.oslash = 611; + t.oe = 944; + t.germandbls = 611; + t.Idieresis = 278; + t.eacute = 556; + t.abreve = 556; + t.uhungarumlaut = 556; + t.ecaron = 556; + t.Ydieresis = 667; + t.divide = 584; + t.Yacute = 667; + t.Acircumflex = 667; + t.aacute = 556; + t.Ucircumflex = 722; + t.yacute = 500; + t.scommaaccent = 500; + t.ecircumflex = 556; + t.Uring = 722; + t.Udieresis = 722; + t.aogonek = 556; + t.Uacute = 722; + t.uogonek = 556; + t.Edieresis = 667; + t.Dcroat = 722; + t.commaaccent = 250; + t.copyright = 737; + t.Emacron = 667; + t.ccaron = 500; + t.aring = 556; + t.Ncommaaccent = 722; + t.lacute = 222; + t.agrave = 556; + t.Tcommaaccent = 611; + t.Cacute = 722; + t.atilde = 556; + t.Edotaccent = 667; + t.scaron = 500; + t.scedilla = 500; + t.iacute = 278; + t.lozenge = 471; + t.Rcaron = 722; + t.Gcommaaccent = 778; + t.ucircumflex = 556; + t.acircumflex = 556; + t.Amacron = 667; + t.rcaron = 333; + t.ccedilla = 500; + t.Zdotaccent = 611; + t.Thorn = 667; + t.Omacron = 778; + t.Racute = 722; + t.Sacute = 667; + t.dcaron = 643; + t.Umacron = 722; + t.uring = 556; + t.threesuperior = 333; + t.Ograve = 778; + t.Agrave = 667; + t.Abreve = 667; + t.multiply = 584; + t.uacute = 556; + t.Tcaron = 611; + t.partialdiff = 476; + t.ydieresis = 500; + t.Nacute = 722; + t.icircumflex = 278; + t.Ecircumflex = 667; + t.adieresis = 556; + t.edieresis = 556; + t.cacute = 500; + t.nacute = 556; + t.umacron = 556; + t.Ncaron = 722; + t.Iacute = 278; + t.plusminus = 584; + t.brokenbar = 260; + t.registered = 737; + t.Gbreve = 778; + t.Idotaccent = 278; + t.summation = 600; + t.Egrave = 667; + t.racute = 333; + t.omacron = 556; + t.Zacute = 611; + t.Zcaron = 611; + t.greaterequal = 549; + t.Eth = 722; + t.Ccedilla = 722; + t.lcommaaccent = 222; + t.tcaron = 317; + t.eogonek = 556; + t.Uogonek = 722; + t.Aacute = 667; + t.Adieresis = 667; + t.egrave = 556; + t.zacute = 500; + t.iogonek = 222; + t.Oacute = 778; + t.oacute = 556; + t.amacron = 556; + t.sacute = 500; + t.idieresis = 278; + t.Ocircumflex = 778; + t.Ugrave = 722; + t.Delta = 612; + t.thorn = 556; + t.twosuperior = 333; + t.Odieresis = 778; + t.mu = 556; + t.igrave = 278; + t.ohungarumlaut = 556; + t.Eogonek = 667; + t.dcroat = 556; + t.threequarters = 834; + t.Scedilla = 667; + t.lcaron = 299; + t.Kcommaaccent = 667; + t.Lacute = 556; + t.trademark = 1000; + t.edotaccent = 556; + t.Igrave = 278; + t.Imacron = 278; + t.Lcaron = 556; + t.onehalf = 834; + t.lessequal = 549; + t.ocircumflex = 556; + t.ntilde = 556; + t.Uhungarumlaut = 722; + t.Eacute = 667; + t.emacron = 556; + t.gbreve = 556; + t.onequarter = 834; + t.Scaron = 667; + t.Scommaaccent = 667; + t.Ohungarumlaut = 778; + t.degree = 400; + t.ograve = 556; + t.Ccaron = 722; + t.ugrave = 556; + t.radical = 453; + t.Dcaron = 722; + t.rcommaaccent = 333; + t.Ntilde = 722; + t.otilde = 556; + t.Rcommaaccent = 722; + t.Lcommaaccent = 556; + t.Atilde = 667; + t.Aogonek = 667; + t.Aring = 667; + t.Otilde = 778; + t.zdotaccent = 500; + t.Ecaron = 667; + t.Iogonek = 278; + t.kcommaaccent = 500; + t.minus = 584; + t.Icircumflex = 278; + t.ncaron = 556; + t.tcommaaccent = 278; + t.logicalnot = 584; + t.odieresis = 556; + t.udieresis = 556; + t.notequal = 549; + t.gcommaaccent = 556; + t.eth = 556; + t.zcaron = 500; + t.ncommaaccent = 556; + t.onesuperior = 333; + t.imacron = 278; + t.Euro = 556; + }); + t["Helvetica-Bold"] = getLookupTableFactory(function (t) { + t.space = 278; + t.exclam = 333; + t.quotedbl = 474; + t.numbersign = 556; + t.dollar = 556; + t.percent = 889; + t.ampersand = 722; + t.quoteright = 278; + t.parenleft = 333; + t.parenright = 333; + t.asterisk = 389; + t.plus = 584; + t.comma = 278; + t.hyphen = 333; + t.period = 278; + t.slash = 278; + t.zero = 556; + t.one = 556; + t.two = 556; + t.three = 556; + t.four = 556; + t.five = 556; + t.six = 556; + t.seven = 556; + t.eight = 556; + t.nine = 556; + t.colon = 333; + t.semicolon = 333; + t.less = 584; + t.equal = 584; + t.greater = 584; + t.question = 611; + t.at = 975; + t.A = 722; + t.B = 722; + t.C = 722; + t.D = 722; + t.E = 667; + t.F = 611; + t.G = 778; + t.H = 722; + t.I = 278; + t.J = 556; + t.K = 722; + t.L = 611; + t.M = 833; + t.N = 722; + t.O = 778; + t.P = 667; + t.Q = 778; + t.R = 722; + t.S = 667; + t.T = 611; + t.U = 722; + t.V = 667; + t.W = 944; + t.X = 667; + t.Y = 667; + t.Z = 611; + t.bracketleft = 333; + t.backslash = 278; + t.bracketright = 333; + t.asciicircum = 584; + t.underscore = 556; + t.quoteleft = 278; + t.a = 556; + t.b = 611; + t.c = 556; + t.d = 611; + t.e = 556; + t.f = 333; + t.g = 611; + t.h = 611; + t.i = 278; + t.j = 278; + t.k = 556; + t.l = 278; + t.m = 889; + t.n = 611; + t.o = 611; + t.p = 611; + t.q = 611; + t.r = 389; + t.s = 556; + t.t = 333; + t.u = 611; + t.v = 556; + t.w = 778; + t.x = 556; + t.y = 556; + t.z = 500; + t.braceleft = 389; + t.bar = 280; + t.braceright = 389; + t.asciitilde = 584; + t.exclamdown = 333; + t.cent = 556; + t.sterling = 556; + t.fraction = 167; + t.yen = 556; + t.florin = 556; + t.section = 556; + t.currency = 556; + t.quotesingle = 238; + t.quotedblleft = 500; + t.guillemotleft = 556; + t.guilsinglleft = 333; + t.guilsinglright = 333; + t.fi = 611; + t.fl = 611; + t.endash = 556; + t.dagger = 556; + t.daggerdbl = 556; + t.periodcentered = 278; + t.paragraph = 556; + t.bullet = 350; + t.quotesinglbase = 278; + t.quotedblbase = 500; + t.quotedblright = 500; + t.guillemotright = 556; + t.ellipsis = 1000; + t.perthousand = 1000; + t.questiondown = 611; + t.grave = 333; + t.acute = 333; + t.circumflex = 333; + t.tilde = 333; + t.macron = 333; + t.breve = 333; + t.dotaccent = 333; + t.dieresis = 333; + t.ring = 333; + t.cedilla = 333; + t.hungarumlaut = 333; + t.ogonek = 333; + t.caron = 333; + t.emdash = 1000; + t.AE = 1000; + t.ordfeminine = 370; + t.Lslash = 611; + t.Oslash = 778; + t.OE = 1000; + t.ordmasculine = 365; + t.ae = 889; + t.dotlessi = 278; + t.lslash = 278; + t.oslash = 611; + t.oe = 944; + t.germandbls = 611; + t.Idieresis = 278; + t.eacute = 556; + t.abreve = 556; + t.uhungarumlaut = 611; + t.ecaron = 556; + t.Ydieresis = 667; + t.divide = 584; + t.Yacute = 667; + t.Acircumflex = 722; + t.aacute = 556; + t.Ucircumflex = 722; + t.yacute = 556; + t.scommaaccent = 556; + t.ecircumflex = 556; + t.Uring = 722; + t.Udieresis = 722; + t.aogonek = 556; + t.Uacute = 722; + t.uogonek = 611; + t.Edieresis = 667; + t.Dcroat = 722; + t.commaaccent = 250; + t.copyright = 737; + t.Emacron = 667; + t.ccaron = 556; + t.aring = 556; + t.Ncommaaccent = 722; + t.lacute = 278; + t.agrave = 556; + t.Tcommaaccent = 611; + t.Cacute = 722; + t.atilde = 556; + t.Edotaccent = 667; + t.scaron = 556; + t.scedilla = 556; + t.iacute = 278; + t.lozenge = 494; + t.Rcaron = 722; + t.Gcommaaccent = 778; + t.ucircumflex = 611; + t.acircumflex = 556; + t.Amacron = 722; + t.rcaron = 389; + t.ccedilla = 556; + t.Zdotaccent = 611; + t.Thorn = 667; + t.Omacron = 778; + t.Racute = 722; + t.Sacute = 667; + t.dcaron = 743; + t.Umacron = 722; + t.uring = 611; + t.threesuperior = 333; + t.Ograve = 778; + t.Agrave = 722; + t.Abreve = 722; + t.multiply = 584; + t.uacute = 611; + t.Tcaron = 611; + t.partialdiff = 494; + t.ydieresis = 556; + t.Nacute = 722; + t.icircumflex = 278; + t.Ecircumflex = 667; + t.adieresis = 556; + t.edieresis = 556; + t.cacute = 556; + t.nacute = 611; + t.umacron = 611; + t.Ncaron = 722; + t.Iacute = 278; + t.plusminus = 584; + t.brokenbar = 280; + t.registered = 737; + t.Gbreve = 778; + t.Idotaccent = 278; + t.summation = 600; + t.Egrave = 667; + t.racute = 389; + t.omacron = 611; + t.Zacute = 611; + t.Zcaron = 611; + t.greaterequal = 549; + t.Eth = 722; + t.Ccedilla = 722; + t.lcommaaccent = 278; + t.tcaron = 389; + t.eogonek = 556; + t.Uogonek = 722; + t.Aacute = 722; + t.Adieresis = 722; + t.egrave = 556; + t.zacute = 500; + t.iogonek = 278; + t.Oacute = 778; + t.oacute = 611; + t.amacron = 556; + t.sacute = 556; + t.idieresis = 278; + t.Ocircumflex = 778; + t.Ugrave = 722; + t.Delta = 612; + t.thorn = 611; + t.twosuperior = 333; + t.Odieresis = 778; + t.mu = 611; + t.igrave = 278; + t.ohungarumlaut = 611; + t.Eogonek = 667; + t.dcroat = 611; + t.threequarters = 834; + t.Scedilla = 667; + t.lcaron = 400; + t.Kcommaaccent = 722; + t.Lacute = 611; + t.trademark = 1000; + t.edotaccent = 556; + t.Igrave = 278; + t.Imacron = 278; + t.Lcaron = 611; + t.onehalf = 834; + t.lessequal = 549; + t.ocircumflex = 611; + t.ntilde = 611; + t.Uhungarumlaut = 722; + t.Eacute = 667; + t.emacron = 556; + t.gbreve = 611; + t.onequarter = 834; + t.Scaron = 667; + t.Scommaaccent = 667; + t.Ohungarumlaut = 778; + t.degree = 400; + t.ograve = 611; + t.Ccaron = 722; + t.ugrave = 611; + t.radical = 549; + t.Dcaron = 722; + t.rcommaaccent = 389; + t.Ntilde = 722; + t.otilde = 611; + t.Rcommaaccent = 722; + t.Lcommaaccent = 611; + t.Atilde = 722; + t.Aogonek = 722; + t.Aring = 722; + t.Otilde = 778; + t.zdotaccent = 500; + t.Ecaron = 667; + t.Iogonek = 278; + t.kcommaaccent = 556; + t.minus = 584; + t.Icircumflex = 278; + t.ncaron = 611; + t.tcommaaccent = 333; + t.logicalnot = 584; + t.odieresis = 611; + t.udieresis = 611; + t.notequal = 549; + t.gcommaaccent = 611; + t.eth = 611; + t.zcaron = 500; + t.ncommaaccent = 611; + t.onesuperior = 333; + t.imacron = 278; + t.Euro = 556; + }); + t["Helvetica-BoldOblique"] = getLookupTableFactory(function (t) { + t.space = 278; + t.exclam = 333; + t.quotedbl = 474; + t.numbersign = 556; + t.dollar = 556; + t.percent = 889; + t.ampersand = 722; + t.quoteright = 278; + t.parenleft = 333; + t.parenright = 333; + t.asterisk = 389; + t.plus = 584; + t.comma = 278; + t.hyphen = 333; + t.period = 278; + t.slash = 278; + t.zero = 556; + t.one = 556; + t.two = 556; + t.three = 556; + t.four = 556; + t.five = 556; + t.six = 556; + t.seven = 556; + t.eight = 556; + t.nine = 556; + t.colon = 333; + t.semicolon = 333; + t.less = 584; + t.equal = 584; + t.greater = 584; + t.question = 611; + t.at = 975; + t.A = 722; + t.B = 722; + t.C = 722; + t.D = 722; + t.E = 667; + t.F = 611; + t.G = 778; + t.H = 722; + t.I = 278; + t.J = 556; + t.K = 722; + t.L = 611; + t.M = 833; + t.N = 722; + t.O = 778; + t.P = 667; + t.Q = 778; + t.R = 722; + t.S = 667; + t.T = 611; + t.U = 722; + t.V = 667; + t.W = 944; + t.X = 667; + t.Y = 667; + t.Z = 611; + t.bracketleft = 333; + t.backslash = 278; + t.bracketright = 333; + t.asciicircum = 584; + t.underscore = 556; + t.quoteleft = 278; + t.a = 556; + t.b = 611; + t.c = 556; + t.d = 611; + t.e = 556; + t.f = 333; + t.g = 611; + t.h = 611; + t.i = 278; + t.j = 278; + t.k = 556; + t.l = 278; + t.m = 889; + t.n = 611; + t.o = 611; + t.p = 611; + t.q = 611; + t.r = 389; + t.s = 556; + t.t = 333; + t.u = 611; + t.v = 556; + t.w = 778; + t.x = 556; + t.y = 556; + t.z = 500; + t.braceleft = 389; + t.bar = 280; + t.braceright = 389; + t.asciitilde = 584; + t.exclamdown = 333; + t.cent = 556; + t.sterling = 556; + t.fraction = 167; + t.yen = 556; + t.florin = 556; + t.section = 556; + t.currency = 556; + t.quotesingle = 238; + t.quotedblleft = 500; + t.guillemotleft = 556; + t.guilsinglleft = 333; + t.guilsinglright = 333; + t.fi = 611; + t.fl = 611; + t.endash = 556; + t.dagger = 556; + t.daggerdbl = 556; + t.periodcentered = 278; + t.paragraph = 556; + t.bullet = 350; + t.quotesinglbase = 278; + t.quotedblbase = 500; + t.quotedblright = 500; + t.guillemotright = 556; + t.ellipsis = 1000; + t.perthousand = 1000; + t.questiondown = 611; + t.grave = 333; + t.acute = 333; + t.circumflex = 333; + t.tilde = 333; + t.macron = 333; + t.breve = 333; + t.dotaccent = 333; + t.dieresis = 333; + t.ring = 333; + t.cedilla = 333; + t.hungarumlaut = 333; + t.ogonek = 333; + t.caron = 333; + t.emdash = 1000; + t.AE = 1000; + t.ordfeminine = 370; + t.Lslash = 611; + t.Oslash = 778; + t.OE = 1000; + t.ordmasculine = 365; + t.ae = 889; + t.dotlessi = 278; + t.lslash = 278; + t.oslash = 611; + t.oe = 944; + t.germandbls = 611; + t.Idieresis = 278; + t.eacute = 556; + t.abreve = 556; + t.uhungarumlaut = 611; + t.ecaron = 556; + t.Ydieresis = 667; + t.divide = 584; + t.Yacute = 667; + t.Acircumflex = 722; + t.aacute = 556; + t.Ucircumflex = 722; + t.yacute = 556; + t.scommaaccent = 556; + t.ecircumflex = 556; + t.Uring = 722; + t.Udieresis = 722; + t.aogonek = 556; + t.Uacute = 722; + t.uogonek = 611; + t.Edieresis = 667; + t.Dcroat = 722; + t.commaaccent = 250; + t.copyright = 737; + t.Emacron = 667; + t.ccaron = 556; + t.aring = 556; + t.Ncommaaccent = 722; + t.lacute = 278; + t.agrave = 556; + t.Tcommaaccent = 611; + t.Cacute = 722; + t.atilde = 556; + t.Edotaccent = 667; + t.scaron = 556; + t.scedilla = 556; + t.iacute = 278; + t.lozenge = 494; + t.Rcaron = 722; + t.Gcommaaccent = 778; + t.ucircumflex = 611; + t.acircumflex = 556; + t.Amacron = 722; + t.rcaron = 389; + t.ccedilla = 556; + t.Zdotaccent = 611; + t.Thorn = 667; + t.Omacron = 778; + t.Racute = 722; + t.Sacute = 667; + t.dcaron = 743; + t.Umacron = 722; + t.uring = 611; + t.threesuperior = 333; + t.Ograve = 778; + t.Agrave = 722; + t.Abreve = 722; + t.multiply = 584; + t.uacute = 611; + t.Tcaron = 611; + t.partialdiff = 494; + t.ydieresis = 556; + t.Nacute = 722; + t.icircumflex = 278; + t.Ecircumflex = 667; + t.adieresis = 556; + t.edieresis = 556; + t.cacute = 556; + t.nacute = 611; + t.umacron = 611; + t.Ncaron = 722; + t.Iacute = 278; + t.plusminus = 584; + t.brokenbar = 280; + t.registered = 737; + t.Gbreve = 778; + t.Idotaccent = 278; + t.summation = 600; + t.Egrave = 667; + t.racute = 389; + t.omacron = 611; + t.Zacute = 611; + t.Zcaron = 611; + t.greaterequal = 549; + t.Eth = 722; + t.Ccedilla = 722; + t.lcommaaccent = 278; + t.tcaron = 389; + t.eogonek = 556; + t.Uogonek = 722; + t.Aacute = 722; + t.Adieresis = 722; + t.egrave = 556; + t.zacute = 500; + t.iogonek = 278; + t.Oacute = 778; + t.oacute = 611; + t.amacron = 556; + t.sacute = 556; + t.idieresis = 278; + t.Ocircumflex = 778; + t.Ugrave = 722; + t.Delta = 612; + t.thorn = 611; + t.twosuperior = 333; + t.Odieresis = 778; + t.mu = 611; + t.igrave = 278; + t.ohungarumlaut = 611; + t.Eogonek = 667; + t.dcroat = 611; + t.threequarters = 834; + t.Scedilla = 667; + t.lcaron = 400; + t.Kcommaaccent = 722; + t.Lacute = 611; + t.trademark = 1000; + t.edotaccent = 556; + t.Igrave = 278; + t.Imacron = 278; + t.Lcaron = 611; + t.onehalf = 834; + t.lessequal = 549; + t.ocircumflex = 611; + t.ntilde = 611; + t.Uhungarumlaut = 722; + t.Eacute = 667; + t.emacron = 556; + t.gbreve = 611; + t.onequarter = 834; + t.Scaron = 667; + t.Scommaaccent = 667; + t.Ohungarumlaut = 778; + t.degree = 400; + t.ograve = 611; + t.Ccaron = 722; + t.ugrave = 611; + t.radical = 549; + t.Dcaron = 722; + t.rcommaaccent = 389; + t.Ntilde = 722; + t.otilde = 611; + t.Rcommaaccent = 722; + t.Lcommaaccent = 611; + t.Atilde = 722; + t.Aogonek = 722; + t.Aring = 722; + t.Otilde = 778; + t.zdotaccent = 500; + t.Ecaron = 667; + t.Iogonek = 278; + t.kcommaaccent = 556; + t.minus = 584; + t.Icircumflex = 278; + t.ncaron = 611; + t.tcommaaccent = 333; + t.logicalnot = 584; + t.odieresis = 611; + t.udieresis = 611; + t.notequal = 549; + t.gcommaaccent = 611; + t.eth = 611; + t.zcaron = 500; + t.ncommaaccent = 611; + t.onesuperior = 333; + t.imacron = 278; + t.Euro = 556; + }); + t["Helvetica-Oblique"] = getLookupTableFactory(function (t) { + t.space = 278; + t.exclam = 278; + t.quotedbl = 355; + t.numbersign = 556; + t.dollar = 556; + t.percent = 889; + t.ampersand = 667; + t.quoteright = 222; + t.parenleft = 333; + t.parenright = 333; + t.asterisk = 389; + t.plus = 584; + t.comma = 278; + t.hyphen = 333; + t.period = 278; + t.slash = 278; + t.zero = 556; + t.one = 556; + t.two = 556; + t.three = 556; + t.four = 556; + t.five = 556; + t.six = 556; + t.seven = 556; + t.eight = 556; + t.nine = 556; + t.colon = 278; + t.semicolon = 278; + t.less = 584; + t.equal = 584; + t.greater = 584; + t.question = 556; + t.at = 1015; + t.A = 667; + t.B = 667; + t.C = 722; + t.D = 722; + t.E = 667; + t.F = 611; + t.G = 778; + t.H = 722; + t.I = 278; + t.J = 500; + t.K = 667; + t.L = 556; + t.M = 833; + t.N = 722; + t.O = 778; + t.P = 667; + t.Q = 778; + t.R = 722; + t.S = 667; + t.T = 611; + t.U = 722; + t.V = 667; + t.W = 944; + t.X = 667; + t.Y = 667; + t.Z = 611; + t.bracketleft = 278; + t.backslash = 278; + t.bracketright = 278; + t.asciicircum = 469; + t.underscore = 556; + t.quoteleft = 222; + t.a = 556; + t.b = 556; + t.c = 500; + t.d = 556; + t.e = 556; + t.f = 278; + t.g = 556; + t.h = 556; + t.i = 222; + t.j = 222; + t.k = 500; + t.l = 222; + t.m = 833; + t.n = 556; + t.o = 556; + t.p = 556; + t.q = 556; + t.r = 333; + t.s = 500; + t.t = 278; + t.u = 556; + t.v = 500; + t.w = 722; + t.x = 500; + t.y = 500; + t.z = 500; + t.braceleft = 334; + t.bar = 260; + t.braceright = 334; + t.asciitilde = 584; + t.exclamdown = 333; + t.cent = 556; + t.sterling = 556; + t.fraction = 167; + t.yen = 556; + t.florin = 556; + t.section = 556; + t.currency = 556; + t.quotesingle = 191; + t.quotedblleft = 333; + t.guillemotleft = 556; + t.guilsinglleft = 333; + t.guilsinglright = 333; + t.fi = 500; + t.fl = 500; + t.endash = 556; + t.dagger = 556; + t.daggerdbl = 556; + t.periodcentered = 278; + t.paragraph = 537; + t.bullet = 350; + t.quotesinglbase = 222; + t.quotedblbase = 333; + t.quotedblright = 333; + t.guillemotright = 556; + t.ellipsis = 1000; + t.perthousand = 1000; + t.questiondown = 611; + t.grave = 333; + t.acute = 333; + t.circumflex = 333; + t.tilde = 333; + t.macron = 333; + t.breve = 333; + t.dotaccent = 333; + t.dieresis = 333; + t.ring = 333; + t.cedilla = 333; + t.hungarumlaut = 333; + t.ogonek = 333; + t.caron = 333; + t.emdash = 1000; + t.AE = 1000; + t.ordfeminine = 370; + t.Lslash = 556; + t.Oslash = 778; + t.OE = 1000; + t.ordmasculine = 365; + t.ae = 889; + t.dotlessi = 278; + t.lslash = 222; + t.oslash = 611; + t.oe = 944; + t.germandbls = 611; + t.Idieresis = 278; + t.eacute = 556; + t.abreve = 556; + t.uhungarumlaut = 556; + t.ecaron = 556; + t.Ydieresis = 667; + t.divide = 584; + t.Yacute = 667; + t.Acircumflex = 667; + t.aacute = 556; + t.Ucircumflex = 722; + t.yacute = 500; + t.scommaaccent = 500; + t.ecircumflex = 556; + t.Uring = 722; + t.Udieresis = 722; + t.aogonek = 556; + t.Uacute = 722; + t.uogonek = 556; + t.Edieresis = 667; + t.Dcroat = 722; + t.commaaccent = 250; + t.copyright = 737; + t.Emacron = 667; + t.ccaron = 500; + t.aring = 556; + t.Ncommaaccent = 722; + t.lacute = 222; + t.agrave = 556; + t.Tcommaaccent = 611; + t.Cacute = 722; + t.atilde = 556; + t.Edotaccent = 667; + t.scaron = 500; + t.scedilla = 500; + t.iacute = 278; + t.lozenge = 471; + t.Rcaron = 722; + t.Gcommaaccent = 778; + t.ucircumflex = 556; + t.acircumflex = 556; + t.Amacron = 667; + t.rcaron = 333; + t.ccedilla = 500; + t.Zdotaccent = 611; + t.Thorn = 667; + t.Omacron = 778; + t.Racute = 722; + t.Sacute = 667; + t.dcaron = 643; + t.Umacron = 722; + t.uring = 556; + t.threesuperior = 333; + t.Ograve = 778; + t.Agrave = 667; + t.Abreve = 667; + t.multiply = 584; + t.uacute = 556; + t.Tcaron = 611; + t.partialdiff = 476; + t.ydieresis = 500; + t.Nacute = 722; + t.icircumflex = 278; + t.Ecircumflex = 667; + t.adieresis = 556; + t.edieresis = 556; + t.cacute = 500; + t.nacute = 556; + t.umacron = 556; + t.Ncaron = 722; + t.Iacute = 278; + t.plusminus = 584; + t.brokenbar = 260; + t.registered = 737; + t.Gbreve = 778; + t.Idotaccent = 278; + t.summation = 600; + t.Egrave = 667; + t.racute = 333; + t.omacron = 556; + t.Zacute = 611; + t.Zcaron = 611; + t.greaterequal = 549; + t.Eth = 722; + t.Ccedilla = 722; + t.lcommaaccent = 222; + t.tcaron = 317; + t.eogonek = 556; + t.Uogonek = 722; + t.Aacute = 667; + t.Adieresis = 667; + t.egrave = 556; + t.zacute = 500; + t.iogonek = 222; + t.Oacute = 778; + t.oacute = 556; + t.amacron = 556; + t.sacute = 500; + t.idieresis = 278; + t.Ocircumflex = 778; + t.Ugrave = 722; + t.Delta = 612; + t.thorn = 556; + t.twosuperior = 333; + t.Odieresis = 778; + t.mu = 556; + t.igrave = 278; + t.ohungarumlaut = 556; + t.Eogonek = 667; + t.dcroat = 556; + t.threequarters = 834; + t.Scedilla = 667; + t.lcaron = 299; + t.Kcommaaccent = 667; + t.Lacute = 556; + t.trademark = 1000; + t.edotaccent = 556; + t.Igrave = 278; + t.Imacron = 278; + t.Lcaron = 556; + t.onehalf = 834; + t.lessequal = 549; + t.ocircumflex = 556; + t.ntilde = 556; + t.Uhungarumlaut = 722; + t.Eacute = 667; + t.emacron = 556; + t.gbreve = 556; + t.onequarter = 834; + t.Scaron = 667; + t.Scommaaccent = 667; + t.Ohungarumlaut = 778; + t.degree = 400; + t.ograve = 556; + t.Ccaron = 722; + t.ugrave = 556; + t.radical = 453; + t.Dcaron = 722; + t.rcommaaccent = 333; + t.Ntilde = 722; + t.otilde = 556; + t.Rcommaaccent = 722; + t.Lcommaaccent = 556; + t.Atilde = 667; + t.Aogonek = 667; + t.Aring = 667; + t.Otilde = 778; + t.zdotaccent = 500; + t.Ecaron = 667; + t.Iogonek = 278; + t.kcommaaccent = 500; + t.minus = 584; + t.Icircumflex = 278; + t.ncaron = 556; + t.tcommaaccent = 278; + t.logicalnot = 584; + t.odieresis = 556; + t.udieresis = 556; + t.notequal = 549; + t.gcommaaccent = 556; + t.eth = 556; + t.zcaron = 500; + t.ncommaaccent = 556; + t.onesuperior = 333; + t.imacron = 278; + t.Euro = 556; + }); + t.Symbol = getLookupTableFactory(function (t) { + t.space = 250; + t.exclam = 333; + t.universal = 713; + t.numbersign = 500; + t.existential = 549; + t.percent = 833; + t.ampersand = 778; + t.suchthat = 439; + t.parenleft = 333; + t.parenright = 333; + t.asteriskmath = 500; + t.plus = 549; + t.comma = 250; + t.minus = 549; + t.period = 250; + t.slash = 278; + t.zero = 500; + t.one = 500; + t.two = 500; + t.three = 500; + t.four = 500; + t.five = 500; + t.six = 500; + t.seven = 500; + t.eight = 500; + t.nine = 500; + t.colon = 278; + t.semicolon = 278; + t.less = 549; + t.equal = 549; + t.greater = 549; + t.question = 444; + t.congruent = 549; + t.Alpha = 722; + t.Beta = 667; + t.Chi = 722; + t.Delta = 612; + t.Epsilon = 611; + t.Phi = 763; + t.Gamma = 603; + t.Eta = 722; + t.Iota = 333; + t.theta1 = 631; + t.Kappa = 722; + t.Lambda = 686; + t.Mu = 889; + t.Nu = 722; + t.Omicron = 722; + t.Pi = 768; + t.Theta = 741; + t.Rho = 556; + t.Sigma = 592; + t.Tau = 611; + t.Upsilon = 690; + t.sigma1 = 439; + t.Omega = 768; + t.Xi = 645; + t.Psi = 795; + t.Zeta = 611; + t.bracketleft = 333; + t.therefore = 863; + t.bracketright = 333; + t.perpendicular = 658; + t.underscore = 500; + t.radicalex = 500; + t.alpha = 631; + t.beta = 549; + t.chi = 549; + t.delta = 494; + t.epsilon = 439; + t.phi = 521; + t.gamma = 411; + t.eta = 603; + t.iota = 329; + t.phi1 = 603; + t.kappa = 549; + t.lambda = 549; + t.mu = 576; + t.nu = 521; + t.omicron = 549; + t.pi = 549; + t.theta = 521; + t.rho = 549; + t.sigma = 603; + t.tau = 439; + t.upsilon = 576; + t.omega1 = 713; + t.omega = 686; + t.xi = 493; + t.psi = 686; + t.zeta = 494; + t.braceleft = 480; + t.bar = 200; + t.braceright = 480; + t.similar = 549; + t.Euro = 750; + t.Upsilon1 = 620; + t.minute = 247; + t.lessequal = 549; + t.fraction = 167; + t.infinity = 713; + t.florin = 500; + t.club = 753; + t.diamond = 753; + t.heart = 753; + t.spade = 753; + t.arrowboth = 1042; + t.arrowleft = 987; + t.arrowup = 603; + t.arrowright = 987; + t.arrowdown = 603; + t.degree = 400; + t.plusminus = 549; + t.second = 411; + t.greaterequal = 549; + t.multiply = 549; + t.proportional = 713; + t.partialdiff = 494; + t.bullet = 460; + t.divide = 549; + t.notequal = 549; + t.equivalence = 549; + t.approxequal = 549; + t.ellipsis = 1000; + t.arrowvertex = 603; + t.arrowhorizex = 1000; + t.carriagereturn = 658; + t.aleph = 823; + t.Ifraktur = 686; + t.Rfraktur = 795; + t.weierstrass = 987; + t.circlemultiply = 768; + t.circleplus = 768; + t.emptyset = 823; + t.intersection = 768; + t.union = 768; + t.propersuperset = 713; + t.reflexsuperset = 713; + t.notsubset = 713; + t.propersubset = 713; + t.reflexsubset = 713; + t.element = 713; + t.notelement = 713; + t.angle = 768; + t.gradient = 713; + t.registerserif = 790; + t.copyrightserif = 790; + t.trademarkserif = 890; + t.product = 823; + t.radical = 549; + t.dotmath = 250; + t.logicalnot = 713; + t.logicaland = 603; + t.logicalor = 603; + t.arrowdblboth = 1042; + t.arrowdblleft = 987; + t.arrowdblup = 603; + t.arrowdblright = 987; + t.arrowdbldown = 603; + t.lozenge = 494; + t.angleleft = 329; + t.registersans = 790; + t.copyrightsans = 790; + t.trademarksans = 786; + t.summation = 713; + t.parenlefttp = 384; + t.parenleftex = 384; + t.parenleftbt = 384; + t.bracketlefttp = 384; + t.bracketleftex = 384; + t.bracketleftbt = 384; + t.bracelefttp = 494; + t.braceleftmid = 494; + t.braceleftbt = 494; + t.braceex = 494; + t.angleright = 329; + t.integral = 274; + t.integraltp = 686; + t.integralex = 686; + t.integralbt = 686; + t.parenrighttp = 384; + t.parenrightex = 384; + t.parenrightbt = 384; + t.bracketrighttp = 384; + t.bracketrightex = 384; + t.bracketrightbt = 384; + t.bracerighttp = 494; + t.bracerightmid = 494; + t.bracerightbt = 494; + t.apple = 790; + }); + t["Times-Roman"] = getLookupTableFactory(function (t) { + t.space = 250; + t.exclam = 333; + t.quotedbl = 408; + t.numbersign = 500; + t.dollar = 500; + t.percent = 833; + t.ampersand = 778; + t.quoteright = 333; + t.parenleft = 333; + t.parenright = 333; + t.asterisk = 500; + t.plus = 564; + t.comma = 250; + t.hyphen = 333; + t.period = 250; + t.slash = 278; + t.zero = 500; + t.one = 500; + t.two = 500; + t.three = 500; + t.four = 500; + t.five = 500; + t.six = 500; + t.seven = 500; + t.eight = 500; + t.nine = 500; + t.colon = 278; + t.semicolon = 278; + t.less = 564; + t.equal = 564; + t.greater = 564; + t.question = 444; + t.at = 921; + t.A = 722; + t.B = 667; + t.C = 667; + t.D = 722; + t.E = 611; + t.F = 556; + t.G = 722; + t.H = 722; + t.I = 333; + t.J = 389; + t.K = 722; + t.L = 611; + t.M = 889; + t.N = 722; + t.O = 722; + t.P = 556; + t.Q = 722; + t.R = 667; + t.S = 556; + t.T = 611; + t.U = 722; + t.V = 722; + t.W = 944; + t.X = 722; + t.Y = 722; + t.Z = 611; + t.bracketleft = 333; + t.backslash = 278; + t.bracketright = 333; + t.asciicircum = 469; + t.underscore = 500; + t.quoteleft = 333; + t.a = 444; + t.b = 500; + t.c = 444; + t.d = 500; + t.e = 444; + t.f = 333; + t.g = 500; + t.h = 500; + t.i = 278; + t.j = 278; + t.k = 500; + t.l = 278; + t.m = 778; + t.n = 500; + t.o = 500; + t.p = 500; + t.q = 500; + t.r = 333; + t.s = 389; + t.t = 278; + t.u = 500; + t.v = 500; + t.w = 722; + t.x = 500; + t.y = 500; + t.z = 444; + t.braceleft = 480; + t.bar = 200; + t.braceright = 480; + t.asciitilde = 541; + t.exclamdown = 333; + t.cent = 500; + t.sterling = 500; + t.fraction = 167; + t.yen = 500; + t.florin = 500; + t.section = 500; + t.currency = 500; + t.quotesingle = 180; + t.quotedblleft = 444; + t.guillemotleft = 500; + t.guilsinglleft = 333; + t.guilsinglright = 333; + t.fi = 556; + t.fl = 556; + t.endash = 500; + t.dagger = 500; + t.daggerdbl = 500; + t.periodcentered = 250; + t.paragraph = 453; + t.bullet = 350; + t.quotesinglbase = 333; + t.quotedblbase = 444; + t.quotedblright = 444; + t.guillemotright = 500; + t.ellipsis = 1000; + t.perthousand = 1000; + t.questiondown = 444; + t.grave = 333; + t.acute = 333; + t.circumflex = 333; + t.tilde = 333; + t.macron = 333; + t.breve = 333; + t.dotaccent = 333; + t.dieresis = 333; + t.ring = 333; + t.cedilla = 333; + t.hungarumlaut = 333; + t.ogonek = 333; + t.caron = 333; + t.emdash = 1000; + t.AE = 889; + t.ordfeminine = 276; + t.Lslash = 611; + t.Oslash = 722; + t.OE = 889; + t.ordmasculine = 310; + t.ae = 667; + t.dotlessi = 278; + t.lslash = 278; + t.oslash = 500; + t.oe = 722; + t.germandbls = 500; + t.Idieresis = 333; + t.eacute = 444; + t.abreve = 444; + t.uhungarumlaut = 500; + t.ecaron = 444; + t.Ydieresis = 722; + t.divide = 564; + t.Yacute = 722; + t.Acircumflex = 722; + t.aacute = 444; + t.Ucircumflex = 722; + t.yacute = 500; + t.scommaaccent = 389; + t.ecircumflex = 444; + t.Uring = 722; + t.Udieresis = 722; + t.aogonek = 444; + t.Uacute = 722; + t.uogonek = 500; + t.Edieresis = 611; + t.Dcroat = 722; + t.commaaccent = 250; + t.copyright = 760; + t.Emacron = 611; + t.ccaron = 444; + t.aring = 444; + t.Ncommaaccent = 722; + t.lacute = 278; + t.agrave = 444; + t.Tcommaaccent = 611; + t.Cacute = 667; + t.atilde = 444; + t.Edotaccent = 611; + t.scaron = 389; + t.scedilla = 389; + t.iacute = 278; + t.lozenge = 471; + t.Rcaron = 667; + t.Gcommaaccent = 722; + t.ucircumflex = 500; + t.acircumflex = 444; + t.Amacron = 722; + t.rcaron = 333; + t.ccedilla = 444; + t.Zdotaccent = 611; + t.Thorn = 556; + t.Omacron = 722; + t.Racute = 667; + t.Sacute = 556; + t.dcaron = 588; + t.Umacron = 722; + t.uring = 500; + t.threesuperior = 300; + t.Ograve = 722; + t.Agrave = 722; + t.Abreve = 722; + t.multiply = 564; + t.uacute = 500; + t.Tcaron = 611; + t.partialdiff = 476; + t.ydieresis = 500; + t.Nacute = 722; + t.icircumflex = 278; + t.Ecircumflex = 611; + t.adieresis = 444; + t.edieresis = 444; + t.cacute = 444; + t.nacute = 500; + t.umacron = 500; + t.Ncaron = 722; + t.Iacute = 333; + t.plusminus = 564; + t.brokenbar = 200; + t.registered = 760; + t.Gbreve = 722; + t.Idotaccent = 333; + t.summation = 600; + t.Egrave = 611; + t.racute = 333; + t.omacron = 500; + t.Zacute = 611; + t.Zcaron = 611; + t.greaterequal = 549; + t.Eth = 722; + t.Ccedilla = 667; + t.lcommaaccent = 278; + t.tcaron = 326; + t.eogonek = 444; + t.Uogonek = 722; + t.Aacute = 722; + t.Adieresis = 722; + t.egrave = 444; + t.zacute = 444; + t.iogonek = 278; + t.Oacute = 722; + t.oacute = 500; + t.amacron = 444; + t.sacute = 389; + t.idieresis = 278; + t.Ocircumflex = 722; + t.Ugrave = 722; + t.Delta = 612; + t.thorn = 500; + t.twosuperior = 300; + t.Odieresis = 722; + t.mu = 500; + t.igrave = 278; + t.ohungarumlaut = 500; + t.Eogonek = 611; + t.dcroat = 500; + t.threequarters = 750; + t.Scedilla = 556; + t.lcaron = 344; + t.Kcommaaccent = 722; + t.Lacute = 611; + t.trademark = 980; + t.edotaccent = 444; + t.Igrave = 333; + t.Imacron = 333; + t.Lcaron = 611; + t.onehalf = 750; + t.lessequal = 549; + t.ocircumflex = 500; + t.ntilde = 500; + t.Uhungarumlaut = 722; + t.Eacute = 611; + t.emacron = 444; + t.gbreve = 500; + t.onequarter = 750; + t.Scaron = 556; + t.Scommaaccent = 556; + t.Ohungarumlaut = 722; + t.degree = 400; + t.ograve = 500; + t.Ccaron = 667; + t.ugrave = 500; + t.radical = 453; + t.Dcaron = 722; + t.rcommaaccent = 333; + t.Ntilde = 722; + t.otilde = 500; + t.Rcommaaccent = 667; + t.Lcommaaccent = 611; + t.Atilde = 722; + t.Aogonek = 722; + t.Aring = 722; + t.Otilde = 722; + t.zdotaccent = 444; + t.Ecaron = 611; + t.Iogonek = 333; + t.kcommaaccent = 500; + t.minus = 564; + t.Icircumflex = 333; + t.ncaron = 500; + t.tcommaaccent = 278; + t.logicalnot = 564; + t.odieresis = 500; + t.udieresis = 500; + t.notequal = 549; + t.gcommaaccent = 500; + t.eth = 500; + t.zcaron = 444; + t.ncommaaccent = 500; + t.onesuperior = 300; + t.imacron = 278; + t.Euro = 500; + }); + t["Times-Bold"] = getLookupTableFactory(function (t) { + t.space = 250; + t.exclam = 333; + t.quotedbl = 555; + t.numbersign = 500; + t.dollar = 500; + t.percent = 1000; + t.ampersand = 833; + t.quoteright = 333; + t.parenleft = 333; + t.parenright = 333; + t.asterisk = 500; + t.plus = 570; + t.comma = 250; + t.hyphen = 333; + t.period = 250; + t.slash = 278; + t.zero = 500; + t.one = 500; + t.two = 500; + t.three = 500; + t.four = 500; + t.five = 500; + t.six = 500; + t.seven = 500; + t.eight = 500; + t.nine = 500; + t.colon = 333; + t.semicolon = 333; + t.less = 570; + t.equal = 570; + t.greater = 570; + t.question = 500; + t.at = 930; + t.A = 722; + t.B = 667; + t.C = 722; + t.D = 722; + t.E = 667; + t.F = 611; + t.G = 778; + t.H = 778; + t.I = 389; + t.J = 500; + t.K = 778; + t.L = 667; + t.M = 944; + t.N = 722; + t.O = 778; + t.P = 611; + t.Q = 778; + t.R = 722; + t.S = 556; + t.T = 667; + t.U = 722; + t.V = 722; + t.W = 1000; + t.X = 722; + t.Y = 722; + t.Z = 667; + t.bracketleft = 333; + t.backslash = 278; + t.bracketright = 333; + t.asciicircum = 581; + t.underscore = 500; + t.quoteleft = 333; + t.a = 500; + t.b = 556; + t.c = 444; + t.d = 556; + t.e = 444; + t.f = 333; + t.g = 500; + t.h = 556; + t.i = 278; + t.j = 333; + t.k = 556; + t.l = 278; + t.m = 833; + t.n = 556; + t.o = 500; + t.p = 556; + t.q = 556; + t.r = 444; + t.s = 389; + t.t = 333; + t.u = 556; + t.v = 500; + t.w = 722; + t.x = 500; + t.y = 500; + t.z = 444; + t.braceleft = 394; + t.bar = 220; + t.braceright = 394; + t.asciitilde = 520; + t.exclamdown = 333; + t.cent = 500; + t.sterling = 500; + t.fraction = 167; + t.yen = 500; + t.florin = 500; + t.section = 500; + t.currency = 500; + t.quotesingle = 278; + t.quotedblleft = 500; + t.guillemotleft = 500; + t.guilsinglleft = 333; + t.guilsinglright = 333; + t.fi = 556; + t.fl = 556; + t.endash = 500; + t.dagger = 500; + t.daggerdbl = 500; + t.periodcentered = 250; + t.paragraph = 540; + t.bullet = 350; + t.quotesinglbase = 333; + t.quotedblbase = 500; + t.quotedblright = 500; + t.guillemotright = 500; + t.ellipsis = 1000; + t.perthousand = 1000; + t.questiondown = 500; + t.grave = 333; + t.acute = 333; + t.circumflex = 333; + t.tilde = 333; + t.macron = 333; + t.breve = 333; + t.dotaccent = 333; + t.dieresis = 333; + t.ring = 333; + t.cedilla = 333; + t.hungarumlaut = 333; + t.ogonek = 333; + t.caron = 333; + t.emdash = 1000; + t.AE = 1000; + t.ordfeminine = 300; + t.Lslash = 667; + t.Oslash = 778; + t.OE = 1000; + t.ordmasculine = 330; + t.ae = 722; + t.dotlessi = 278; + t.lslash = 278; + t.oslash = 500; + t.oe = 722; + t.germandbls = 556; + t.Idieresis = 389; + t.eacute = 444; + t.abreve = 500; + t.uhungarumlaut = 556; + t.ecaron = 444; + t.Ydieresis = 722; + t.divide = 570; + t.Yacute = 722; + t.Acircumflex = 722; + t.aacute = 500; + t.Ucircumflex = 722; + t.yacute = 500; + t.scommaaccent = 389; + t.ecircumflex = 444; + t.Uring = 722; + t.Udieresis = 722; + t.aogonek = 500; + t.Uacute = 722; + t.uogonek = 556; + t.Edieresis = 667; + t.Dcroat = 722; + t.commaaccent = 250; + t.copyright = 747; + t.Emacron = 667; + t.ccaron = 444; + t.aring = 500; + t.Ncommaaccent = 722; + t.lacute = 278; + t.agrave = 500; + t.Tcommaaccent = 667; + t.Cacute = 722; + t.atilde = 500; + t.Edotaccent = 667; + t.scaron = 389; + t.scedilla = 389; + t.iacute = 278; + t.lozenge = 494; + t.Rcaron = 722; + t.Gcommaaccent = 778; + t.ucircumflex = 556; + t.acircumflex = 500; + t.Amacron = 722; + t.rcaron = 444; + t.ccedilla = 444; + t.Zdotaccent = 667; + t.Thorn = 611; + t.Omacron = 778; + t.Racute = 722; + t.Sacute = 556; + t.dcaron = 672; + t.Umacron = 722; + t.uring = 556; + t.threesuperior = 300; + t.Ograve = 778; + t.Agrave = 722; + t.Abreve = 722; + t.multiply = 570; + t.uacute = 556; + t.Tcaron = 667; + t.partialdiff = 494; + t.ydieresis = 500; + t.Nacute = 722; + t.icircumflex = 278; + t.Ecircumflex = 667; + t.adieresis = 500; + t.edieresis = 444; + t.cacute = 444; + t.nacute = 556; + t.umacron = 556; + t.Ncaron = 722; + t.Iacute = 389; + t.plusminus = 570; + t.brokenbar = 220; + t.registered = 747; + t.Gbreve = 778; + t.Idotaccent = 389; + t.summation = 600; + t.Egrave = 667; + t.racute = 444; + t.omacron = 500; + t.Zacute = 667; + t.Zcaron = 667; + t.greaterequal = 549; + t.Eth = 722; + t.Ccedilla = 722; + t.lcommaaccent = 278; + t.tcaron = 416; + t.eogonek = 444; + t.Uogonek = 722; + t.Aacute = 722; + t.Adieresis = 722; + t.egrave = 444; + t.zacute = 444; + t.iogonek = 278; + t.Oacute = 778; + t.oacute = 500; + t.amacron = 500; + t.sacute = 389; + t.idieresis = 278; + t.Ocircumflex = 778; + t.Ugrave = 722; + t.Delta = 612; + t.thorn = 556; + t.twosuperior = 300; + t.Odieresis = 778; + t.mu = 556; + t.igrave = 278; + t.ohungarumlaut = 500; + t.Eogonek = 667; + t.dcroat = 556; + t.threequarters = 750; + t.Scedilla = 556; + t.lcaron = 394; + t.Kcommaaccent = 778; + t.Lacute = 667; + t.trademark = 1000; + t.edotaccent = 444; + t.Igrave = 389; + t.Imacron = 389; + t.Lcaron = 667; + t.onehalf = 750; + t.lessequal = 549; + t.ocircumflex = 500; + t.ntilde = 556; + t.Uhungarumlaut = 722; + t.Eacute = 667; + t.emacron = 444; + t.gbreve = 500; + t.onequarter = 750; + t.Scaron = 556; + t.Scommaaccent = 556; + t.Ohungarumlaut = 778; + t.degree = 400; + t.ograve = 500; + t.Ccaron = 722; + t.ugrave = 556; + t.radical = 549; + t.Dcaron = 722; + t.rcommaaccent = 444; + t.Ntilde = 722; + t.otilde = 500; + t.Rcommaaccent = 722; + t.Lcommaaccent = 667; + t.Atilde = 722; + t.Aogonek = 722; + t.Aring = 722; + t.Otilde = 778; + t.zdotaccent = 444; + t.Ecaron = 667; + t.Iogonek = 389; + t.kcommaaccent = 556; + t.minus = 570; + t.Icircumflex = 389; + t.ncaron = 556; + t.tcommaaccent = 333; + t.logicalnot = 570; + t.odieresis = 500; + t.udieresis = 556; + t.notequal = 549; + t.gcommaaccent = 500; + t.eth = 500; + t.zcaron = 444; + t.ncommaaccent = 556; + t.onesuperior = 300; + t.imacron = 278; + t.Euro = 500; + }); + t["Times-BoldItalic"] = getLookupTableFactory(function (t) { + t.space = 250; + t.exclam = 389; + t.quotedbl = 555; + t.numbersign = 500; + t.dollar = 500; + t.percent = 833; + t.ampersand = 778; + t.quoteright = 333; + t.parenleft = 333; + t.parenright = 333; + t.asterisk = 500; + t.plus = 570; + t.comma = 250; + t.hyphen = 333; + t.period = 250; + t.slash = 278; + t.zero = 500; + t.one = 500; + t.two = 500; + t.three = 500; + t.four = 500; + t.five = 500; + t.six = 500; + t.seven = 500; + t.eight = 500; + t.nine = 500; + t.colon = 333; + t.semicolon = 333; + t.less = 570; + t.equal = 570; + t.greater = 570; + t.question = 500; + t.at = 832; + t.A = 667; + t.B = 667; + t.C = 667; + t.D = 722; + t.E = 667; + t.F = 667; + t.G = 722; + t.H = 778; + t.I = 389; + t.J = 500; + t.K = 667; + t.L = 611; + t.M = 889; + t.N = 722; + t.O = 722; + t.P = 611; + t.Q = 722; + t.R = 667; + t.S = 556; + t.T = 611; + t.U = 722; + t.V = 667; + t.W = 889; + t.X = 667; + t.Y = 611; + t.Z = 611; + t.bracketleft = 333; + t.backslash = 278; + t.bracketright = 333; + t.asciicircum = 570; + t.underscore = 500; + t.quoteleft = 333; + t.a = 500; + t.b = 500; + t.c = 444; + t.d = 500; + t.e = 444; + t.f = 333; + t.g = 500; + t.h = 556; + t.i = 278; + t.j = 278; + t.k = 500; + t.l = 278; + t.m = 778; + t.n = 556; + t.o = 500; + t.p = 500; + t.q = 500; + t.r = 389; + t.s = 389; + t.t = 278; + t.u = 556; + t.v = 444; + t.w = 667; + t.x = 500; + t.y = 444; + t.z = 389; + t.braceleft = 348; + t.bar = 220; + t.braceright = 348; + t.asciitilde = 570; + t.exclamdown = 389; + t.cent = 500; + t.sterling = 500; + t.fraction = 167; + t.yen = 500; + t.florin = 500; + t.section = 500; + t.currency = 500; + t.quotesingle = 278; + t.quotedblleft = 500; + t.guillemotleft = 500; + t.guilsinglleft = 333; + t.guilsinglright = 333; + t.fi = 556; + t.fl = 556; + t.endash = 500; + t.dagger = 500; + t.daggerdbl = 500; + t.periodcentered = 250; + t.paragraph = 500; + t.bullet = 350; + t.quotesinglbase = 333; + t.quotedblbase = 500; + t.quotedblright = 500; + t.guillemotright = 500; + t.ellipsis = 1000; + t.perthousand = 1000; + t.questiondown = 500; + t.grave = 333; + t.acute = 333; + t.circumflex = 333; + t.tilde = 333; + t.macron = 333; + t.breve = 333; + t.dotaccent = 333; + t.dieresis = 333; + t.ring = 333; + t.cedilla = 333; + t.hungarumlaut = 333; + t.ogonek = 333; + t.caron = 333; + t.emdash = 1000; + t.AE = 944; + t.ordfeminine = 266; + t.Lslash = 611; + t.Oslash = 722; + t.OE = 944; + t.ordmasculine = 300; + t.ae = 722; + t.dotlessi = 278; + t.lslash = 278; + t.oslash = 500; + t.oe = 722; + t.germandbls = 500; + t.Idieresis = 389; + t.eacute = 444; + t.abreve = 500; + t.uhungarumlaut = 556; + t.ecaron = 444; + t.Ydieresis = 611; + t.divide = 570; + t.Yacute = 611; + t.Acircumflex = 667; + t.aacute = 500; + t.Ucircumflex = 722; + t.yacute = 444; + t.scommaaccent = 389; + t.ecircumflex = 444; + t.Uring = 722; + t.Udieresis = 722; + t.aogonek = 500; + t.Uacute = 722; + t.uogonek = 556; + t.Edieresis = 667; + t.Dcroat = 722; + t.commaaccent = 250; + t.copyright = 747; + t.Emacron = 667; + t.ccaron = 444; + t.aring = 500; + t.Ncommaaccent = 722; + t.lacute = 278; + t.agrave = 500; + t.Tcommaaccent = 611; + t.Cacute = 667; + t.atilde = 500; + t.Edotaccent = 667; + t.scaron = 389; + t.scedilla = 389; + t.iacute = 278; + t.lozenge = 494; + t.Rcaron = 667; + t.Gcommaaccent = 722; + t.ucircumflex = 556; + t.acircumflex = 500; + t.Amacron = 667; + t.rcaron = 389; + t.ccedilla = 444; + t.Zdotaccent = 611; + t.Thorn = 611; + t.Omacron = 722; + t.Racute = 667; + t.Sacute = 556; + t.dcaron = 608; + t.Umacron = 722; + t.uring = 556; + t.threesuperior = 300; + t.Ograve = 722; + t.Agrave = 667; + t.Abreve = 667; + t.multiply = 570; + t.uacute = 556; + t.Tcaron = 611; + t.partialdiff = 494; + t.ydieresis = 444; + t.Nacute = 722; + t.icircumflex = 278; + t.Ecircumflex = 667; + t.adieresis = 500; + t.edieresis = 444; + t.cacute = 444; + t.nacute = 556; + t.umacron = 556; + t.Ncaron = 722; + t.Iacute = 389; + t.plusminus = 570; + t.brokenbar = 220; + t.registered = 747; + t.Gbreve = 722; + t.Idotaccent = 389; + t.summation = 600; + t.Egrave = 667; + t.racute = 389; + t.omacron = 500; + t.Zacute = 611; + t.Zcaron = 611; + t.greaterequal = 549; + t.Eth = 722; + t.Ccedilla = 667; + t.lcommaaccent = 278; + t.tcaron = 366; + t.eogonek = 444; + t.Uogonek = 722; + t.Aacute = 667; + t.Adieresis = 667; + t.egrave = 444; + t.zacute = 389; + t.iogonek = 278; + t.Oacute = 722; + t.oacute = 500; + t.amacron = 500; + t.sacute = 389; + t.idieresis = 278; + t.Ocircumflex = 722; + t.Ugrave = 722; + t.Delta = 612; + t.thorn = 500; + t.twosuperior = 300; + t.Odieresis = 722; + t.mu = 576; + t.igrave = 278; + t.ohungarumlaut = 500; + t.Eogonek = 667; + t.dcroat = 500; + t.threequarters = 750; + t.Scedilla = 556; + t.lcaron = 382; + t.Kcommaaccent = 667; + t.Lacute = 611; + t.trademark = 1000; + t.edotaccent = 444; + t.Igrave = 389; + t.Imacron = 389; + t.Lcaron = 611; + t.onehalf = 750; + t.lessequal = 549; + t.ocircumflex = 500; + t.ntilde = 556; + t.Uhungarumlaut = 722; + t.Eacute = 667; + t.emacron = 444; + t.gbreve = 500; + t.onequarter = 750; + t.Scaron = 556; + t.Scommaaccent = 556; + t.Ohungarumlaut = 722; + t.degree = 400; + t.ograve = 500; + t.Ccaron = 667; + t.ugrave = 556; + t.radical = 549; + t.Dcaron = 722; + t.rcommaaccent = 389; + t.Ntilde = 722; + t.otilde = 500; + t.Rcommaaccent = 667; + t.Lcommaaccent = 611; + t.Atilde = 667; + t.Aogonek = 667; + t.Aring = 667; + t.Otilde = 722; + t.zdotaccent = 389; + t.Ecaron = 667; + t.Iogonek = 389; + t.kcommaaccent = 500; + t.minus = 606; + t.Icircumflex = 389; + t.ncaron = 556; + t.tcommaaccent = 278; + t.logicalnot = 606; + t.odieresis = 500; + t.udieresis = 556; + t.notequal = 549; + t.gcommaaccent = 500; + t.eth = 500; + t.zcaron = 389; + t.ncommaaccent = 556; + t.onesuperior = 300; + t.imacron = 278; + t.Euro = 500; + }); + t["Times-Italic"] = getLookupTableFactory(function (t) { + t.space = 250; + t.exclam = 333; + t.quotedbl = 420; + t.numbersign = 500; + t.dollar = 500; + t.percent = 833; + t.ampersand = 778; + t.quoteright = 333; + t.parenleft = 333; + t.parenright = 333; + t.asterisk = 500; + t.plus = 675; + t.comma = 250; + t.hyphen = 333; + t.period = 250; + t.slash = 278; + t.zero = 500; + t.one = 500; + t.two = 500; + t.three = 500; + t.four = 500; + t.five = 500; + t.six = 500; + t.seven = 500; + t.eight = 500; + t.nine = 500; + t.colon = 333; + t.semicolon = 333; + t.less = 675; + t.equal = 675; + t.greater = 675; + t.question = 500; + t.at = 920; + t.A = 611; + t.B = 611; + t.C = 667; + t.D = 722; + t.E = 611; + t.F = 611; + t.G = 722; + t.H = 722; + t.I = 333; + t.J = 444; + t.K = 667; + t.L = 556; + t.M = 833; + t.N = 667; + t.O = 722; + t.P = 611; + t.Q = 722; + t.R = 611; + t.S = 500; + t.T = 556; + t.U = 722; + t.V = 611; + t.W = 833; + t.X = 611; + t.Y = 556; + t.Z = 556; + t.bracketleft = 389; + t.backslash = 278; + t.bracketright = 389; + t.asciicircum = 422; + t.underscore = 500; + t.quoteleft = 333; + t.a = 500; + t.b = 500; + t.c = 444; + t.d = 500; + t.e = 444; + t.f = 278; + t.g = 500; + t.h = 500; + t.i = 278; + t.j = 278; + t.k = 444; + t.l = 278; + t.m = 722; + t.n = 500; + t.o = 500; + t.p = 500; + t.q = 500; + t.r = 389; + t.s = 389; + t.t = 278; + t.u = 500; + t.v = 444; + t.w = 667; + t.x = 444; + t.y = 444; + t.z = 389; + t.braceleft = 400; + t.bar = 275; + t.braceright = 400; + t.asciitilde = 541; + t.exclamdown = 389; + t.cent = 500; + t.sterling = 500; + t.fraction = 167; + t.yen = 500; + t.florin = 500; + t.section = 500; + t.currency = 500; + t.quotesingle = 214; + t.quotedblleft = 556; + t.guillemotleft = 500; + t.guilsinglleft = 333; + t.guilsinglright = 333; + t.fi = 500; + t.fl = 500; + t.endash = 500; + t.dagger = 500; + t.daggerdbl = 500; + t.periodcentered = 250; + t.paragraph = 523; + t.bullet = 350; + t.quotesinglbase = 333; + t.quotedblbase = 556; + t.quotedblright = 556; + t.guillemotright = 500; + t.ellipsis = 889; + t.perthousand = 1000; + t.questiondown = 500; + t.grave = 333; + t.acute = 333; + t.circumflex = 333; + t.tilde = 333; + t.macron = 333; + t.breve = 333; + t.dotaccent = 333; + t.dieresis = 333; + t.ring = 333; + t.cedilla = 333; + t.hungarumlaut = 333; + t.ogonek = 333; + t.caron = 333; + t.emdash = 889; + t.AE = 889; + t.ordfeminine = 276; + t.Lslash = 556; + t.Oslash = 722; + t.OE = 944; + t.ordmasculine = 310; + t.ae = 667; + t.dotlessi = 278; + t.lslash = 278; + t.oslash = 500; + t.oe = 667; + t.germandbls = 500; + t.Idieresis = 333; + t.eacute = 444; + t.abreve = 500; + t.uhungarumlaut = 500; + t.ecaron = 444; + t.Ydieresis = 556; + t.divide = 675; + t.Yacute = 556; + t.Acircumflex = 611; + t.aacute = 500; + t.Ucircumflex = 722; + t.yacute = 444; + t.scommaaccent = 389; + t.ecircumflex = 444; + t.Uring = 722; + t.Udieresis = 722; + t.aogonek = 500; + t.Uacute = 722; + t.uogonek = 500; + t.Edieresis = 611; + t.Dcroat = 722; + t.commaaccent = 250; + t.copyright = 760; + t.Emacron = 611; + t.ccaron = 444; + t.aring = 500; + t.Ncommaaccent = 667; + t.lacute = 278; + t.agrave = 500; + t.Tcommaaccent = 556; + t.Cacute = 667; + t.atilde = 500; + t.Edotaccent = 611; + t.scaron = 389; + t.scedilla = 389; + t.iacute = 278; + t.lozenge = 471; + t.Rcaron = 611; + t.Gcommaaccent = 722; + t.ucircumflex = 500; + t.acircumflex = 500; + t.Amacron = 611; + t.rcaron = 389; + t.ccedilla = 444; + t.Zdotaccent = 556; + t.Thorn = 611; + t.Omacron = 722; + t.Racute = 611; + t.Sacute = 500; + t.dcaron = 544; + t.Umacron = 722; + t.uring = 500; + t.threesuperior = 300; + t.Ograve = 722; + t.Agrave = 611; + t.Abreve = 611; + t.multiply = 675; + t.uacute = 500; + t.Tcaron = 556; + t.partialdiff = 476; + t.ydieresis = 444; + t.Nacute = 667; + t.icircumflex = 278; + t.Ecircumflex = 611; + t.adieresis = 500; + t.edieresis = 444; + t.cacute = 444; + t.nacute = 500; + t.umacron = 500; + t.Ncaron = 667; + t.Iacute = 333; + t.plusminus = 675; + t.brokenbar = 275; + t.registered = 760; + t.Gbreve = 722; + t.Idotaccent = 333; + t.summation = 600; + t.Egrave = 611; + t.racute = 389; + t.omacron = 500; + t.Zacute = 556; + t.Zcaron = 556; + t.greaterequal = 549; + t.Eth = 722; + t.Ccedilla = 667; + t.lcommaaccent = 278; + t.tcaron = 300; + t.eogonek = 444; + t.Uogonek = 722; + t.Aacute = 611; + t.Adieresis = 611; + t.egrave = 444; + t.zacute = 389; + t.iogonek = 278; + t.Oacute = 722; + t.oacute = 500; + t.amacron = 500; + t.sacute = 389; + t.idieresis = 278; + t.Ocircumflex = 722; + t.Ugrave = 722; + t.Delta = 612; + t.thorn = 500; + t.twosuperior = 300; + t.Odieresis = 722; + t.mu = 500; + t.igrave = 278; + t.ohungarumlaut = 500; + t.Eogonek = 611; + t.dcroat = 500; + t.threequarters = 750; + t.Scedilla = 500; + t.lcaron = 300; + t.Kcommaaccent = 667; + t.Lacute = 556; + t.trademark = 980; + t.edotaccent = 444; + t.Igrave = 333; + t.Imacron = 333; + t.Lcaron = 611; + t.onehalf = 750; + t.lessequal = 549; + t.ocircumflex = 500; + t.ntilde = 500; + t.Uhungarumlaut = 722; + t.Eacute = 611; + t.emacron = 444; + t.gbreve = 500; + t.onequarter = 750; + t.Scaron = 500; + t.Scommaaccent = 500; + t.Ohungarumlaut = 722; + t.degree = 400; + t.ograve = 500; + t.Ccaron = 667; + t.ugrave = 500; + t.radical = 453; + t.Dcaron = 722; + t.rcommaaccent = 389; + t.Ntilde = 667; + t.otilde = 500; + t.Rcommaaccent = 611; + t.Lcommaaccent = 556; + t.Atilde = 611; + t.Aogonek = 611; + t.Aring = 611; + t.Otilde = 722; + t.zdotaccent = 389; + t.Ecaron = 611; + t.Iogonek = 333; + t.kcommaaccent = 444; + t.minus = 675; + t.Icircumflex = 333; + t.ncaron = 500; + t.tcommaaccent = 278; + t.logicalnot = 675; + t.odieresis = 500; + t.udieresis = 500; + t.notequal = 549; + t.gcommaaccent = 500; + t.eth = 500; + t.zcaron = 389; + t.ncommaaccent = 500; + t.onesuperior = 300; + t.imacron = 278; + t.Euro = 500; + }); + t.ZapfDingbats = getLookupTableFactory(function (t) { + t.space = 278; + t.a1 = 974; + t.a2 = 961; + t.a202 = 974; + t.a3 = 980; + t.a4 = 719; + t.a5 = 789; + t.a119 = 790; + t.a118 = 791; + t.a117 = 690; + t.a11 = 960; + t.a12 = 939; + t.a13 = 549; + t.a14 = 855; + t.a15 = 911; + t.a16 = 933; + t.a105 = 911; + t.a17 = 945; + t.a18 = 974; + t.a19 = 755; + t.a20 = 846; + t.a21 = 762; + t.a22 = 761; + t.a23 = 571; + t.a24 = 677; + t.a25 = 763; + t.a26 = 760; + t.a27 = 759; + t.a28 = 754; + t.a6 = 494; + t.a7 = 552; + t.a8 = 537; + t.a9 = 577; + t.a10 = 692; + t.a29 = 786; + t.a30 = 788; + t.a31 = 788; + t.a32 = 790; + t.a33 = 793; + t.a34 = 794; + t.a35 = 816; + t.a36 = 823; + t.a37 = 789; + t.a38 = 841; + t.a39 = 823; + t.a40 = 833; + t.a41 = 816; + t.a42 = 831; + t.a43 = 923; + t.a44 = 744; + t.a45 = 723; + t.a46 = 749; + t.a47 = 790; + t.a48 = 792; + t.a49 = 695; + t.a50 = 776; + t.a51 = 768; + t.a52 = 792; + t.a53 = 759; + t.a54 = 707; + t.a55 = 708; + t.a56 = 682; + t.a57 = 701; + t.a58 = 826; + t.a59 = 815; + t.a60 = 789; + t.a61 = 789; + t.a62 = 707; + t.a63 = 687; + t.a64 = 696; + t.a65 = 689; + t.a66 = 786; + t.a67 = 787; + t.a68 = 713; + t.a69 = 791; + t.a70 = 785; + t.a71 = 791; + t.a72 = 873; + t.a73 = 761; + t.a74 = 762; + t.a203 = 762; + t.a75 = 759; + t.a204 = 759; + t.a76 = 892; + t.a77 = 892; + t.a78 = 788; + t.a79 = 784; + t.a81 = 438; + t.a82 = 138; + t.a83 = 277; + t.a84 = 415; + t.a97 = 392; + t.a98 = 392; + t.a99 = 668; + t.a100 = 668; + t.a89 = 390; + t.a90 = 390; + t.a93 = 317; + t.a94 = 317; + t.a91 = 276; + t.a92 = 276; + t.a205 = 509; + t.a85 = 509; + t.a206 = 410; + t.a86 = 410; + t.a87 = 234; + t.a88 = 234; + t.a95 = 334; + t.a96 = 334; + t.a101 = 732; + t.a102 = 544; + t.a103 = 544; + t.a104 = 910; + t.a106 = 667; + t.a107 = 760; + t.a108 = 760; + t.a112 = 776; + t.a111 = 595; + t.a110 = 694; + t.a109 = 626; + t.a120 = 788; + t.a121 = 788; + t.a122 = 788; + t.a123 = 788; + t.a124 = 788; + t.a125 = 788; + t.a126 = 788; + t.a127 = 788; + t.a128 = 788; + t.a129 = 788; + t.a130 = 788; + t.a131 = 788; + t.a132 = 788; + t.a133 = 788; + t.a134 = 788; + t.a135 = 788; + t.a136 = 788; + t.a137 = 788; + t.a138 = 788; + t.a139 = 788; + t.a140 = 788; + t.a141 = 788; + t.a142 = 788; + t.a143 = 788; + t.a144 = 788; + t.a145 = 788; + t.a146 = 788; + t.a147 = 788; + t.a148 = 788; + t.a149 = 788; + t.a150 = 788; + t.a151 = 788; + t.a152 = 788; + t.a153 = 788; + t.a154 = 788; + t.a155 = 788; + t.a156 = 788; + t.a157 = 788; + t.a158 = 788; + t.a159 = 788; + t.a160 = 894; + t.a161 = 838; + t.a163 = 1016; + t.a164 = 458; + t.a196 = 748; + t.a165 = 924; + t.a192 = 748; + t.a166 = 918; + t.a167 = 927; + t.a168 = 928; + t.a169 = 928; + t.a170 = 834; + t.a171 = 873; + t.a172 = 828; + t.a173 = 924; + t.a162 = 924; + t.a174 = 917; + t.a175 = 930; + t.a176 = 931; + t.a177 = 463; + t.a178 = 883; + t.a179 = 836; + t.a193 = 836; + t.a180 = 867; + t.a199 = 867; + t.a181 = 696; + t.a200 = 696; + t.a182 = 874; + t.a201 = 874; + t.a183 = 760; + t.a184 = 946; + t.a197 = 771; + t.a185 = 865; + t.a194 = 771; + t.a198 = 888; + t.a186 = 967; + t.a195 = 888; + t.a187 = 831; + t.a188 = 873; + t.a189 = 927; + t.a190 = 970; + t.a191 = 918; + }); +}); +const getFontBasicMetrics = getLookupTableFactory(function (t) { + t.Courier = { + ascent: 629, + descent: -157, + capHeight: 562, + xHeight: -426 + }; + t["Courier-Bold"] = { + ascent: 629, + descent: -157, + capHeight: 562, + xHeight: 439 + }; + t["Courier-Oblique"] = { + ascent: 629, + descent: -157, + capHeight: 562, + xHeight: 426 + }; + t["Courier-BoldOblique"] = { + ascent: 629, + descent: -157, + capHeight: 562, + xHeight: 426 + }; + t.Helvetica = { + ascent: 718, + descent: -207, + capHeight: 718, + xHeight: 523 + }; + t["Helvetica-Bold"] = { + ascent: 718, + descent: -207, + capHeight: 718, + xHeight: 532 + }; + t["Helvetica-Oblique"] = { + ascent: 718, + descent: -207, + capHeight: 718, + xHeight: 523 + }; + t["Helvetica-BoldOblique"] = { + ascent: 718, + descent: -207, + capHeight: 718, + xHeight: 532 + }; + t["Times-Roman"] = { + ascent: 683, + descent: -217, + capHeight: 662, + xHeight: 450 + }; + t["Times-Bold"] = { + ascent: 683, + descent: -217, + capHeight: 676, + xHeight: 461 + }; + t["Times-Italic"] = { + ascent: 683, + descent: -217, + capHeight: 653, + xHeight: 441 + }; + t["Times-BoldItalic"] = { + ascent: 683, + descent: -217, + capHeight: 669, + xHeight: 462 + }; + t.Symbol = { + ascent: Math.NaN, + descent: Math.NaN, + capHeight: Math.NaN, + xHeight: Math.NaN + }; + t.ZapfDingbats = { + ascent: Math.NaN, + descent: Math.NaN, + capHeight: Math.NaN, + xHeight: Math.NaN + }; +}); + +;// ./src/core/glyf.js +const ON_CURVE_POINT = 1 << 0; +const X_SHORT_VECTOR = 1 << 1; +const Y_SHORT_VECTOR = 1 << 2; +const REPEAT_FLAG = 1 << 3; +const X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 1 << 4; +const Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 1 << 5; +const OVERLAP_SIMPLE = 1 << 6; +const ARG_1_AND_2_ARE_WORDS = 1 << 0; +const ARGS_ARE_XY_VALUES = 1 << 1; +const WE_HAVE_A_SCALE = 1 << 3; +const MORE_COMPONENTS = 1 << 5; +const WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6; +const WE_HAVE_A_TWO_BY_TWO = 1 << 7; +const WE_HAVE_INSTRUCTIONS = 1 << 8; +class GlyfTable { + constructor({ + glyfTable, + isGlyphLocationsLong, + locaTable, + numGlyphs + }) { + this.glyphs = []; + const loca = new DataView(locaTable.buffer, locaTable.byteOffset, locaTable.byteLength); + const glyf = new DataView(glyfTable.buffer, glyfTable.byteOffset, glyfTable.byteLength); + const offsetSize = isGlyphLocationsLong ? 4 : 2; + let prev = isGlyphLocationsLong ? loca.getUint32(0) : 2 * loca.getUint16(0); + let pos = 0; + for (let i = 0; i < numGlyphs; i++) { + pos += offsetSize; + const next = isGlyphLocationsLong ? loca.getUint32(pos) : 2 * loca.getUint16(pos); + if (next === prev) { + this.glyphs.push(new Glyph({})); + continue; + } + const glyph = Glyph.parse(prev, glyf); + this.glyphs.push(glyph); + prev = next; + } + } + getSize() { + return this.glyphs.reduce((a, g) => { + const size = g.getSize(); + return a + (size + 3 & ~3); + }, 0); + } + write() { + const totalSize = this.getSize(); + const glyfTable = new DataView(new ArrayBuffer(totalSize)); + const isLocationLong = totalSize > 0x1fffe; + const offsetSize = isLocationLong ? 4 : 2; + const locaTable = new DataView(new ArrayBuffer((this.glyphs.length + 1) * offsetSize)); + if (isLocationLong) { + locaTable.setUint32(0, 0); + } else { + locaTable.setUint16(0, 0); + } + let pos = 0; + let locaIndex = 0; + for (const glyph of this.glyphs) { + pos += glyph.write(pos, glyfTable); + pos = pos + 3 & ~3; + locaIndex += offsetSize; + if (isLocationLong) { + locaTable.setUint32(locaIndex, pos); + } else { + locaTable.setUint16(locaIndex, pos >> 1); + } + } + return { + isLocationLong, + loca: new Uint8Array(locaTable.buffer), + glyf: new Uint8Array(glyfTable.buffer) + }; + } + scale(factors) { + for (let i = 0, ii = this.glyphs.length; i < ii; i++) { + this.glyphs[i].scale(factors[i]); + } + } +} +class Glyph { + constructor({ + header = null, + simple = null, + composites = null + }) { + this.header = header; + this.simple = simple; + this.composites = composites; + } + static parse(pos, glyf) { + const [read, header] = GlyphHeader.parse(pos, glyf); + pos += read; + if (header.numberOfContours < 0) { + const composites = []; + while (true) { + const [n, composite] = CompositeGlyph.parse(pos, glyf); + pos += n; + composites.push(composite); + if (!(composite.flags & MORE_COMPONENTS)) { + break; + } + } + return new Glyph({ + header, + composites + }); + } + const simple = SimpleGlyph.parse(pos, glyf, header.numberOfContours); + return new Glyph({ + header, + simple + }); + } + getSize() { + if (!this.header) { + return 0; + } + const size = this.simple ? this.simple.getSize() : this.composites.reduce((a, c) => a + c.getSize(), 0); + return this.header.getSize() + size; + } + write(pos, buf) { + if (!this.header) { + return 0; + } + const spos = pos; + pos += this.header.write(pos, buf); + if (this.simple) { + pos += this.simple.write(pos, buf); + } else { + for (const composite of this.composites) { + pos += composite.write(pos, buf); + } + } + return pos - spos; + } + scale(factor) { + if (!this.header) { + return; + } + const xMiddle = (this.header.xMin + this.header.xMax) / 2; + this.header.scale(xMiddle, factor); + if (this.simple) { + this.simple.scale(xMiddle, factor); + } else { + for (const composite of this.composites) { + composite.scale(xMiddle, factor); + } + } + } +} +class GlyphHeader { + constructor({ + numberOfContours, + xMin, + yMin, + xMax, + yMax + }) { + this.numberOfContours = numberOfContours; + this.xMin = xMin; + this.yMin = yMin; + this.xMax = xMax; + this.yMax = yMax; + } + static parse(pos, glyf) { + return [10, new GlyphHeader({ + numberOfContours: glyf.getInt16(pos), + xMin: glyf.getInt16(pos + 2), + yMin: glyf.getInt16(pos + 4), + xMax: glyf.getInt16(pos + 6), + yMax: glyf.getInt16(pos + 8) + })]; + } + getSize() { + return 10; + } + write(pos, buf) { + buf.setInt16(pos, this.numberOfContours); + buf.setInt16(pos + 2, this.xMin); + buf.setInt16(pos + 4, this.yMin); + buf.setInt16(pos + 6, this.xMax); + buf.setInt16(pos + 8, this.yMax); + return 10; + } + scale(x, factor) { + this.xMin = Math.round(x + (this.xMin - x) * factor); + this.xMax = Math.round(x + (this.xMax - x) * factor); + } +} +class Contour { + constructor({ + flags, + xCoordinates, + yCoordinates + }) { + this.xCoordinates = xCoordinates; + this.yCoordinates = yCoordinates; + this.flags = flags; + } +} +class SimpleGlyph { + constructor({ + contours, + instructions + }) { + this.contours = contours; + this.instructions = instructions; + } + static parse(pos, glyf, numberOfContours) { + const endPtsOfContours = []; + for (let i = 0; i < numberOfContours; i++) { + const endPt = glyf.getUint16(pos); + pos += 2; + endPtsOfContours.push(endPt); + } + const numberOfPt = endPtsOfContours[numberOfContours - 1] + 1; + const instructionLength = glyf.getUint16(pos); + pos += 2; + const instructions = new Uint8Array(glyf).slice(pos, pos + instructionLength); + pos += instructionLength; + const flags = []; + for (let i = 0; i < numberOfPt; pos++, i++) { + let flag = glyf.getUint8(pos); + flags.push(flag); + if (flag & REPEAT_FLAG) { + const count = glyf.getUint8(++pos); + flag ^= REPEAT_FLAG; + for (let m = 0; m < count; m++) { + flags.push(flag); + } + i += count; + } + } + const allXCoordinates = []; + let xCoordinates = []; + let yCoordinates = []; + let pointFlags = []; + const contours = []; + let endPtsOfContoursIndex = 0; + let lastCoordinate = 0; + for (let i = 0; i < numberOfPt; i++) { + const flag = flags[i]; + if (flag & X_SHORT_VECTOR) { + const x = glyf.getUint8(pos++); + lastCoordinate += flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR ? x : -x; + xCoordinates.push(lastCoordinate); + } else if (flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) { + xCoordinates.push(lastCoordinate); + } else { + lastCoordinate += glyf.getInt16(pos); + pos += 2; + xCoordinates.push(lastCoordinate); + } + if (endPtsOfContours[endPtsOfContoursIndex] === i) { + endPtsOfContoursIndex++; + allXCoordinates.push(xCoordinates); + xCoordinates = []; + } + } + lastCoordinate = 0; + endPtsOfContoursIndex = 0; + for (let i = 0; i < numberOfPt; i++) { + const flag = flags[i]; + if (flag & Y_SHORT_VECTOR) { + const y = glyf.getUint8(pos++); + lastCoordinate += flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR ? y : -y; + yCoordinates.push(lastCoordinate); + } else if (flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) { + yCoordinates.push(lastCoordinate); + } else { + lastCoordinate += glyf.getInt16(pos); + pos += 2; + yCoordinates.push(lastCoordinate); + } + pointFlags.push(flag & ON_CURVE_POINT | flag & OVERLAP_SIMPLE); + if (endPtsOfContours[endPtsOfContoursIndex] === i) { + xCoordinates = allXCoordinates[endPtsOfContoursIndex]; + endPtsOfContoursIndex++; + contours.push(new Contour({ + flags: pointFlags, + xCoordinates, + yCoordinates + })); + yCoordinates = []; + pointFlags = []; + } + } + return new SimpleGlyph({ + contours, + instructions + }); + } + getSize() { + let size = this.contours.length * 2 + 2 + this.instructions.length; + let lastX = 0; + let lastY = 0; + for (const contour of this.contours) { + size += contour.flags.length; + for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) { + const x = contour.xCoordinates[i]; + const y = contour.yCoordinates[i]; + let abs = Math.abs(x - lastX); + if (abs > 255) { + size += 2; + } else if (abs > 0) { + size += 1; + } + lastX = x; + abs = Math.abs(y - lastY); + if (abs > 255) { + size += 2; + } else if (abs > 0) { + size += 1; + } + lastY = y; + } + } + return size; + } + write(pos, buf) { + const spos = pos; + const xCoordinates = []; + const yCoordinates = []; + const flags = []; + let lastX = 0; + let lastY = 0; + for (const contour of this.contours) { + for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) { + let flag = contour.flags[i]; + const x = contour.xCoordinates[i]; + let delta = x - lastX; + if (delta === 0) { + flag |= X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR; + xCoordinates.push(0); + } else { + const abs = Math.abs(delta); + if (abs <= 255) { + flag |= delta >= 0 ? X_SHORT_VECTOR | X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR : X_SHORT_VECTOR; + xCoordinates.push(abs); + } else { + xCoordinates.push(delta); + } + } + lastX = x; + const y = contour.yCoordinates[i]; + delta = y - lastY; + if (delta === 0) { + flag |= Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR; + yCoordinates.push(0); + } else { + const abs = Math.abs(delta); + if (abs <= 255) { + flag |= delta >= 0 ? Y_SHORT_VECTOR | Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR : Y_SHORT_VECTOR; + yCoordinates.push(abs); + } else { + yCoordinates.push(delta); + } + } + lastY = y; + flags.push(flag); + } + buf.setUint16(pos, xCoordinates.length - 1); + pos += 2; + } + buf.setUint16(pos, this.instructions.length); + pos += 2; + if (this.instructions.length) { + new Uint8Array(buf.buffer, 0, buf.buffer.byteLength).set(this.instructions, pos); + pos += this.instructions.length; + } + for (const flag of flags) { + buf.setUint8(pos++, flag); + } + for (let i = 0, ii = xCoordinates.length; i < ii; i++) { + const x = xCoordinates[i]; + const flag = flags[i]; + if (flag & X_SHORT_VECTOR) { + buf.setUint8(pos++, x); + } else if (!(flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR)) { + buf.setInt16(pos, x); + pos += 2; + } + } + for (let i = 0, ii = yCoordinates.length; i < ii; i++) { + const y = yCoordinates[i]; + const flag = flags[i]; + if (flag & Y_SHORT_VECTOR) { + buf.setUint8(pos++, y); + } else if (!(flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR)) { + buf.setInt16(pos, y); + pos += 2; + } + } + return pos - spos; + } + scale(x, factor) { + for (const contour of this.contours) { + if (contour.xCoordinates.length === 0) { + continue; + } + for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) { + contour.xCoordinates[i] = Math.round(x + (contour.xCoordinates[i] - x) * factor); + } + } + } +} +class CompositeGlyph { + constructor({ + flags, + glyphIndex, + argument1, + argument2, + transf, + instructions + }) { + this.flags = flags; + this.glyphIndex = glyphIndex; + this.argument1 = argument1; + this.argument2 = argument2; + this.transf = transf; + this.instructions = instructions; + } + static parse(pos, glyf) { + const spos = pos; + const transf = []; + let flags = glyf.getUint16(pos); + const glyphIndex = glyf.getUint16(pos + 2); + pos += 4; + let argument1, argument2; + if (flags & ARG_1_AND_2_ARE_WORDS) { + if (flags & ARGS_ARE_XY_VALUES) { + argument1 = glyf.getInt16(pos); + argument2 = glyf.getInt16(pos + 2); + } else { + argument1 = glyf.getUint16(pos); + argument2 = glyf.getUint16(pos + 2); + } + pos += 4; + flags ^= ARG_1_AND_2_ARE_WORDS; + } else { + if (flags & ARGS_ARE_XY_VALUES) { + argument1 = glyf.getInt8(pos); + argument2 = glyf.getInt8(pos + 1); + } else { + argument1 = glyf.getUint8(pos); + argument2 = glyf.getUint8(pos + 1); + } + pos += 2; + } + if (flags & WE_HAVE_A_SCALE) { + transf.push(glyf.getUint16(pos)); + pos += 2; + } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { + transf.push(glyf.getUint16(pos), glyf.getUint16(pos + 2)); + pos += 4; + } else if (flags & WE_HAVE_A_TWO_BY_TWO) { + transf.push(glyf.getUint16(pos), glyf.getUint16(pos + 2), glyf.getUint16(pos + 4), glyf.getUint16(pos + 6)); + pos += 8; + } + let instructions = null; + if (flags & WE_HAVE_INSTRUCTIONS) { + const instructionLength = glyf.getUint16(pos); + pos += 2; + instructions = new Uint8Array(glyf).slice(pos, pos + instructionLength); + pos += instructionLength; + } + return [pos - spos, new CompositeGlyph({ + flags, + glyphIndex, + argument1, + argument2, + transf, + instructions + })]; + } + getSize() { + let size = 2 + 2 + this.transf.length * 2; + if (this.flags & WE_HAVE_INSTRUCTIONS) { + size += 2 + this.instructions.length; + } + size += 2; + if (this.flags & 2) { + if (!(this.argument1 >= -128 && this.argument1 <= 127 && this.argument2 >= -128 && this.argument2 <= 127)) { + size += 2; + } + } else if (!(this.argument1 >= 0 && this.argument1 <= 255 && this.argument2 >= 0 && this.argument2 <= 255)) { + size += 2; + } + return size; + } + write(pos, buf) { + const spos = pos; + if (this.flags & ARGS_ARE_XY_VALUES) { + if (!(this.argument1 >= -128 && this.argument1 <= 127 && this.argument2 >= -128 && this.argument2 <= 127)) { + this.flags |= ARG_1_AND_2_ARE_WORDS; + } + } else if (!(this.argument1 >= 0 && this.argument1 <= 255 && this.argument2 >= 0 && this.argument2 <= 255)) { + this.flags |= ARG_1_AND_2_ARE_WORDS; + } + buf.setUint16(pos, this.flags); + buf.setUint16(pos + 2, this.glyphIndex); + pos += 4; + if (this.flags & ARG_1_AND_2_ARE_WORDS) { + if (this.flags & ARGS_ARE_XY_VALUES) { + buf.setInt16(pos, this.argument1); + buf.setInt16(pos + 2, this.argument2); + } else { + buf.setUint16(pos, this.argument1); + buf.setUint16(pos + 2, this.argument2); + } + pos += 4; + } else { + buf.setUint8(pos, this.argument1); + buf.setUint8(pos + 1, this.argument2); + pos += 2; + } + if (this.flags & WE_HAVE_INSTRUCTIONS) { + buf.setUint16(pos, this.instructions.length); + pos += 2; + if (this.instructions.length) { + new Uint8Array(buf.buffer, 0, buf.buffer.byteLength).set(this.instructions, pos); + pos += this.instructions.length; + } + } + return pos - spos; + } + scale(x, factor) {} +} + +;// ./src/core/opentype_file_builder.js + + +function writeInt16(dest, offset, num) { + dest[offset] = num >> 8 & 0xff; + dest[offset + 1] = num & 0xff; +} +function writeInt32(dest, offset, num) { + dest[offset] = num >> 24 & 0xff; + dest[offset + 1] = num >> 16 & 0xff; + dest[offset + 2] = num >> 8 & 0xff; + dest[offset + 3] = num & 0xff; +} +function writeData(dest, offset, data) { + if (data instanceof Uint8Array) { + dest.set(data, offset); + } else if (typeof data === "string") { + for (let i = 0, ii = data.length; i < ii; i++) { + dest[offset++] = data.charCodeAt(i) & 0xff; + } + } else { + for (const num of data) { + dest[offset++] = num & 0xff; + } + } +} +const OTF_HEADER_SIZE = 12; +const OTF_TABLE_ENTRY_SIZE = 16; +class OpenTypeFileBuilder { + constructor(sfnt) { + this.sfnt = sfnt; + this.tables = Object.create(null); + } + static getSearchParams(entriesCount, entrySize) { + let maxPower2 = 1, + log2 = 0; + while ((maxPower2 ^ entriesCount) > maxPower2) { + maxPower2 <<= 1; + log2++; + } + const searchRange = maxPower2 * entrySize; + return { + range: searchRange, + entry: log2, + rangeShift: entrySize * entriesCount - searchRange + }; + } + toArray() { + let sfnt = this.sfnt; + const tables = this.tables; + const tablesNames = Object.keys(tables); + tablesNames.sort(); + const numTables = tablesNames.length; + let i, j, jj, table, tableName; + let offset = OTF_HEADER_SIZE + numTables * OTF_TABLE_ENTRY_SIZE; + const tableOffsets = [offset]; + for (i = 0; i < numTables; i++) { + table = tables[tablesNames[i]]; + const paddedLength = (table.length + 3 & ~3) >>> 0; + offset += paddedLength; + tableOffsets.push(offset); + } + const file = new Uint8Array(offset); + for (i = 0; i < numTables; i++) { + table = tables[tablesNames[i]]; + writeData(file, tableOffsets[i], table); + } + if (sfnt === "true") { + sfnt = string32(0x00010000); + } + file[0] = sfnt.charCodeAt(0) & 0xff; + file[1] = sfnt.charCodeAt(1) & 0xff; + file[2] = sfnt.charCodeAt(2) & 0xff; + file[3] = sfnt.charCodeAt(3) & 0xff; + writeInt16(file, 4, numTables); + const searchParams = OpenTypeFileBuilder.getSearchParams(numTables, 16); + writeInt16(file, 6, searchParams.range); + writeInt16(file, 8, searchParams.entry); + writeInt16(file, 10, searchParams.rangeShift); + offset = OTF_HEADER_SIZE; + for (i = 0; i < numTables; i++) { + tableName = tablesNames[i]; + file[offset] = tableName.charCodeAt(0) & 0xff; + file[offset + 1] = tableName.charCodeAt(1) & 0xff; + file[offset + 2] = tableName.charCodeAt(2) & 0xff; + file[offset + 3] = tableName.charCodeAt(3) & 0xff; + let checksum = 0; + for (j = tableOffsets[i], jj = tableOffsets[i + 1]; j < jj; j += 4) { + const quad = readUint32(file, j); + checksum = checksum + quad >>> 0; + } + writeInt32(file, offset + 4, checksum); + writeInt32(file, offset + 8, tableOffsets[i]); + writeInt32(file, offset + 12, tables[tableName].length); + offset += OTF_TABLE_ENTRY_SIZE; + } + return file; + } + addTable(tag, data) { + if (tag in this.tables) { + throw new Error("Table " + tag + " already exists"); + } + this.tables[tag] = data; + } +} + +;// ./src/core/type1_parser.js + + + + +const HINTING_ENABLED = false; +const COMMAND_MAP = { + hstem: [1], + vstem: [3], + vmoveto: [4], + rlineto: [5], + hlineto: [6], + vlineto: [7], + rrcurveto: [8], + callsubr: [10], + flex: [12, 35], + drop: [12, 18], + endchar: [14], + rmoveto: [21], + hmoveto: [22], + vhcurveto: [30], + hvcurveto: [31] +}; +class Type1CharString { + constructor() { + this.width = 0; + this.lsb = 0; + this.flexing = false; + this.output = []; + this.stack = []; + } + convert(encoded, subrs, seacAnalysisEnabled) { + const count = encoded.length; + let error = false; + let wx, sbx, subrNumber; + for (let i = 0; i < count; i++) { + let value = encoded[i]; + if (value < 32) { + if (value === 12) { + value = (value << 8) + encoded[++i]; + } + switch (value) { + case 1: + if (!HINTING_ENABLED) { + this.stack = []; + break; + } + error = this.executeCommand(2, COMMAND_MAP.hstem); + break; + case 3: + if (!HINTING_ENABLED) { + this.stack = []; + break; + } + error = this.executeCommand(2, COMMAND_MAP.vstem); + break; + case 4: + if (this.flexing) { + if (this.stack.length < 1) { + error = true; + break; + } + const dy = this.stack.pop(); + this.stack.push(0, dy); + break; + } + error = this.executeCommand(1, COMMAND_MAP.vmoveto); + break; + case 5: + error = this.executeCommand(2, COMMAND_MAP.rlineto); + break; + case 6: + error = this.executeCommand(1, COMMAND_MAP.hlineto); + break; + case 7: + error = this.executeCommand(1, COMMAND_MAP.vlineto); + break; + case 8: + error = this.executeCommand(6, COMMAND_MAP.rrcurveto); + break; + case 9: + this.stack = []; + break; + case 10: + if (this.stack.length < 1) { + error = true; + break; + } + subrNumber = this.stack.pop(); + if (!subrs[subrNumber]) { + error = true; + break; + } + error = this.convert(subrs[subrNumber], subrs, seacAnalysisEnabled); + break; + case 11: + return error; + case 13: + if (this.stack.length < 2) { + error = true; + break; + } + wx = this.stack.pop(); + sbx = this.stack.pop(); + this.lsb = sbx; + this.width = wx; + this.stack.push(wx, sbx); + error = this.executeCommand(2, COMMAND_MAP.hmoveto); + break; + case 14: + this.output.push(COMMAND_MAP.endchar[0]); + break; + case 21: + if (this.flexing) { + break; + } + error = this.executeCommand(2, COMMAND_MAP.rmoveto); + break; + case 22: + if (this.flexing) { + this.stack.push(0); + break; + } + error = this.executeCommand(1, COMMAND_MAP.hmoveto); + break; + case 30: + error = this.executeCommand(4, COMMAND_MAP.vhcurveto); + break; + case 31: + error = this.executeCommand(4, COMMAND_MAP.hvcurveto); + break; + case (12 << 8) + 0: + this.stack = []; + break; + case (12 << 8) + 1: + if (!HINTING_ENABLED) { + this.stack = []; + break; + } + error = this.executeCommand(2, COMMAND_MAP.vstem); + break; + case (12 << 8) + 2: + if (!HINTING_ENABLED) { + this.stack = []; + break; + } + error = this.executeCommand(2, COMMAND_MAP.hstem); + break; + case (12 << 8) + 6: + if (seacAnalysisEnabled) { + const asb = this.stack.at(-5); + this.seac = this.stack.splice(-4, 4); + this.seac[0] += this.lsb - asb; + error = this.executeCommand(0, COMMAND_MAP.endchar); + } else { + error = this.executeCommand(4, COMMAND_MAP.endchar); + } + break; + case (12 << 8) + 7: + if (this.stack.length < 4) { + error = true; + break; + } + this.stack.pop(); + wx = this.stack.pop(); + const sby = this.stack.pop(); + sbx = this.stack.pop(); + this.lsb = sbx; + this.width = wx; + this.stack.push(wx, sbx, sby); + error = this.executeCommand(3, COMMAND_MAP.rmoveto); + break; + case (12 << 8) + 12: + if (this.stack.length < 2) { + error = true; + break; + } + const num2 = this.stack.pop(); + const num1 = this.stack.pop(); + this.stack.push(num1 / num2); + break; + case (12 << 8) + 16: + if (this.stack.length < 2) { + error = true; + break; + } + subrNumber = this.stack.pop(); + const numArgs = this.stack.pop(); + if (subrNumber === 0 && numArgs === 3) { + const flexArgs = this.stack.splice(-17, 17); + this.stack.push(flexArgs[2] + flexArgs[0], flexArgs[3] + flexArgs[1], flexArgs[4], flexArgs[5], flexArgs[6], flexArgs[7], flexArgs[8], flexArgs[9], flexArgs[10], flexArgs[11], flexArgs[12], flexArgs[13], flexArgs[14]); + error = this.executeCommand(13, COMMAND_MAP.flex, true); + this.flexing = false; + this.stack.push(flexArgs[15], flexArgs[16]); + } else if (subrNumber === 1 && numArgs === 0) { + this.flexing = true; + } + break; + case (12 << 8) + 17: + break; + case (12 << 8) + 33: + this.stack = []; + break; + default: + warn('Unknown type 1 charstring command of "' + value + '"'); + break; + } + if (error) { + break; + } + continue; + } else if (value <= 246) { + value -= 139; + } else if (value <= 250) { + value = (value - 247) * 256 + encoded[++i] + 108; + } else if (value <= 254) { + value = -((value - 251) * 256) - encoded[++i] - 108; + } else { + value = (encoded[++i] & 0xff) << 24 | (encoded[++i] & 0xff) << 16 | (encoded[++i] & 0xff) << 8 | (encoded[++i] & 0xff) << 0; + } + this.stack.push(value); + } + return error; + } + executeCommand(howManyArgs, command, keepStack) { + const stackLength = this.stack.length; + if (howManyArgs > stackLength) { + return true; + } + const start = stackLength - howManyArgs; + for (let i = start; i < stackLength; i++) { + let value = this.stack[i]; + if (Number.isInteger(value)) { + this.output.push(28, value >> 8 & 0xff, value & 0xff); + } else { + value = 65536 * value | 0; + this.output.push(255, value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff); + } + } + this.output.push(...command); + if (keepStack) { + this.stack.splice(start, howManyArgs); + } else { + this.stack.length = 0; + } + return false; + } +} +const EEXEC_ENCRYPT_KEY = 55665; +const CHAR_STRS_ENCRYPT_KEY = 4330; +function isHexDigit(code) { + return code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102; +} +function decrypt(data, key, discardNumber) { + if (discardNumber >= data.length) { + return new Uint8Array(0); + } + const c1 = 52845, + c2 = 22719; + let r = key | 0, + i, + j; + for (i = 0; i < discardNumber; i++) { + r = (data[i] + r) * c1 + c2 & (1 << 16) - 1; + } + const count = data.length - discardNumber; + const decrypted = new Uint8Array(count); + for (i = discardNumber, j = 0; j < count; i++, j++) { + const value = data[i]; + decrypted[j] = value ^ r >> 8; + r = (value + r) * c1 + c2 & (1 << 16) - 1; + } + return decrypted; +} +function decryptAscii(data, key, discardNumber) { + const c1 = 52845, + c2 = 22719; + let r = key | 0; + const count = data.length, + maybeLength = count >>> 1; + const decrypted = new Uint8Array(maybeLength); + let i, j; + for (i = 0, j = 0; i < count; i++) { + const digit1 = data[i]; + if (!isHexDigit(digit1)) { + continue; + } + i++; + let digit2; + while (i < count && !isHexDigit(digit2 = data[i])) { + i++; + } + if (i < count) { + const value = parseInt(String.fromCharCode(digit1, digit2), 16); + decrypted[j++] = value ^ r >> 8; + r = (value + r) * c1 + c2 & (1 << 16) - 1; + } + } + return decrypted.slice(discardNumber, j); +} +function isSpecial(c) { + return c === 0x2f || c === 0x5b || c === 0x5d || c === 0x7b || c === 0x7d || c === 0x28 || c === 0x29; +} +class Type1Parser { + constructor(stream, encrypted, seacAnalysisEnabled) { + if (encrypted) { + const data = stream.getBytes(); + const isBinary = !((isHexDigit(data[0]) || isWhiteSpace(data[0])) && isHexDigit(data[1]) && isHexDigit(data[2]) && isHexDigit(data[3]) && isHexDigit(data[4]) && isHexDigit(data[5]) && isHexDigit(data[6]) && isHexDigit(data[7])); + stream = new Stream(isBinary ? decrypt(data, EEXEC_ENCRYPT_KEY, 4) : decryptAscii(data, EEXEC_ENCRYPT_KEY, 4)); + } + this.seacAnalysisEnabled = !!seacAnalysisEnabled; + this.stream = stream; + this.nextChar(); + } + readNumberArray() { + this.getToken(); + const array = []; + while (true) { + const token = this.getToken(); + if (token === null || token === "]" || token === "}") { + break; + } + array.push(parseFloat(token || 0)); + } + return array; + } + readNumber() { + const token = this.getToken(); + return parseFloat(token || 0); + } + readInt() { + const token = this.getToken(); + return parseInt(token || 0, 10) | 0; + } + readBoolean() { + const token = this.getToken(); + return token === "true" ? 1 : 0; + } + nextChar() { + return this.currentChar = this.stream.getByte(); + } + prevChar() { + this.stream.skip(-2); + return this.currentChar = this.stream.getByte(); + } + getToken() { + let comment = false; + let ch = this.currentChar; + while (true) { + if (ch === -1) { + return null; + } + if (comment) { + if (ch === 0x0a || ch === 0x0d) { + comment = false; + } + } else if (ch === 0x25) { + comment = true; + } else if (!isWhiteSpace(ch)) { + break; + } + ch = this.nextChar(); + } + if (isSpecial(ch)) { + this.nextChar(); + return String.fromCharCode(ch); + } + let token = ""; + do { + token += String.fromCharCode(ch); + ch = this.nextChar(); + } while (ch >= 0 && !isWhiteSpace(ch) && !isSpecial(ch)); + return token; + } + readCharStrings(bytes, lenIV) { + if (lenIV === -1) { + return bytes; + } + return decrypt(bytes, CHAR_STRS_ENCRYPT_KEY, lenIV); + } + extractFontProgram(properties) { + const stream = this.stream; + const subrs = [], + charstrings = []; + const privateData = Object.create(null); + privateData.lenIV = 4; + const program = { + subrs: [], + charstrings: [], + properties: { + privateData + } + }; + let token, length, data, lenIV; + while ((token = this.getToken()) !== null) { + if (token !== "/") { + continue; + } + token = this.getToken(); + switch (token) { + case "CharStrings": + this.getToken(); + this.getToken(); + this.getToken(); + this.getToken(); + while (true) { + token = this.getToken(); + if (token === null || token === "end") { + break; + } + if (token !== "/") { + continue; + } + const glyph = this.getToken(); + length = this.readInt(); + this.getToken(); + data = length > 0 ? stream.getBytes(length) : new Uint8Array(0); + lenIV = program.properties.privateData.lenIV; + const encoded = this.readCharStrings(data, lenIV); + this.nextChar(); + token = this.getToken(); + if (token === "noaccess") { + this.getToken(); + } else if (token === "/") { + this.prevChar(); + } + charstrings.push({ + glyph, + encoded + }); + } + break; + case "Subrs": + this.readInt(); + this.getToken(); + while (this.getToken() === "dup") { + const index = this.readInt(); + length = this.readInt(); + this.getToken(); + data = length > 0 ? stream.getBytes(length) : new Uint8Array(0); + lenIV = program.properties.privateData.lenIV; + const encoded = this.readCharStrings(data, lenIV); + this.nextChar(); + token = this.getToken(); + if (token === "noaccess") { + this.getToken(); + } + subrs[index] = encoded; + } + break; + case "BlueValues": + case "OtherBlues": + case "FamilyBlues": + case "FamilyOtherBlues": + const blueArray = this.readNumberArray(); + if (blueArray.length > 0 && blueArray.length % 2 === 0 && HINTING_ENABLED) { + program.properties.privateData[token] = blueArray; + } + break; + case "StemSnapH": + case "StemSnapV": + program.properties.privateData[token] = this.readNumberArray(); + break; + case "StdHW": + case "StdVW": + program.properties.privateData[token] = this.readNumberArray()[0]; + break; + case "BlueShift": + case "lenIV": + case "BlueFuzz": + case "BlueScale": + case "LanguageGroup": + program.properties.privateData[token] = this.readNumber(); + break; + case "ExpansionFactor": + program.properties.privateData[token] = this.readNumber() || 0.06; + break; + case "ForceBold": + program.properties.privateData[token] = this.readBoolean(); + break; + } + } + for (const { + encoded, + glyph + } of charstrings) { + const charString = new Type1CharString(); + const error = charString.convert(encoded, subrs, this.seacAnalysisEnabled); + let output = charString.output; + if (error) { + output = [14]; + } + const charStringObject = { + glyphName: glyph, + charstring: output, + width: charString.width, + lsb: charString.lsb, + seac: charString.seac + }; + if (glyph === ".notdef") { + program.charstrings.unshift(charStringObject); + } else { + program.charstrings.push(charStringObject); + } + if (properties.builtInEncoding) { + const index = properties.builtInEncoding.indexOf(glyph); + if (index > -1 && properties.widths[index] === undefined && index >= properties.firstChar && index <= properties.lastChar) { + properties.widths[index] = charString.width; + } + } + } + return program; + } + extractFontHeader(properties) { + let token; + while ((token = this.getToken()) !== null) { + if (token !== "/") { + continue; + } + token = this.getToken(); + switch (token) { + case "FontMatrix": + const matrix = this.readNumberArray(); + properties.fontMatrix = matrix; + break; + case "Encoding": + const encodingArg = this.getToken(); + let encoding; + if (!/^\d+$/.test(encodingArg)) { + encoding = getEncoding(encodingArg); + } else { + encoding = []; + const size = parseInt(encodingArg, 10) | 0; + this.getToken(); + for (let j = 0; j < size; j++) { + token = this.getToken(); + while (token !== "dup" && token !== "def") { + token = this.getToken(); + if (token === null) { + return; + } + } + if (token === "def") { + break; + } + const index = this.readInt(); + this.getToken(); + const glyph = this.getToken(); + encoding[index] = glyph; + this.getToken(); + } + } + properties.builtInEncoding = encoding; + break; + case "FontBBox": + const fontBBox = this.readNumberArray(); + properties.ascent = Math.max(fontBBox[3], fontBBox[1]); + properties.descent = Math.min(fontBBox[1], fontBBox[3]); + properties.ascentScaled = true; + break; + } + } + } +} + +;// ./src/core/type1_font.js + + + + + + +function findBlock(streamBytes, signature, startIndex) { + const streamBytesLength = streamBytes.length; + const signatureLength = signature.length; + const scanLength = streamBytesLength - signatureLength; + let i = startIndex, + found = false; + while (i < scanLength) { + let j = 0; + while (j < signatureLength && streamBytes[i + j] === signature[j]) { + j++; + } + if (j >= signatureLength) { + i += j; + while (i < streamBytesLength && isWhiteSpace(streamBytes[i])) { + i++; + } + found = true; + break; + } + i++; + } + return { + found, + length: i + }; +} +function getHeaderBlock(stream, suggestedLength) { + const EEXEC_SIGNATURE = [0x65, 0x65, 0x78, 0x65, 0x63]; + const streamStartPos = stream.pos; + let headerBytes, headerBytesLength, block; + try { + headerBytes = stream.getBytes(suggestedLength); + headerBytesLength = headerBytes.length; + } catch {} + if (headerBytesLength === suggestedLength) { + block = findBlock(headerBytes, EEXEC_SIGNATURE, suggestedLength - 2 * EEXEC_SIGNATURE.length); + if (block.found && block.length === suggestedLength) { + return { + stream: new Stream(headerBytes), + length: suggestedLength + }; + } + } + warn('Invalid "Length1" property in Type1 font -- trying to recover.'); + stream.pos = streamStartPos; + const SCAN_BLOCK_LENGTH = 2048; + let actualLength; + while (true) { + const scanBytes = stream.peekBytes(SCAN_BLOCK_LENGTH); + block = findBlock(scanBytes, EEXEC_SIGNATURE, 0); + if (block.length === 0) { + break; + } + stream.pos += block.length; + if (block.found) { + actualLength = stream.pos - streamStartPos; + break; + } + } + stream.pos = streamStartPos; + if (actualLength) { + return { + stream: new Stream(stream.getBytes(actualLength)), + length: actualLength + }; + } + warn('Unable to recover "Length1" property in Type1 font -- using as is.'); + return { + stream: new Stream(stream.getBytes(suggestedLength)), + length: suggestedLength + }; +} +function getEexecBlock(stream, suggestedLength) { + const eexecBytes = stream.getBytes(); + if (eexecBytes.length === 0) { + throw new FormatError("getEexecBlock - no font program found."); + } + return { + stream: new Stream(eexecBytes), + length: eexecBytes.length + }; +} +class Type1Font { + constructor(name, file, properties) { + const PFB_HEADER_SIZE = 6; + let headerBlockLength = properties.length1; + let eexecBlockLength = properties.length2; + let pfbHeader = file.peekBytes(PFB_HEADER_SIZE); + const pfbHeaderPresent = pfbHeader[0] === 0x80 && pfbHeader[1] === 0x01; + if (pfbHeaderPresent) { + file.skip(PFB_HEADER_SIZE); + headerBlockLength = pfbHeader[5] << 24 | pfbHeader[4] << 16 | pfbHeader[3] << 8 | pfbHeader[2]; + } + const headerBlock = getHeaderBlock(file, headerBlockLength); + const headerBlockParser = new Type1Parser(headerBlock.stream, false, SEAC_ANALYSIS_ENABLED); + headerBlockParser.extractFontHeader(properties); + if (pfbHeaderPresent) { + pfbHeader = file.getBytes(PFB_HEADER_SIZE); + eexecBlockLength = pfbHeader[5] << 24 | pfbHeader[4] << 16 | pfbHeader[3] << 8 | pfbHeader[2]; + } + const eexecBlock = getEexecBlock(file, eexecBlockLength); + const eexecBlockParser = new Type1Parser(eexecBlock.stream, true, SEAC_ANALYSIS_ENABLED); + const data = eexecBlockParser.extractFontProgram(properties); + for (const key in data.properties) { + properties[key] = data.properties[key]; + } + const charstrings = data.charstrings; + const type2Charstrings = this.getType2Charstrings(charstrings); + const subrs = this.getType2Subrs(data.subrs); + this.charstrings = charstrings; + this.data = this.wrap(name, type2Charstrings, this.charstrings, subrs, properties); + this.seacs = this.getSeacs(data.charstrings); + } + get numGlyphs() { + return this.charstrings.length + 1; + } + getCharset() { + const charset = [".notdef"]; + for (const { + glyphName + } of this.charstrings) { + charset.push(glyphName); + } + return charset; + } + getGlyphMapping(properties) { + const charstrings = this.charstrings; + if (properties.composite) { + const charCodeToGlyphId = Object.create(null); + for (let glyphId = 0, charstringsLen = charstrings.length; glyphId < charstringsLen; glyphId++) { + const charCode = properties.cMap.charCodeOf(glyphId); + charCodeToGlyphId[charCode] = glyphId + 1; + } + return charCodeToGlyphId; + } + const glyphNames = [".notdef"]; + let builtInEncoding, glyphId; + for (glyphId = 0; glyphId < charstrings.length; glyphId++) { + glyphNames.push(charstrings[glyphId].glyphName); + } + const encoding = properties.builtInEncoding; + if (encoding) { + builtInEncoding = Object.create(null); + for (const charCode in encoding) { + glyphId = glyphNames.indexOf(encoding[charCode]); + if (glyphId >= 0) { + builtInEncoding[charCode] = glyphId; + } + } + } + return type1FontGlyphMapping(properties, builtInEncoding, glyphNames); + } + hasGlyphId(id) { + if (id < 0 || id >= this.numGlyphs) { + return false; + } + if (id === 0) { + return true; + } + const glyph = this.charstrings[id - 1]; + return glyph.charstring.length > 0; + } + getSeacs(charstrings) { + const seacMap = []; + for (let i = 0, ii = charstrings.length; i < ii; i++) { + const charstring = charstrings[i]; + if (charstring.seac) { + seacMap[i + 1] = charstring.seac; + } + } + return seacMap; + } + getType2Charstrings(type1Charstrings) { + const type2Charstrings = []; + for (const type1Charstring of type1Charstrings) { + type2Charstrings.push(type1Charstring.charstring); + } + return type2Charstrings; + } + getType2Subrs(type1Subrs) { + let bias = 0; + const count = type1Subrs.length; + if (count < 1133) { + bias = 107; + } else if (count < 33769) { + bias = 1131; + } else { + bias = 32768; + } + const type2Subrs = []; + let i; + for (i = 0; i < bias; i++) { + type2Subrs.push([0x0b]); + } + for (i = 0; i < count; i++) { + type2Subrs.push(type1Subrs[i]); + } + return type2Subrs; + } + wrap(name, glyphs, charstrings, subrs, properties) { + const cff = new CFF(); + cff.header = new CFFHeader(1, 0, 4, 4); + cff.names = [name]; + const topDict = new CFFTopDict(); + topDict.setByName("version", 391); + topDict.setByName("Notice", 392); + topDict.setByName("FullName", 393); + topDict.setByName("FamilyName", 394); + topDict.setByName("Weight", 395); + topDict.setByName("Encoding", null); + topDict.setByName("FontMatrix", properties.fontMatrix); + topDict.setByName("FontBBox", properties.bbox); + topDict.setByName("charset", null); + topDict.setByName("CharStrings", null); + topDict.setByName("Private", null); + cff.topDict = topDict; + const strings = new CFFStrings(); + strings.add("Version 0.11"); + strings.add("See original notice"); + strings.add(name); + strings.add(name); + strings.add("Medium"); + cff.strings = strings; + cff.globalSubrIndex = new CFFIndex(); + const count = glyphs.length; + const charsetArray = [".notdef"]; + let i, ii; + for (i = 0; i < count; i++) { + const glyphName = charstrings[i].glyphName; + const index = CFFStandardStrings.indexOf(glyphName); + if (index === -1) { + strings.add(glyphName); + } + charsetArray.push(glyphName); + } + cff.charset = new CFFCharset(false, 0, charsetArray); + const charStringsIndex = new CFFIndex(); + charStringsIndex.add([0x8b, 0x0e]); + for (i = 0; i < count; i++) { + charStringsIndex.add(glyphs[i]); + } + cff.charStrings = charStringsIndex; + const privateDict = new CFFPrivateDict(); + privateDict.setByName("Subrs", null); + const fields = ["BlueValues", "OtherBlues", "FamilyBlues", "FamilyOtherBlues", "StemSnapH", "StemSnapV", "BlueShift", "BlueFuzz", "BlueScale", "LanguageGroup", "ExpansionFactor", "ForceBold", "StdHW", "StdVW"]; + for (i = 0, ii = fields.length; i < ii; i++) { + const field = fields[i]; + if (!(field in properties.privateData)) { + continue; + } + const value = properties.privateData[field]; + if (Array.isArray(value)) { + for (let j = value.length - 1; j > 0; j--) { + value[j] -= value[j - 1]; + } + } + privateDict.setByName(field, value); + } + cff.topDict.privateDict = privateDict; + const subrIndex = new CFFIndex(); + for (i = 0, ii = subrs.length; i < ii; i++) { + subrIndex.add(subrs[i]); + } + privateDict.subrsIndex = subrIndex; + const compiler = new CFFCompiler(cff); + return compiler.compile(); + } +} + +;// ./src/core/fonts.js + + + + + + + + + + + + + + + + + +const PRIVATE_USE_AREAS = [[0xe000, 0xf8ff], [0x100000, 0x10fffd]]; +const PDF_GLYPH_SPACE_UNITS = 1000; +const EXPORT_DATA_PROPERTIES = ["ascent", "bbox", "black", "bold", "charProcOperatorList", "composite", "cssFontInfo", "data", "defaultVMetrics", "defaultWidth", "descent", "fallbackName", "fontMatrix", "isInvalidPDFjsFont", "isType3Font", "italic", "loadedName", "mimetype", "missingFile", "name", "remeasure", "subtype", "systemFontInfo", "type", "vertical"]; +const EXPORT_DATA_EXTRA_PROPERTIES = ["cMap", "defaultEncoding", "differences", "isMonospace", "isSerifFont", "isSymbolicFont", "seacMap", "toFontChar", "toUnicode", "vmetrics", "widths"]; +function adjustWidths(properties) { + if (!properties.fontMatrix) { + return; + } + if (properties.fontMatrix[0] === FONT_IDENTITY_MATRIX[0]) { + return; + } + const scale = 0.001 / properties.fontMatrix[0]; + const glyphsWidths = properties.widths; + for (const glyph in glyphsWidths) { + glyphsWidths[glyph] *= scale; + } + properties.defaultWidth *= scale; +} +function adjustTrueTypeToUnicode(properties, isSymbolicFont, nameRecords) { + if (properties.isInternalFont) { + return; + } + if (properties.hasIncludedToUnicodeMap) { + return; + } + if (properties.hasEncoding) { + return; + } + if (properties.toUnicode instanceof IdentityToUnicodeMap) { + return; + } + if (!isSymbolicFont) { + return; + } + if (nameRecords.length === 0) { + return; + } + if (properties.defaultEncoding === WinAnsiEncoding) { + return; + } + for (const r of nameRecords) { + if (!isWinNameRecord(r)) { + return; + } + } + const encoding = WinAnsiEncoding; + const toUnicode = [], + glyphsUnicodeMap = getGlyphsUnicode(); + for (const charCode in encoding) { + const glyphName = encoding[charCode]; + if (glyphName === "") { + continue; + } + const unicode = glyphsUnicodeMap[glyphName]; + if (unicode === undefined) { + continue; + } + toUnicode[charCode] = String.fromCharCode(unicode); + } + if (toUnicode.length > 0) { + properties.toUnicode.amend(toUnicode); + } +} +function adjustType1ToUnicode(properties, builtInEncoding) { + if (properties.isInternalFont) { + return; + } + if (properties.hasIncludedToUnicodeMap) { + return; + } + if (builtInEncoding === properties.defaultEncoding) { + return; + } + if (properties.toUnicode instanceof IdentityToUnicodeMap) { + return; + } + const toUnicode = [], + glyphsUnicodeMap = getGlyphsUnicode(); + for (const charCode in builtInEncoding) { + if (properties.hasEncoding) { + if (properties.baseEncodingName || properties.differences[charCode] !== undefined) { + continue; + } + } + const glyphName = builtInEncoding[charCode]; + const unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap); + if (unicode !== -1) { + toUnicode[charCode] = String.fromCharCode(unicode); + } + } + if (toUnicode.length > 0) { + properties.toUnicode.amend(toUnicode); + } +} +function amendFallbackToUnicode(properties) { + if (!properties.fallbackToUnicode) { + return; + } + if (properties.toUnicode instanceof IdentityToUnicodeMap) { + return; + } + const toUnicode = []; + for (const charCode in properties.fallbackToUnicode) { + if (properties.toUnicode.has(charCode)) { + continue; + } + toUnicode[charCode] = properties.fallbackToUnicode[charCode]; + } + if (toUnicode.length > 0) { + properties.toUnicode.amend(toUnicode); + } +} +class fonts_Glyph { + constructor(originalCharCode, fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont) { + this.originalCharCode = originalCharCode; + this.fontChar = fontChar; + this.unicode = unicode; + this.accent = accent; + this.width = width; + this.vmetric = vmetric; + this.operatorListId = operatorListId; + this.isSpace = isSpace; + this.isInFont = isInFont; + } + get category() { + return shadow(this, "category", getCharUnicodeCategory(this.unicode), true); + } +} +function int16(b0, b1) { + return (b0 << 8) + b1; +} +function writeSignedInt16(bytes, index, value) { + bytes[index + 1] = value; + bytes[index] = value >>> 8; +} +function signedInt16(b0, b1) { + const value = (b0 << 8) + b1; + return value & 1 << 15 ? value - 0x10000 : value; +} +function writeUint32(bytes, index, value) { + bytes[index + 3] = value & 0xff; + bytes[index + 2] = value >>> 8; + bytes[index + 1] = value >>> 16; + bytes[index] = value >>> 24; +} +function int32(b0, b1, b2, b3) { + return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3; +} +function string16(value) { + return String.fromCharCode(value >> 8 & 0xff, value & 0xff); +} +function safeString16(value) { + if (value > 0x7fff) { + value = 0x7fff; + } else if (value < -0x8000) { + value = -0x8000; + } + return String.fromCharCode(value >> 8 & 0xff, value & 0xff); +} +function isTrueTypeFile(file) { + const header = file.peekBytes(4); + return readUint32(header, 0) === 0x00010000 || bytesToString(header) === "true"; +} +function isTrueTypeCollectionFile(file) { + const header = file.peekBytes(4); + return bytesToString(header) === "ttcf"; +} +function isOpenTypeFile(file) { + const header = file.peekBytes(4); + return bytesToString(header) === "OTTO"; +} +function isType1File(file) { + const header = file.peekBytes(2); + if (header[0] === 0x25 && header[1] === 0x21) { + return true; + } + if (header[0] === 0x80 && header[1] === 0x01) { + return true; + } + return false; +} +function isCFFFile(file) { + const header = file.peekBytes(4); + if (header[0] >= 1 && header[3] >= 1 && header[3] <= 4) { + return true; + } + return false; +} +function getFontFileType(file, { + type, + subtype, + composite +}) { + let fileType, fileSubtype; + if (isTrueTypeFile(file) || isTrueTypeCollectionFile(file)) { + fileType = composite ? "CIDFontType2" : "TrueType"; + } else if (isOpenTypeFile(file)) { + fileType = composite ? "CIDFontType2" : "OpenType"; + } else if (isType1File(file)) { + if (composite) { + fileType = "CIDFontType0"; + } else { + fileType = type === "MMType1" ? "MMType1" : "Type1"; + } + } else if (isCFFFile(file)) { + if (composite) { + fileType = "CIDFontType0"; + fileSubtype = "CIDFontType0C"; + } else { + fileType = type === "MMType1" ? "MMType1" : "Type1"; + fileSubtype = "Type1C"; + } + } else { + warn("getFontFileType: Unable to detect correct font file Type/Subtype."); + fileType = type; + fileSubtype = subtype; + } + return [fileType, fileSubtype]; +} +function applyStandardFontGlyphMap(map, glyphMap) { + for (const charCode in glyphMap) { + map[+charCode] = glyphMap[charCode]; + } +} +function buildToFontChar(encoding, glyphsUnicodeMap, differences) { + const toFontChar = []; + let unicode; + for (let i = 0, ii = encoding.length; i < ii; i++) { + unicode = getUnicodeForGlyph(encoding[i], glyphsUnicodeMap); + if (unicode !== -1) { + toFontChar[i] = unicode; + } + } + for (const charCode in differences) { + unicode = getUnicodeForGlyph(differences[charCode], glyphsUnicodeMap); + if (unicode !== -1) { + toFontChar[+charCode] = unicode; + } + } + return toFontChar; +} +function isMacNameRecord(r) { + return r.platform === 1 && r.encoding === 0 && r.language === 0; +} +function isWinNameRecord(r) { + return r.platform === 3 && r.encoding === 1 && r.language === 0x409; +} +function convertCidString(charCode, cid, shouldThrow = false) { + switch (cid.length) { + case 1: + return cid.charCodeAt(0); + case 2: + return cid.charCodeAt(0) << 8 | cid.charCodeAt(1); + } + const msg = `Unsupported CID string (charCode ${charCode}): "${cid}".`; + if (shouldThrow) { + throw new FormatError(msg); + } + warn(msg); + return cid; +} +function adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId, toUnicode) { + const newMap = Object.create(null); + const toUnicodeExtraMap = new Map(); + const toFontChar = []; + const usedGlyphIds = new Set(); + let privateUseAreaIndex = 0; + const privateUseOffetStart = PRIVATE_USE_AREAS[privateUseAreaIndex][0]; + let nextAvailableFontCharCode = privateUseOffetStart; + let privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1]; + const isInPrivateArea = code => PRIVATE_USE_AREAS[0][0] <= code && code <= PRIVATE_USE_AREAS[0][1] || PRIVATE_USE_AREAS[1][0] <= code && code <= PRIVATE_USE_AREAS[1][1]; + for (const originalCharCode in charCodeToGlyphId) { + let glyphId = charCodeToGlyphId[originalCharCode]; + if (!hasGlyph(glyphId)) { + continue; + } + if (nextAvailableFontCharCode > privateUseOffetEnd) { + privateUseAreaIndex++; + if (privateUseAreaIndex >= PRIVATE_USE_AREAS.length) { + warn("Ran out of space in font private use area."); + break; + } + nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0]; + privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1]; + } + const fontCharCode = nextAvailableFontCharCode++; + if (glyphId === 0) { + glyphId = newGlyphZeroId; + } + let unicode = toUnicode.get(originalCharCode); + if (typeof unicode === "string") { + unicode = unicode.codePointAt(0); + } + if (unicode && !isInPrivateArea(unicode) && !usedGlyphIds.has(glyphId)) { + toUnicodeExtraMap.set(unicode, glyphId); + usedGlyphIds.add(glyphId); + } + newMap[fontCharCode] = glyphId; + toFontChar[originalCharCode] = fontCharCode; + } + return { + toFontChar, + charCodeToGlyphId: newMap, + toUnicodeExtraMap, + nextAvailableFontCharCode + }; +} +function getRanges(glyphs, toUnicodeExtraMap, numGlyphs) { + const codes = []; + for (const charCode in glyphs) { + if (glyphs[charCode] >= numGlyphs) { + continue; + } + codes.push({ + fontCharCode: charCode | 0, + glyphId: glyphs[charCode] + }); + } + if (toUnicodeExtraMap) { + for (const [unicode, glyphId] of toUnicodeExtraMap) { + if (glyphId >= numGlyphs) { + continue; + } + codes.push({ + fontCharCode: unicode, + glyphId + }); + } + } + if (codes.length === 0) { + codes.push({ + fontCharCode: 0, + glyphId: 0 + }); + } + codes.sort(function fontGetRangesSort(a, b) { + return a.fontCharCode - b.fontCharCode; + }); + const ranges = []; + const length = codes.length; + for (let n = 0; n < length;) { + const start = codes[n].fontCharCode; + const codeIndices = [codes[n].glyphId]; + ++n; + let end = start; + while (n < length && end + 1 === codes[n].fontCharCode) { + codeIndices.push(codes[n].glyphId); + ++end; + ++n; + if (end === 0xffff) { + break; + } + } + ranges.push([start, end, codeIndices]); + } + return ranges; +} +function createCmapTable(glyphs, toUnicodeExtraMap, numGlyphs) { + const ranges = getRanges(glyphs, toUnicodeExtraMap, numGlyphs); + const numTables = ranges.at(-1)[1] > 0xffff ? 2 : 1; + let cmap = "\x00\x00" + string16(numTables) + "\x00\x03" + "\x00\x01" + string32(4 + numTables * 8); + let i, ii, j, jj; + for (i = ranges.length - 1; i >= 0; --i) { + if (ranges[i][0] <= 0xffff) { + break; + } + } + const bmpLength = i + 1; + if (ranges[i][0] < 0xffff && ranges[i][1] === 0xffff) { + ranges[i][1] = 0xfffe; + } + const trailingRangesCount = ranges[i][1] < 0xffff ? 1 : 0; + const segCount = bmpLength + trailingRangesCount; + const searchParams = OpenTypeFileBuilder.getSearchParams(segCount, 2); + let startCount = ""; + let endCount = ""; + let idDeltas = ""; + let idRangeOffsets = ""; + let glyphsIds = ""; + let bias = 0; + let range, start, end, codes; + for (i = 0, ii = bmpLength; i < ii; i++) { + range = ranges[i]; + start = range[0]; + end = range[1]; + startCount += string16(start); + endCount += string16(end); + codes = range[2]; + let contiguous = true; + for (j = 1, jj = codes.length; j < jj; ++j) { + if (codes[j] !== codes[j - 1] + 1) { + contiguous = false; + break; + } + } + if (!contiguous) { + const offset = (segCount - i) * 2 + bias * 2; + bias += end - start + 1; + idDeltas += string16(0); + idRangeOffsets += string16(offset); + for (j = 0, jj = codes.length; j < jj; ++j) { + glyphsIds += string16(codes[j]); + } + } else { + const startCode = codes[0]; + idDeltas += string16(startCode - start & 0xffff); + idRangeOffsets += string16(0); + } + } + if (trailingRangesCount > 0) { + endCount += "\xFF\xFF"; + startCount += "\xFF\xFF"; + idDeltas += "\x00\x01"; + idRangeOffsets += "\x00\x00"; + } + const format314 = "\x00\x00" + string16(2 * segCount) + string16(searchParams.range) + string16(searchParams.entry) + string16(searchParams.rangeShift) + endCount + "\x00\x00" + startCount + idDeltas + idRangeOffsets + glyphsIds; + let format31012 = ""; + let header31012 = ""; + if (numTables > 1) { + cmap += "\x00\x03" + "\x00\x0A" + string32(4 + numTables * 8 + 4 + format314.length); + format31012 = ""; + for (i = 0, ii = ranges.length; i < ii; i++) { + range = ranges[i]; + start = range[0]; + codes = range[2]; + let code = codes[0]; + for (j = 1, jj = codes.length; j < jj; ++j) { + if (codes[j] !== codes[j - 1] + 1) { + end = range[0] + j - 1; + format31012 += string32(start) + string32(end) + string32(code); + start = end + 1; + code = codes[j]; + } + } + format31012 += string32(start) + string32(range[1]) + string32(code); + } + header31012 = "\x00\x0C" + "\x00\x00" + string32(format31012.length + 16) + "\x00\x00\x00\x00" + string32(format31012.length / 12); + } + return cmap + "\x00\x04" + string16(format314.length + 4) + format314 + header31012 + format31012; +} +function validateOS2Table(os2, file) { + file.pos = (file.start || 0) + os2.offset; + const version = file.getUint16(); + file.skip(60); + const selection = file.getUint16(); + if (version < 4 && selection & 0x0300) { + return false; + } + const firstChar = file.getUint16(); + const lastChar = file.getUint16(); + if (firstChar > lastChar) { + return false; + } + file.skip(6); + const usWinAscent = file.getUint16(); + if (usWinAscent === 0) { + return false; + } + os2.data[8] = os2.data[9] = 0; + return true; +} +function createOS2Table(properties, charstrings, override) { + override ||= { + unitsPerEm: 0, + yMax: 0, + yMin: 0, + ascent: 0, + descent: 0 + }; + let ulUnicodeRange1 = 0; + let ulUnicodeRange2 = 0; + let ulUnicodeRange3 = 0; + let ulUnicodeRange4 = 0; + let firstCharIndex = null; + let lastCharIndex = 0; + let position = -1; + if (charstrings) { + for (let code in charstrings) { + code |= 0; + if (firstCharIndex > code || !firstCharIndex) { + firstCharIndex = code; + } + if (lastCharIndex < code) { + lastCharIndex = code; + } + position = getUnicodeRangeFor(code, position); + if (position < 32) { + ulUnicodeRange1 |= 1 << position; + } else if (position < 64) { + ulUnicodeRange2 |= 1 << position - 32; + } else if (position < 96) { + ulUnicodeRange3 |= 1 << position - 64; + } else if (position < 123) { + ulUnicodeRange4 |= 1 << position - 96; + } else { + throw new FormatError("Unicode ranges Bits > 123 are reserved for internal usage"); + } + } + if (lastCharIndex > 0xffff) { + lastCharIndex = 0xffff; + } + } else { + firstCharIndex = 0; + lastCharIndex = 255; + } + const bbox = properties.bbox || [0, 0, 0, 0]; + const unitsPerEm = override.unitsPerEm || (properties.fontMatrix ? 1 / Math.max(...properties.fontMatrix.slice(0, 4).map(Math.abs)) : 1000); + const scale = properties.ascentScaled ? 1.0 : unitsPerEm / PDF_GLYPH_SPACE_UNITS; + const typoAscent = override.ascent || Math.round(scale * (properties.ascent || bbox[3])); + let typoDescent = override.descent || Math.round(scale * (properties.descent || bbox[1])); + if (typoDescent > 0 && properties.descent > 0 && bbox[1] < 0) { + typoDescent = -typoDescent; + } + const winAscent = override.yMax || typoAscent; + const winDescent = -override.yMin || -typoDescent; + return "\x00\x03" + "\x02\x24" + "\x01\xF4" + "\x00\x05" + "\x00\x00" + "\x02\x8A" + "\x02\xBB" + "\x00\x00" + "\x00\x8C" + "\x02\x8A" + "\x02\xBB" + "\x00\x00" + "\x01\xDF" + "\x00\x31" + "\x01\x02" + "\x00\x00" + "\x00\x00\x06" + String.fromCharCode(properties.fixedPitch ? 0x09 : 0x00) + "\x00\x00\x00\x00\x00\x00" + string32(ulUnicodeRange1) + string32(ulUnicodeRange2) + string32(ulUnicodeRange3) + string32(ulUnicodeRange4) + "\x2A\x32\x31\x2A" + string16(properties.italicAngle ? 1 : 0) + string16(firstCharIndex || properties.firstChar) + string16(lastCharIndex || properties.lastChar) + string16(typoAscent) + string16(typoDescent) + "\x00\x64" + string16(winAscent) + string16(winDescent) + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + string16(properties.xHeight) + string16(properties.capHeight) + string16(0) + string16(firstCharIndex || properties.firstChar) + "\x00\x03"; +} +function createPostTable(properties) { + const angle = Math.floor(properties.italicAngle * 2 ** 16); + return "\x00\x03\x00\x00" + string32(angle) + "\x00\x00" + "\x00\x00" + string32(properties.fixedPitch ? 1 : 0) + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00"; +} +function createPostscriptName(name) { + return name.replaceAll(/[^\x21-\x7E]|[[\](){}<>/%]/g, "").slice(0, 63); +} +function createNameTable(name, proto) { + if (!proto) { + proto = [[], []]; + } + const strings = [proto[0][0] || "Original licence", proto[0][1] || name, proto[0][2] || "Unknown", proto[0][3] || "uniqueID", proto[0][4] || name, proto[0][5] || "Version 0.11", proto[0][6] || createPostscriptName(name), proto[0][7] || "Unknown", proto[0][8] || "Unknown", proto[0][9] || "Unknown"]; + const stringsUnicode = []; + let i, ii, j, jj, str; + for (i = 0, ii = strings.length; i < ii; i++) { + str = proto[1][i] || strings[i]; + const strBufUnicode = []; + for (j = 0, jj = str.length; j < jj; j++) { + strBufUnicode.push(string16(str.charCodeAt(j))); + } + stringsUnicode.push(strBufUnicode.join("")); + } + const names = [strings, stringsUnicode]; + const platforms = ["\x00\x01", "\x00\x03"]; + const encodings = ["\x00\x00", "\x00\x01"]; + const languages = ["\x00\x00", "\x04\x09"]; + const namesRecordCount = strings.length * platforms.length; + let nameTable = "\x00\x00" + string16(namesRecordCount) + string16(namesRecordCount * 12 + 6); + let strOffset = 0; + for (i = 0, ii = platforms.length; i < ii; i++) { + const strs = names[i]; + for (j = 0, jj = strs.length; j < jj; j++) { + str = strs[j]; + const nameRecord = platforms[i] + encodings[i] + languages[i] + string16(j) + string16(str.length) + string16(strOffset); + nameTable += nameRecord; + strOffset += str.length; + } + } + nameTable += strings.join("") + stringsUnicode.join(""); + return nameTable; +} +class Font { + constructor(name, file, properties) { + this.name = name; + this.psName = null; + this.mimetype = null; + this.disableFontFace = false; + this.loadedName = properties.loadedName; + this.isType3Font = properties.isType3Font; + this.missingFile = false; + this.cssFontInfo = properties.cssFontInfo; + this._charsCache = Object.create(null); + this._glyphCache = Object.create(null); + let isSerifFont = !!(properties.flags & FontFlags.Serif); + if (!isSerifFont && !properties.isSimulatedFlags) { + const baseName = name.replaceAll(/[,_]/g, "-").split("-", 1)[0], + serifFonts = getSerifFonts(); + for (const namePart of baseName.split("+")) { + if (serifFonts[namePart]) { + isSerifFont = true; + break; + } + } + } + this.isSerifFont = isSerifFont; + this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic); + this.isMonospace = !!(properties.flags & FontFlags.FixedPitch); + let { + type, + subtype + } = properties; + this.type = type; + this.subtype = subtype; + this.systemFontInfo = properties.systemFontInfo; + const matches = name.match(/^InvalidPDFjsFont_(.*)_\d+$/); + this.isInvalidPDFjsFont = !!matches; + if (this.isInvalidPDFjsFont) { + this.fallbackName = matches[1]; + } else if (this.isMonospace) { + this.fallbackName = "monospace"; + } else if (this.isSerifFont) { + this.fallbackName = "serif"; + } else { + this.fallbackName = "sans-serif"; + } + if (this.systemFontInfo?.guessFallback) { + this.systemFontInfo.guessFallback = false; + this.systemFontInfo.css += `,${this.fallbackName}`; + } + this.differences = properties.differences; + this.widths = properties.widths; + this.defaultWidth = properties.defaultWidth; + this.composite = properties.composite; + this.cMap = properties.cMap; + this.capHeight = properties.capHeight / PDF_GLYPH_SPACE_UNITS; + this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS; + this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS; + this.lineHeight = this.ascent - this.descent; + this.fontMatrix = properties.fontMatrix; + this.bbox = properties.bbox; + this.defaultEncoding = properties.defaultEncoding; + this.toUnicode = properties.toUnicode; + this.toFontChar = []; + if (properties.type === "Type3") { + for (let charCode = 0; charCode < 256; charCode++) { + this.toFontChar[charCode] = this.differences[charCode] || properties.defaultEncoding[charCode]; + } + return; + } + this.cidEncoding = properties.cidEncoding || ""; + this.vertical = !!properties.vertical; + if (this.vertical) { + this.vmetrics = properties.vmetrics; + this.defaultVMetrics = properties.defaultVMetrics; + } + if (!file || file.isEmpty) { + if (file) { + warn('Font file is empty in "' + name + '" (' + this.loadedName + ")"); + } + this.fallbackToSystemFont(properties); + return; + } + [type, subtype] = getFontFileType(file, properties); + if (type !== this.type || subtype !== this.subtype) { + info("Inconsistent font file Type/SubType, expected: " + `${this.type}/${this.subtype} but found: ${type}/${subtype}.`); + } + let data; + try { + switch (type) { + case "MMType1": + info("MMType1 font (" + name + "), falling back to Type1."); + case "Type1": + case "CIDFontType0": + this.mimetype = "font/opentype"; + const cff = subtype === "Type1C" || subtype === "CIDFontType0C" ? new CFFFont(file, properties) : new Type1Font(name, file, properties); + adjustWidths(properties); + data = this.convert(name, cff, properties); + break; + case "OpenType": + case "TrueType": + case "CIDFontType2": + this.mimetype = "font/opentype"; + data = this.checkAndRepair(name, file, properties); + if (this.isOpenType) { + adjustWidths(properties); + type = "OpenType"; + } + break; + default: + throw new FormatError(`Font ${type} is not supported`); + } + } catch (e) { + warn(e); + this.fallbackToSystemFont(properties); + return; + } + amendFallbackToUnicode(properties); + this.data = data; + this.type = type; + this.subtype = subtype; + this.fontMatrix = properties.fontMatrix; + this.widths = properties.widths; + this.defaultWidth = properties.defaultWidth; + this.toUnicode = properties.toUnicode; + this.seacMap = properties.seacMap; + } + get renderer() { + const renderer = FontRendererFactory.create(this, SEAC_ANALYSIS_ENABLED); + return shadow(this, "renderer", renderer); + } + exportData(extraProperties = false) { + const exportDataProperties = extraProperties ? [...EXPORT_DATA_PROPERTIES, ...EXPORT_DATA_EXTRA_PROPERTIES] : EXPORT_DATA_PROPERTIES; + const data = Object.create(null); + let property, value; + for (property of exportDataProperties) { + value = this[property]; + if (value !== undefined) { + data[property] = value; + } + } + return data; + } + fallbackToSystemFont(properties) { + this.missingFile = true; + const { + name, + type + } = this; + let fontName = normalizeFontName(name); + const stdFontMap = getStdFontMap(), + nonStdFontMap = getNonStdFontMap(); + const isStandardFont = !!stdFontMap[fontName]; + const isMappedToStandardFont = !!(nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]); + fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName; + const fontBasicMetricsMap = getFontBasicMetrics(); + const metrics = fontBasicMetricsMap[fontName]; + if (metrics) { + if (isNaN(this.ascent)) { + this.ascent = metrics.ascent / PDF_GLYPH_SPACE_UNITS; + } + if (isNaN(this.descent)) { + this.descent = metrics.descent / PDF_GLYPH_SPACE_UNITS; + } + if (isNaN(this.capHeight)) { + this.capHeight = metrics.capHeight / PDF_GLYPH_SPACE_UNITS; + } + } + this.bold = /bold/gi.test(fontName); + this.italic = /oblique|italic/gi.test(fontName); + this.black = /Black/g.test(name); + const isNarrow = /Narrow/g.test(name); + this.remeasure = (!isStandardFont || isNarrow) && Object.keys(this.widths).length > 0; + if ((isStandardFont || isMappedToStandardFont) && type === "CIDFontType2" && this.cidEncoding.startsWith("Identity-")) { + const cidToGidMap = properties.cidToGidMap; + const map = []; + applyStandardFontGlyphMap(map, getGlyphMapForStandardFonts()); + if (/Arial-?Black/i.test(name)) { + applyStandardFontGlyphMap(map, getSupplementalGlyphMapForArialBlack()); + } else if (/Calibri/i.test(name)) { + applyStandardFontGlyphMap(map, getSupplementalGlyphMapForCalibri()); + } + if (cidToGidMap) { + for (const charCode in map) { + const cid = map[charCode]; + if (cidToGidMap[cid] !== undefined) { + map[+charCode] = cidToGidMap[cid]; + } + } + if (cidToGidMap.length !== this.toUnicode.length && properties.hasIncludedToUnicodeMap && this.toUnicode instanceof IdentityToUnicodeMap) { + this.toUnicode.forEach(function (charCode, unicodeCharCode) { + const cid = map[charCode]; + if (cidToGidMap[cid] === undefined) { + map[+charCode] = unicodeCharCode; + } + }); + } + } + if (!(this.toUnicode instanceof IdentityToUnicodeMap)) { + this.toUnicode.forEach(function (charCode, unicodeCharCode) { + map[+charCode] = unicodeCharCode; + }); + } + this.toFontChar = map; + this.toUnicode = new ToUnicodeMap(map); + } else if (/Symbol/i.test(fontName)) { + this.toFontChar = buildToFontChar(SymbolSetEncoding, getGlyphsUnicode(), this.differences); + } else if (/Dingbats/i.test(fontName)) { + this.toFontChar = buildToFontChar(ZapfDingbatsEncoding, getDingbatsGlyphsUnicode(), this.differences); + } else if (isStandardFont || isMappedToStandardFont) { + const map = buildToFontChar(this.defaultEncoding, getGlyphsUnicode(), this.differences); + if (type === "CIDFontType2" && !this.cidEncoding.startsWith("Identity-") && !(this.toUnicode instanceof IdentityToUnicodeMap)) { + this.toUnicode.forEach(function (charCode, unicodeCharCode) { + map[+charCode] = unicodeCharCode; + }); + } + this.toFontChar = map; + } else { + const glyphsUnicodeMap = getGlyphsUnicode(); + const map = []; + this.toUnicode.forEach((charCode, unicodeCharCode) => { + if (!this.composite) { + const glyphName = this.differences[charCode] || this.defaultEncoding[charCode]; + const unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap); + if (unicode !== -1) { + unicodeCharCode = unicode; + } + } + map[+charCode] = unicodeCharCode; + }); + if (this.composite && this.toUnicode instanceof IdentityToUnicodeMap) { + if (/Tahoma|Verdana/i.test(name)) { + applyStandardFontGlyphMap(map, getGlyphMapForStandardFonts()); + } + } + this.toFontChar = map; + } + amendFallbackToUnicode(properties); + this.loadedName = fontName.split("-", 1)[0]; + } + checkAndRepair(name, font, properties) { + const VALID_TABLES = ["OS/2", "cmap", "head", "hhea", "hmtx", "maxp", "name", "post", "loca", "glyf", "fpgm", "prep", "cvt ", "CFF "]; + function readTables(file, numTables) { + const tables = Object.create(null); + tables["OS/2"] = null; + tables.cmap = null; + tables.head = null; + tables.hhea = null; + tables.hmtx = null; + tables.maxp = null; + tables.name = null; + tables.post = null; + for (let i = 0; i < numTables; i++) { + const table = readTableEntry(file); + if (!VALID_TABLES.includes(table.tag)) { + continue; + } + if (table.length === 0) { + continue; + } + tables[table.tag] = table; + } + return tables; + } + function readTableEntry(file) { + const tag = file.getString(4); + const checksum = file.getInt32() >>> 0; + const offset = file.getInt32() >>> 0; + const length = file.getInt32() >>> 0; + const previousPosition = file.pos; + file.pos = file.start || 0; + file.skip(offset); + const data = file.getBytes(length); + file.pos = previousPosition; + if (tag === "head") { + data[8] = data[9] = data[10] = data[11] = 0; + data[17] |= 0x20; + } + return { + tag, + checksum, + length, + offset, + data + }; + } + function readOpenTypeHeader(ttf) { + return { + version: ttf.getString(4), + numTables: ttf.getUint16(), + searchRange: ttf.getUint16(), + entrySelector: ttf.getUint16(), + rangeShift: ttf.getUint16() + }; + } + function readTrueTypeCollectionHeader(ttc) { + const ttcTag = ttc.getString(4); + assert(ttcTag === "ttcf", "Must be a TrueType Collection font."); + const majorVersion = ttc.getUint16(); + const minorVersion = ttc.getUint16(); + const numFonts = ttc.getInt32() >>> 0; + const offsetTable = []; + for (let i = 0; i < numFonts; i++) { + offsetTable.push(ttc.getInt32() >>> 0); + } + const header = { + ttcTag, + majorVersion, + minorVersion, + numFonts, + offsetTable + }; + switch (majorVersion) { + case 1: + return header; + case 2: + header.dsigTag = ttc.getInt32() >>> 0; + header.dsigLength = ttc.getInt32() >>> 0; + header.dsigOffset = ttc.getInt32() >>> 0; + return header; + } + throw new FormatError(`Invalid TrueType Collection majorVersion: ${majorVersion}.`); + } + function readTrueTypeCollectionData(ttc, fontName) { + const { + numFonts, + offsetTable + } = readTrueTypeCollectionHeader(ttc); + const fontNameParts = fontName.split("+"); + let fallbackData; + for (let i = 0; i < numFonts; i++) { + ttc.pos = (ttc.start || 0) + offsetTable[i]; + const potentialHeader = readOpenTypeHeader(ttc); + const potentialTables = readTables(ttc, potentialHeader.numTables); + if (!potentialTables.name) { + throw new FormatError('TrueType Collection font must contain a "name" table.'); + } + const [nameTable] = readNameTable(potentialTables.name); + for (let j = 0, jj = nameTable.length; j < jj; j++) { + for (let k = 0, kk = nameTable[j].length; k < kk; k++) { + const nameEntry = nameTable[j][k]?.replaceAll(/\s/g, ""); + if (!nameEntry) { + continue; + } + if (nameEntry === fontName) { + return { + header: potentialHeader, + tables: potentialTables + }; + } + if (fontNameParts.length < 2) { + continue; + } + for (const part of fontNameParts) { + if (nameEntry === part) { + fallbackData = { + name: part, + header: potentialHeader, + tables: potentialTables + }; + } + } + } + } + } + if (fallbackData) { + warn(`TrueType Collection does not contain "${fontName}" font, ` + `falling back to "${fallbackData.name}" font instead.`); + return { + header: fallbackData.header, + tables: fallbackData.tables + }; + } + throw new FormatError(`TrueType Collection does not contain "${fontName}" font.`); + } + function readCmapTable(cmap, file, isSymbolicFont, hasEncoding) { + if (!cmap) { + warn("No cmap table available."); + return { + platformId: -1, + encodingId: -1, + mappings: [], + hasShortCmap: false + }; + } + let segment; + let start = (file.start || 0) + cmap.offset; + file.pos = start; + file.skip(2); + const numTables = file.getUint16(); + let potentialTable; + let canBreak = false; + for (let i = 0; i < numTables; i++) { + const platformId = file.getUint16(); + const encodingId = file.getUint16(); + const offset = file.getInt32() >>> 0; + let useTable = false; + if (potentialTable?.platformId === platformId && potentialTable?.encodingId === encodingId) { + continue; + } + if (platformId === 0 && (encodingId === 0 || encodingId === 1 || encodingId === 3)) { + useTable = true; + } else if (platformId === 1 && encodingId === 0) { + useTable = true; + } else if (platformId === 3 && encodingId === 1 && (hasEncoding || !potentialTable)) { + useTable = true; + if (!isSymbolicFont) { + canBreak = true; + } + } else if (isSymbolicFont && platformId === 3 && encodingId === 0) { + useTable = true; + let correctlySorted = true; + if (i < numTables - 1) { + const nextBytes = file.peekBytes(2), + nextPlatformId = int16(nextBytes[0], nextBytes[1]); + if (nextPlatformId < platformId) { + correctlySorted = false; + } + } + if (correctlySorted) { + canBreak = true; + } + } + if (useTable) { + potentialTable = { + platformId, + encodingId, + offset + }; + } + if (canBreak) { + break; + } + } + if (potentialTable) { + file.pos = start + potentialTable.offset; + } + if (!potentialTable || file.peekByte() === -1) { + warn("Could not find a preferred cmap table."); + return { + platformId: -1, + encodingId: -1, + mappings: [], + hasShortCmap: false + }; + } + const format = file.getUint16(); + let hasShortCmap = false; + const mappings = []; + let j, glyphId; + if (format === 0) { + file.skip(2 + 2); + for (j = 0; j < 256; j++) { + const index = file.getByte(); + if (!index) { + continue; + } + mappings.push({ + charCode: j, + glyphId: index + }); + } + hasShortCmap = true; + } else if (format === 2) { + file.skip(2 + 2); + const subHeaderKeys = []; + let maxSubHeaderKey = 0; + for (let i = 0; i < 256; i++) { + const subHeaderKey = file.getUint16() >> 3; + subHeaderKeys.push(subHeaderKey); + maxSubHeaderKey = Math.max(subHeaderKey, maxSubHeaderKey); + } + const subHeaders = []; + for (let i = 0; i <= maxSubHeaderKey; i++) { + subHeaders.push({ + firstCode: file.getUint16(), + entryCount: file.getUint16(), + idDelta: signedInt16(file.getByte(), file.getByte()), + idRangePos: file.pos + file.getUint16() + }); + } + for (let i = 0; i < 256; i++) { + if (subHeaderKeys[i] === 0) { + file.pos = subHeaders[0].idRangePos + 2 * i; + glyphId = file.getUint16(); + mappings.push({ + charCode: i, + glyphId + }); + } else { + const s = subHeaders[subHeaderKeys[i]]; + for (j = 0; j < s.entryCount; j++) { + const charCode = (i << 8) + j + s.firstCode; + file.pos = s.idRangePos + 2 * j; + glyphId = file.getUint16(); + if (glyphId !== 0) { + glyphId = (glyphId + s.idDelta) % 65536; + } + mappings.push({ + charCode, + glyphId + }); + } + } + } + } else if (format === 4) { + file.skip(2 + 2); + const segCount = file.getUint16() >> 1; + file.skip(6); + const segments = []; + let segIndex; + for (segIndex = 0; segIndex < segCount; segIndex++) { + segments.push({ + end: file.getUint16() + }); + } + file.skip(2); + for (segIndex = 0; segIndex < segCount; segIndex++) { + segments[segIndex].start = file.getUint16(); + } + for (segIndex = 0; segIndex < segCount; segIndex++) { + segments[segIndex].delta = file.getUint16(); + } + let offsetsCount = 0, + offsetIndex; + for (segIndex = 0; segIndex < segCount; segIndex++) { + segment = segments[segIndex]; + const rangeOffset = file.getUint16(); + if (!rangeOffset) { + segment.offsetIndex = -1; + continue; + } + offsetIndex = (rangeOffset >> 1) - (segCount - segIndex); + segment.offsetIndex = offsetIndex; + offsetsCount = Math.max(offsetsCount, offsetIndex + segment.end - segment.start + 1); + } + const offsets = []; + for (j = 0; j < offsetsCount; j++) { + offsets.push(file.getUint16()); + } + for (segIndex = 0; segIndex < segCount; segIndex++) { + segment = segments[segIndex]; + start = segment.start; + const end = segment.end; + const delta = segment.delta; + offsetIndex = segment.offsetIndex; + for (j = start; j <= end; j++) { + if (j === 0xffff) { + continue; + } + glyphId = offsetIndex < 0 ? j : offsets[offsetIndex + j - start]; + glyphId = glyphId + delta & 0xffff; + mappings.push({ + charCode: j, + glyphId + }); + } + } + } else if (format === 6) { + file.skip(2 + 2); + const firstCode = file.getUint16(); + const entryCount = file.getUint16(); + for (j = 0; j < entryCount; j++) { + glyphId = file.getUint16(); + const charCode = firstCode + j; + mappings.push({ + charCode, + glyphId + }); + } + } else if (format === 12) { + file.skip(2 + 4 + 4); + const nGroups = file.getInt32() >>> 0; + for (j = 0; j < nGroups; j++) { + const startCharCode = file.getInt32() >>> 0; + const endCharCode = file.getInt32() >>> 0; + let glyphCode = file.getInt32() >>> 0; + for (let charCode = startCharCode; charCode <= endCharCode; charCode++) { + mappings.push({ + charCode, + glyphId: glyphCode++ + }); + } + } + } else { + warn("cmap table has unsupported format: " + format); + return { + platformId: -1, + encodingId: -1, + mappings: [], + hasShortCmap: false + }; + } + mappings.sort(function (a, b) { + return a.charCode - b.charCode; + }); + for (let i = 1; i < mappings.length; i++) { + if (mappings[i - 1].charCode === mappings[i].charCode) { + mappings.splice(i, 1); + i--; + } + } + return { + platformId: potentialTable.platformId, + encodingId: potentialTable.encodingId, + mappings, + hasShortCmap + }; + } + function sanitizeMetrics(file, header, metrics, headTable, numGlyphs, dupFirstEntry) { + if (!header) { + if (metrics) { + metrics.data = null; + } + return; + } + file.pos = (file.start || 0) + header.offset; + file.pos += 4; + file.pos += 2; + file.pos += 2; + file.pos += 2; + file.pos += 2; + file.pos += 2; + file.pos += 2; + file.pos += 2; + file.pos += 2; + file.pos += 2; + const caretOffset = file.getUint16(); + file.pos += 8; + file.pos += 2; + let numOfMetrics = file.getUint16(); + if (caretOffset !== 0) { + const macStyle = int16(headTable.data[44], headTable.data[45]); + if (!(macStyle & 2)) { + header.data[22] = 0; + header.data[23] = 0; + } + } + if (numOfMetrics > numGlyphs) { + info(`The numOfMetrics (${numOfMetrics}) should not be ` + `greater than the numGlyphs (${numGlyphs}).`); + numOfMetrics = numGlyphs; + header.data[34] = (numOfMetrics & 0xff00) >> 8; + header.data[35] = numOfMetrics & 0x00ff; + } + const numOfSidebearings = numGlyphs - numOfMetrics; + const numMissing = numOfSidebearings - (metrics.length - numOfMetrics * 4 >> 1); + if (numMissing > 0) { + const entries = new Uint8Array(metrics.length + numMissing * 2); + entries.set(metrics.data); + if (dupFirstEntry) { + entries[metrics.length] = metrics.data[2]; + entries[metrics.length + 1] = metrics.data[3]; + } + metrics.data = entries; + } + } + function sanitizeGlyph(source, sourceStart, sourceEnd, dest, destStart, hintsValid) { + const glyphProfile = { + length: 0, + sizeOfInstructions: 0 + }; + if (sourceStart < 0 || sourceStart >= source.length || sourceEnd > source.length || sourceEnd - sourceStart <= 12) { + return glyphProfile; + } + const glyf = source.subarray(sourceStart, sourceEnd); + const xMin = signedInt16(glyf[2], glyf[3]); + const yMin = signedInt16(glyf[4], glyf[5]); + const xMax = signedInt16(glyf[6], glyf[7]); + const yMax = signedInt16(glyf[8], glyf[9]); + if (xMin > xMax) { + writeSignedInt16(glyf, 2, xMax); + writeSignedInt16(glyf, 6, xMin); + } + if (yMin > yMax) { + writeSignedInt16(glyf, 4, yMax); + writeSignedInt16(glyf, 8, yMin); + } + const contoursCount = signedInt16(glyf[0], glyf[1]); + if (contoursCount < 0) { + if (contoursCount < -1) { + return glyphProfile; + } + dest.set(glyf, destStart); + glyphProfile.length = glyf.length; + return glyphProfile; + } + let i, + j = 10, + flagsCount = 0; + for (i = 0; i < contoursCount; i++) { + const endPoint = glyf[j] << 8 | glyf[j + 1]; + flagsCount = endPoint + 1; + j += 2; + } + const instructionsStart = j; + const instructionsLength = glyf[j] << 8 | glyf[j + 1]; + glyphProfile.sizeOfInstructions = instructionsLength; + j += 2 + instructionsLength; + const instructionsEnd = j; + let coordinatesLength = 0; + for (i = 0; i < flagsCount; i++) { + const flag = glyf[j++]; + if (flag & 0xc0) { + glyf[j - 1] = flag & 0x3f; + } + let xLength = 2; + if (flag & 2) { + xLength = 1; + } else if (flag & 16) { + xLength = 0; + } + let yLength = 2; + if (flag & 4) { + yLength = 1; + } else if (flag & 32) { + yLength = 0; + } + const xyLength = xLength + yLength; + coordinatesLength += xyLength; + if (flag & 8) { + const repeat = glyf[j++]; + if (repeat === 0) { + glyf[j - 1] ^= 8; + } + i += repeat; + coordinatesLength += repeat * xyLength; + } + } + if (coordinatesLength === 0) { + return glyphProfile; + } + let glyphDataLength = j + coordinatesLength; + if (glyphDataLength > glyf.length) { + return glyphProfile; + } + if (!hintsValid && instructionsLength > 0) { + dest.set(glyf.subarray(0, instructionsStart), destStart); + dest.set([0, 0], destStart + instructionsStart); + dest.set(glyf.subarray(instructionsEnd, glyphDataLength), destStart + instructionsStart + 2); + glyphDataLength -= instructionsLength; + if (glyf.length - glyphDataLength > 3) { + glyphDataLength = glyphDataLength + 3 & ~3; + } + glyphProfile.length = glyphDataLength; + return glyphProfile; + } + if (glyf.length - glyphDataLength > 3) { + glyphDataLength = glyphDataLength + 3 & ~3; + dest.set(glyf.subarray(0, glyphDataLength), destStart); + glyphProfile.length = glyphDataLength; + return glyphProfile; + } + dest.set(glyf, destStart); + glyphProfile.length = glyf.length; + return glyphProfile; + } + function sanitizeHead(head, numGlyphs, locaLength) { + const data = head.data; + const version = int32(data[0], data[1], data[2], data[3]); + if (version >> 16 !== 1) { + info("Attempting to fix invalid version in head table: " + version); + data[0] = 0; + data[1] = 1; + data[2] = 0; + data[3] = 0; + } + const indexToLocFormat = int16(data[50], data[51]); + if (indexToLocFormat < 0 || indexToLocFormat > 1) { + info("Attempting to fix invalid indexToLocFormat in head table: " + indexToLocFormat); + const numGlyphsPlusOne = numGlyphs + 1; + if (locaLength === numGlyphsPlusOne << 1) { + data[50] = 0; + data[51] = 0; + } else if (locaLength === numGlyphsPlusOne << 2) { + data[50] = 0; + data[51] = 1; + } else { + throw new FormatError("Could not fix indexToLocFormat: " + indexToLocFormat); + } + } + } + function sanitizeGlyphLocations(loca, glyf, numGlyphs, isGlyphLocationsLong, hintsValid, dupFirstEntry, maxSizeOfInstructions) { + let itemSize, itemDecode, itemEncode; + if (isGlyphLocationsLong) { + itemSize = 4; + itemDecode = function fontItemDecodeLong(data, offset) { + return data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3]; + }; + itemEncode = function fontItemEncodeLong(data, offset, value) { + data[offset] = value >>> 24 & 0xff; + data[offset + 1] = value >> 16 & 0xff; + data[offset + 2] = value >> 8 & 0xff; + data[offset + 3] = value & 0xff; + }; + } else { + itemSize = 2; + itemDecode = function fontItemDecode(data, offset) { + return data[offset] << 9 | data[offset + 1] << 1; + }; + itemEncode = function fontItemEncode(data, offset, value) { + data[offset] = value >> 9 & 0xff; + data[offset + 1] = value >> 1 & 0xff; + }; + } + const numGlyphsOut = dupFirstEntry ? numGlyphs + 1 : numGlyphs; + const locaDataSize = itemSize * (1 + numGlyphsOut); + const locaData = new Uint8Array(locaDataSize); + locaData.set(loca.data.subarray(0, locaDataSize)); + loca.data = locaData; + const oldGlyfData = glyf.data; + const oldGlyfDataLength = oldGlyfData.length; + const newGlyfData = new Uint8Array(oldGlyfDataLength); + let i, j; + const locaEntries = []; + for (i = 0, j = 0; i < numGlyphs + 1; i++, j += itemSize) { + let offset = itemDecode(locaData, j); + if (offset > oldGlyfDataLength) { + offset = oldGlyfDataLength; + } + locaEntries.push({ + index: i, + offset, + endOffset: 0 + }); + } + locaEntries.sort((a, b) => a.offset - b.offset); + for (i = 0; i < numGlyphs; i++) { + locaEntries[i].endOffset = locaEntries[i + 1].offset; + } + locaEntries.sort((a, b) => a.index - b.index); + for (i = 0; i < numGlyphs; i++) { + const { + offset, + endOffset + } = locaEntries[i]; + if (offset !== 0 || endOffset !== 0) { + break; + } + const nextOffset = locaEntries[i + 1].offset; + if (nextOffset === 0) { + continue; + } + locaEntries[i].endOffset = nextOffset; + break; + } + const last = locaEntries.at(-2); + if (last.offset !== 0 && last.endOffset === 0) { + last.endOffset = oldGlyfDataLength; + } + const missingGlyphs = Object.create(null); + let writeOffset = 0; + itemEncode(locaData, 0, writeOffset); + for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) { + const glyphProfile = sanitizeGlyph(oldGlyfData, locaEntries[i].offset, locaEntries[i].endOffset, newGlyfData, writeOffset, hintsValid); + const newLength = glyphProfile.length; + if (newLength === 0) { + missingGlyphs[i] = true; + } + if (glyphProfile.sizeOfInstructions > maxSizeOfInstructions) { + maxSizeOfInstructions = glyphProfile.sizeOfInstructions; + } + writeOffset += newLength; + itemEncode(locaData, j, writeOffset); + } + if (writeOffset === 0) { + const simpleGlyph = new Uint8Array([0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0]); + for (i = 0, j = itemSize; i < numGlyphsOut; i++, j += itemSize) { + itemEncode(locaData, j, simpleGlyph.length); + } + glyf.data = simpleGlyph; + } else if (dupFirstEntry) { + const firstEntryLength = itemDecode(locaData, itemSize); + if (newGlyfData.length > firstEntryLength + writeOffset) { + glyf.data = newGlyfData.subarray(0, firstEntryLength + writeOffset); + } else { + glyf.data = new Uint8Array(firstEntryLength + writeOffset); + glyf.data.set(newGlyfData.subarray(0, writeOffset)); + } + glyf.data.set(newGlyfData.subarray(0, firstEntryLength), writeOffset); + itemEncode(loca.data, locaData.length - itemSize, writeOffset + firstEntryLength); + } else { + glyf.data = newGlyfData.subarray(0, writeOffset); + } + return { + missingGlyphs, + maxSizeOfInstructions + }; + } + function readPostScriptTable(post, propertiesObj, maxpNumGlyphs) { + const start = (font.start || 0) + post.offset; + font.pos = start; + const length = post.length, + end = start + length; + const version = font.getInt32(); + font.skip(28); + let glyphNames; + let valid = true; + let i; + switch (version) { + case 0x00010000: + glyphNames = MacStandardGlyphOrdering; + break; + case 0x00020000: + const numGlyphs = font.getUint16(); + if (numGlyphs !== maxpNumGlyphs) { + valid = false; + break; + } + const glyphNameIndexes = []; + for (i = 0; i < numGlyphs; ++i) { + const index = font.getUint16(); + if (index >= 32768) { + valid = false; + break; + } + glyphNameIndexes.push(index); + } + if (!valid) { + break; + } + const customNames = [], + strBuf = []; + while (font.pos < end) { + const stringLength = font.getByte(); + strBuf.length = stringLength; + for (i = 0; i < stringLength; ++i) { + strBuf[i] = String.fromCharCode(font.getByte()); + } + customNames.push(strBuf.join("")); + } + glyphNames = []; + for (i = 0; i < numGlyphs; ++i) { + const j = glyphNameIndexes[i]; + if (j < 258) { + glyphNames.push(MacStandardGlyphOrdering[j]); + continue; + } + glyphNames.push(customNames[j - 258]); + } + break; + case 0x00030000: + break; + default: + warn("Unknown/unsupported post table version " + version); + valid = false; + if (propertiesObj.defaultEncoding) { + glyphNames = propertiesObj.defaultEncoding; + } + break; + } + propertiesObj.glyphNames = glyphNames; + return valid; + } + function readNameTable(nameTable) { + const start = (font.start || 0) + nameTable.offset; + font.pos = start; + const names = [[], []], + records = []; + const length = nameTable.length, + end = start + length; + const format = font.getUint16(); + const FORMAT_0_HEADER_LENGTH = 6; + if (format !== 0 || length < FORMAT_0_HEADER_LENGTH) { + return [names, records]; + } + const numRecords = font.getUint16(); + const stringsStart = font.getUint16(); + const NAME_RECORD_LENGTH = 12; + let i, ii; + for (i = 0; i < numRecords && font.pos + NAME_RECORD_LENGTH <= end; i++) { + const r = { + platform: font.getUint16(), + encoding: font.getUint16(), + language: font.getUint16(), + name: font.getUint16(), + length: font.getUint16(), + offset: font.getUint16() + }; + if (isMacNameRecord(r) || isWinNameRecord(r)) { + records.push(r); + } + } + for (i = 0, ii = records.length; i < ii; i++) { + const record = records[i]; + if (record.length <= 0) { + continue; + } + const pos = start + stringsStart + record.offset; + if (pos + record.length > end) { + continue; + } + font.pos = pos; + const nameIndex = record.name; + if (record.encoding) { + let str = ""; + for (let j = 0, jj = record.length; j < jj; j += 2) { + str += String.fromCharCode(font.getUint16()); + } + names[1][nameIndex] = str; + } else { + names[0][nameIndex] = font.getString(record.length); + } + } + return [names, records]; + } + const TTOpsStackDeltas = [0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, -2, 0, 0, -2, -5, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, -1, -1, 1, -1, -999, 0, 1, 0, -1, -2, 0, -1, -2, -1, -1, 0, -1, -1, 0, 0, -999, -999, -1, -1, -1, -1, -2, -999, -2, -2, -999, 0, -2, -2, 0, 0, -2, 0, -2, 0, 0, 0, -2, -1, -1, 1, 1, 0, 0, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, 0, -999, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -999, -999, -999, -999, -999, -1, -1, -2, -2, 0, 0, 0, 0, -1, -1, -999, -2, -2, 0, 0, -1, -2, -2, 0, 0, 0, -1, -1, -1, -2]; + function sanitizeTTProgram(table, ttContext) { + let data = table.data; + let i = 0, + j, + n, + b, + funcId, + pc, + lastEndf = 0, + lastDeff = 0; + const stack = []; + const callstack = []; + const functionsCalled = []; + let tooComplexToFollowFunctions = ttContext.tooComplexToFollowFunctions; + let inFDEF = false, + ifLevel = 0, + inELSE = 0; + for (let ii = data.length; i < ii;) { + const op = data[i++]; + if (op === 0x40) { + n = data[i++]; + if (inFDEF || inELSE) { + i += n; + } else { + for (j = 0; j < n; j++) { + stack.push(data[i++]); + } + } + } else if (op === 0x41) { + n = data[i++]; + if (inFDEF || inELSE) { + i += n * 2; + } else { + for (j = 0; j < n; j++) { + b = data[i++]; + stack.push(b << 8 | data[i++]); + } + } + } else if ((op & 0xf8) === 0xb0) { + n = op - 0xb0 + 1; + if (inFDEF || inELSE) { + i += n; + } else { + for (j = 0; j < n; j++) { + stack.push(data[i++]); + } + } + } else if ((op & 0xf8) === 0xb8) { + n = op - 0xb8 + 1; + if (inFDEF || inELSE) { + i += n * 2; + } else { + for (j = 0; j < n; j++) { + b = data[i++]; + stack.push(signedInt16(b, data[i++])); + } + } + } else if (op === 0x2b && !tooComplexToFollowFunctions) { + if (!inFDEF && !inELSE) { + funcId = stack.at(-1); + if (isNaN(funcId)) { + info("TT: CALL empty stack (or invalid entry)."); + } else { + ttContext.functionsUsed[funcId] = true; + if (funcId in ttContext.functionsStackDeltas) { + const newStackLength = stack.length + ttContext.functionsStackDeltas[funcId]; + if (newStackLength < 0) { + warn("TT: CALL invalid functions stack delta."); + ttContext.hintsValid = false; + return; + } + stack.length = newStackLength; + } else if (funcId in ttContext.functionsDefined && !functionsCalled.includes(funcId)) { + callstack.push({ + data, + i, + stackTop: stack.length - 1 + }); + functionsCalled.push(funcId); + pc = ttContext.functionsDefined[funcId]; + if (!pc) { + warn("TT: CALL non-existent function"); + ttContext.hintsValid = false; + return; + } + data = pc.data; + i = pc.i; + } + } + } + } else if (op === 0x2c && !tooComplexToFollowFunctions) { + if (inFDEF || inELSE) { + warn("TT: nested FDEFs not allowed"); + tooComplexToFollowFunctions = true; + } + inFDEF = true; + lastDeff = i; + funcId = stack.pop(); + ttContext.functionsDefined[funcId] = { + data, + i + }; + } else if (op === 0x2d) { + if (inFDEF) { + inFDEF = false; + lastEndf = i; + } else { + pc = callstack.pop(); + if (!pc) { + warn("TT: ENDF bad stack"); + ttContext.hintsValid = false; + return; + } + funcId = functionsCalled.pop(); + data = pc.data; + i = pc.i; + ttContext.functionsStackDeltas[funcId] = stack.length - pc.stackTop; + } + } else if (op === 0x89) { + if (inFDEF || inELSE) { + warn("TT: nested IDEFs not allowed"); + tooComplexToFollowFunctions = true; + } + inFDEF = true; + lastDeff = i; + } else if (op === 0x58) { + ++ifLevel; + } else if (op === 0x1b) { + inELSE = ifLevel; + } else if (op === 0x59) { + if (inELSE === ifLevel) { + inELSE = 0; + } + --ifLevel; + } else if (op === 0x1c) { + if (!inFDEF && !inELSE) { + const offset = stack.at(-1); + if (offset > 0) { + i += offset - 1; + } + } + } + if (!inFDEF && !inELSE) { + let stackDelta = 0; + if (op <= 0x8e) { + stackDelta = TTOpsStackDeltas[op]; + } else if (op >= 0xc0 && op <= 0xdf) { + stackDelta = -1; + } else if (op >= 0xe0) { + stackDelta = -2; + } + if (op >= 0x71 && op <= 0x75) { + n = stack.pop(); + if (!isNaN(n)) { + stackDelta = -n * 2; + } + } + while (stackDelta < 0 && stack.length > 0) { + stack.pop(); + stackDelta++; + } + while (stackDelta > 0) { + stack.push(NaN); + stackDelta--; + } + } + } + ttContext.tooComplexToFollowFunctions = tooComplexToFollowFunctions; + const content = [data]; + if (i > data.length) { + content.push(new Uint8Array(i - data.length)); + } + if (lastDeff > lastEndf) { + warn("TT: complementing a missing function tail"); + content.push(new Uint8Array([0x22, 0x2d])); + } + foldTTTable(table, content); + } + function checkInvalidFunctions(ttContext, maxFunctionDefs) { + if (ttContext.tooComplexToFollowFunctions) { + return; + } + if (ttContext.functionsDefined.length > maxFunctionDefs) { + warn("TT: more functions defined than expected"); + ttContext.hintsValid = false; + return; + } + for (let j = 0, jj = ttContext.functionsUsed.length; j < jj; j++) { + if (j > maxFunctionDefs) { + warn("TT: invalid function id: " + j); + ttContext.hintsValid = false; + return; + } + if (ttContext.functionsUsed[j] && !ttContext.functionsDefined[j]) { + warn("TT: undefined function: " + j); + ttContext.hintsValid = false; + return; + } + } + } + function foldTTTable(table, content) { + if (content.length > 1) { + let newLength = 0; + let j, jj; + for (j = 0, jj = content.length; j < jj; j++) { + newLength += content[j].length; + } + newLength = newLength + 3 & ~3; + const result = new Uint8Array(newLength); + let pos = 0; + for (j = 0, jj = content.length; j < jj; j++) { + result.set(content[j], pos); + pos += content[j].length; + } + table.data = result; + table.length = newLength; + } + } + function sanitizeTTPrograms(fpgm, prep, cvt, maxFunctionDefs) { + const ttContext = { + functionsDefined: [], + functionsUsed: [], + functionsStackDeltas: [], + tooComplexToFollowFunctions: false, + hintsValid: true + }; + if (fpgm) { + sanitizeTTProgram(fpgm, ttContext); + } + if (prep) { + sanitizeTTProgram(prep, ttContext); + } + if (fpgm) { + checkInvalidFunctions(ttContext, maxFunctionDefs); + } + if (cvt && cvt.length & 1) { + const cvtData = new Uint8Array(cvt.length + 1); + cvtData.set(cvt.data); + cvt.data = cvtData; + } + return ttContext.hintsValid; + } + font = new Stream(new Uint8Array(font.getBytes())); + let header, tables; + if (isTrueTypeCollectionFile(font)) { + const ttcData = readTrueTypeCollectionData(font, this.name); + header = ttcData.header; + tables = ttcData.tables; + } else { + header = readOpenTypeHeader(font); + tables = readTables(font, header.numTables); + } + let cff, cffFile; + const isTrueType = !tables["CFF "]; + if (!isTrueType) { + const isComposite = properties.composite && (properties.cidToGidMap?.length > 0 || !(properties.cMap instanceof IdentityCMap)); + if (header.version === "OTTO" && !isComposite || !tables.head || !tables.hhea || !tables.maxp || !tables.post) { + cffFile = new Stream(tables["CFF "].data); + cff = new CFFFont(cffFile, properties); + adjustWidths(properties); + return this.convert(name, cff, properties); + } + delete tables.glyf; + delete tables.loca; + delete tables.fpgm; + delete tables.prep; + delete tables["cvt "]; + this.isOpenType = true; + } else { + if (!tables.loca) { + throw new FormatError('Required "loca" table is not found'); + } + if (!tables.glyf) { + warn('Required "glyf" table is not found -- trying to recover.'); + tables.glyf = { + tag: "glyf", + data: new Uint8Array(0) + }; + } + this.isOpenType = false; + } + if (!tables.maxp) { + throw new FormatError('Required "maxp" table is not found'); + } + font.pos = (font.start || 0) + tables.maxp.offset; + let version = font.getInt32(); + const numGlyphs = font.getUint16(); + if (version !== 0x00010000 && version !== 0x00005000) { + if (tables.maxp.length === 6) { + version = 0x0005000; + } else if (tables.maxp.length >= 32) { + version = 0x00010000; + } else { + throw new FormatError(`"maxp" table has a wrong version number`); + } + writeUint32(tables.maxp.data, 0, version); + } + if (properties.scaleFactors?.length === numGlyphs && isTrueType) { + const { + scaleFactors + } = properties; + const isGlyphLocationsLong = int16(tables.head.data[50], tables.head.data[51]); + const glyphs = new GlyfTable({ + glyfTable: tables.glyf.data, + isGlyphLocationsLong, + locaTable: tables.loca.data, + numGlyphs + }); + glyphs.scale(scaleFactors); + const { + glyf, + loca, + isLocationLong + } = glyphs.write(); + tables.glyf.data = glyf; + tables.loca.data = loca; + if (isLocationLong !== !!isGlyphLocationsLong) { + tables.head.data[50] = 0; + tables.head.data[51] = isLocationLong ? 1 : 0; + } + const metrics = tables.hmtx.data; + for (let i = 0; i < numGlyphs; i++) { + const j = 4 * i; + const advanceWidth = Math.round(scaleFactors[i] * int16(metrics[j], metrics[j + 1])); + metrics[j] = advanceWidth >> 8 & 0xff; + metrics[j + 1] = advanceWidth & 0xff; + const lsb = Math.round(scaleFactors[i] * signedInt16(metrics[j + 2], metrics[j + 3])); + writeSignedInt16(metrics, j + 2, lsb); + } + } + let numGlyphsOut = numGlyphs + 1; + let dupFirstEntry = true; + if (numGlyphsOut > 0xffff) { + dupFirstEntry = false; + numGlyphsOut = numGlyphs; + warn("Not enough space in glyfs to duplicate first glyph."); + } + let maxFunctionDefs = 0; + let maxSizeOfInstructions = 0; + if (version >= 0x00010000 && tables.maxp.length >= 32) { + font.pos += 8; + const maxZones = font.getUint16(); + if (maxZones > 2) { + tables.maxp.data[14] = 0; + tables.maxp.data[15] = 2; + } + font.pos += 4; + maxFunctionDefs = font.getUint16(); + font.pos += 4; + maxSizeOfInstructions = font.getUint16(); + } + tables.maxp.data[4] = numGlyphsOut >> 8; + tables.maxp.data[5] = numGlyphsOut & 255; + const hintsValid = sanitizeTTPrograms(tables.fpgm, tables.prep, tables["cvt "], maxFunctionDefs); + if (!hintsValid) { + delete tables.fpgm; + delete tables.prep; + delete tables["cvt "]; + } + sanitizeMetrics(font, tables.hhea, tables.hmtx, tables.head, numGlyphsOut, dupFirstEntry); + if (!tables.head) { + throw new FormatError('Required "head" table is not found'); + } + sanitizeHead(tables.head, numGlyphs, isTrueType ? tables.loca.length : 0); + let missingGlyphs = Object.create(null); + if (isTrueType) { + const isGlyphLocationsLong = int16(tables.head.data[50], tables.head.data[51]); + const glyphsInfo = sanitizeGlyphLocations(tables.loca, tables.glyf, numGlyphs, isGlyphLocationsLong, hintsValid, dupFirstEntry, maxSizeOfInstructions); + missingGlyphs = glyphsInfo.missingGlyphs; + if (version >= 0x00010000 && tables.maxp.length >= 32) { + tables.maxp.data[26] = glyphsInfo.maxSizeOfInstructions >> 8; + tables.maxp.data[27] = glyphsInfo.maxSizeOfInstructions & 255; + } + } + if (!tables.hhea) { + throw new FormatError('Required "hhea" table is not found'); + } + if (tables.hhea.data[10] === 0 && tables.hhea.data[11] === 0) { + tables.hhea.data[10] = 0xff; + tables.hhea.data[11] = 0xff; + } + const metricsOverride = { + unitsPerEm: int16(tables.head.data[18], tables.head.data[19]), + yMax: signedInt16(tables.head.data[42], tables.head.data[43]), + yMin: signedInt16(tables.head.data[38], tables.head.data[39]), + ascent: signedInt16(tables.hhea.data[4], tables.hhea.data[5]), + descent: signedInt16(tables.hhea.data[6], tables.hhea.data[7]), + lineGap: signedInt16(tables.hhea.data[8], tables.hhea.data[9]) + }; + this.ascent = metricsOverride.ascent / metricsOverride.unitsPerEm; + this.descent = metricsOverride.descent / metricsOverride.unitsPerEm; + this.lineGap = metricsOverride.lineGap / metricsOverride.unitsPerEm; + if (this.cssFontInfo?.lineHeight) { + this.lineHeight = this.cssFontInfo.metrics.lineHeight; + this.lineGap = this.cssFontInfo.metrics.lineGap; + } else { + this.lineHeight = this.ascent - this.descent + this.lineGap; + } + if (tables.post) { + readPostScriptTable(tables.post, properties, numGlyphs); + } + tables.post = { + tag: "post", + data: createPostTable(properties) + }; + const charCodeToGlyphId = Object.create(null); + function hasGlyph(glyphId) { + return !missingGlyphs[glyphId]; + } + if (properties.composite) { + const cidToGidMap = properties.cidToGidMap || []; + const isCidToGidMapEmpty = cidToGidMap.length === 0; + properties.cMap.forEach(function (charCode, cid) { + if (typeof cid === "string") { + cid = convertCidString(charCode, cid, true); + } + if (cid > 0xffff) { + throw new FormatError("Max size of CID is 65,535"); + } + let glyphId = -1; + if (isCidToGidMapEmpty) { + glyphId = cid; + } else if (cidToGidMap[cid] !== undefined) { + glyphId = cidToGidMap[cid]; + } + if (glyphId >= 0 && glyphId < numGlyphs && hasGlyph(glyphId)) { + charCodeToGlyphId[charCode] = glyphId; + } + }); + } else { + const cmapTable = readCmapTable(tables.cmap, font, this.isSymbolicFont, properties.hasEncoding); + const cmapPlatformId = cmapTable.platformId; + const cmapEncodingId = cmapTable.encodingId; + const cmapMappings = cmapTable.mappings; + let baseEncoding = [], + forcePostTable = false; + if (properties.hasEncoding && (properties.baseEncodingName === "MacRomanEncoding" || properties.baseEncodingName === "WinAnsiEncoding")) { + baseEncoding = getEncoding(properties.baseEncodingName); + } + if (properties.hasEncoding && !this.isSymbolicFont && (cmapPlatformId === 3 && cmapEncodingId === 1 || cmapPlatformId === 1 && cmapEncodingId === 0)) { + const glyphsUnicodeMap = getGlyphsUnicode(); + for (let charCode = 0; charCode < 256; charCode++) { + let glyphName; + if (this.differences[charCode] !== undefined) { + glyphName = this.differences[charCode]; + } else if (baseEncoding.length && baseEncoding[charCode] !== "") { + glyphName = baseEncoding[charCode]; + } else { + glyphName = StandardEncoding[charCode]; + } + if (!glyphName) { + continue; + } + const standardGlyphName = recoverGlyphName(glyphName, glyphsUnicodeMap); + let unicodeOrCharCode; + if (cmapPlatformId === 3 && cmapEncodingId === 1) { + unicodeOrCharCode = glyphsUnicodeMap[standardGlyphName]; + } else if (cmapPlatformId === 1 && cmapEncodingId === 0) { + unicodeOrCharCode = MacRomanEncoding.indexOf(standardGlyphName); + } + if (unicodeOrCharCode === undefined) { + if (!properties.glyphNames && properties.hasIncludedToUnicodeMap && !(this.toUnicode instanceof IdentityToUnicodeMap)) { + const unicode = this.toUnicode.get(charCode); + if (unicode) { + unicodeOrCharCode = unicode.codePointAt(0); + } + } + if (unicodeOrCharCode === undefined) { + continue; + } + } + for (const mapping of cmapMappings) { + if (mapping.charCode !== unicodeOrCharCode) { + continue; + } + charCodeToGlyphId[charCode] = mapping.glyphId; + break; + } + } + } else if (cmapPlatformId === 0) { + for (const mapping of cmapMappings) { + charCodeToGlyphId[mapping.charCode] = mapping.glyphId; + } + forcePostTable = true; + } else if (cmapPlatformId === 3 && cmapEncodingId === 0) { + for (const mapping of cmapMappings) { + let charCode = mapping.charCode; + if (charCode >= 0xf000 && charCode <= 0xf0ff) { + charCode &= 0xff; + } + charCodeToGlyphId[charCode] = mapping.glyphId; + } + } else { + for (const mapping of cmapMappings) { + charCodeToGlyphId[mapping.charCode] = mapping.glyphId; + } + } + if (properties.glyphNames && (baseEncoding.length || this.differences.length)) { + for (let i = 0; i < 256; ++i) { + if (!forcePostTable && charCodeToGlyphId[i] !== undefined) { + continue; + } + const glyphName = this.differences[i] || baseEncoding[i]; + if (!glyphName) { + continue; + } + const glyphId = properties.glyphNames.indexOf(glyphName); + if (glyphId > 0 && hasGlyph(glyphId)) { + charCodeToGlyphId[i] = glyphId; + } + } + } + } + if (charCodeToGlyphId.length === 0) { + charCodeToGlyphId[0] = 0; + } + let glyphZeroId = numGlyphsOut - 1; + if (!dupFirstEntry) { + glyphZeroId = 0; + } + if (!properties.cssFontInfo) { + const newMapping = adjustMapping(charCodeToGlyphId, hasGlyph, glyphZeroId, this.toUnicode); + this.toFontChar = newMapping.toFontChar; + tables.cmap = { + tag: "cmap", + data: createCmapTable(newMapping.charCodeToGlyphId, newMapping.toUnicodeExtraMap, numGlyphsOut) + }; + if (!tables["OS/2"] || !validateOS2Table(tables["OS/2"], font)) { + tables["OS/2"] = { + tag: "OS/2", + data: createOS2Table(properties, newMapping.charCodeToGlyphId, metricsOverride) + }; + } + } + if (!isTrueType) { + try { + cffFile = new Stream(tables["CFF "].data); + const parser = new CFFParser(cffFile, properties, SEAC_ANALYSIS_ENABLED); + cff = parser.parse(); + cff.duplicateFirstGlyph(); + const compiler = new CFFCompiler(cff); + tables["CFF "].data = compiler.compile(); + } catch { + warn("Failed to compile font " + properties.loadedName); + } + } + if (!tables.name) { + tables.name = { + tag: "name", + data: createNameTable(this.name) + }; + } else { + const [namePrototype, nameRecords] = readNameTable(tables.name); + tables.name.data = createNameTable(name, namePrototype); + this.psName = namePrototype[0][6] || null; + if (!properties.composite) { + adjustTrueTypeToUnicode(properties, this.isSymbolicFont, nameRecords); + } + } + const builder = new OpenTypeFileBuilder(header.version); + for (const tableTag in tables) { + builder.addTable(tableTag, tables[tableTag].data); + } + return builder.toArray(); + } + convert(fontName, font, properties) { + properties.fixedPitch = false; + if (properties.builtInEncoding) { + adjustType1ToUnicode(properties, properties.builtInEncoding); + } + let glyphZeroId = 1; + if (font instanceof CFFFont) { + glyphZeroId = font.numGlyphs - 1; + } + const mapping = font.getGlyphMapping(properties); + let newMapping = null; + let newCharCodeToGlyphId = mapping; + let toUnicodeExtraMap = null; + if (!properties.cssFontInfo) { + newMapping = adjustMapping(mapping, font.hasGlyphId.bind(font), glyphZeroId, this.toUnicode); + this.toFontChar = newMapping.toFontChar; + newCharCodeToGlyphId = newMapping.charCodeToGlyphId; + toUnicodeExtraMap = newMapping.toUnicodeExtraMap; + } + const numGlyphs = font.numGlyphs; + function getCharCodes(charCodeToGlyphId, glyphId) { + let charCodes = null; + for (const charCode in charCodeToGlyphId) { + if (glyphId === charCodeToGlyphId[charCode]) { + (charCodes ||= []).push(charCode | 0); + } + } + return charCodes; + } + function createCharCode(charCodeToGlyphId, glyphId) { + for (const charCode in charCodeToGlyphId) { + if (glyphId === charCodeToGlyphId[charCode]) { + return charCode | 0; + } + } + newMapping.charCodeToGlyphId[newMapping.nextAvailableFontCharCode] = glyphId; + return newMapping.nextAvailableFontCharCode++; + } + const seacs = font.seacs; + if (newMapping && SEAC_ANALYSIS_ENABLED && seacs?.length) { + const matrix = properties.fontMatrix || FONT_IDENTITY_MATRIX; + const charset = font.getCharset(); + const seacMap = Object.create(null); + for (let glyphId in seacs) { + glyphId |= 0; + const seac = seacs[glyphId]; + const baseGlyphName = StandardEncoding[seac[2]]; + const accentGlyphName = StandardEncoding[seac[3]]; + const baseGlyphId = charset.indexOf(baseGlyphName); + const accentGlyphId = charset.indexOf(accentGlyphName); + if (baseGlyphId < 0 || accentGlyphId < 0) { + continue; + } + const accentOffset = { + x: seac[0] * matrix[0] + seac[1] * matrix[2] + matrix[4], + y: seac[0] * matrix[1] + seac[1] * matrix[3] + matrix[5] + }; + const charCodes = getCharCodes(mapping, glyphId); + if (!charCodes) { + continue; + } + for (const charCode of charCodes) { + const charCodeToGlyphId = newMapping.charCodeToGlyphId; + const baseFontCharCode = createCharCode(charCodeToGlyphId, baseGlyphId); + const accentFontCharCode = createCharCode(charCodeToGlyphId, accentGlyphId); + seacMap[charCode] = { + baseFontCharCode, + accentFontCharCode, + accentOffset + }; + } + } + properties.seacMap = seacMap; + } + const unitsPerEm = properties.fontMatrix ? 1 / Math.max(...properties.fontMatrix.slice(0, 4).map(Math.abs)) : 1000; + const builder = new OpenTypeFileBuilder("\x4F\x54\x54\x4F"); + builder.addTable("CFF ", font.data); + builder.addTable("OS/2", createOS2Table(properties, newCharCodeToGlyphId)); + builder.addTable("cmap", createCmapTable(newCharCodeToGlyphId, toUnicodeExtraMap, numGlyphs)); + builder.addTable("head", "\x00\x01\x00\x00" + "\x00\x00\x10\x00" + "\x00\x00\x00\x00" + "\x5F\x0F\x3C\xF5" + "\x00\x00" + safeString16(unitsPerEm) + "\x00\x00\x00\x00\x9e\x0b\x7e\x27" + "\x00\x00\x00\x00\x9e\x0b\x7e\x27" + "\x00\x00" + safeString16(properties.descent) + "\x0F\xFF" + safeString16(properties.ascent) + string16(properties.italicAngle ? 2 : 0) + "\x00\x11" + "\x00\x00" + "\x00\x00" + "\x00\x00"); + builder.addTable("hhea", "\x00\x01\x00\x00" + safeString16(properties.ascent) + safeString16(properties.descent) + "\x00\x00" + "\xFF\xFF" + "\x00\x00" + "\x00\x00" + "\x00\x00" + safeString16(properties.capHeight) + safeString16(Math.tan(properties.italicAngle) * properties.xHeight) + "\x00\x00" + "\x00\x00" + "\x00\x00" + "\x00\x00" + "\x00\x00" + "\x00\x00" + string16(numGlyphs)); + builder.addTable("hmtx", function fontFieldsHmtx() { + const charstrings = font.charstrings; + const cffWidths = font.cff ? font.cff.widths : null; + let hmtx = "\x00\x00\x00\x00"; + for (let i = 1, ii = numGlyphs; i < ii; i++) { + let width = 0; + if (charstrings) { + const charstring = charstrings[i - 1]; + width = "width" in charstring ? charstring.width : 0; + } else if (cffWidths) { + width = Math.ceil(cffWidths[i] || 0); + } + hmtx += string16(width) + string16(0); + } + return hmtx; + }()); + builder.addTable("maxp", "\x00\x00\x50\x00" + string16(numGlyphs)); + builder.addTable("name", createNameTable(fontName)); + builder.addTable("post", createPostTable(properties)); + return builder.toArray(); + } + get _spaceWidth() { + const possibleSpaceReplacements = ["space", "minus", "one", "i", "I"]; + let width; + for (const glyphName of possibleSpaceReplacements) { + if (glyphName in this.widths) { + width = this.widths[glyphName]; + break; + } + const glyphsUnicodeMap = getGlyphsUnicode(); + const glyphUnicode = glyphsUnicodeMap[glyphName]; + let charcode = 0; + if (this.composite && this.cMap.contains(glyphUnicode)) { + charcode = this.cMap.lookup(glyphUnicode); + if (typeof charcode === "string") { + charcode = convertCidString(glyphUnicode, charcode); + } + } + if (!charcode && this.toUnicode) { + charcode = this.toUnicode.charCodeOf(glyphUnicode); + } + if (charcode <= 0) { + charcode = glyphUnicode; + } + width = this.widths[charcode]; + if (width) { + break; + } + } + return shadow(this, "_spaceWidth", width || this.defaultWidth); + } + _charToGlyph(charcode, isSpace = false) { + let glyph = this._glyphCache[charcode]; + if (glyph?.isSpace === isSpace) { + return glyph; + } + let fontCharCode, width, operatorListId; + let widthCode = charcode; + if (this.cMap?.contains(charcode)) { + widthCode = this.cMap.lookup(charcode); + if (typeof widthCode === "string") { + widthCode = convertCidString(charcode, widthCode); + } + } + width = this.widths[widthCode]; + if (typeof width !== "number") { + width = this.defaultWidth; + } + const vmetric = this.vmetrics?.[widthCode]; + let unicode = this.toUnicode.get(charcode) || charcode; + if (typeof unicode === "number") { + unicode = String.fromCharCode(unicode); + } + let isInFont = this.toFontChar[charcode] !== undefined; + fontCharCode = this.toFontChar[charcode] || charcode; + if (this.missingFile) { + const glyphName = this.differences[charcode] || this.defaultEncoding[charcode]; + if ((glyphName === ".notdef" || glyphName === "") && this.type === "Type1") { + fontCharCode = 0x20; + if (glyphName === "") { + width ||= this._spaceWidth; + unicode = String.fromCharCode(fontCharCode); + } + } + fontCharCode = mapSpecialUnicodeValues(fontCharCode); + } + if (this.isType3Font) { + operatorListId = fontCharCode; + } + let accent = null; + if (this.seacMap?.[charcode]) { + isInFont = true; + const seac = this.seacMap[charcode]; + fontCharCode = seac.baseFontCharCode; + accent = { + fontChar: String.fromCodePoint(seac.accentFontCharCode), + offset: seac.accentOffset + }; + } + let fontChar = ""; + if (typeof fontCharCode === "number") { + if (fontCharCode <= 0x10ffff) { + fontChar = String.fromCodePoint(fontCharCode); + } else { + warn(`charToGlyph - invalid fontCharCode: ${fontCharCode}`); + } + } + if (this.missingFile && this.vertical && fontChar.length === 1) { + const vertical = getVerticalPresentationForm()[fontChar.charCodeAt(0)]; + if (vertical) { + fontChar = unicode = String.fromCharCode(vertical); + } + } + glyph = new fonts_Glyph(charcode, fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont); + return this._glyphCache[charcode] = glyph; + } + charsToGlyphs(chars) { + let glyphs = this._charsCache[chars]; + if (glyphs) { + return glyphs; + } + glyphs = []; + if (this.cMap) { + const c = Object.create(null), + ii = chars.length; + let i = 0; + while (i < ii) { + this.cMap.readCharCode(chars, i, c); + const { + charcode, + length + } = c; + i += length; + const glyph = this._charToGlyph(charcode, length === 1 && chars.charCodeAt(i - 1) === 0x20); + glyphs.push(glyph); + } + } else { + for (let i = 0, ii = chars.length; i < ii; ++i) { + const charcode = chars.charCodeAt(i); + const glyph = this._charToGlyph(charcode, charcode === 0x20); + glyphs.push(glyph); + } + } + return this._charsCache[chars] = glyphs; + } + getCharPositions(chars) { + const positions = []; + if (this.cMap) { + const c = Object.create(null); + let i = 0; + while (i < chars.length) { + this.cMap.readCharCode(chars, i, c); + const length = c.length; + positions.push([i, i + length]); + i += length; + } + } else { + for (let i = 0, ii = chars.length; i < ii; ++i) { + positions.push([i, i + 1]); + } + } + return positions; + } + get glyphCacheValues() { + return Object.values(this._glyphCache); + } + encodeString(str) { + const buffers = []; + const currentBuf = []; + const hasCurrentBufErrors = () => buffers.length % 2 === 1; + const getCharCode = this.toUnicode instanceof IdentityToUnicodeMap ? unicode => this.toUnicode.charCodeOf(unicode) : unicode => this.toUnicode.charCodeOf(String.fromCodePoint(unicode)); + for (let i = 0, ii = str.length; i < ii; i++) { + const unicode = str.codePointAt(i); + if (unicode > 0xd7ff && (unicode < 0xe000 || unicode > 0xfffd)) { + i++; + } + if (this.toUnicode) { + const charCode = getCharCode(unicode); + if (charCode !== -1) { + if (hasCurrentBufErrors()) { + buffers.push(currentBuf.join("")); + currentBuf.length = 0; + } + const charCodeLength = this.cMap ? this.cMap.getCharCodeLength(charCode) : 1; + for (let j = charCodeLength - 1; j >= 0; j--) { + currentBuf.push(String.fromCharCode(charCode >> 8 * j & 0xff)); + } + continue; + } + } + if (!hasCurrentBufErrors()) { + buffers.push(currentBuf.join("")); + currentBuf.length = 0; + } + currentBuf.push(String.fromCodePoint(unicode)); + } + buffers.push(currentBuf.join("")); + return buffers; + } +} +class ErrorFont { + constructor(error) { + this.error = error; + this.loadedName = "g_font_error"; + this.missingFile = true; + } + charsToGlyphs() { + return []; + } + encodeString(chars) { + return [chars]; + } + exportData(extraProperties = false) { + return { + error: this.error + }; + } +} + +;// ./src/core/pattern.js + + + + +const ShadingType = { + FUNCTION_BASED: 1, + AXIAL: 2, + RADIAL: 3, + FREE_FORM_MESH: 4, + LATTICE_FORM_MESH: 5, + COONS_PATCH_MESH: 6, + TENSOR_PATCH_MESH: 7 +}; +class Pattern { + constructor() { + unreachable("Cannot initialize Pattern."); + } + static parseShading(shading, xref, res, pdfFunctionFactory, localColorSpaceCache) { + const dict = shading instanceof BaseStream ? shading.dict : shading; + const type = dict.get("ShadingType"); + try { + switch (type) { + case ShadingType.AXIAL: + case ShadingType.RADIAL: + return new RadialAxialShading(dict, xref, res, pdfFunctionFactory, localColorSpaceCache); + case ShadingType.FREE_FORM_MESH: + case ShadingType.LATTICE_FORM_MESH: + case ShadingType.COONS_PATCH_MESH: + case ShadingType.TENSOR_PATCH_MESH: + return new MeshShading(shading, xref, res, pdfFunctionFactory, localColorSpaceCache); + default: + throw new FormatError("Unsupported ShadingType: " + type); + } + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn(ex); + return new DummyShading(); + } + } +} +class BaseShading { + static SMALL_NUMBER = 1e-6; + getIR() { + unreachable("Abstract method `getIR` called."); + } +} +class RadialAxialShading extends BaseShading { + constructor(dict, xref, resources, pdfFunctionFactory, localColorSpaceCache) { + super(); + this.shadingType = dict.get("ShadingType"); + let coordsLen = 0; + if (this.shadingType === ShadingType.AXIAL) { + coordsLen = 4; + } else if (this.shadingType === ShadingType.RADIAL) { + coordsLen = 6; + } + this.coordsArr = dict.getArray("Coords"); + if (!isNumberArray(this.coordsArr, coordsLen)) { + throw new FormatError("RadialAxialShading: Invalid /Coords array."); + } + const cs = ColorSpace.parse({ + cs: dict.getRaw("CS") || dict.getRaw("ColorSpace"), + xref, + resources, + pdfFunctionFactory, + localColorSpaceCache + }); + this.bbox = lookupNormalRect(dict.getArray("BBox"), null); + let t0 = 0.0, + t1 = 1.0; + const domainArr = dict.getArray("Domain"); + if (isNumberArray(domainArr, 2)) { + [t0, t1] = domainArr; + } + let extendStart = false, + extendEnd = false; + const extendArr = dict.getArray("Extend"); + if (isBooleanArray(extendArr, 2)) { + [extendStart, extendEnd] = extendArr; + } + if (this.shadingType === ShadingType.RADIAL && (!extendStart || !extendEnd)) { + const [x1, y1, r1, x2, y2, r2] = this.coordsArr; + const distance = Math.hypot(x1 - x2, y1 - y2); + if (r1 <= r2 + distance && r2 <= r1 + distance) { + warn("Unsupported radial gradient."); + } + } + this.extendStart = extendStart; + this.extendEnd = extendEnd; + const fnObj = dict.getRaw("Function"); + const fn = pdfFunctionFactory.createFromArray(fnObj); + const NUMBER_OF_SAMPLES = 840; + const step = (t1 - t0) / NUMBER_OF_SAMPLES; + const colorStops = this.colorStops = []; + if (t0 >= t1 || step <= 0) { + info("Bad shading domain."); + return; + } + const color = new Float32Array(cs.numComps), + ratio = new Float32Array(1); + let rgbColor; + let iBase = 0; + ratio[0] = t0; + fn(ratio, 0, color, 0); + let rgbBase = cs.getRgb(color, 0); + const cssColorBase = Util.makeHexColor(rgbBase[0], rgbBase[1], rgbBase[2]); + colorStops.push([0, cssColorBase]); + let iPrev = 1; + ratio[0] = t0 + step; + fn(ratio, 0, color, 0); + let rgbPrev = cs.getRgb(color, 0); + let maxSlopeR = rgbPrev[0] - rgbBase[0] + 1; + let maxSlopeG = rgbPrev[1] - rgbBase[1] + 1; + let maxSlopeB = rgbPrev[2] - rgbBase[2] + 1; + let minSlopeR = rgbPrev[0] - rgbBase[0] - 1; + let minSlopeG = rgbPrev[1] - rgbBase[1] - 1; + let minSlopeB = rgbPrev[2] - rgbBase[2] - 1; + for (let i = 2; i < NUMBER_OF_SAMPLES; i++) { + ratio[0] = t0 + i * step; + fn(ratio, 0, color, 0); + rgbColor = cs.getRgb(color, 0); + const run = i - iBase; + maxSlopeR = Math.min(maxSlopeR, (rgbColor[0] - rgbBase[0] + 1) / run); + maxSlopeG = Math.min(maxSlopeG, (rgbColor[1] - rgbBase[1] + 1) / run); + maxSlopeB = Math.min(maxSlopeB, (rgbColor[2] - rgbBase[2] + 1) / run); + minSlopeR = Math.max(minSlopeR, (rgbColor[0] - rgbBase[0] - 1) / run); + minSlopeG = Math.max(minSlopeG, (rgbColor[1] - rgbBase[1] - 1) / run); + minSlopeB = Math.max(minSlopeB, (rgbColor[2] - rgbBase[2] - 1) / run); + const slopesExist = minSlopeR <= maxSlopeR && minSlopeG <= maxSlopeG && minSlopeB <= maxSlopeB; + if (!slopesExist) { + const cssColor = Util.makeHexColor(rgbPrev[0], rgbPrev[1], rgbPrev[2]); + colorStops.push([iPrev / NUMBER_OF_SAMPLES, cssColor]); + maxSlopeR = rgbColor[0] - rgbPrev[0] + 1; + maxSlopeG = rgbColor[1] - rgbPrev[1] + 1; + maxSlopeB = rgbColor[2] - rgbPrev[2] + 1; + minSlopeR = rgbColor[0] - rgbPrev[0] - 1; + minSlopeG = rgbColor[1] - rgbPrev[1] - 1; + minSlopeB = rgbColor[2] - rgbPrev[2] - 1; + iBase = iPrev; + rgbBase = rgbPrev; + } + iPrev = i; + rgbPrev = rgbColor; + } + const cssColor = Util.makeHexColor(rgbPrev[0], rgbPrev[1], rgbPrev[2]); + colorStops.push([1, cssColor]); + let background = "transparent"; + if (dict.has("Background")) { + rgbColor = cs.getRgb(dict.get("Background"), 0); + background = Util.makeHexColor(rgbColor[0], rgbColor[1], rgbColor[2]); + } + if (!extendStart) { + colorStops.unshift([0, background]); + colorStops[1][0] += BaseShading.SMALL_NUMBER; + } + if (!extendEnd) { + colorStops.at(-1)[0] -= BaseShading.SMALL_NUMBER; + colorStops.push([1, background]); + } + this.colorStops = colorStops; + } + getIR() { + const { + coordsArr, + shadingType + } = this; + let type, p0, p1, r0, r1; + if (shadingType === ShadingType.AXIAL) { + p0 = [coordsArr[0], coordsArr[1]]; + p1 = [coordsArr[2], coordsArr[3]]; + r0 = null; + r1 = null; + type = "axial"; + } else if (shadingType === ShadingType.RADIAL) { + p0 = [coordsArr[0], coordsArr[1]]; + p1 = [coordsArr[3], coordsArr[4]]; + r0 = coordsArr[2]; + r1 = coordsArr[5]; + type = "radial"; + } else { + unreachable(`getPattern type unknown: ${shadingType}`); + } + return ["RadialAxial", type, this.bbox, this.colorStops, p0, p1, r0, r1]; + } +} +class MeshStreamReader { + constructor(stream, context) { + this.stream = stream; + this.context = context; + this.buffer = 0; + this.bufferLength = 0; + const numComps = context.numComps; + this.tmpCompsBuf = new Float32Array(numComps); + const csNumComps = context.colorSpace.numComps; + this.tmpCsCompsBuf = context.colorFn ? new Float32Array(csNumComps) : this.tmpCompsBuf; + } + get hasData() { + if (this.stream.end) { + return this.stream.pos < this.stream.end; + } + if (this.bufferLength > 0) { + return true; + } + const nextByte = this.stream.getByte(); + if (nextByte < 0) { + return false; + } + this.buffer = nextByte; + this.bufferLength = 8; + return true; + } + readBits(n) { + let buffer = this.buffer; + let bufferLength = this.bufferLength; + if (n === 32) { + if (bufferLength === 0) { + return (this.stream.getByte() << 24 | this.stream.getByte() << 16 | this.stream.getByte() << 8 | this.stream.getByte()) >>> 0; + } + buffer = buffer << 24 | this.stream.getByte() << 16 | this.stream.getByte() << 8 | this.stream.getByte(); + const nextByte = this.stream.getByte(); + this.buffer = nextByte & (1 << bufferLength) - 1; + return (buffer << 8 - bufferLength | (nextByte & 0xff) >> bufferLength) >>> 0; + } + if (n === 8 && bufferLength === 0) { + return this.stream.getByte(); + } + while (bufferLength < n) { + buffer = buffer << 8 | this.stream.getByte(); + bufferLength += 8; + } + bufferLength -= n; + this.bufferLength = bufferLength; + this.buffer = buffer & (1 << bufferLength) - 1; + return buffer >> bufferLength; + } + align() { + this.buffer = 0; + this.bufferLength = 0; + } + readFlag() { + return this.readBits(this.context.bitsPerFlag); + } + readCoordinate() { + const bitsPerCoordinate = this.context.bitsPerCoordinate; + const xi = this.readBits(bitsPerCoordinate); + const yi = this.readBits(bitsPerCoordinate); + const decode = this.context.decode; + const scale = bitsPerCoordinate < 32 ? 1 / ((1 << bitsPerCoordinate) - 1) : 2.3283064365386963e-10; + return [xi * scale * (decode[1] - decode[0]) + decode[0], yi * scale * (decode[3] - decode[2]) + decode[2]]; + } + readComponents() { + const numComps = this.context.numComps; + const bitsPerComponent = this.context.bitsPerComponent; + const scale = bitsPerComponent < 32 ? 1 / ((1 << bitsPerComponent) - 1) : 2.3283064365386963e-10; + const decode = this.context.decode; + const components = this.tmpCompsBuf; + for (let i = 0, j = 4; i < numComps; i++, j += 2) { + const ci = this.readBits(bitsPerComponent); + components[i] = ci * scale * (decode[j + 1] - decode[j]) + decode[j]; + } + const color = this.tmpCsCompsBuf; + if (this.context.colorFn) { + this.context.colorFn(components, 0, color, 0); + } + return this.context.colorSpace.getRgb(color, 0); + } +} +let bCache = Object.create(null); +function buildB(count) { + const lut = []; + for (let i = 0; i <= count; i++) { + const t = i / count, + t_ = 1 - t; + lut.push(new Float32Array([t_ ** 3, 3 * t * t_ ** 2, 3 * t ** 2 * t_, t ** 3])); + } + return lut; +} +function getB(count) { + return bCache[count] ||= buildB(count); +} +function clearPatternCaches() { + bCache = Object.create(null); +} +class MeshShading extends BaseShading { + static MIN_SPLIT_PATCH_CHUNKS_AMOUNT = 3; + static MAX_SPLIT_PATCH_CHUNKS_AMOUNT = 20; + static TRIANGLE_DENSITY = 20; + constructor(stream, xref, resources, pdfFunctionFactory, localColorSpaceCache) { + super(); + if (!(stream instanceof BaseStream)) { + throw new FormatError("Mesh data is not a stream"); + } + const dict = stream.dict; + this.shadingType = dict.get("ShadingType"); + this.bbox = lookupNormalRect(dict.getArray("BBox"), null); + const cs = ColorSpace.parse({ + cs: dict.getRaw("CS") || dict.getRaw("ColorSpace"), + xref, + resources, + pdfFunctionFactory, + localColorSpaceCache + }); + this.background = dict.has("Background") ? cs.getRgb(dict.get("Background"), 0) : null; + const fnObj = dict.getRaw("Function"); + const fn = fnObj ? pdfFunctionFactory.createFromArray(fnObj) : null; + this.coords = []; + this.colors = []; + this.figures = []; + const decodeContext = { + bitsPerCoordinate: dict.get("BitsPerCoordinate"), + bitsPerComponent: dict.get("BitsPerComponent"), + bitsPerFlag: dict.get("BitsPerFlag"), + decode: dict.getArray("Decode"), + colorFn: fn, + colorSpace: cs, + numComps: fn ? 1 : cs.numComps + }; + const reader = new MeshStreamReader(stream, decodeContext); + let patchMesh = false; + switch (this.shadingType) { + case ShadingType.FREE_FORM_MESH: + this._decodeType4Shading(reader); + break; + case ShadingType.LATTICE_FORM_MESH: + const verticesPerRow = dict.get("VerticesPerRow") | 0; + if (verticesPerRow < 2) { + throw new FormatError("Invalid VerticesPerRow"); + } + this._decodeType5Shading(reader, verticesPerRow); + break; + case ShadingType.COONS_PATCH_MESH: + this._decodeType6Shading(reader); + patchMesh = true; + break; + case ShadingType.TENSOR_PATCH_MESH: + this._decodeType7Shading(reader); + patchMesh = true; + break; + default: + unreachable("Unsupported mesh type."); + break; + } + if (patchMesh) { + this._updateBounds(); + for (let i = 0, ii = this.figures.length; i < ii; i++) { + this._buildFigureFromPatch(i); + } + } + this._updateBounds(); + this._packData(); + } + _decodeType4Shading(reader) { + const coords = this.coords; + const colors = this.colors; + const operators = []; + const ps = []; + let verticesLeft = 0; + while (reader.hasData) { + const f = reader.readFlag(); + const coord = reader.readCoordinate(); + const color = reader.readComponents(); + if (verticesLeft === 0) { + if (!(0 <= f && f <= 2)) { + throw new FormatError("Unknown type4 flag"); + } + switch (f) { + case 0: + verticesLeft = 3; + break; + case 1: + ps.push(ps.at(-2), ps.at(-1)); + verticesLeft = 1; + break; + case 2: + ps.push(ps.at(-3), ps.at(-1)); + verticesLeft = 1; + break; + } + operators.push(f); + } + ps.push(coords.length); + coords.push(coord); + colors.push(color); + verticesLeft--; + reader.align(); + } + this.figures.push({ + type: "triangles", + coords: new Int32Array(ps), + colors: new Int32Array(ps) + }); + } + _decodeType5Shading(reader, verticesPerRow) { + const coords = this.coords; + const colors = this.colors; + const ps = []; + while (reader.hasData) { + const coord = reader.readCoordinate(); + const color = reader.readComponents(); + ps.push(coords.length); + coords.push(coord); + colors.push(color); + } + this.figures.push({ + type: "lattice", + coords: new Int32Array(ps), + colors: new Int32Array(ps), + verticesPerRow + }); + } + _decodeType6Shading(reader) { + const coords = this.coords; + const colors = this.colors; + const ps = new Int32Array(16); + const cs = new Int32Array(4); + while (reader.hasData) { + const f = reader.readFlag(); + if (!(0 <= f && f <= 3)) { + throw new FormatError("Unknown type6 flag"); + } + const pi = coords.length; + for (let i = 0, ii = f !== 0 ? 8 : 12; i < ii; i++) { + coords.push(reader.readCoordinate()); + } + const ci = colors.length; + for (let i = 0, ii = f !== 0 ? 2 : 4; i < ii; i++) { + colors.push(reader.readComponents()); + } + let tmp1, tmp2, tmp3, tmp4; + switch (f) { + case 0: + ps[12] = pi + 3; + ps[13] = pi + 4; + ps[14] = pi + 5; + ps[15] = pi + 6; + ps[8] = pi + 2; + ps[11] = pi + 7; + ps[4] = pi + 1; + ps[7] = pi + 8; + ps[0] = pi; + ps[1] = pi + 11; + ps[2] = pi + 10; + ps[3] = pi + 9; + cs[2] = ci + 1; + cs[3] = ci + 2; + cs[0] = ci; + cs[1] = ci + 3; + break; + case 1: + tmp1 = ps[12]; + tmp2 = ps[13]; + tmp3 = ps[14]; + tmp4 = ps[15]; + ps[12] = tmp4; + ps[13] = pi + 0; + ps[14] = pi + 1; + ps[15] = pi + 2; + ps[8] = tmp3; + ps[11] = pi + 3; + ps[4] = tmp2; + ps[7] = pi + 4; + ps[0] = tmp1; + ps[1] = pi + 7; + ps[2] = pi + 6; + ps[3] = pi + 5; + tmp1 = cs[2]; + tmp2 = cs[3]; + cs[2] = tmp2; + cs[3] = ci; + cs[0] = tmp1; + cs[1] = ci + 1; + break; + case 2: + tmp1 = ps[15]; + tmp2 = ps[11]; + ps[12] = ps[3]; + ps[13] = pi + 0; + ps[14] = pi + 1; + ps[15] = pi + 2; + ps[8] = ps[7]; + ps[11] = pi + 3; + ps[4] = tmp2; + ps[7] = pi + 4; + ps[0] = tmp1; + ps[1] = pi + 7; + ps[2] = pi + 6; + ps[3] = pi + 5; + tmp1 = cs[3]; + cs[2] = cs[1]; + cs[3] = ci; + cs[0] = tmp1; + cs[1] = ci + 1; + break; + case 3: + ps[12] = ps[0]; + ps[13] = pi + 0; + ps[14] = pi + 1; + ps[15] = pi + 2; + ps[8] = ps[1]; + ps[11] = pi + 3; + ps[4] = ps[2]; + ps[7] = pi + 4; + ps[0] = ps[3]; + ps[1] = pi + 7; + ps[2] = pi + 6; + ps[3] = pi + 5; + cs[2] = cs[0]; + cs[3] = ci; + cs[0] = cs[1]; + cs[1] = ci + 1; + break; + } + ps[5] = coords.length; + coords.push([(-4 * coords[ps[0]][0] - coords[ps[15]][0] + 6 * (coords[ps[4]][0] + coords[ps[1]][0]) - 2 * (coords[ps[12]][0] + coords[ps[3]][0]) + 3 * (coords[ps[13]][0] + coords[ps[7]][0])) / 9, (-4 * coords[ps[0]][1] - coords[ps[15]][1] + 6 * (coords[ps[4]][1] + coords[ps[1]][1]) - 2 * (coords[ps[12]][1] + coords[ps[3]][1]) + 3 * (coords[ps[13]][1] + coords[ps[7]][1])) / 9]); + ps[6] = coords.length; + coords.push([(-4 * coords[ps[3]][0] - coords[ps[12]][0] + 6 * (coords[ps[2]][0] + coords[ps[7]][0]) - 2 * (coords[ps[0]][0] + coords[ps[15]][0]) + 3 * (coords[ps[4]][0] + coords[ps[14]][0])) / 9, (-4 * coords[ps[3]][1] - coords[ps[12]][1] + 6 * (coords[ps[2]][1] + coords[ps[7]][1]) - 2 * (coords[ps[0]][1] + coords[ps[15]][1]) + 3 * (coords[ps[4]][1] + coords[ps[14]][1])) / 9]); + ps[9] = coords.length; + coords.push([(-4 * coords[ps[12]][0] - coords[ps[3]][0] + 6 * (coords[ps[8]][0] + coords[ps[13]][0]) - 2 * (coords[ps[0]][0] + coords[ps[15]][0]) + 3 * (coords[ps[11]][0] + coords[ps[1]][0])) / 9, (-4 * coords[ps[12]][1] - coords[ps[3]][1] + 6 * (coords[ps[8]][1] + coords[ps[13]][1]) - 2 * (coords[ps[0]][1] + coords[ps[15]][1]) + 3 * (coords[ps[11]][1] + coords[ps[1]][1])) / 9]); + ps[10] = coords.length; + coords.push([(-4 * coords[ps[15]][0] - coords[ps[0]][0] + 6 * (coords[ps[11]][0] + coords[ps[14]][0]) - 2 * (coords[ps[12]][0] + coords[ps[3]][0]) + 3 * (coords[ps[2]][0] + coords[ps[8]][0])) / 9, (-4 * coords[ps[15]][1] - coords[ps[0]][1] + 6 * (coords[ps[11]][1] + coords[ps[14]][1]) - 2 * (coords[ps[12]][1] + coords[ps[3]][1]) + 3 * (coords[ps[2]][1] + coords[ps[8]][1])) / 9]); + this.figures.push({ + type: "patch", + coords: new Int32Array(ps), + colors: new Int32Array(cs) + }); + } + } + _decodeType7Shading(reader) { + const coords = this.coords; + const colors = this.colors; + const ps = new Int32Array(16); + const cs = new Int32Array(4); + while (reader.hasData) { + const f = reader.readFlag(); + if (!(0 <= f && f <= 3)) { + throw new FormatError("Unknown type7 flag"); + } + const pi = coords.length; + for (let i = 0, ii = f !== 0 ? 12 : 16; i < ii; i++) { + coords.push(reader.readCoordinate()); + } + const ci = colors.length; + for (let i = 0, ii = f !== 0 ? 2 : 4; i < ii; i++) { + colors.push(reader.readComponents()); + } + let tmp1, tmp2, tmp3, tmp4; + switch (f) { + case 0: + ps[12] = pi + 3; + ps[13] = pi + 4; + ps[14] = pi + 5; + ps[15] = pi + 6; + ps[8] = pi + 2; + ps[9] = pi + 13; + ps[10] = pi + 14; + ps[11] = pi + 7; + ps[4] = pi + 1; + ps[5] = pi + 12; + ps[6] = pi + 15; + ps[7] = pi + 8; + ps[0] = pi; + ps[1] = pi + 11; + ps[2] = pi + 10; + ps[3] = pi + 9; + cs[2] = ci + 1; + cs[3] = ci + 2; + cs[0] = ci; + cs[1] = ci + 3; + break; + case 1: + tmp1 = ps[12]; + tmp2 = ps[13]; + tmp3 = ps[14]; + tmp4 = ps[15]; + ps[12] = tmp4; + ps[13] = pi + 0; + ps[14] = pi + 1; + ps[15] = pi + 2; + ps[8] = tmp3; + ps[9] = pi + 9; + ps[10] = pi + 10; + ps[11] = pi + 3; + ps[4] = tmp2; + ps[5] = pi + 8; + ps[6] = pi + 11; + ps[7] = pi + 4; + ps[0] = tmp1; + ps[1] = pi + 7; + ps[2] = pi + 6; + ps[3] = pi + 5; + tmp1 = cs[2]; + tmp2 = cs[3]; + cs[2] = tmp2; + cs[3] = ci; + cs[0] = tmp1; + cs[1] = ci + 1; + break; + case 2: + tmp1 = ps[15]; + tmp2 = ps[11]; + ps[12] = ps[3]; + ps[13] = pi + 0; + ps[14] = pi + 1; + ps[15] = pi + 2; + ps[8] = ps[7]; + ps[9] = pi + 9; + ps[10] = pi + 10; + ps[11] = pi + 3; + ps[4] = tmp2; + ps[5] = pi + 8; + ps[6] = pi + 11; + ps[7] = pi + 4; + ps[0] = tmp1; + ps[1] = pi + 7; + ps[2] = pi + 6; + ps[3] = pi + 5; + tmp1 = cs[3]; + cs[2] = cs[1]; + cs[3] = ci; + cs[0] = tmp1; + cs[1] = ci + 1; + break; + case 3: + ps[12] = ps[0]; + ps[13] = pi + 0; + ps[14] = pi + 1; + ps[15] = pi + 2; + ps[8] = ps[1]; + ps[9] = pi + 9; + ps[10] = pi + 10; + ps[11] = pi + 3; + ps[4] = ps[2]; + ps[5] = pi + 8; + ps[6] = pi + 11; + ps[7] = pi + 4; + ps[0] = ps[3]; + ps[1] = pi + 7; + ps[2] = pi + 6; + ps[3] = pi + 5; + cs[2] = cs[0]; + cs[3] = ci; + cs[0] = cs[1]; + cs[1] = ci + 1; + break; + } + this.figures.push({ + type: "patch", + coords: new Int32Array(ps), + colors: new Int32Array(cs) + }); + } + } + _buildFigureFromPatch(index) { + const figure = this.figures[index]; + assert(figure.type === "patch", "Unexpected patch mesh figure"); + const coords = this.coords, + colors = this.colors; + const pi = figure.coords; + const ci = figure.colors; + const figureMinX = Math.min(coords[pi[0]][0], coords[pi[3]][0], coords[pi[12]][0], coords[pi[15]][0]); + const figureMinY = Math.min(coords[pi[0]][1], coords[pi[3]][1], coords[pi[12]][1], coords[pi[15]][1]); + const figureMaxX = Math.max(coords[pi[0]][0], coords[pi[3]][0], coords[pi[12]][0], coords[pi[15]][0]); + const figureMaxY = Math.max(coords[pi[0]][1], coords[pi[3]][1], coords[pi[12]][1], coords[pi[15]][1]); + let splitXBy = Math.ceil((figureMaxX - figureMinX) * MeshShading.TRIANGLE_DENSITY / (this.bounds[2] - this.bounds[0])); + splitXBy = Math.max(MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT, Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitXBy)); + let splitYBy = Math.ceil((figureMaxY - figureMinY) * MeshShading.TRIANGLE_DENSITY / (this.bounds[3] - this.bounds[1])); + splitYBy = Math.max(MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT, Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitYBy)); + const verticesPerRow = splitXBy + 1; + const figureCoords = new Int32Array((splitYBy + 1) * verticesPerRow); + const figureColors = new Int32Array((splitYBy + 1) * verticesPerRow); + let k = 0; + const cl = new Uint8Array(3), + cr = new Uint8Array(3); + const c0 = colors[ci[0]], + c1 = colors[ci[1]], + c2 = colors[ci[2]], + c3 = colors[ci[3]]; + const bRow = getB(splitYBy), + bCol = getB(splitXBy); + for (let row = 0; row <= splitYBy; row++) { + cl[0] = (c0[0] * (splitYBy - row) + c2[0] * row) / splitYBy | 0; + cl[1] = (c0[1] * (splitYBy - row) + c2[1] * row) / splitYBy | 0; + cl[2] = (c0[2] * (splitYBy - row) + c2[2] * row) / splitYBy | 0; + cr[0] = (c1[0] * (splitYBy - row) + c3[0] * row) / splitYBy | 0; + cr[1] = (c1[1] * (splitYBy - row) + c3[1] * row) / splitYBy | 0; + cr[2] = (c1[2] * (splitYBy - row) + c3[2] * row) / splitYBy | 0; + for (let col = 0; col <= splitXBy; col++, k++) { + if ((row === 0 || row === splitYBy) && (col === 0 || col === splitXBy)) { + continue; + } + let x = 0, + y = 0; + let q = 0; + for (let i = 0; i <= 3; i++) { + for (let j = 0; j <= 3; j++, q++) { + const m = bRow[row][i] * bCol[col][j]; + x += coords[pi[q]][0] * m; + y += coords[pi[q]][1] * m; + } + } + figureCoords[k] = coords.length; + coords.push([x, y]); + figureColors[k] = colors.length; + const newColor = new Uint8Array(3); + newColor[0] = (cl[0] * (splitXBy - col) + cr[0] * col) / splitXBy | 0; + newColor[1] = (cl[1] * (splitXBy - col) + cr[1] * col) / splitXBy | 0; + newColor[2] = (cl[2] * (splitXBy - col) + cr[2] * col) / splitXBy | 0; + colors.push(newColor); + } + } + figureCoords[0] = pi[0]; + figureColors[0] = ci[0]; + figureCoords[splitXBy] = pi[3]; + figureColors[splitXBy] = ci[1]; + figureCoords[verticesPerRow * splitYBy] = pi[12]; + figureColors[verticesPerRow * splitYBy] = ci[2]; + figureCoords[verticesPerRow * splitYBy + splitXBy] = pi[15]; + figureColors[verticesPerRow * splitYBy + splitXBy] = ci[3]; + this.figures[index] = { + type: "lattice", + coords: figureCoords, + colors: figureColors, + verticesPerRow + }; + } + _updateBounds() { + let minX = this.coords[0][0], + minY = this.coords[0][1], + maxX = minX, + maxY = minY; + for (let i = 1, ii = this.coords.length; i < ii; i++) { + const x = this.coords[i][0], + y = this.coords[i][1]; + minX = minX > x ? x : minX; + minY = minY > y ? y : minY; + maxX = maxX < x ? x : maxX; + maxY = maxY < y ? y : maxY; + } + this.bounds = [minX, minY, maxX, maxY]; + } + _packData() { + let i, ii, j, jj; + const coords = this.coords; + const coordsPacked = new Float32Array(coords.length * 2); + for (i = 0, j = 0, ii = coords.length; i < ii; i++) { + const xy = coords[i]; + coordsPacked[j++] = xy[0]; + coordsPacked[j++] = xy[1]; + } + this.coords = coordsPacked; + const colors = this.colors; + const colorsPacked = new Uint8Array(colors.length * 3); + for (i = 0, j = 0, ii = colors.length; i < ii; i++) { + const c = colors[i]; + colorsPacked[j++] = c[0]; + colorsPacked[j++] = c[1]; + colorsPacked[j++] = c[2]; + } + this.colors = colorsPacked; + const figures = this.figures; + for (i = 0, ii = figures.length; i < ii; i++) { + const figure = figures[i], + ps = figure.coords, + cs = figure.colors; + for (j = 0, jj = ps.length; j < jj; j++) { + ps[j] *= 2; + cs[j] *= 3; + } + } + } + getIR() { + const { + bounds + } = this; + if (bounds[2] - bounds[0] === 0 || bounds[3] - bounds[1] === 0) { + throw new FormatError(`Invalid MeshShading bounds: [${bounds}].`); + } + return ["Mesh", this.shadingType, this.coords, this.colors, this.figures, bounds, this.bbox, this.background]; + } +} +class DummyShading extends BaseShading { + getIR() { + return ["Dummy"]; + } +} +function getTilingPatternIR(operatorList, dict, color) { + const matrix = lookupMatrix(dict.getArray("Matrix"), IDENTITY_MATRIX); + const bbox = lookupNormalRect(dict.getArray("BBox"), null); + if (!bbox || bbox[2] - bbox[0] === 0 || bbox[3] - bbox[1] === 0) { + throw new FormatError(`Invalid getTilingPatternIR /BBox array.`); + } + const xstep = dict.get("XStep"); + if (typeof xstep !== "number") { + throw new FormatError(`Invalid getTilingPatternIR /XStep value.`); + } + const ystep = dict.get("YStep"); + if (typeof ystep !== "number") { + throw new FormatError(`Invalid getTilingPatternIR /YStep value.`); + } + const paintType = dict.get("PaintType"); + if (!Number.isInteger(paintType)) { + throw new FormatError(`Invalid getTilingPatternIR /PaintType value.`); + } + const tilingType = dict.get("TilingType"); + if (!Number.isInteger(tilingType)) { + throw new FormatError(`Invalid getTilingPatternIR /TilingType value.`); + } + return ["TilingPattern", color, operatorList, matrix, bbox, xstep, ystep, paintType, tilingType]; +} + +;// ./src/core/calibri_factors.js +const CalibriBoldFactors = [1.3877, 1, 1, 1, 0.97801, 0.92482, 0.89552, 0.91133, 0.81988, 0.97566, 0.98152, 0.93548, 0.93548, 1.2798, 0.85284, 0.92794, 1, 0.96134, 1.54657, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.82845, 0.82845, 0.85284, 0.85284, 0.85284, 0.75859, 0.92138, 0.83908, 0.7762, 0.73293, 0.87289, 0.73133, 0.7514, 0.81921, 0.87356, 0.95958, 0.59526, 0.75727, 0.69225, 1.04924, 0.9121, 0.86943, 0.79795, 0.88198, 0.77958, 0.70864, 0.81055, 0.90399, 0.88653, 0.96017, 0.82577, 0.77892, 0.78257, 0.97507, 1.54657, 0.97507, 0.85284, 0.89552, 0.90176, 0.88762, 0.8785, 0.75241, 0.8785, 0.90518, 0.95015, 0.77618, 0.8785, 0.88401, 0.91916, 0.86304, 0.88401, 0.91488, 0.8785, 0.8801, 0.8785, 0.8785, 0.91343, 0.7173, 1.04106, 0.8785, 0.85075, 0.95794, 0.82616, 0.85162, 0.79492, 0.88331, 1.69808, 0.88331, 0.85284, 0.97801, 0.89552, 0.91133, 0.89552, 0.91133, 1.7801, 0.89552, 1.24487, 1.13254, 1.12401, 0.96839, 0.85284, 0.68787, 0.70645, 0.85592, 0.90747, 1.01466, 1.0088, 0.90323, 1, 1.07463, 1, 0.91056, 0.75806, 1.19118, 0.96839, 0.78864, 0.82845, 0.84133, 0.75859, 0.83908, 0.83908, 0.83908, 0.83908, 0.83908, 0.83908, 0.77539, 0.73293, 0.73133, 0.73133, 0.73133, 0.73133, 0.95958, 0.95958, 0.95958, 0.95958, 0.88506, 0.9121, 0.86943, 0.86943, 0.86943, 0.86943, 0.86943, 0.85284, 0.87508, 0.90399, 0.90399, 0.90399, 0.90399, 0.77892, 0.79795, 0.90807, 0.88762, 0.88762, 0.88762, 0.88762, 0.88762, 0.88762, 0.8715, 0.75241, 0.90518, 0.90518, 0.90518, 0.90518, 0.88401, 0.88401, 0.88401, 0.88401, 0.8785, 0.8785, 0.8801, 0.8801, 0.8801, 0.8801, 0.8801, 0.90747, 0.89049, 0.8785, 0.8785, 0.8785, 0.8785, 0.85162, 0.8785, 0.85162, 0.83908, 0.88762, 0.83908, 0.88762, 0.83908, 0.88762, 0.73293, 0.75241, 0.73293, 0.75241, 0.73293, 0.75241, 0.73293, 0.75241, 0.87289, 0.83016, 0.88506, 0.93125, 0.73133, 0.90518, 0.73133, 0.90518, 0.73133, 0.90518, 0.73133, 0.90518, 0.73133, 0.90518, 0.81921, 0.77618, 0.81921, 0.77618, 0.81921, 0.77618, 1, 1, 0.87356, 0.8785, 0.91075, 0.89608, 0.95958, 0.88401, 0.95958, 0.88401, 0.95958, 0.88401, 0.95958, 0.88401, 0.95958, 0.88401, 0.76229, 0.90167, 0.59526, 0.91916, 1, 1, 0.86304, 0.69225, 0.88401, 1, 1, 0.70424, 0.79468, 0.91926, 0.88175, 0.70823, 0.94903, 0.9121, 0.8785, 1, 1, 0.9121, 0.8785, 0.87802, 0.88656, 0.8785, 0.86943, 0.8801, 0.86943, 0.8801, 0.86943, 0.8801, 0.87402, 0.89291, 0.77958, 0.91343, 1, 1, 0.77958, 0.91343, 0.70864, 0.7173, 0.70864, 0.7173, 0.70864, 0.7173, 0.70864, 0.7173, 1, 1, 0.81055, 0.75841, 0.81055, 1.06452, 0.90399, 0.8785, 0.90399, 0.8785, 0.90399, 0.8785, 0.90399, 0.8785, 0.90399, 0.8785, 0.90399, 0.8785, 0.96017, 0.95794, 0.77892, 0.85162, 0.77892, 0.78257, 0.79492, 0.78257, 0.79492, 0.78257, 0.79492, 0.9297, 0.56892, 0.83908, 0.88762, 0.77539, 0.8715, 0.87508, 0.89049, 1, 1, 0.81055, 1.04106, 1.20528, 1.20528, 1, 1.15543, 0.70674, 0.98387, 0.94721, 1.33431, 1.45894, 0.95161, 1.06303, 0.83908, 0.80352, 0.57184, 0.6965, 0.56289, 0.82001, 0.56029, 0.81235, 1.02988, 0.83908, 0.7762, 0.68156, 0.80367, 0.73133, 0.78257, 0.87356, 0.86943, 0.95958, 0.75727, 0.89019, 1.04924, 0.9121, 0.7648, 0.86943, 0.87356, 0.79795, 0.78275, 0.81055, 0.77892, 0.9762, 0.82577, 0.99819, 0.84896, 0.95958, 0.77892, 0.96108, 1.01407, 0.89049, 1.02988, 0.94211, 0.96108, 0.8936, 0.84021, 0.87842, 0.96399, 0.79109, 0.89049, 1.00813, 1.02988, 0.86077, 0.87445, 0.92099, 0.84723, 0.86513, 0.8801, 0.75638, 0.85714, 0.78216, 0.79586, 0.87965, 0.94211, 0.97747, 0.78287, 0.97926, 0.84971, 1.02988, 0.94211, 0.8801, 0.94211, 0.84971, 0.73133, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90264, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90518, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90548, 1, 1, 1, 1, 1, 1, 0.96017, 0.95794, 0.96017, 0.95794, 0.96017, 0.95794, 0.77892, 0.85162, 1, 1, 0.89552, 0.90527, 1, 0.90363, 0.92794, 0.92794, 0.92794, 0.92794, 0.87012, 0.87012, 0.87012, 0.89552, 0.89552, 1.42259, 0.71143, 1.06152, 1, 1, 1.03372, 1.03372, 0.97171, 1.4956, 2.2807, 0.93835, 0.83406, 0.91133, 0.84107, 0.91133, 1, 1, 1, 0.72021, 1, 1.23108, 0.83489, 0.88525, 0.88525, 0.81499, 0.90527, 1.81055, 0.90527, 1.81055, 1.31006, 1.53711, 0.94434, 1.08696, 1, 0.95018, 0.77192, 0.85284, 0.90747, 1.17534, 0.69825, 0.9716, 1.37077, 0.90747, 0.90747, 0.85356, 0.90747, 0.90747, 1.44947, 0.85284, 0.8941, 0.8941, 0.70572, 0.8, 0.70572, 0.70572, 0.70572, 0.70572, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.99862, 0.99862, 1, 1, 1, 1, 1, 1.08004, 0.91027, 1, 1, 1, 0.99862, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90727, 0.90727, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const CalibriBoldMetrics = { + lineHeight: 1.2207, + lineGap: 0.2207 +}; +const CalibriBoldItalicFactors = [1.3877, 1, 1, 1, 0.97801, 0.92482, 0.89552, 0.91133, 0.81988, 0.97566, 0.98152, 0.93548, 0.93548, 1.2798, 0.85284, 0.92794, 1, 0.96134, 1.56239, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.82845, 0.82845, 0.85284, 0.85284, 0.85284, 0.75859, 0.92138, 0.83908, 0.7762, 0.71805, 0.87289, 0.73133, 0.7514, 0.81921, 0.87356, 0.95958, 0.59526, 0.75727, 0.69225, 1.04924, 0.90872, 0.85938, 0.79795, 0.87068, 0.77958, 0.69766, 0.81055, 0.90399, 0.88653, 0.96068, 0.82577, 0.77892, 0.78257, 0.97507, 1.529, 0.97507, 0.85284, 0.89552, 0.90176, 0.94908, 0.86411, 0.74012, 0.86411, 0.88323, 0.95015, 0.86411, 0.86331, 0.88401, 0.91916, 0.86304, 0.88401, 0.9039, 0.86331, 0.86331, 0.86411, 0.86411, 0.90464, 0.70852, 1.04106, 0.86331, 0.84372, 0.95794, 0.82616, 0.84548, 0.79492, 0.88331, 1.69808, 0.88331, 0.85284, 0.97801, 0.89552, 0.91133, 0.89552, 0.91133, 1.7801, 0.89552, 1.24487, 1.13254, 1.19129, 0.96839, 0.85284, 0.68787, 0.70645, 0.85592, 0.90747, 1.01466, 1.0088, 0.90323, 1, 1.07463, 1, 0.91056, 0.75806, 1.19118, 0.96839, 0.78864, 0.82845, 0.84133, 0.75859, 0.83908, 0.83908, 0.83908, 0.83908, 0.83908, 0.83908, 0.77539, 0.71805, 0.73133, 0.73133, 0.73133, 0.73133, 0.95958, 0.95958, 0.95958, 0.95958, 0.88506, 0.90872, 0.85938, 0.85938, 0.85938, 0.85938, 0.85938, 0.85284, 0.87068, 0.90399, 0.90399, 0.90399, 0.90399, 0.77892, 0.79795, 0.90807, 0.94908, 0.94908, 0.94908, 0.94908, 0.94908, 0.94908, 0.85887, 0.74012, 0.88323, 0.88323, 0.88323, 0.88323, 0.88401, 0.88401, 0.88401, 0.88401, 0.8785, 0.86331, 0.86331, 0.86331, 0.86331, 0.86331, 0.86331, 0.90747, 0.89049, 0.86331, 0.86331, 0.86331, 0.86331, 0.84548, 0.86411, 0.84548, 0.83908, 0.94908, 0.83908, 0.94908, 0.83908, 0.94908, 0.71805, 0.74012, 0.71805, 0.74012, 0.71805, 0.74012, 0.71805, 0.74012, 0.87289, 0.79538, 0.88506, 0.92726, 0.73133, 0.88323, 0.73133, 0.88323, 0.73133, 0.88323, 0.73133, 0.88323, 0.73133, 0.88323, 0.81921, 0.86411, 0.81921, 0.86411, 0.81921, 0.86411, 1, 1, 0.87356, 0.86331, 0.91075, 0.8777, 0.95958, 0.88401, 0.95958, 0.88401, 0.95958, 0.88401, 0.95958, 0.88401, 0.95958, 0.88401, 0.76467, 0.90167, 0.59526, 0.91916, 1, 1, 0.86304, 0.69225, 0.88401, 1, 1, 0.70424, 0.77312, 0.91926, 0.88175, 0.70823, 0.94903, 0.90872, 0.86331, 1, 1, 0.90872, 0.86331, 0.86906, 0.88116, 0.86331, 0.85938, 0.86331, 0.85938, 0.86331, 0.85938, 0.86331, 0.87402, 0.86549, 0.77958, 0.90464, 1, 1, 0.77958, 0.90464, 0.69766, 0.70852, 0.69766, 0.70852, 0.69766, 0.70852, 0.69766, 0.70852, 1, 1, 0.81055, 0.75841, 0.81055, 1.06452, 0.90399, 0.86331, 0.90399, 0.86331, 0.90399, 0.86331, 0.90399, 0.86331, 0.90399, 0.86331, 0.90399, 0.86331, 0.96068, 0.95794, 0.77892, 0.84548, 0.77892, 0.78257, 0.79492, 0.78257, 0.79492, 0.78257, 0.79492, 0.9297, 0.56892, 0.83908, 0.94908, 0.77539, 0.85887, 0.87068, 0.89049, 1, 1, 0.81055, 1.04106, 1.20528, 1.20528, 1, 1.15543, 0.70088, 0.98387, 0.94721, 1.33431, 1.45894, 0.95161, 1.48387, 0.83908, 0.80352, 0.57118, 0.6965, 0.56347, 0.79179, 0.55853, 0.80346, 1.02988, 0.83908, 0.7762, 0.67174, 0.86036, 0.73133, 0.78257, 0.87356, 0.86441, 0.95958, 0.75727, 0.89019, 1.04924, 0.90872, 0.74889, 0.85938, 0.87891, 0.79795, 0.7957, 0.81055, 0.77892, 0.97447, 0.82577, 0.97466, 0.87179, 0.95958, 0.77892, 0.94252, 0.95612, 0.8753, 1.02988, 0.92733, 0.94252, 0.87411, 0.84021, 0.8728, 0.95612, 0.74081, 0.8753, 1.02189, 1.02988, 0.84814, 0.87445, 0.91822, 0.84723, 0.85668, 0.86331, 0.81344, 0.87581, 0.76422, 0.82046, 0.96057, 0.92733, 0.99375, 0.78022, 0.95452, 0.86015, 1.02988, 0.92733, 0.86331, 0.92733, 0.86015, 0.73133, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90631, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.88323, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.85174, 1, 1, 1, 1, 1, 1, 0.96068, 0.95794, 0.96068, 0.95794, 0.96068, 0.95794, 0.77892, 0.84548, 1, 1, 0.89552, 0.90527, 1, 0.90363, 0.92794, 0.92794, 0.92794, 0.89807, 0.87012, 0.87012, 0.87012, 0.89552, 0.89552, 1.42259, 0.71094, 1.06152, 1, 1, 1.03372, 1.03372, 0.97171, 1.4956, 2.2807, 0.92972, 0.83406, 0.91133, 0.83326, 0.91133, 1, 1, 1, 0.72021, 1, 1.23108, 0.83489, 0.88525, 0.88525, 0.81499, 0.90616, 1.81055, 0.90527, 1.81055, 1.3107, 1.53711, 0.94434, 1.08696, 1, 0.95018, 0.77192, 0.85284, 0.90747, 1.17534, 0.69825, 0.9716, 1.37077, 0.90747, 0.90747, 0.85356, 0.90747, 0.90747, 1.44947, 0.85284, 0.8941, 0.8941, 0.70572, 0.8, 0.70572, 0.70572, 0.70572, 0.70572, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.99862, 0.99862, 1, 1, 1, 1, 1, 1.08004, 0.91027, 1, 1, 1, 0.99862, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90727, 0.90727, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const CalibriBoldItalicMetrics = { + lineHeight: 1.2207, + lineGap: 0.2207 +}; +const CalibriItalicFactors = [1.3877, 1, 1, 1, 1.17223, 1.1293, 0.89552, 0.91133, 0.80395, 1.02269, 1.15601, 0.91056, 0.91056, 1.2798, 0.85284, 0.89807, 1, 0.90861, 1.39543, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.96309, 0.96309, 0.85284, 0.85284, 0.85284, 0.83319, 0.88071, 0.8675, 0.81552, 0.72346, 0.85193, 0.73206, 0.7522, 0.81105, 0.86275, 0.90685, 0.6377, 0.77892, 0.75593, 1.02638, 0.89249, 0.84118, 0.77452, 0.85374, 0.75186, 0.67789, 0.79776, 0.88844, 0.85066, 0.94309, 0.77818, 0.7306, 0.76659, 1.10369, 1.38313, 1.10369, 1.06139, 0.89552, 0.8739, 0.9245, 0.9245, 0.83203, 0.9245, 0.85865, 1.09842, 0.9245, 0.9245, 1.03297, 1.07692, 0.90918, 1.03297, 0.94959, 0.9245, 0.92274, 0.9245, 0.9245, 1.02933, 0.77832, 1.20562, 0.9245, 0.8916, 0.98986, 0.86621, 0.89453, 0.79004, 0.94152, 1.77256, 0.94152, 0.85284, 0.97801, 0.89552, 0.91133, 0.89552, 0.91133, 1.91729, 0.89552, 1.17889, 1.13254, 1.16359, 0.92098, 0.85284, 0.68787, 0.71353, 0.84737, 0.90747, 1.0088, 1.0044, 0.87683, 1, 1.09091, 1, 0.92229, 0.739, 1.15642, 0.92098, 0.76288, 0.80504, 0.80972, 0.75859, 0.8675, 0.8675, 0.8675, 0.8675, 0.8675, 0.8675, 0.76318, 0.72346, 0.73206, 0.73206, 0.73206, 0.73206, 0.90685, 0.90685, 0.90685, 0.90685, 0.86477, 0.89249, 0.84118, 0.84118, 0.84118, 0.84118, 0.84118, 0.85284, 0.84557, 0.88844, 0.88844, 0.88844, 0.88844, 0.7306, 0.77452, 0.86331, 0.9245, 0.9245, 0.9245, 0.9245, 0.9245, 0.9245, 0.84843, 0.83203, 0.85865, 0.85865, 0.85865, 0.85865, 0.82601, 0.82601, 0.82601, 0.82601, 0.94469, 0.9245, 0.92274, 0.92274, 0.92274, 0.92274, 0.92274, 0.90747, 0.86651, 0.9245, 0.9245, 0.9245, 0.9245, 0.89453, 0.9245, 0.89453, 0.8675, 0.9245, 0.8675, 0.9245, 0.8675, 0.9245, 0.72346, 0.83203, 0.72346, 0.83203, 0.72346, 0.83203, 0.72346, 0.83203, 0.85193, 0.8875, 0.86477, 0.99034, 0.73206, 0.85865, 0.73206, 0.85865, 0.73206, 0.85865, 0.73206, 0.85865, 0.73206, 0.85865, 0.81105, 0.9245, 0.81105, 0.9245, 0.81105, 0.9245, 1, 1, 0.86275, 0.9245, 0.90872, 0.93591, 0.90685, 0.82601, 0.90685, 0.82601, 0.90685, 0.82601, 0.90685, 1.03297, 0.90685, 0.82601, 0.77896, 1.05611, 0.6377, 1.07692, 1, 1, 0.90918, 0.75593, 1.03297, 1, 1, 0.76032, 0.9375, 0.98156, 0.93407, 0.77261, 1.11429, 0.89249, 0.9245, 1, 1, 0.89249, 0.9245, 0.92534, 0.86698, 0.9245, 0.84118, 0.92274, 0.84118, 0.92274, 0.84118, 0.92274, 0.8667, 0.86291, 0.75186, 1.02933, 1, 1, 0.75186, 1.02933, 0.67789, 0.77832, 0.67789, 0.77832, 0.67789, 0.77832, 0.67789, 0.77832, 1, 1, 0.79776, 0.97655, 0.79776, 1.23023, 0.88844, 0.9245, 0.88844, 0.9245, 0.88844, 0.9245, 0.88844, 0.9245, 0.88844, 0.9245, 0.88844, 0.9245, 0.94309, 0.98986, 0.7306, 0.89453, 0.7306, 0.76659, 0.79004, 0.76659, 0.79004, 0.76659, 0.79004, 1.09231, 0.54873, 0.8675, 0.9245, 0.76318, 0.84843, 0.84557, 0.86651, 1, 1, 0.79776, 1.20562, 1.18622, 1.18622, 1, 1.1437, 0.67009, 0.96334, 0.93695, 1.35191, 1.40909, 0.95161, 1.48387, 0.8675, 0.90861, 0.6192, 0.7363, 0.64824, 0.82411, 0.56321, 0.85696, 1.23516, 0.8675, 0.81552, 0.7286, 0.84134, 0.73206, 0.76659, 0.86275, 0.84369, 0.90685, 0.77892, 0.85871, 1.02638, 0.89249, 0.75828, 0.84118, 0.85984, 0.77452, 0.76466, 0.79776, 0.7306, 0.90782, 0.77818, 0.903, 0.87291, 0.90685, 0.7306, 0.99058, 1.03667, 0.94635, 1.23516, 0.9849, 0.99058, 0.92393, 0.8916, 0.942, 1.03667, 0.75026, 0.94635, 1.0297, 1.23516, 0.90918, 0.94048, 0.98217, 0.89746, 0.84153, 0.92274, 0.82507, 0.88832, 0.84438, 0.88178, 1.03525, 0.9849, 1.00225, 0.78086, 0.97248, 0.89404, 1.23516, 0.9849, 0.92274, 0.9849, 0.89404, 0.73206, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.89693, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.85865, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.90933, 1, 1, 1, 1, 1, 1, 0.94309, 0.98986, 0.94309, 0.98986, 0.94309, 0.98986, 0.7306, 0.89453, 1, 1, 0.89552, 0.90527, 1, 0.90186, 1.12308, 1.12308, 1.12308, 1.12308, 1.2566, 1.2566, 1.2566, 0.89552, 0.89552, 1.42259, 0.68994, 1.03809, 1, 1, 1.0176, 1.0176, 1.11523, 1.4956, 2.01462, 0.97858, 0.82616, 0.91133, 0.83437, 0.91133, 1, 1, 1, 0.70508, 1, 1.23108, 0.79801, 0.84426, 0.84426, 0.774, 0.90572, 1.81055, 0.90749, 1.81055, 1.28809, 1.55469, 0.94434, 1.07806, 1, 0.97094, 0.7589, 0.85284, 0.90747, 1.19658, 0.69825, 0.97622, 1.33512, 0.90747, 0.90747, 0.85284, 0.90747, 0.90747, 1.44947, 0.85284, 0.8941, 0.8941, 0.70572, 0.8, 0.70572, 0.70572, 0.70572, 0.70572, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.99862, 0.99862, 1, 1, 1, 1, 1, 1.0336, 0.91027, 1, 1, 1, 0.99862, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.05859, 1.05859, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const CalibriItalicMetrics = { + lineHeight: 1.2207, + lineGap: 0.2207 +}; +const CalibriRegularFactors = [1.3877, 1, 1, 1, 1.17223, 1.1293, 0.89552, 0.91133, 0.80395, 1.02269, 1.15601, 0.91056, 0.91056, 1.2798, 0.85284, 0.89807, 1, 0.90861, 1.39016, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.91133, 0.96309, 0.96309, 0.85284, 0.85284, 0.85284, 0.83319, 0.88071, 0.8675, 0.81552, 0.73834, 0.85193, 0.73206, 0.7522, 0.81105, 0.86275, 0.90685, 0.6377, 0.77892, 0.75593, 1.02638, 0.89385, 0.85122, 0.77452, 0.86503, 0.75186, 0.68887, 0.79776, 0.88844, 0.85066, 0.94258, 0.77818, 0.7306, 0.76659, 1.10369, 1.39016, 1.10369, 1.06139, 0.89552, 0.8739, 0.86128, 0.94469, 0.8457, 0.94469, 0.89464, 1.09842, 0.84636, 0.94469, 1.03297, 1.07692, 0.90918, 1.03297, 0.95897, 0.94469, 0.9482, 0.94469, 0.94469, 1.04692, 0.78223, 1.20562, 0.94469, 0.90332, 0.98986, 0.86621, 0.90527, 0.79004, 0.94152, 1.77256, 0.94152, 0.85284, 0.97801, 0.89552, 0.91133, 0.89552, 0.91133, 1.91729, 0.89552, 1.17889, 1.13254, 1.08707, 0.92098, 0.85284, 0.68787, 0.71353, 0.84737, 0.90747, 1.0088, 1.0044, 0.87683, 1, 1.09091, 1, 0.92229, 0.739, 1.15642, 0.92098, 0.76288, 0.80504, 0.80972, 0.75859, 0.8675, 0.8675, 0.8675, 0.8675, 0.8675, 0.8675, 0.76318, 0.73834, 0.73206, 0.73206, 0.73206, 0.73206, 0.90685, 0.90685, 0.90685, 0.90685, 0.86477, 0.89385, 0.85122, 0.85122, 0.85122, 0.85122, 0.85122, 0.85284, 0.85311, 0.88844, 0.88844, 0.88844, 0.88844, 0.7306, 0.77452, 0.86331, 0.86128, 0.86128, 0.86128, 0.86128, 0.86128, 0.86128, 0.8693, 0.8457, 0.89464, 0.89464, 0.89464, 0.89464, 0.82601, 0.82601, 0.82601, 0.82601, 0.94469, 0.94469, 0.9482, 0.9482, 0.9482, 0.9482, 0.9482, 0.90747, 0.86651, 0.94469, 0.94469, 0.94469, 0.94469, 0.90527, 0.94469, 0.90527, 0.8675, 0.86128, 0.8675, 0.86128, 0.8675, 0.86128, 0.73834, 0.8457, 0.73834, 0.8457, 0.73834, 0.8457, 0.73834, 0.8457, 0.85193, 0.92454, 0.86477, 0.9921, 0.73206, 0.89464, 0.73206, 0.89464, 0.73206, 0.89464, 0.73206, 0.89464, 0.73206, 0.89464, 0.81105, 0.84636, 0.81105, 0.84636, 0.81105, 0.84636, 1, 1, 0.86275, 0.94469, 0.90872, 0.95786, 0.90685, 0.82601, 0.90685, 0.82601, 0.90685, 0.82601, 0.90685, 1.03297, 0.90685, 0.82601, 0.77741, 1.05611, 0.6377, 1.07692, 1, 1, 0.90918, 0.75593, 1.03297, 1, 1, 0.76032, 0.90452, 0.98156, 1.11842, 0.77261, 1.11429, 0.89385, 0.94469, 1, 1, 0.89385, 0.94469, 0.95877, 0.86901, 0.94469, 0.85122, 0.9482, 0.85122, 0.9482, 0.85122, 0.9482, 0.8667, 0.90016, 0.75186, 1.04692, 1, 1, 0.75186, 1.04692, 0.68887, 0.78223, 0.68887, 0.78223, 0.68887, 0.78223, 0.68887, 0.78223, 1, 1, 0.79776, 0.92188, 0.79776, 1.23023, 0.88844, 0.94469, 0.88844, 0.94469, 0.88844, 0.94469, 0.88844, 0.94469, 0.88844, 0.94469, 0.88844, 0.94469, 0.94258, 0.98986, 0.7306, 0.90527, 0.7306, 0.76659, 0.79004, 0.76659, 0.79004, 0.76659, 0.79004, 1.09231, 0.54873, 0.8675, 0.86128, 0.76318, 0.8693, 0.85311, 0.86651, 1, 1, 0.79776, 1.20562, 1.18622, 1.18622, 1, 1.1437, 0.67742, 0.96334, 0.93695, 1.35191, 1.40909, 0.95161, 1.48387, 0.86686, 0.90861, 0.62267, 0.74359, 0.65649, 0.85498, 0.56963, 0.88254, 1.23516, 0.8675, 0.81552, 0.75443, 0.84503, 0.73206, 0.76659, 0.86275, 0.85122, 0.90685, 0.77892, 0.85746, 1.02638, 0.89385, 0.75657, 0.85122, 0.86275, 0.77452, 0.74171, 0.79776, 0.7306, 0.95165, 0.77818, 0.89772, 0.88831, 0.90685, 0.7306, 0.98142, 1.02191, 0.96576, 1.23516, 0.99018, 0.98142, 0.9236, 0.89258, 0.94035, 1.02191, 0.78848, 0.96576, 0.9561, 1.23516, 0.90918, 0.92578, 0.95424, 0.89746, 0.83969, 0.9482, 0.80113, 0.89442, 0.85208, 0.86155, 0.98022, 0.99018, 1.00452, 0.81209, 0.99247, 0.89181, 1.23516, 0.99018, 0.9482, 0.99018, 0.89181, 0.73206, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.88844, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.89464, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.96766, 1, 1, 1, 1, 1, 1, 0.94258, 0.98986, 0.94258, 0.98986, 0.94258, 0.98986, 0.7306, 0.90527, 1, 1, 0.89552, 0.90527, 1, 0.90186, 1.12308, 1.12308, 1.12308, 1.12308, 1.2566, 1.2566, 1.2566, 0.89552, 0.89552, 1.42259, 0.69043, 1.03809, 1, 1, 1.0176, 1.0176, 1.11523, 1.4956, 2.01462, 0.99331, 0.82616, 0.91133, 0.84286, 0.91133, 1, 1, 1, 0.70508, 1, 1.23108, 0.79801, 0.84426, 0.84426, 0.774, 0.90527, 1.81055, 0.90527, 1.81055, 1.28809, 1.55469, 0.94434, 1.07806, 1, 0.97094, 0.7589, 0.85284, 0.90747, 1.19658, 0.69825, 0.97622, 1.33512, 0.90747, 0.90747, 0.85356, 0.90747, 0.90747, 1.44947, 0.85284, 0.8941, 0.8941, 0.70572, 0.8, 0.70572, 0.70572, 0.70572, 0.70572, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.99862, 0.99862, 1, 1, 1, 1, 1, 1.0336, 0.91027, 1, 1, 1, 0.99862, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.05859, 1.05859, 1, 1, 1, 1.07185, 0.99413, 0.96334, 1.08065, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const CalibriRegularMetrics = { + lineHeight: 1.2207, + lineGap: 0.2207 +}; + +;// ./src/core/helvetica_factors.js +const HelveticaBoldFactors = [0.76116, 1, 1, 1.0006, 0.99998, 0.99974, 0.99973, 0.99973, 0.99982, 0.99977, 1.00087, 0.99998, 0.99998, 0.99959, 1.00003, 1.0006, 0.99998, 1.0006, 1.0006, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99998, 1, 1.00003, 1.00003, 1.00003, 1.00026, 0.9999, 0.99977, 0.99977, 0.99977, 0.99977, 1.00001, 1.00026, 1.00022, 0.99977, 1.0006, 0.99973, 0.99977, 1.00026, 0.99999, 0.99977, 1.00022, 1.00001, 1.00022, 0.99977, 1.00001, 1.00026, 0.99977, 1.00001, 1.00016, 1.00001, 1.00001, 1.00026, 0.99998, 1.0006, 0.99998, 1.00003, 0.99973, 0.99998, 0.99973, 1.00026, 0.99973, 1.00026, 0.99973, 0.99998, 1.00026, 1.00026, 1.0006, 1.0006, 0.99973, 1.0006, 0.99982, 1.00026, 1.00026, 1.00026, 1.00026, 0.99959, 0.99973, 0.99998, 1.00026, 0.99973, 1.00022, 0.99973, 0.99973, 1, 0.99959, 1.00077, 0.99959, 1.00003, 0.99998, 0.99973, 0.99973, 0.99973, 0.99973, 1.00077, 0.99973, 0.99998, 1.00025, 0.99968, 0.99973, 1.00003, 1.00025, 0.60299, 1.00024, 1.06409, 1, 1, 0.99998, 1, 0.99973, 1.0006, 0.99998, 1, 0.99936, 0.99973, 1.00002, 1.00002, 1.00002, 1.00026, 0.99977, 0.99977, 0.99977, 0.99977, 0.99977, 0.99977, 1, 0.99977, 1.00001, 1.00001, 1.00001, 1.00001, 1.0006, 1.0006, 1.0006, 1.0006, 0.99977, 0.99977, 1.00022, 1.00022, 1.00022, 1.00022, 1.00022, 1.00003, 1.00022, 0.99977, 0.99977, 0.99977, 0.99977, 1.00001, 1.00001, 1.00026, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99982, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1.0006, 1.0006, 1.0006, 1.0006, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 1.06409, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 0.99973, 1.00026, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 1.03374, 0.99977, 1.00026, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00022, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.00042, 0.99973, 0.99973, 1.0006, 0.99977, 0.99973, 0.99973, 1.00026, 1.0006, 1.00026, 1.0006, 1.00026, 1.03828, 1.00026, 0.99999, 1.00026, 1.0006, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.9993, 0.9998, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 1, 1.00016, 0.99977, 0.99959, 0.99977, 0.99959, 0.99977, 0.99959, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00026, 0.99998, 1.00026, 0.8121, 1.00026, 0.99998, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 1.00016, 1.00022, 1.00001, 0.99973, 1.00001, 1.00026, 1, 1.00026, 1, 1.00026, 1, 1.0006, 0.99973, 0.99977, 0.99973, 1, 0.99982, 1.00022, 1.00026, 1.00001, 0.99973, 1.00026, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 1.00034, 0.99977, 1, 0.99997, 1.00026, 1.00078, 1.00036, 0.99973, 1.00013, 1.0006, 0.99977, 0.99977, 0.99988, 0.85148, 1.00001, 1.00026, 0.99977, 1.00022, 1.0006, 0.99977, 1.00001, 0.99999, 0.99977, 1.00069, 1.00022, 0.99977, 1.00001, 0.99984, 1.00026, 1.00001, 1.00024, 1.00001, 0.9999, 1, 1.0006, 1.00001, 1.00041, 0.99962, 1.00026, 1.0006, 0.99995, 1.00041, 0.99942, 0.99973, 0.99927, 1.00082, 0.99902, 1.00026, 1.00087, 1.0006, 1.00069, 0.99973, 0.99867, 0.99973, 0.9993, 1.00026, 1.00049, 1.00056, 1, 0.99988, 0.99935, 0.99995, 0.99954, 1.00055, 0.99945, 1.00032, 1.0006, 0.99995, 1.00026, 0.99995, 1.00032, 1.00001, 1.00008, 0.99971, 1.00019, 0.9994, 1.00001, 1.0006, 1.00044, 0.99973, 1.00023, 1.00047, 1, 0.99942, 0.99561, 0.99989, 1.00035, 0.99977, 1.00035, 0.99977, 1.00019, 0.99944, 1.00001, 1.00021, 0.99926, 1.00035, 1.00035, 0.99942, 1.00048, 0.99999, 0.99977, 1.00022, 1.00035, 1.00001, 0.99977, 1.00026, 0.99989, 1.00057, 1.00001, 0.99936, 1.00052, 1.00012, 0.99996, 1.00043, 1, 1.00035, 0.9994, 0.99976, 1.00035, 0.99973, 1.00052, 1.00041, 1.00119, 1.00037, 0.99973, 1.00002, 0.99986, 1.00041, 1.00041, 0.99902, 0.9996, 1.00034, 0.99999, 1.00026, 0.99999, 1.00026, 0.99973, 1.00052, 0.99973, 1, 0.99973, 1.00041, 1.00075, 0.9994, 1.0003, 0.99999, 1, 1.00041, 0.99955, 1, 0.99915, 0.99973, 0.99973, 1.00026, 1.00119, 0.99955, 0.99973, 1.0006, 0.99911, 1.0006, 1.00026, 0.99972, 1.00026, 0.99902, 1.00041, 0.99973, 0.99999, 1, 1, 1.00038, 1.0005, 1.00016, 1.00022, 1.00016, 1.00022, 1.00016, 1.00022, 1.00001, 0.99973, 1, 1, 0.99973, 1, 1, 0.99955, 1.0006, 1.0006, 1.0006, 1.0006, 1, 1, 1, 0.99973, 0.99973, 0.99972, 1, 1, 1.00106, 0.99999, 0.99998, 0.99998, 0.99999, 0.99998, 1.66475, 1, 0.99973, 0.99973, 1.00023, 0.99973, 0.99971, 1.00047, 1.00023, 1, 0.99991, 0.99984, 1.00002, 1.00002, 1.00002, 1.00002, 1, 1, 1, 1, 1, 1, 1, 0.99972, 1, 1.20985, 1.39713, 1.00003, 1.00031, 1.00015, 1, 0.99561, 1.00027, 1.00031, 1.00031, 0.99915, 1.00031, 1.00031, 0.99999, 1.00003, 0.99999, 0.99999, 1.41144, 1.6, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.40579, 1.40579, 1.36625, 0.99999, 1, 0.99861, 0.99861, 1, 1.00026, 1.00026, 1.00026, 1.00026, 0.99972, 0.99999, 0.99999, 0.99999, 0.99999, 1.40483, 1, 0.99977, 1.00054, 1, 1, 0.99953, 0.99962, 1.00042, 0.9995, 1, 1, 1, 1, 1, 1, 1, 1, 0.99998, 0.99998, 0.99998, 0.99998, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const HelveticaBoldMetrics = { + lineHeight: 1.2, + lineGap: 0.2 +}; +const HelveticaBoldItalicFactors = [0.76116, 1, 1, 1.0006, 0.99998, 0.99974, 0.99973, 0.99973, 0.99982, 0.99977, 1.00087, 0.99998, 0.99998, 0.99959, 1.00003, 1.0006, 0.99998, 1.0006, 1.0006, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99998, 1, 1.00003, 1.00003, 1.00003, 1.00026, 0.9999, 0.99977, 0.99977, 0.99977, 0.99977, 1.00001, 1.00026, 1.00022, 0.99977, 1.0006, 0.99973, 0.99977, 1.00026, 0.99999, 0.99977, 1.00022, 1.00001, 1.00022, 0.99977, 1.00001, 1.00026, 0.99977, 1.00001, 1.00016, 1.00001, 1.00001, 1.00026, 0.99998, 1.0006, 0.99998, 1.00003, 0.99973, 0.99998, 0.99973, 1.00026, 0.99973, 1.00026, 0.99973, 0.99998, 1.00026, 1.00026, 1.0006, 1.0006, 0.99973, 1.0006, 0.99982, 1.00026, 1.00026, 1.00026, 1.00026, 0.99959, 0.99973, 0.99998, 1.00026, 0.99973, 1.00022, 0.99973, 0.99973, 1, 0.99959, 1.00077, 0.99959, 1.00003, 0.99998, 0.99973, 0.99973, 0.99973, 0.99973, 1.00077, 0.99973, 0.99998, 1.00025, 0.99968, 0.99973, 1.00003, 1.00025, 0.60299, 1.00024, 1.06409, 1, 1, 0.99998, 1, 0.99973, 1.0006, 0.99998, 1, 0.99936, 0.99973, 1.00002, 1.00002, 1.00002, 1.00026, 0.99977, 0.99977, 0.99977, 0.99977, 0.99977, 0.99977, 1, 0.99977, 1.00001, 1.00001, 1.00001, 1.00001, 1.0006, 1.0006, 1.0006, 1.0006, 0.99977, 0.99977, 1.00022, 1.00022, 1.00022, 1.00022, 1.00022, 1.00003, 1.00022, 0.99977, 0.99977, 0.99977, 0.99977, 1.00001, 1.00001, 1.00026, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99982, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1.0006, 1.0006, 1.0006, 1.0006, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 1.06409, 1.00026, 1.00026, 1.00026, 1.00026, 1.00026, 0.99973, 1.00026, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 1.0044, 0.99977, 1.00026, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00022, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 0.99971, 0.99973, 0.99973, 1.0006, 0.99977, 0.99973, 0.99973, 1.00026, 1.0006, 1.00026, 1.0006, 1.00026, 1.01011, 1.00026, 0.99999, 1.00026, 1.0006, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.9993, 0.9998, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 1.00022, 1.00026, 1, 1.00016, 0.99977, 0.99959, 0.99977, 0.99959, 0.99977, 0.99959, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00026, 0.99998, 1.00026, 0.8121, 1.00026, 0.99998, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 0.99977, 1.00026, 1.00016, 1.00022, 1.00001, 0.99973, 1.00001, 1.00026, 1, 1.00026, 1, 1.00026, 1, 1.0006, 0.99973, 0.99977, 0.99973, 1, 0.99982, 1.00022, 1.00026, 1.00001, 0.99973, 1.00026, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99977, 1, 1, 1.00026, 0.99969, 0.99972, 0.99981, 0.9998, 1.0006, 0.99977, 0.99977, 1.00022, 0.91155, 1.00001, 1.00026, 0.99977, 1.00022, 1.0006, 0.99977, 1.00001, 0.99999, 0.99977, 0.99966, 1.00022, 1.00032, 1.00001, 0.99944, 1.00026, 1.00001, 0.99968, 1.00001, 1.00047, 1, 1.0006, 1.00001, 0.99981, 1.00101, 1.00026, 1.0006, 0.99948, 0.99981, 1.00064, 0.99973, 0.99942, 1.00101, 1.00061, 1.00026, 1.00069, 1.0006, 1.00014, 0.99973, 1.01322, 0.99973, 1.00065, 1.00026, 1.00012, 0.99923, 1, 1.00064, 1.00076, 0.99948, 1.00055, 1.00063, 1.00007, 0.99943, 1.0006, 0.99948, 1.00026, 0.99948, 0.99943, 1.00001, 1.00001, 1.00029, 1.00038, 1.00035, 1.00001, 1.0006, 1.0006, 0.99973, 0.99978, 1.00001, 1.00057, 0.99989, 0.99967, 0.99964, 0.99967, 0.99977, 0.99999, 0.99977, 1.00038, 0.99977, 1.00001, 0.99973, 1.00066, 0.99967, 0.99967, 1.00041, 0.99998, 0.99999, 0.99977, 1.00022, 0.99967, 1.00001, 0.99977, 1.00026, 0.99964, 1.00031, 1.00001, 0.99999, 0.99999, 1, 1.00023, 1, 1, 0.99999, 1.00035, 1.00001, 0.99999, 0.99973, 0.99977, 0.99999, 1.00058, 0.99973, 0.99973, 0.99955, 0.9995, 1.00026, 1.00026, 1.00032, 0.99989, 1.00034, 0.99999, 1.00026, 1.00026, 1.00026, 0.99973, 0.45998, 0.99973, 1.00026, 0.99973, 1.00001, 0.99999, 0.99982, 0.99994, 0.99996, 1, 1.00042, 1.00044, 1.00029, 1.00023, 0.99973, 0.99973, 1.00026, 0.99949, 1.00002, 0.99973, 1.0006, 1.0006, 1.0006, 0.99975, 1.00026, 1.00026, 1.00032, 0.98685, 0.99973, 1.00026, 1, 1, 0.99966, 1.00044, 1.00016, 1.00022, 1.00016, 1.00022, 1.00016, 1.00022, 1.00001, 0.99973, 1, 1, 0.99973, 1, 1, 0.99955, 1.0006, 1.0006, 1.0006, 1.0006, 1, 1, 1, 0.99973, 0.99973, 0.99972, 1, 1, 1.00106, 0.99999, 0.99998, 0.99998, 0.99999, 0.99998, 1.66475, 1, 0.99973, 0.99973, 1, 0.99973, 0.99971, 0.99978, 1, 1, 0.99991, 0.99984, 1.00002, 1.00002, 1.00002, 1.00002, 1.00098, 1, 1, 1, 1.00049, 1, 1, 0.99972, 1, 1.20985, 1.39713, 1.00003, 1.00031, 1.00015, 1, 0.99561, 1.00027, 1.00031, 1.00031, 0.99915, 1.00031, 1.00031, 0.99999, 1.00003, 0.99999, 0.99999, 1.41144, 1.6, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.40579, 1.40579, 1.36625, 0.99999, 1, 0.99861, 0.99861, 1, 1.00026, 1.00026, 1.00026, 1.00026, 0.99972, 0.99999, 0.99999, 0.99999, 0.99999, 1.40483, 1, 0.99977, 1.00054, 1, 1, 0.99953, 0.99962, 1.00042, 0.9995, 1, 1, 1, 1, 1, 1, 1, 1, 0.99998, 0.99998, 0.99998, 0.99998, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const HelveticaBoldItalicMetrics = { + lineHeight: 1.35, + lineGap: 0.2 +}; +const HelveticaItalicFactors = [0.76116, 1, 1, 1.0006, 1.0006, 1.00006, 0.99973, 0.99973, 0.99982, 1.00001, 1.00043, 0.99998, 0.99998, 0.99959, 1.00003, 1.0006, 0.99998, 1.0006, 1.0006, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1.0006, 1, 1.00003, 1.00003, 1.00003, 0.99973, 0.99987, 1.00001, 1.00001, 0.99977, 0.99977, 1.00001, 1.00026, 1.00022, 0.99977, 1.0006, 1, 1.00001, 0.99973, 0.99999, 0.99977, 1.00022, 1.00001, 1.00022, 0.99977, 1.00001, 1.00026, 0.99977, 1.00001, 1.00016, 1.00001, 1.00001, 1.00026, 1.0006, 1.0006, 1.0006, 0.99949, 0.99973, 0.99998, 0.99973, 0.99973, 1, 0.99973, 0.99973, 1.0006, 0.99973, 0.99973, 0.99924, 0.99924, 1, 0.99924, 0.99999, 0.99973, 0.99973, 0.99973, 0.99973, 0.99998, 1, 1.0006, 0.99973, 1, 0.99977, 1, 1, 1, 1.00005, 1.0009, 1.00005, 1.00003, 0.99998, 0.99973, 0.99973, 0.99973, 0.99973, 1.0009, 0.99973, 0.99998, 1.00025, 0.99968, 0.99973, 1.00003, 1.00025, 0.60299, 1.00024, 1.06409, 1, 1, 0.99998, 1, 0.9998, 1.0006, 0.99998, 1, 0.99936, 0.99973, 1.00002, 1.00002, 1.00002, 1.00026, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1, 0.99977, 1.00001, 1.00001, 1.00001, 1.00001, 1.0006, 1.0006, 1.0006, 1.0006, 0.99977, 0.99977, 1.00022, 1.00022, 1.00022, 1.00022, 1.00022, 1.00003, 1.00022, 0.99977, 0.99977, 0.99977, 0.99977, 1.00001, 1.00001, 1.00026, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99982, 1, 0.99973, 0.99973, 0.99973, 0.99973, 1.0006, 1.0006, 1.0006, 1.0006, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1.06409, 1.00026, 0.99973, 0.99973, 0.99973, 0.99973, 1, 0.99973, 1, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 0.99977, 1, 0.99977, 1, 0.99977, 1, 0.99977, 1, 0.99977, 1.0288, 0.99977, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 0.99924, 1.0006, 1.0006, 0.99946, 1.00034, 1, 0.99924, 1.00001, 1, 1, 0.99973, 0.99924, 0.99973, 0.99924, 0.99973, 1.06311, 0.99973, 1.00024, 0.99973, 0.99924, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 1.00041, 0.9998, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1, 1.00016, 0.99977, 0.99998, 0.99977, 0.99998, 0.99977, 0.99998, 1.00001, 1, 1.00001, 1, 1.00001, 1, 1.00001, 1, 1.00026, 1.0006, 1.00026, 0.89547, 1.00026, 1.0006, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 1.00016, 0.99977, 1.00001, 1, 1.00001, 1.00026, 1, 1.00026, 1, 1.00026, 1, 0.99924, 0.99973, 1.00001, 0.99973, 1, 0.99982, 1.00022, 1.00026, 1.00001, 1, 1.00026, 1.0006, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 1.00001, 1, 1.00054, 0.99977, 1.00084, 1.00007, 0.99973, 1.00013, 0.99924, 1.00001, 1.00001, 0.99945, 0.91221, 1.00001, 1.00026, 0.99977, 1.00022, 1.0006, 1.00001, 1.00001, 0.99999, 0.99977, 0.99933, 1.00022, 1.00054, 1.00001, 1.00065, 1.00026, 1.00001, 1.0001, 1.00001, 1.00052, 1, 1.0006, 1.00001, 0.99945, 0.99897, 0.99968, 0.99924, 1.00036, 0.99945, 0.99949, 1, 1.0006, 0.99897, 0.99918, 0.99968, 0.99911, 0.99924, 1, 0.99962, 1.01487, 1, 1.0005, 0.99973, 1.00012, 1.00043, 1, 0.99995, 0.99994, 1.00036, 0.99947, 1.00019, 1.00063, 1.00025, 0.99924, 1.00036, 0.99973, 1.00036, 1.00025, 1.00001, 1.00001, 1.00027, 1.0001, 1.00068, 1.00001, 1.0006, 1.0006, 1, 1.00008, 0.99957, 0.99972, 0.9994, 0.99954, 0.99975, 1.00051, 1.00001, 1.00019, 1.00001, 1.0001, 0.99986, 1.00001, 1.00001, 1.00038, 0.99954, 0.99954, 0.9994, 1.00066, 0.99999, 0.99977, 1.00022, 1.00054, 1.00001, 0.99977, 1.00026, 0.99975, 1.0001, 1.00001, 0.99993, 0.9995, 0.99955, 1.00016, 0.99978, 0.99974, 1.00019, 1.00022, 0.99955, 1.00053, 0.99973, 1.00089, 1.00005, 0.99967, 1.00048, 0.99973, 1.00002, 1.00034, 0.99973, 0.99973, 0.99964, 1.00006, 1.00066, 0.99947, 0.99973, 0.98894, 0.99973, 1, 0.44898, 1, 0.99946, 1, 1.00039, 1.00082, 0.99991, 0.99991, 0.99985, 1.00022, 1.00023, 1.00061, 1.00006, 0.99966, 0.99973, 0.99973, 0.99973, 1.00019, 1.0008, 1, 0.99924, 0.99924, 0.99924, 0.99983, 1.00044, 0.99973, 0.99964, 0.98332, 1, 0.99973, 1, 1, 0.99962, 0.99895, 1.00016, 0.99977, 1.00016, 0.99977, 1.00016, 0.99977, 1.00001, 1, 1, 1, 0.99973, 1, 1, 0.99955, 0.99924, 0.99924, 0.99924, 0.99924, 0.99998, 0.99998, 0.99998, 0.99973, 0.99973, 0.99972, 1, 1, 1.00267, 0.99999, 0.99998, 0.99998, 1, 0.99998, 1.66475, 1, 0.99973, 0.99973, 1.00023, 0.99973, 1.00423, 0.99925, 0.99999, 1, 0.99991, 0.99984, 1.00002, 1.00002, 1.00002, 1.00002, 1.00049, 1, 1.00245, 1, 1, 1, 1, 0.96329, 1, 1.20985, 1.39713, 1.00003, 0.8254, 1.00015, 1, 1.00035, 1.00027, 1.00031, 1.00031, 1.00003, 1.00031, 1.00031, 0.99999, 1.00003, 0.99999, 0.99999, 1.41144, 1.6, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.40579, 1.40579, 1.36625, 0.99999, 1, 0.99861, 0.99861, 1, 1.00026, 1.00026, 1.00026, 1.00026, 0.95317, 0.99999, 0.99999, 0.99999, 0.99999, 1.40483, 1, 0.99977, 1.00054, 1, 1, 0.99953, 0.99962, 1.00042, 0.9995, 1, 1, 1, 1, 1, 1, 1, 1, 0.99998, 0.99998, 0.99998, 0.99998, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const HelveticaItalicMetrics = { + lineHeight: 1.35, + lineGap: 0.2 +}; +const HelveticaRegularFactors = [0.76116, 1, 1, 1.0006, 1.0006, 1.00006, 0.99973, 0.99973, 0.99982, 1.00001, 1.00043, 0.99998, 0.99998, 0.99959, 1.00003, 1.0006, 0.99998, 1.0006, 1.0006, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1.0006, 1, 1.00003, 1.00003, 1.00003, 0.99973, 0.99987, 1.00001, 1.00001, 0.99977, 0.99977, 1.00001, 1.00026, 1.00022, 0.99977, 1.0006, 1, 1.00001, 0.99973, 0.99999, 0.99977, 1.00022, 1.00001, 1.00022, 0.99977, 1.00001, 1.00026, 0.99977, 1.00001, 1.00016, 1.00001, 1.00001, 1.00026, 1.0006, 1.0006, 1.0006, 0.99949, 0.99973, 0.99998, 0.99973, 0.99973, 1, 0.99973, 0.99973, 1.0006, 0.99973, 0.99973, 0.99924, 0.99924, 1, 0.99924, 0.99999, 0.99973, 0.99973, 0.99973, 0.99973, 0.99998, 1, 1.0006, 0.99973, 1, 0.99977, 1, 1, 1, 1.00005, 1.0009, 1.00005, 1.00003, 0.99998, 0.99973, 0.99973, 0.99973, 0.99973, 1.0009, 0.99973, 0.99998, 1.00025, 0.99968, 0.99973, 1.00003, 1.00025, 0.60299, 1.00024, 1.06409, 1, 1, 0.99998, 1, 0.9998, 1.0006, 0.99998, 1, 0.99936, 0.99973, 1.00002, 1.00002, 1.00002, 1.00026, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1, 0.99977, 1.00001, 1.00001, 1.00001, 1.00001, 1.0006, 1.0006, 1.0006, 1.0006, 0.99977, 0.99977, 1.00022, 1.00022, 1.00022, 1.00022, 1.00022, 1.00003, 1.00022, 0.99977, 0.99977, 0.99977, 0.99977, 1.00001, 1.00001, 1.00026, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99982, 1, 0.99973, 0.99973, 0.99973, 0.99973, 1.0006, 1.0006, 1.0006, 1.0006, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 0.99973, 1.06409, 1.00026, 0.99973, 0.99973, 0.99973, 0.99973, 1, 0.99973, 1, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 0.99977, 1, 0.99977, 1, 0.99977, 1, 0.99977, 1, 0.99977, 1.04596, 0.99977, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00001, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 1.0006, 0.99924, 1.0006, 1.0006, 1.00019, 1.00034, 1, 0.99924, 1.00001, 1, 1, 0.99973, 0.99924, 0.99973, 0.99924, 0.99973, 1.02572, 0.99973, 1.00005, 0.99973, 0.99924, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99999, 0.9998, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1.00022, 0.99973, 1, 1.00016, 0.99977, 0.99998, 0.99977, 0.99998, 0.99977, 0.99998, 1.00001, 1, 1.00001, 1, 1.00001, 1, 1.00001, 1, 1.00026, 1.0006, 1.00026, 0.84533, 1.00026, 1.0006, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 0.99977, 0.99973, 1.00016, 0.99977, 1.00001, 1, 1.00001, 1.00026, 1, 1.00026, 1, 1.00026, 1, 0.99924, 0.99973, 1.00001, 0.99973, 1, 0.99982, 1.00022, 1.00026, 1.00001, 1, 1.00026, 1.0006, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99998, 0.99928, 1, 0.99977, 1.00013, 1.00055, 0.99947, 0.99945, 0.99941, 0.99924, 1.00001, 1.00001, 1.0004, 0.91621, 1.00001, 1.00026, 0.99977, 1.00022, 1.0006, 1.00001, 1.00005, 0.99999, 0.99977, 1.00015, 1.00022, 0.99977, 1.00001, 0.99973, 1.00026, 1.00001, 1.00019, 1.00001, 0.99946, 1, 1.0006, 1.00001, 0.99978, 1.00045, 0.99973, 0.99924, 1.00023, 0.99978, 0.99966, 1, 1.00065, 1.00045, 1.00019, 0.99973, 0.99973, 0.99924, 1, 1, 0.96499, 1, 1.00055, 0.99973, 1.00008, 1.00027, 1, 0.9997, 0.99995, 1.00023, 0.99933, 1.00019, 1.00015, 1.00031, 0.99924, 1.00023, 0.99973, 1.00023, 1.00031, 1.00001, 0.99928, 1.00029, 1.00092, 1.00035, 1.00001, 1.0006, 1.0006, 1, 0.99988, 0.99975, 1, 1.00082, 0.99561, 0.9996, 1.00035, 1.00001, 0.99962, 1.00001, 1.00092, 0.99964, 1.00001, 0.99963, 0.99999, 1.00035, 1.00035, 1.00082, 0.99962, 0.99999, 0.99977, 1.00022, 1.00035, 1.00001, 0.99977, 1.00026, 0.9996, 0.99967, 1.00001, 1.00034, 1.00074, 1.00054, 1.00053, 1.00063, 0.99971, 0.99962, 1.00035, 0.99975, 0.99977, 0.99973, 1.00043, 0.99953, 1.0007, 0.99915, 0.99973, 1.00008, 0.99892, 1.00073, 1.00073, 1.00114, 0.99915, 1.00073, 0.99955, 0.99973, 1.00092, 0.99973, 1, 0.99998, 1, 1.0003, 1, 1.00043, 1.00001, 0.99969, 1.0003, 1, 1.00035, 1.00001, 0.9995, 1, 1.00092, 0.99973, 0.99973, 0.99973, 1.0007, 0.9995, 1, 0.99924, 1.0006, 0.99924, 0.99972, 1.00062, 0.99973, 1.00114, 1.00073, 1, 0.99955, 1, 1, 1.00047, 0.99968, 1.00016, 0.99977, 1.00016, 0.99977, 1.00016, 0.99977, 1.00001, 1, 1, 1, 0.99973, 1, 1, 0.99955, 0.99924, 0.99924, 0.99924, 0.99924, 0.99998, 0.99998, 0.99998, 0.99973, 0.99973, 0.99972, 1, 1, 1.00267, 0.99999, 0.99998, 0.99998, 1, 0.99998, 1.66475, 1, 0.99973, 0.99973, 1.00023, 0.99973, 0.99971, 0.99925, 1.00023, 1, 0.99991, 0.99984, 1.00002, 1.00002, 1.00002, 1.00002, 1, 1, 1, 1, 1, 1, 1, 0.96329, 1, 1.20985, 1.39713, 1.00003, 0.8254, 1.00015, 1, 1.00035, 1.00027, 1.00031, 1.00031, 0.99915, 1.00031, 1.00031, 0.99999, 1.00003, 0.99999, 0.99999, 1.41144, 1.6, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.41144, 1.40579, 1.40579, 1.36625, 0.99999, 1, 0.99861, 0.99861, 1, 1.00026, 1.00026, 1.00026, 1.00026, 0.95317, 0.99999, 0.99999, 0.99999, 0.99999, 1.40483, 1, 0.99977, 1.00054, 1, 1, 0.99953, 0.99962, 1.00042, 0.9995, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const HelveticaRegularMetrics = { + lineHeight: 1.2, + lineGap: 0.2 +}; + +;// ./src/core/liberationsans_widths.js +const LiberationSansBoldWidths = [365, 0, 333, 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 737, 552, 400, 549, 333, 333, 333, 576, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278, 611, 611, 611, 611, 611, 611, 611, 549, 611, 611, 611, 611, 611, 556, 611, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 719, 722, 611, 667, 556, 667, 556, 667, 556, 667, 556, 667, 556, 778, 611, 778, 611, 778, 611, 778, 611, 722, 611, 722, 611, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 785, 556, 556, 278, 722, 556, 556, 611, 278, 611, 278, 611, 385, 611, 479, 611, 278, 722, 611, 722, 611, 722, 611, 708, 723, 611, 778, 611, 778, 611, 778, 611, 1000, 944, 722, 389, 722, 389, 722, 389, 667, 556, 667, 556, 667, 556, 667, 556, 611, 333, 611, 479, 611, 333, 722, 611, 722, 611, 722, 611, 722, 611, 722, 611, 722, 611, 944, 778, 667, 556, 667, 611, 500, 611, 500, 611, 500, 278, 556, 722, 556, 1000, 889, 778, 611, 667, 556, 611, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 465, 722, 333, 853, 906, 474, 825, 927, 838, 278, 722, 722, 601, 719, 667, 611, 722, 778, 278, 722, 667, 833, 722, 644, 778, 722, 667, 600, 611, 667, 821, 667, 809, 802, 278, 667, 615, 451, 611, 278, 582, 615, 610, 556, 606, 475, 460, 611, 541, 278, 558, 556, 612, 556, 445, 611, 766, 619, 520, 684, 446, 582, 715, 576, 753, 845, 278, 582, 611, 582, 845, 667, 669, 885, 567, 711, 667, 278, 276, 556, 1094, 1062, 875, 610, 722, 622, 719, 722, 719, 722, 567, 712, 667, 904, 626, 719, 719, 610, 702, 833, 722, 778, 719, 667, 722, 611, 622, 854, 667, 730, 703, 1005, 1019, 870, 979, 719, 711, 1031, 719, 556, 618, 615, 417, 635, 556, 709, 497, 615, 615, 500, 635, 740, 604, 611, 604, 611, 556, 490, 556, 875, 556, 615, 581, 833, 844, 729, 854, 615, 552, 854, 583, 556, 556, 611, 417, 552, 556, 278, 281, 278, 969, 906, 611, 500, 615, 556, 604, 778, 611, 487, 447, 944, 778, 944, 778, 944, 778, 667, 556, 333, 333, 556, 1000, 1000, 552, 278, 278, 278, 278, 500, 500, 500, 556, 556, 350, 1000, 1000, 240, 479, 333, 333, 604, 333, 167, 396, 556, 556, 1094, 556, 885, 489, 1115, 1000, 768, 600, 834, 834, 834, 834, 1000, 500, 1000, 500, 1000, 500, 500, 494, 612, 823, 713, 584, 549, 713, 979, 722, 274, 549, 549, 583, 549, 549, 604, 584, 604, 604, 708, 625, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 729, 604, 604, 354, 354, 1000, 990, 990, 990, 990, 494, 604, 604, 604, 604, 354, 1021, 1052, 917, 750, 750, 531, 656, 594, 510, 500, 750, 750, 611, 611, 333, 333, 333, 333, 333, 333, 333, 333, 222, 222, 333, 333, 333, 333, 333, 333, 333, 333]; +const LiberationSansBoldMapping = [-1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 402, 506, 507, 508, 509, 510, 511, 536, 537, 538, 539, 710, 711, 713, 728, 729, 730, 731, 732, 733, 900, 901, 902, 903, 904, 905, 906, 908, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1138, 1139, 1168, 1169, 7808, 7809, 7810, 7811, 7812, 7813, 7922, 7923, 8208, 8209, 8211, 8212, 8213, 8215, 8216, 8217, 8218, 8219, 8220, 8221, 8222, 8224, 8225, 8226, 8230, 8240, 8242, 8243, 8249, 8250, 8252, 8254, 8260, 8319, 8355, 8356, 8359, 8364, 8453, 8467, 8470, 8482, 8486, 8494, 8539, 8540, 8541, 8542, 8592, 8593, 8594, 8595, 8596, 8597, 8616, 8706, 8710, 8719, 8721, 8722, 8730, 8734, 8735, 8745, 8747, 8776, 8800, 8801, 8804, 8805, 8962, 8976, 8992, 8993, 9472, 9474, 9484, 9488, 9492, 9496, 9500, 9508, 9516, 9524, 9532, 9552, 9553, 9554, 9555, 9556, 9557, 9558, 9559, 9560, 9561, 9562, 9563, 9564, 9565, 9566, 9567, 9568, 9569, 9570, 9571, 9572, 9573, 9574, 9575, 9576, 9577, 9578, 9579, 9580, 9600, 9604, 9608, 9612, 9616, 9617, 9618, 9619, 9632, 9633, 9642, 9643, 9644, 9650, 9658, 9660, 9668, 9674, 9675, 9679, 9688, 9689, 9702, 9786, 9787, 9788, 9792, 9794, 9824, 9827, 9829, 9830, 9834, 9835, 9836, 61441, 61442, 61445, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]; +const LiberationSansBoldItalicWidths = [365, 0, 333, 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 737, 552, 400, 549, 333, 333, 333, 576, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278, 611, 611, 611, 611, 611, 611, 611, 549, 611, 611, 611, 611, 611, 556, 611, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 740, 722, 611, 667, 556, 667, 556, 667, 556, 667, 556, 667, 556, 778, 611, 778, 611, 778, 611, 778, 611, 722, 611, 722, 611, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 782, 556, 556, 278, 722, 556, 556, 611, 278, 611, 278, 611, 396, 611, 479, 611, 278, 722, 611, 722, 611, 722, 611, 708, 723, 611, 778, 611, 778, 611, 778, 611, 1000, 944, 722, 389, 722, 389, 722, 389, 667, 556, 667, 556, 667, 556, 667, 556, 611, 333, 611, 479, 611, 333, 722, 611, 722, 611, 722, 611, 722, 611, 722, 611, 722, 611, 944, 778, 667, 556, 667, 611, 500, 611, 500, 611, 500, 278, 556, 722, 556, 1000, 889, 778, 611, 667, 556, 611, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 722, 333, 854, 906, 473, 844, 930, 847, 278, 722, 722, 610, 671, 667, 611, 722, 778, 278, 722, 667, 833, 722, 657, 778, 718, 667, 590, 611, 667, 822, 667, 829, 781, 278, 667, 620, 479, 611, 278, 591, 620, 621, 556, 610, 479, 492, 611, 558, 278, 566, 556, 603, 556, 450, 611, 712, 605, 532, 664, 409, 591, 704, 578, 773, 834, 278, 591, 611, 591, 834, 667, 667, 886, 614, 719, 667, 278, 278, 556, 1094, 1042, 854, 622, 719, 677, 719, 722, 708, 722, 614, 722, 667, 927, 643, 719, 719, 615, 687, 833, 722, 778, 719, 667, 722, 611, 677, 781, 667, 729, 708, 979, 989, 854, 1000, 708, 719, 1042, 729, 556, 619, 604, 534, 618, 556, 736, 510, 611, 611, 507, 622, 740, 604, 611, 611, 611, 556, 889, 556, 885, 556, 646, 583, 889, 935, 707, 854, 594, 552, 865, 589, 556, 556, 611, 469, 563, 556, 278, 278, 278, 969, 906, 611, 507, 619, 556, 611, 778, 611, 575, 467, 944, 778, 944, 778, 944, 778, 667, 556, 333, 333, 556, 1000, 1000, 552, 278, 278, 278, 278, 500, 500, 500, 556, 556, 350, 1000, 1000, 240, 479, 333, 333, 604, 333, 167, 396, 556, 556, 1104, 556, 885, 516, 1146, 1000, 768, 600, 834, 834, 834, 834, 999, 500, 1000, 500, 1000, 500, 500, 494, 612, 823, 713, 584, 549, 713, 979, 722, 274, 549, 549, 583, 549, 549, 604, 584, 604, 604, 708, 625, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 729, 604, 604, 354, 354, 1000, 990, 990, 990, 990, 494, 604, 604, 604, 604, 354, 1021, 1052, 917, 750, 750, 531, 656, 594, 510, 500, 750, 750, 611, 611, 333, 333, 333, 333, 333, 333, 333, 333, 222, 222, 333, 333, 333, 333, 333, 333, 333, 333]; +const LiberationSansBoldItalicMapping = [-1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 402, 506, 507, 508, 509, 510, 511, 536, 537, 538, 539, 710, 711, 713, 728, 729, 730, 731, 732, 733, 900, 901, 902, 903, 904, 905, 906, 908, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1138, 1139, 1168, 1169, 7808, 7809, 7810, 7811, 7812, 7813, 7922, 7923, 8208, 8209, 8211, 8212, 8213, 8215, 8216, 8217, 8218, 8219, 8220, 8221, 8222, 8224, 8225, 8226, 8230, 8240, 8242, 8243, 8249, 8250, 8252, 8254, 8260, 8319, 8355, 8356, 8359, 8364, 8453, 8467, 8470, 8482, 8486, 8494, 8539, 8540, 8541, 8542, 8592, 8593, 8594, 8595, 8596, 8597, 8616, 8706, 8710, 8719, 8721, 8722, 8730, 8734, 8735, 8745, 8747, 8776, 8800, 8801, 8804, 8805, 8962, 8976, 8992, 8993, 9472, 9474, 9484, 9488, 9492, 9496, 9500, 9508, 9516, 9524, 9532, 9552, 9553, 9554, 9555, 9556, 9557, 9558, 9559, 9560, 9561, 9562, 9563, 9564, 9565, 9566, 9567, 9568, 9569, 9570, 9571, 9572, 9573, 9574, 9575, 9576, 9577, 9578, 9579, 9580, 9600, 9604, 9608, 9612, 9616, 9617, 9618, 9619, 9632, 9633, 9642, 9643, 9644, 9650, 9658, 9660, 9668, 9674, 9675, 9679, 9688, 9689, 9702, 9786, 9787, 9788, 9792, 9794, 9824, 9827, 9829, 9830, 9834, 9835, 9836, 61441, 61442, 61445, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]; +const LiberationSansItalicWidths = [365, 0, 333, 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 737, 552, 400, 549, 333, 333, 333, 576, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611, 667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278, 556, 556, 556, 556, 556, 556, 556, 549, 611, 556, 556, 556, 556, 500, 556, 500, 667, 556, 667, 556, 667, 556, 722, 500, 722, 500, 722, 500, 722, 500, 722, 625, 722, 556, 667, 556, 667, 556, 667, 556, 667, 556, 667, 556, 778, 556, 778, 556, 778, 556, 778, 556, 722, 556, 722, 556, 278, 278, 278, 278, 278, 278, 278, 222, 278, 278, 733, 444, 500, 222, 667, 500, 500, 556, 222, 556, 222, 556, 281, 556, 400, 556, 222, 722, 556, 722, 556, 722, 556, 615, 723, 556, 778, 556, 778, 556, 778, 556, 1000, 944, 722, 333, 722, 333, 722, 333, 667, 500, 667, 500, 667, 500, 667, 500, 611, 278, 611, 354, 611, 278, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 944, 722, 667, 500, 667, 611, 500, 611, 500, 611, 500, 222, 556, 667, 556, 1000, 889, 778, 611, 667, 500, 611, 278, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 667, 278, 789, 846, 389, 794, 865, 775, 222, 667, 667, 570, 671, 667, 611, 722, 778, 278, 667, 667, 833, 722, 648, 778, 725, 667, 600, 611, 667, 837, 667, 831, 761, 278, 667, 570, 439, 555, 222, 550, 570, 571, 500, 556, 439, 463, 555, 542, 222, 500, 492, 548, 500, 447, 556, 670, 573, 486, 603, 374, 550, 652, 546, 728, 779, 222, 550, 556, 550, 779, 667, 667, 843, 544, 708, 667, 278, 278, 500, 1066, 982, 844, 589, 715, 639, 724, 667, 651, 667, 544, 704, 667, 917, 614, 715, 715, 589, 686, 833, 722, 778, 725, 667, 722, 611, 639, 795, 667, 727, 673, 920, 923, 805, 886, 651, 694, 1022, 682, 556, 562, 522, 493, 553, 556, 688, 465, 556, 556, 472, 564, 686, 550, 556, 556, 556, 500, 833, 500, 835, 500, 572, 518, 830, 851, 621, 736, 526, 492, 752, 534, 556, 556, 556, 378, 496, 500, 222, 222, 222, 910, 828, 556, 472, 565, 500, 556, 778, 556, 492, 339, 944, 722, 944, 722, 944, 722, 667, 500, 333, 333, 556, 1000, 1000, 552, 222, 222, 222, 222, 333, 333, 333, 556, 556, 350, 1000, 1000, 188, 354, 333, 333, 500, 333, 167, 365, 556, 556, 1094, 556, 885, 323, 1083, 1000, 768, 600, 834, 834, 834, 834, 1000, 500, 998, 500, 1000, 500, 500, 494, 612, 823, 713, 584, 549, 713, 979, 719, 274, 549, 549, 584, 549, 549, 604, 584, 604, 604, 708, 625, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 729, 604, 604, 354, 354, 1000, 990, 990, 990, 990, 494, 604, 604, 604, 604, 354, 1021, 1052, 917, 750, 750, 531, 656, 594, 510, 500, 750, 750, 500, 500, 333, 333, 333, 333, 333, 333, 333, 333, 222, 222, 294, 294, 324, 324, 316, 328, 398, 285]; +const LiberationSansItalicMapping = [-1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 402, 506, 507, 508, 509, 510, 511, 536, 537, 538, 539, 710, 711, 713, 728, 729, 730, 731, 732, 733, 900, 901, 902, 903, 904, 905, 906, 908, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1138, 1139, 1168, 1169, 7808, 7809, 7810, 7811, 7812, 7813, 7922, 7923, 8208, 8209, 8211, 8212, 8213, 8215, 8216, 8217, 8218, 8219, 8220, 8221, 8222, 8224, 8225, 8226, 8230, 8240, 8242, 8243, 8249, 8250, 8252, 8254, 8260, 8319, 8355, 8356, 8359, 8364, 8453, 8467, 8470, 8482, 8486, 8494, 8539, 8540, 8541, 8542, 8592, 8593, 8594, 8595, 8596, 8597, 8616, 8706, 8710, 8719, 8721, 8722, 8730, 8734, 8735, 8745, 8747, 8776, 8800, 8801, 8804, 8805, 8962, 8976, 8992, 8993, 9472, 9474, 9484, 9488, 9492, 9496, 9500, 9508, 9516, 9524, 9532, 9552, 9553, 9554, 9555, 9556, 9557, 9558, 9559, 9560, 9561, 9562, 9563, 9564, 9565, 9566, 9567, 9568, 9569, 9570, 9571, 9572, 9573, 9574, 9575, 9576, 9577, 9578, 9579, 9580, 9600, 9604, 9608, 9612, 9616, 9617, 9618, 9619, 9632, 9633, 9642, 9643, 9644, 9650, 9658, 9660, 9668, 9674, 9675, 9679, 9688, 9689, 9702, 9786, 9787, 9788, 9792, 9794, 9824, 9827, 9829, 9830, 9834, 9835, 9836, 61441, 61442, 61445, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]; +const LiberationSansRegularWidths = [365, 0, 333, 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 737, 552, 400, 549, 333, 333, 333, 576, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611, 667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278, 556, 556, 556, 556, 556, 556, 556, 549, 611, 556, 556, 556, 556, 500, 556, 500, 667, 556, 667, 556, 667, 556, 722, 500, 722, 500, 722, 500, 722, 500, 722, 615, 722, 556, 667, 556, 667, 556, 667, 556, 667, 556, 667, 556, 778, 556, 778, 556, 778, 556, 778, 556, 722, 556, 722, 556, 278, 278, 278, 278, 278, 278, 278, 222, 278, 278, 735, 444, 500, 222, 667, 500, 500, 556, 222, 556, 222, 556, 292, 556, 334, 556, 222, 722, 556, 722, 556, 722, 556, 604, 723, 556, 778, 556, 778, 556, 778, 556, 1000, 944, 722, 333, 722, 333, 722, 333, 667, 500, 667, 500, 667, 500, 667, 500, 611, 278, 611, 375, 611, 278, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 722, 556, 944, 722, 667, 500, 667, 611, 500, 611, 500, 611, 500, 222, 556, 667, 556, 1000, 889, 778, 611, 667, 500, 611, 278, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 667, 278, 784, 838, 384, 774, 855, 752, 222, 667, 667, 551, 668, 667, 611, 722, 778, 278, 667, 668, 833, 722, 650, 778, 722, 667, 618, 611, 667, 798, 667, 835, 748, 278, 667, 578, 446, 556, 222, 547, 578, 575, 500, 557, 446, 441, 556, 556, 222, 500, 500, 576, 500, 448, 556, 690, 569, 482, 617, 395, 547, 648, 525, 713, 781, 222, 547, 556, 547, 781, 667, 667, 865, 542, 719, 667, 278, 278, 500, 1057, 1010, 854, 583, 722, 635, 719, 667, 656, 667, 542, 677, 667, 923, 604, 719, 719, 583, 656, 833, 722, 778, 719, 667, 722, 611, 635, 760, 667, 740, 667, 917, 938, 792, 885, 656, 719, 1010, 722, 556, 573, 531, 365, 583, 556, 669, 458, 559, 559, 438, 583, 688, 552, 556, 542, 556, 500, 458, 500, 823, 500, 573, 521, 802, 823, 625, 719, 521, 510, 750, 542, 556, 556, 556, 365, 510, 500, 222, 278, 222, 906, 812, 556, 438, 559, 500, 552, 778, 556, 489, 411, 944, 722, 944, 722, 944, 722, 667, 500, 333, 333, 556, 1000, 1000, 552, 222, 222, 222, 222, 333, 333, 333, 556, 556, 350, 1000, 1000, 188, 354, 333, 333, 500, 333, 167, 365, 556, 556, 1094, 556, 885, 323, 1073, 1000, 768, 600, 834, 834, 834, 834, 1000, 500, 1000, 500, 1000, 500, 500, 494, 612, 823, 713, 584, 549, 713, 979, 719, 274, 549, 549, 583, 549, 549, 604, 584, 604, 604, 708, 625, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 729, 604, 604, 354, 354, 1000, 990, 990, 990, 990, 494, 604, 604, 604, 604, 354, 1021, 1052, 917, 750, 750, 531, 656, 594, 510, 500, 750, 750, 500, 500, 333, 333, 333, 333, 333, 333, 333, 333, 222, 222, 294, 294, 324, 324, 316, 328, 398, 285]; +const LiberationSansRegularMapping = [-1, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 402, 506, 507, 508, 509, 510, 511, 536, 537, 538, 539, 710, 711, 713, 728, 729, 730, 731, 732, 733, 900, 901, 902, 903, 904, 905, 906, 908, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1138, 1139, 1168, 1169, 7808, 7809, 7810, 7811, 7812, 7813, 7922, 7923, 8208, 8209, 8211, 8212, 8213, 8215, 8216, 8217, 8218, 8219, 8220, 8221, 8222, 8224, 8225, 8226, 8230, 8240, 8242, 8243, 8249, 8250, 8252, 8254, 8260, 8319, 8355, 8356, 8359, 8364, 8453, 8467, 8470, 8482, 8486, 8494, 8539, 8540, 8541, 8542, 8592, 8593, 8594, 8595, 8596, 8597, 8616, 8706, 8710, 8719, 8721, 8722, 8730, 8734, 8735, 8745, 8747, 8776, 8800, 8801, 8804, 8805, 8962, 8976, 8992, 8993, 9472, 9474, 9484, 9488, 9492, 9496, 9500, 9508, 9516, 9524, 9532, 9552, 9553, 9554, 9555, 9556, 9557, 9558, 9559, 9560, 9561, 9562, 9563, 9564, 9565, 9566, 9567, 9568, 9569, 9570, 9571, 9572, 9573, 9574, 9575, 9576, 9577, 9578, 9579, 9580, 9600, 9604, 9608, 9612, 9616, 9617, 9618, 9619, 9632, 9633, 9642, 9643, 9644, 9650, 9658, 9660, 9668, 9674, 9675, 9679, 9688, 9689, 9702, 9786, 9787, 9788, 9792, 9794, 9824, 9827, 9829, 9830, 9834, 9835, 9836, 61441, 61442, 61445, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]; + +;// ./src/core/myriadpro_factors.js +const MyriadProBoldFactors = [1.36898, 1, 1, 0.72706, 0.80479, 0.83734, 0.98894, 0.99793, 0.9897, 0.93884, 0.86209, 0.94292, 0.94292, 1.16661, 1.02058, 0.93582, 0.96694, 0.93582, 1.19137, 0.99793, 0.99793, 0.99793, 0.99793, 0.99793, 0.99793, 0.99793, 0.99793, 0.99793, 0.99793, 0.78076, 0.78076, 1.02058, 1.02058, 1.02058, 0.72851, 0.78966, 0.90838, 0.83637, 0.82391, 0.96376, 0.80061, 0.86275, 0.8768, 0.95407, 1.0258, 0.73901, 0.85022, 0.83655, 1.0156, 0.95546, 0.92179, 0.87107, 0.92179, 0.82114, 0.8096, 0.89713, 0.94438, 0.95353, 0.94083, 0.91905, 0.90406, 0.9446, 0.94292, 1.18777, 0.94292, 1.02058, 0.89903, 0.90088, 0.94938, 0.97898, 0.81093, 0.97571, 0.94938, 1.024, 0.9577, 0.95933, 0.98621, 1.0474, 0.97455, 0.98981, 0.9672, 0.95933, 0.9446, 0.97898, 0.97407, 0.97646, 0.78036, 1.10208, 0.95442, 0.95298, 0.97579, 0.9332, 0.94039, 0.938, 0.80687, 1.01149, 0.80687, 1.02058, 0.80479, 0.99793, 0.99793, 0.99793, 0.99793, 1.01149, 1.00872, 0.90088, 0.91882, 1.0213, 0.8361, 1.02058, 0.62295, 0.54324, 0.89022, 1.08595, 1, 1, 0.90088, 1, 0.97455, 0.93582, 0.90088, 1, 1.05686, 0.8361, 0.99642, 0.99642, 0.99642, 0.72851, 0.90838, 0.90838, 0.90838, 0.90838, 0.90838, 0.90838, 0.868, 0.82391, 0.80061, 0.80061, 0.80061, 0.80061, 1.0258, 1.0258, 1.0258, 1.0258, 0.97484, 0.95546, 0.92179, 0.92179, 0.92179, 0.92179, 0.92179, 1.02058, 0.92179, 0.94438, 0.94438, 0.94438, 0.94438, 0.90406, 0.86958, 0.98225, 0.94938, 0.94938, 0.94938, 0.94938, 0.94938, 0.94938, 0.9031, 0.81093, 0.94938, 0.94938, 0.94938, 0.94938, 0.98621, 0.98621, 0.98621, 0.98621, 0.93969, 0.95933, 0.9446, 0.9446, 0.9446, 0.9446, 0.9446, 1.08595, 0.9446, 0.95442, 0.95442, 0.95442, 0.95442, 0.94039, 0.97898, 0.94039, 0.90838, 0.94938, 0.90838, 0.94938, 0.90838, 0.94938, 0.82391, 0.81093, 0.82391, 0.81093, 0.82391, 0.81093, 0.82391, 0.81093, 0.96376, 0.84313, 0.97484, 0.97571, 0.80061, 0.94938, 0.80061, 0.94938, 0.80061, 0.94938, 0.80061, 0.94938, 0.80061, 0.94938, 0.8768, 0.9577, 0.8768, 0.9577, 0.8768, 0.9577, 1, 1, 0.95407, 0.95933, 0.97069, 0.95933, 1.0258, 0.98621, 1.0258, 0.98621, 1.0258, 0.98621, 1.0258, 0.98621, 1.0258, 0.98621, 0.887, 1.01591, 0.73901, 1.0474, 1, 1, 0.97455, 0.83655, 0.98981, 1, 1, 0.83655, 0.73977, 0.83655, 0.73903, 0.84638, 1.033, 0.95546, 0.95933, 1, 1, 0.95546, 0.95933, 0.8271, 0.95417, 0.95933, 0.92179, 0.9446, 0.92179, 0.9446, 0.92179, 0.9446, 0.936, 0.91964, 0.82114, 0.97646, 1, 1, 0.82114, 0.97646, 0.8096, 0.78036, 0.8096, 0.78036, 1, 1, 0.8096, 0.78036, 1, 1, 0.89713, 0.77452, 0.89713, 1.10208, 0.94438, 0.95442, 0.94438, 0.95442, 0.94438, 0.95442, 0.94438, 0.95442, 0.94438, 0.95442, 0.94438, 0.95442, 0.94083, 0.97579, 0.90406, 0.94039, 0.90406, 0.9446, 0.938, 0.9446, 0.938, 0.9446, 0.938, 1, 0.99793, 0.90838, 0.94938, 0.868, 0.9031, 0.92179, 0.9446, 1, 1, 0.89713, 1.10208, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90989, 0.9358, 0.91945, 0.83181, 0.75261, 0.87992, 0.82976, 0.96034, 0.83689, 0.97268, 1.0078, 0.90838, 0.83637, 0.8019, 0.90157, 0.80061, 0.9446, 0.95407, 0.92436, 1.0258, 0.85022, 0.97153, 1.0156, 0.95546, 0.89192, 0.92179, 0.92361, 0.87107, 0.96318, 0.89713, 0.93704, 0.95638, 0.91905, 0.91709, 0.92796, 1.0258, 0.93704, 0.94836, 1.0373, 0.95933, 1.0078, 0.95871, 0.94836, 0.96174, 0.92601, 0.9498, 0.98607, 0.95776, 0.95933, 1.05453, 1.0078, 0.98275, 0.9314, 0.95617, 0.91701, 1.05993, 0.9446, 0.78367, 0.9553, 1, 0.86832, 1.0128, 0.95871, 0.99394, 0.87548, 0.96361, 0.86774, 1.0078, 0.95871, 0.9446, 0.95871, 0.86774, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.94083, 0.97579, 0.94083, 0.97579, 0.94083, 0.97579, 0.90406, 0.94039, 0.96694, 1, 0.89903, 1, 1, 1, 0.93582, 0.93582, 0.93582, 1, 0.908, 0.908, 0.918, 0.94219, 0.94219, 0.96544, 1, 1.285, 1, 1, 0.81079, 0.81079, 1, 1, 0.74854, 1, 1, 1, 1, 0.99793, 1, 1, 1, 0.65, 1, 1.36145, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.17173, 1, 0.80535, 0.76169, 1.02058, 1.0732, 1.05486, 1, 1, 1.30692, 1.08595, 1.08595, 1, 1.08595, 1.08595, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.16161, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const MyriadProBoldMetrics = { + lineHeight: 1.2, + lineGap: 0.2 +}; +const MyriadProBoldItalicFactors = [1.36898, 1, 1, 0.66227, 0.80779, 0.81625, 0.97276, 0.97276, 0.97733, 0.92222, 0.83266, 0.94292, 0.94292, 1.16148, 1.02058, 0.93582, 0.96694, 0.93582, 1.17337, 0.97276, 0.97276, 0.97276, 0.97276, 0.97276, 0.97276, 0.97276, 0.97276, 0.97276, 0.97276, 0.78076, 0.78076, 1.02058, 1.02058, 1.02058, 0.71541, 0.76813, 0.85576, 0.80591, 0.80729, 0.94299, 0.77512, 0.83655, 0.86523, 0.92222, 0.98621, 0.71743, 0.81698, 0.79726, 0.98558, 0.92222, 0.90637, 0.83809, 0.90637, 0.80729, 0.76463, 0.86275, 0.90699, 0.91605, 0.9154, 0.85308, 0.85458, 0.90531, 0.94292, 1.21296, 0.94292, 1.02058, 0.89903, 1.18616, 0.99613, 0.91677, 0.78216, 0.91677, 0.90083, 0.98796, 0.9135, 0.92168, 0.95381, 0.98981, 0.95298, 0.95381, 0.93459, 0.92168, 0.91513, 0.92004, 0.91677, 0.95077, 0.748, 1.04502, 0.91677, 0.92061, 0.94236, 0.89544, 0.89364, 0.9, 0.80687, 0.8578, 0.80687, 1.02058, 0.80779, 0.97276, 0.97276, 0.97276, 0.97276, 0.8578, 0.99973, 1.18616, 0.91339, 1.08074, 0.82891, 1.02058, 0.55509, 0.71526, 0.89022, 1.08595, 1, 1, 1.18616, 1, 0.96736, 0.93582, 1.18616, 1, 1.04864, 0.82711, 0.99043, 0.99043, 0.99043, 0.71541, 0.85576, 0.85576, 0.85576, 0.85576, 0.85576, 0.85576, 0.845, 0.80729, 0.77512, 0.77512, 0.77512, 0.77512, 0.98621, 0.98621, 0.98621, 0.98621, 0.95961, 0.92222, 0.90637, 0.90637, 0.90637, 0.90637, 0.90637, 1.02058, 0.90251, 0.90699, 0.90699, 0.90699, 0.90699, 0.85458, 0.83659, 0.94951, 0.99613, 0.99613, 0.99613, 0.99613, 0.99613, 0.99613, 0.85811, 0.78216, 0.90083, 0.90083, 0.90083, 0.90083, 0.95381, 0.95381, 0.95381, 0.95381, 0.9135, 0.92168, 0.91513, 0.91513, 0.91513, 0.91513, 0.91513, 1.08595, 0.91677, 0.91677, 0.91677, 0.91677, 0.91677, 0.89364, 0.92332, 0.89364, 0.85576, 0.99613, 0.85576, 0.99613, 0.85576, 0.99613, 0.80729, 0.78216, 0.80729, 0.78216, 0.80729, 0.78216, 0.80729, 0.78216, 0.94299, 0.76783, 0.95961, 0.91677, 0.77512, 0.90083, 0.77512, 0.90083, 0.77512, 0.90083, 0.77512, 0.90083, 0.77512, 0.90083, 0.86523, 0.9135, 0.86523, 0.9135, 0.86523, 0.9135, 1, 1, 0.92222, 0.92168, 0.92222, 0.92168, 0.98621, 0.95381, 0.98621, 0.95381, 0.98621, 0.95381, 0.98621, 0.95381, 0.98621, 0.95381, 0.86036, 0.97096, 0.71743, 0.98981, 1, 1, 0.95298, 0.79726, 0.95381, 1, 1, 0.79726, 0.6894, 0.79726, 0.74321, 0.81691, 1.0006, 0.92222, 0.92168, 1, 1, 0.92222, 0.92168, 0.79464, 0.92098, 0.92168, 0.90637, 0.91513, 0.90637, 0.91513, 0.90637, 0.91513, 0.909, 0.87514, 0.80729, 0.95077, 1, 1, 0.80729, 0.95077, 0.76463, 0.748, 0.76463, 0.748, 1, 1, 0.76463, 0.748, 1, 1, 0.86275, 0.72651, 0.86275, 1.04502, 0.90699, 0.91677, 0.90699, 0.91677, 0.90699, 0.91677, 0.90699, 0.91677, 0.90699, 0.91677, 0.90699, 0.91677, 0.9154, 0.94236, 0.85458, 0.89364, 0.85458, 0.90531, 0.9, 0.90531, 0.9, 0.90531, 0.9, 1, 0.97276, 0.85576, 0.99613, 0.845, 0.85811, 0.90251, 0.91677, 1, 1, 0.86275, 1.04502, 1.18616, 1.18616, 1.18616, 1.18616, 1.18616, 1.18616, 1.18616, 1.18616, 1.18616, 1.00899, 1.30628, 0.85576, 0.80178, 0.66862, 0.7927, 0.69323, 0.88127, 0.72459, 0.89711, 0.95381, 0.85576, 0.80591, 0.7805, 0.94729, 0.77512, 0.90531, 0.92222, 0.90637, 0.98621, 0.81698, 0.92655, 0.98558, 0.92222, 0.85359, 0.90637, 0.90976, 0.83809, 0.94523, 0.86275, 0.83509, 0.93157, 0.85308, 0.83392, 0.92346, 0.98621, 0.83509, 0.92886, 0.91324, 0.92168, 0.95381, 0.90646, 0.92886, 0.90557, 0.86847, 0.90276, 0.91324, 0.86842, 0.92168, 0.99531, 0.95381, 0.9224, 0.85408, 0.92699, 0.86847, 1.0051, 0.91513, 0.80487, 0.93481, 1, 0.88159, 1.05214, 0.90646, 0.97355, 0.81539, 0.89398, 0.85923, 0.95381, 0.90646, 0.91513, 0.90646, 0.85923, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9154, 0.94236, 0.9154, 0.94236, 0.9154, 0.94236, 0.85458, 0.89364, 0.96694, 1, 0.89903, 1, 1, 1, 0.91782, 0.91782, 0.91782, 1, 0.896, 0.896, 0.896, 0.9332, 0.9332, 0.95973, 1, 1.26, 1, 1, 0.80479, 0.80178, 1, 1, 0.85633, 1, 1, 1, 1, 0.97276, 1, 1, 1, 0.698, 1, 1.36145, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.14542, 1, 0.79199, 0.78694, 1.02058, 1.03493, 1.05486, 1, 1, 1.23026, 1.08595, 1.08595, 1, 1.08595, 1.08595, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.20006, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const MyriadProBoldItalicMetrics = { + lineHeight: 1.2, + lineGap: 0.2 +}; +const MyriadProItalicFactors = [1.36898, 1, 1, 0.65507, 0.84943, 0.85639, 0.88465, 0.88465, 0.86936, 0.88307, 0.86948, 0.85283, 0.85283, 1.06383, 1.02058, 0.75945, 0.9219, 0.75945, 1.17337, 0.88465, 0.88465, 0.88465, 0.88465, 0.88465, 0.88465, 0.88465, 0.88465, 0.88465, 0.88465, 0.75945, 0.75945, 1.02058, 1.02058, 1.02058, 0.69046, 0.70926, 0.85158, 0.77812, 0.76852, 0.89591, 0.70466, 0.76125, 0.80094, 0.86822, 0.83864, 0.728, 0.77212, 0.79475, 0.93637, 0.87514, 0.8588, 0.76013, 0.8588, 0.72421, 0.69866, 0.77598, 0.85991, 0.80811, 0.87832, 0.78112, 0.77512, 0.8562, 1.0222, 1.18417, 1.0222, 1.27014, 0.89903, 1.15012, 0.93859, 0.94399, 0.846, 0.94399, 0.81453, 1.0186, 0.94219, 0.96017, 1.03075, 1.02175, 0.912, 1.03075, 0.96998, 0.96017, 0.93859, 0.94399, 0.94399, 0.95493, 0.746, 1.12658, 0.94578, 0.91, 0.979, 0.882, 0.882, 0.83, 0.85034, 0.83537, 0.85034, 1.02058, 0.70869, 0.88465, 0.88465, 0.88465, 0.88465, 0.83537, 0.90083, 1.15012, 0.9161, 0.94565, 0.73541, 1.02058, 0.53609, 0.69353, 0.79519, 1.08595, 1, 1, 1.15012, 1, 0.91974, 0.75945, 1.15012, 1, 0.9446, 0.73361, 0.9005, 0.9005, 0.9005, 0.62864, 0.85158, 0.85158, 0.85158, 0.85158, 0.85158, 0.85158, 0.773, 0.76852, 0.70466, 0.70466, 0.70466, 0.70466, 0.83864, 0.83864, 0.83864, 0.83864, 0.90561, 0.87514, 0.8588, 0.8588, 0.8588, 0.8588, 0.8588, 1.02058, 0.85751, 0.85991, 0.85991, 0.85991, 0.85991, 0.77512, 0.76013, 0.88075, 0.93859, 0.93859, 0.93859, 0.93859, 0.93859, 0.93859, 0.8075, 0.846, 0.81453, 0.81453, 0.81453, 0.81453, 0.82424, 0.82424, 0.82424, 0.82424, 0.9278, 0.96017, 0.93859, 0.93859, 0.93859, 0.93859, 0.93859, 1.08595, 0.8562, 0.94578, 0.94578, 0.94578, 0.94578, 0.882, 0.94578, 0.882, 0.85158, 0.93859, 0.85158, 0.93859, 0.85158, 0.93859, 0.76852, 0.846, 0.76852, 0.846, 0.76852, 0.846, 0.76852, 0.846, 0.89591, 0.8544, 0.90561, 0.94399, 0.70466, 0.81453, 0.70466, 0.81453, 0.70466, 0.81453, 0.70466, 0.81453, 0.70466, 0.81453, 0.80094, 0.94219, 0.80094, 0.94219, 0.80094, 0.94219, 1, 1, 0.86822, 0.96017, 0.86822, 0.96017, 0.83864, 0.82424, 0.83864, 0.82424, 0.83864, 0.82424, 0.83864, 1.03075, 0.83864, 0.82424, 0.81402, 1.02738, 0.728, 1.02175, 1, 1, 0.912, 0.79475, 1.03075, 1, 1, 0.79475, 0.83911, 0.79475, 0.66266, 0.80553, 1.06676, 0.87514, 0.96017, 1, 1, 0.87514, 0.96017, 0.86865, 0.87396, 0.96017, 0.8588, 0.93859, 0.8588, 0.93859, 0.8588, 0.93859, 0.867, 0.84759, 0.72421, 0.95493, 1, 1, 0.72421, 0.95493, 0.69866, 0.746, 0.69866, 0.746, 1, 1, 0.69866, 0.746, 1, 1, 0.77598, 0.88417, 0.77598, 1.12658, 0.85991, 0.94578, 0.85991, 0.94578, 0.85991, 0.94578, 0.85991, 0.94578, 0.85991, 0.94578, 0.85991, 0.94578, 0.87832, 0.979, 0.77512, 0.882, 0.77512, 0.8562, 0.83, 0.8562, 0.83, 0.8562, 0.83, 1, 0.88465, 0.85158, 0.93859, 0.773, 0.8075, 0.85751, 0.8562, 1, 1, 0.77598, 1.12658, 1.15012, 1.15012, 1.15012, 1.15012, 1.15012, 1.15313, 1.15012, 1.15012, 1.15012, 1.08106, 1.03901, 0.85158, 0.77025, 0.62264, 0.7646, 0.65351, 0.86026, 0.69461, 0.89947, 1.03075, 0.85158, 0.77812, 0.76449, 0.88836, 0.70466, 0.8562, 0.86822, 0.8588, 0.83864, 0.77212, 0.85308, 0.93637, 0.87514, 0.82352, 0.8588, 0.85701, 0.76013, 0.89058, 0.77598, 0.8156, 0.82565, 0.78112, 0.77899, 0.89386, 0.83864, 0.8156, 0.9486, 0.92388, 0.96186, 1.03075, 0.91123, 0.9486, 0.93298, 0.878, 0.93942, 0.92388, 0.84596, 0.96186, 0.95119, 1.03075, 0.922, 0.88787, 0.95829, 0.88, 0.93559, 0.93859, 0.78815, 0.93758, 1, 0.89217, 1.03737, 0.91123, 0.93969, 0.77487, 0.85769, 0.86799, 1.03075, 0.91123, 0.93859, 0.91123, 0.86799, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.87832, 0.979, 0.87832, 0.979, 0.87832, 0.979, 0.77512, 0.882, 0.9219, 1, 0.89903, 1, 1, 1, 0.87321, 0.87321, 0.87321, 1, 1.027, 1.027, 1.027, 0.86847, 0.86847, 0.79121, 1, 1.124, 1, 1, 0.73572, 0.73572, 1, 1, 0.85034, 1, 1, 1, 1, 0.88465, 1, 1, 1, 0.669, 1, 1.36145, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.04828, 1, 0.74948, 0.75187, 1.02058, 0.98391, 1.02119, 1, 1, 1.06233, 1.08595, 1.08595, 1, 1.08595, 1.08595, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.05233, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const MyriadProItalicMetrics = { + lineHeight: 1.2, + lineGap: 0.2 +}; +const MyriadProRegularFactors = [1.36898, 1, 1, 0.76305, 0.82784, 0.94935, 0.89364, 0.92241, 0.89073, 0.90706, 0.98472, 0.85283, 0.85283, 1.0664, 1.02058, 0.74505, 0.9219, 0.74505, 1.23456, 0.92241, 0.92241, 0.92241, 0.92241, 0.92241, 0.92241, 0.92241, 0.92241, 0.92241, 0.92241, 0.74505, 0.74505, 1.02058, 1.02058, 1.02058, 0.73002, 0.72601, 0.91755, 0.8126, 0.80314, 0.92222, 0.73764, 0.79726, 0.83051, 0.90284, 0.86023, 0.74, 0.8126, 0.84869, 0.96518, 0.91115, 0.8858, 0.79761, 0.8858, 0.74498, 0.73914, 0.81363, 0.89591, 0.83659, 0.89633, 0.85608, 0.8111, 0.90531, 1.0222, 1.22736, 1.0222, 1.27014, 0.89903, 0.90088, 0.86667, 1.0231, 0.896, 1.01411, 0.90083, 1.05099, 1.00512, 0.99793, 1.05326, 1.09377, 0.938, 1.06226, 1.00119, 0.99793, 0.98714, 1.0231, 1.01231, 0.98196, 0.792, 1.19137, 0.99074, 0.962, 1.01915, 0.926, 0.942, 0.856, 0.85034, 0.92006, 0.85034, 1.02058, 0.69067, 0.92241, 0.92241, 0.92241, 0.92241, 0.92006, 0.9332, 0.90088, 0.91882, 0.93484, 0.75339, 1.02058, 0.56866, 0.54324, 0.79519, 1.08595, 1, 1, 0.90088, 1, 0.95325, 0.74505, 0.90088, 1, 0.97198, 0.75339, 0.91009, 0.91009, 0.91009, 0.66466, 0.91755, 0.91755, 0.91755, 0.91755, 0.91755, 0.91755, 0.788, 0.80314, 0.73764, 0.73764, 0.73764, 0.73764, 0.86023, 0.86023, 0.86023, 0.86023, 0.92915, 0.91115, 0.8858, 0.8858, 0.8858, 0.8858, 0.8858, 1.02058, 0.8858, 0.89591, 0.89591, 0.89591, 0.89591, 0.8111, 0.79611, 0.89713, 0.86667, 0.86667, 0.86667, 0.86667, 0.86667, 0.86667, 0.86936, 0.896, 0.90083, 0.90083, 0.90083, 0.90083, 0.84224, 0.84224, 0.84224, 0.84224, 0.97276, 0.99793, 0.98714, 0.98714, 0.98714, 0.98714, 0.98714, 1.08595, 0.89876, 0.99074, 0.99074, 0.99074, 0.99074, 0.942, 1.0231, 0.942, 0.91755, 0.86667, 0.91755, 0.86667, 0.91755, 0.86667, 0.80314, 0.896, 0.80314, 0.896, 0.80314, 0.896, 0.80314, 0.896, 0.92222, 0.93372, 0.92915, 1.01411, 0.73764, 0.90083, 0.73764, 0.90083, 0.73764, 0.90083, 0.73764, 0.90083, 0.73764, 0.90083, 0.83051, 1.00512, 0.83051, 1.00512, 0.83051, 1.00512, 1, 1, 0.90284, 0.99793, 0.90976, 0.99793, 0.86023, 0.84224, 0.86023, 0.84224, 0.86023, 0.84224, 0.86023, 1.05326, 0.86023, 0.84224, 0.82873, 1.07469, 0.74, 1.09377, 1, 1, 0.938, 0.84869, 1.06226, 1, 1, 0.84869, 0.83704, 0.84869, 0.81441, 0.85588, 1.08927, 0.91115, 0.99793, 1, 1, 0.91115, 0.99793, 0.91887, 0.90991, 0.99793, 0.8858, 0.98714, 0.8858, 0.98714, 0.8858, 0.98714, 0.894, 0.91434, 0.74498, 0.98196, 1, 1, 0.74498, 0.98196, 0.73914, 0.792, 0.73914, 0.792, 1, 1, 0.73914, 0.792, 1, 1, 0.81363, 0.904, 0.81363, 1.19137, 0.89591, 0.99074, 0.89591, 0.99074, 0.89591, 0.99074, 0.89591, 0.99074, 0.89591, 0.99074, 0.89591, 0.99074, 0.89633, 1.01915, 0.8111, 0.942, 0.8111, 0.90531, 0.856, 0.90531, 0.856, 0.90531, 0.856, 1, 0.92241, 0.91755, 0.86667, 0.788, 0.86936, 0.8858, 0.89876, 1, 1, 0.81363, 1.19137, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90088, 0.90388, 1.03901, 0.92138, 0.78105, 0.7154, 0.86169, 0.80513, 0.94007, 0.82528, 0.98612, 1.06226, 0.91755, 0.8126, 0.81884, 0.92819, 0.73764, 0.90531, 0.90284, 0.8858, 0.86023, 0.8126, 0.91172, 0.96518, 0.91115, 0.83089, 0.8858, 0.87791, 0.79761, 0.89297, 0.81363, 0.88157, 0.89992, 0.85608, 0.81992, 0.94307, 0.86023, 0.88157, 0.95308, 0.98699, 0.99793, 1.06226, 0.95817, 0.95308, 0.97358, 0.928, 0.98088, 0.98699, 0.92761, 0.99793, 0.96017, 1.06226, 0.986, 0.944, 0.95978, 0.938, 0.96705, 0.98714, 0.80442, 0.98972, 1, 0.89762, 1.04552, 0.95817, 0.99007, 0.87064, 0.91879, 0.88888, 1.06226, 0.95817, 0.98714, 0.95817, 0.88888, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.89633, 1.01915, 0.89633, 1.01915, 0.89633, 1.01915, 0.8111, 0.942, 0.9219, 1, 0.89903, 1, 1, 1, 0.93173, 0.93173, 0.93173, 1, 1.06304, 1.06304, 1.06904, 0.89903, 0.89903, 0.80549, 1, 1.156, 1, 1, 0.76575, 0.76575, 1, 1, 0.72458, 1, 1, 1, 1, 0.92241, 1, 1, 1, 0.619, 1, 1.36145, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.07257, 1, 0.74705, 0.71119, 1.02058, 1.024, 1.02119, 1, 1, 1.1536, 1.08595, 1.08595, 1, 1.08595, 1.08595, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.05638, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const MyriadProRegularMetrics = { + lineHeight: 1.2, + lineGap: 0.2 +}; + +;// ./src/core/segoeui_factors.js +const SegoeuiBoldFactors = [1.76738, 1, 1, 0.99297, 0.9824, 1.04016, 1.06497, 1.03424, 0.97529, 1.17647, 1.23203, 1.1085, 1.1085, 1.16939, 1.2107, 0.9754, 1.21408, 0.9754, 1.59578, 1.03424, 1.03424, 1.03424, 1.03424, 1.03424, 1.03424, 1.03424, 1.03424, 1.03424, 1.03424, 0.81378, 0.81378, 1.2107, 1.2107, 1.2107, 0.71703, 0.97847, 0.97363, 0.88776, 0.8641, 1.02096, 0.79795, 0.85132, 0.914, 1.06085, 1.1406, 0.8007, 0.89858, 0.83693, 1.14889, 1.09398, 0.97489, 0.92094, 0.97489, 0.90399, 0.84041, 0.95923, 1.00135, 1, 1.06467, 0.98243, 0.90996, 0.99361, 1.1085, 1.56942, 1.1085, 1.2107, 0.74627, 0.94282, 0.96752, 1.01519, 0.86304, 1.01359, 0.97278, 1.15103, 1.01359, 0.98561, 1.02285, 1.02285, 1.00527, 1.02285, 1.0302, 0.99041, 1.0008, 1.01519, 1.01359, 1.02258, 0.79104, 1.16862, 0.99041, 0.97454, 1.02511, 0.99298, 0.96752, 0.95801, 0.94856, 1.16579, 0.94856, 1.2107, 0.9824, 1.03424, 1.03424, 1, 1.03424, 1.16579, 0.8727, 1.3871, 1.18622, 1.10818, 1.04478, 1.2107, 1.18622, 0.75155, 0.94994, 1.28826, 1.21408, 1.21408, 0.91056, 1, 0.91572, 0.9754, 0.64663, 1.18328, 1.24866, 1.04478, 1.14169, 1.15749, 1.17389, 0.71703, 0.97363, 0.97363, 0.97363, 0.97363, 0.97363, 0.97363, 0.93506, 0.8641, 0.79795, 0.79795, 0.79795, 0.79795, 1.1406, 1.1406, 1.1406, 1.1406, 1.02096, 1.09398, 0.97426, 0.97426, 0.97426, 0.97426, 0.97426, 1.2107, 0.97489, 1.00135, 1.00135, 1.00135, 1.00135, 0.90996, 0.92094, 1.02798, 0.96752, 0.96752, 0.96752, 0.96752, 0.96752, 0.96752, 0.93136, 0.86304, 0.97278, 0.97278, 0.97278, 0.97278, 1.02285, 1.02285, 1.02285, 1.02285, 0.97122, 0.99041, 1, 1, 1, 1, 1, 1.28826, 1.0008, 0.99041, 0.99041, 0.99041, 0.99041, 0.96752, 1.01519, 0.96752, 0.97363, 0.96752, 0.97363, 0.96752, 0.97363, 0.96752, 0.8641, 0.86304, 0.8641, 0.86304, 0.8641, 0.86304, 0.8641, 0.86304, 1.02096, 1.03057, 1.02096, 1.03517, 0.79795, 0.97278, 0.79795, 0.97278, 0.79795, 0.97278, 0.79795, 0.97278, 0.79795, 0.97278, 0.914, 1.01359, 0.914, 1.01359, 0.914, 1.01359, 1, 1, 1.06085, 0.98561, 1.06085, 1.00879, 1.1406, 1.02285, 1.1406, 1.02285, 1.1406, 1.02285, 1.1406, 1.02285, 1.1406, 1.02285, 0.97138, 1.08692, 0.8007, 1.02285, 1, 1, 1.00527, 0.83693, 1.02285, 1, 1, 0.83693, 0.9455, 0.83693, 0.90418, 0.83693, 1.13005, 1.09398, 0.99041, 1, 1, 1.09398, 0.99041, 0.96692, 1.09251, 0.99041, 0.97489, 1.0008, 0.97489, 1.0008, 0.97489, 1.0008, 0.93994, 0.97931, 0.90399, 1.02258, 1, 1, 0.90399, 1.02258, 0.84041, 0.79104, 0.84041, 0.79104, 0.84041, 0.79104, 0.84041, 0.79104, 1, 1, 0.95923, 1.07034, 0.95923, 1.16862, 1.00135, 0.99041, 1.00135, 0.99041, 1.00135, 0.99041, 1.00135, 0.99041, 1.00135, 0.99041, 1.00135, 0.99041, 1.06467, 1.02511, 0.90996, 0.96752, 0.90996, 0.99361, 0.95801, 0.99361, 0.95801, 0.99361, 0.95801, 1.07733, 1.03424, 0.97363, 0.96752, 0.93506, 0.93136, 0.97489, 1.0008, 1, 1, 0.95923, 1.16862, 1.15103, 1.15103, 1.01173, 1.03959, 0.75953, 0.81378, 0.79912, 1.15103, 1.21994, 0.95161, 0.87815, 1.01149, 0.81525, 0.7676, 0.98167, 1.01134, 1.02546, 0.84097, 1.03089, 1.18102, 0.97363, 0.88776, 0.85134, 0.97826, 0.79795, 0.99361, 1.06085, 0.97489, 1.1406, 0.89858, 1.0388, 1.14889, 1.09398, 0.86039, 0.97489, 1.0595, 0.92094, 0.94793, 0.95923, 0.90996, 0.99346, 0.98243, 1.02112, 0.95493, 1.1406, 0.90996, 1.03574, 1.02597, 1.0008, 1.18102, 1.06628, 1.03574, 1.0192, 1.01932, 1.00886, 0.97531, 1.0106, 1.0008, 1.13189, 1.18102, 1.02277, 0.98683, 1.0016, 0.99561, 1.07237, 1.0008, 0.90434, 0.99921, 0.93803, 0.8965, 1.23085, 1.06628, 1.04983, 0.96268, 1.0499, 0.98439, 1.18102, 1.06628, 1.0008, 1.06628, 0.98439, 0.79795, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.09466, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.97278, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.02065, 1, 1, 1, 1, 1, 1, 1.06467, 1.02511, 1.06467, 1.02511, 1.06467, 1.02511, 0.90996, 0.96752, 1, 1.21408, 0.89903, 1, 1, 0.75155, 1.04394, 1.04394, 1.04394, 1.04394, 0.98633, 0.98633, 0.98633, 0.73047, 0.73047, 1.20642, 0.91211, 1.25635, 1.222, 1.02956, 1.03372, 1.03372, 0.96039, 1.24633, 1, 1.12454, 0.93503, 1.03424, 1.19687, 1.03424, 1, 1, 1, 0.771, 1, 1, 1.15749, 1.15749, 1.15749, 1.10948, 0.86279, 0.94434, 0.86279, 0.94434, 0.86182, 1, 1, 1.16897, 1, 0.96085, 0.90137, 1.2107, 1.18416, 1.13973, 0.69825, 0.9716, 2.10339, 1.29004, 1.29004, 1.21172, 1.29004, 1.29004, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.42603, 1, 0.99862, 0.99862, 1, 0.87025, 0.87025, 0.87025, 0.87025, 1.18874, 1.42603, 1, 1.42603, 1.42603, 0.99862, 1, 1, 1, 1, 1, 1.2886, 1.04315, 1.15296, 1.34163, 1, 1, 1, 1.09193, 1.09193, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const SegoeuiBoldMetrics = { + lineHeight: 1.33008, + lineGap: 0 +}; +const SegoeuiBoldItalicFactors = [1.76738, 1, 1, 0.98946, 1.03959, 1.04016, 1.02809, 1.036, 0.97639, 1.10953, 1.23203, 1.11144, 1.11144, 1.16939, 1.21237, 0.9754, 1.21261, 0.9754, 1.59754, 1.036, 1.036, 1.036, 1.036, 1.036, 1.036, 1.036, 1.036, 1.036, 1.036, 0.81378, 0.81378, 1.21237, 1.21237, 1.21237, 0.73541, 0.97847, 0.97363, 0.89723, 0.87897, 1.0426, 0.79429, 0.85292, 0.91149, 1.05815, 1.1406, 0.79631, 0.90128, 0.83853, 1.04396, 1.10615, 0.97552, 0.94436, 0.97552, 0.88641, 0.80527, 0.96083, 1.00135, 1, 1.06777, 0.9817, 0.91142, 0.99361, 1.11144, 1.57293, 1.11144, 1.21237, 0.74627, 1.31818, 1.06585, 0.97042, 0.83055, 0.97042, 0.93503, 1.1261, 0.97042, 0.97922, 1.14236, 0.94552, 1.01054, 1.14236, 1.02471, 0.97922, 0.94165, 0.97042, 0.97042, 1.0276, 0.78929, 1.1261, 0.97922, 0.95874, 1.02197, 0.98507, 0.96752, 0.97168, 0.95107, 1.16579, 0.95107, 1.21237, 1.03959, 1.036, 1.036, 1, 1.036, 1.16579, 0.87357, 1.31818, 1.18754, 1.26781, 1.05356, 1.21237, 1.18622, 0.79487, 0.94994, 1.29004, 1.24047, 1.24047, 1.31818, 1, 0.91484, 0.9754, 1.31818, 1.1349, 1.24866, 1.05356, 1.13934, 1.15574, 1.17389, 0.73541, 0.97363, 0.97363, 0.97363, 0.97363, 0.97363, 0.97363, 0.94385, 0.87897, 0.79429, 0.79429, 0.79429, 0.79429, 1.1406, 1.1406, 1.1406, 1.1406, 1.0426, 1.10615, 0.97552, 0.97552, 0.97552, 0.97552, 0.97552, 1.21237, 0.97552, 1.00135, 1.00135, 1.00135, 1.00135, 0.91142, 0.94436, 0.98721, 1.06585, 1.06585, 1.06585, 1.06585, 1.06585, 1.06585, 0.96705, 0.83055, 0.93503, 0.93503, 0.93503, 0.93503, 1.14236, 1.14236, 1.14236, 1.14236, 0.93125, 0.97922, 0.94165, 0.94165, 0.94165, 0.94165, 0.94165, 1.29004, 0.94165, 0.97922, 0.97922, 0.97922, 0.97922, 0.96752, 0.97042, 0.96752, 0.97363, 1.06585, 0.97363, 1.06585, 0.97363, 1.06585, 0.87897, 0.83055, 0.87897, 0.83055, 0.87897, 0.83055, 0.87897, 0.83055, 1.0426, 1.0033, 1.0426, 0.97042, 0.79429, 0.93503, 0.79429, 0.93503, 0.79429, 0.93503, 0.79429, 0.93503, 0.79429, 0.93503, 0.91149, 0.97042, 0.91149, 0.97042, 0.91149, 0.97042, 1, 1, 1.05815, 0.97922, 1.05815, 0.97922, 1.1406, 1.14236, 1.1406, 1.14236, 1.1406, 1.14236, 1.1406, 1.14236, 1.1406, 1.14236, 0.97441, 1.04302, 0.79631, 1.01582, 1, 1, 1.01054, 0.83853, 1.14236, 1, 1, 0.83853, 1.09125, 0.83853, 0.90418, 0.83853, 1.19508, 1.10615, 0.97922, 1, 1, 1.10615, 0.97922, 1.01034, 1.10466, 0.97922, 0.97552, 0.94165, 0.97552, 0.94165, 0.97552, 0.94165, 0.91602, 0.91981, 0.88641, 1.0276, 1, 1, 0.88641, 1.0276, 0.80527, 0.78929, 0.80527, 0.78929, 0.80527, 0.78929, 0.80527, 0.78929, 1, 1, 0.96083, 1.05403, 0.95923, 1.16862, 1.00135, 0.97922, 1.00135, 0.97922, 1.00135, 0.97922, 1.00135, 0.97922, 1.00135, 0.97922, 1.00135, 0.97922, 1.06777, 1.02197, 0.91142, 0.96752, 0.91142, 0.99361, 0.97168, 0.99361, 0.97168, 0.99361, 0.97168, 1.23199, 1.036, 0.97363, 1.06585, 0.94385, 0.96705, 0.97552, 0.94165, 1, 1, 0.96083, 1.1261, 1.31818, 1.31818, 1.31818, 1.31818, 1.31818, 1.31818, 1.31818, 1.31818, 1.31818, 0.95161, 1.27126, 1.00811, 0.83284, 0.77702, 0.99137, 0.95253, 1.0347, 0.86142, 1.07205, 1.14236, 0.97363, 0.89723, 0.86869, 1.09818, 0.79429, 0.99361, 1.05815, 0.97552, 1.1406, 0.90128, 1.06662, 1.04396, 1.10615, 0.84918, 0.97552, 1.04694, 0.94436, 0.98015, 0.96083, 0.91142, 1.00356, 0.9817, 1.01945, 0.98999, 1.1406, 0.91142, 1.04961, 0.9898, 1.00639, 1.14236, 1.07514, 1.04961, 0.99607, 1.02897, 1.008, 0.9898, 0.95134, 1.00639, 1.11121, 1.14236, 1.00518, 0.97981, 1.02186, 1, 1.08578, 0.94165, 0.99314, 0.98387, 0.93028, 0.93377, 1.35125, 1.07514, 1.10687, 0.93491, 1.04232, 1.00351, 1.14236, 1.07514, 0.94165, 1.07514, 1.00351, 0.79429, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.09097, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.93503, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.96609, 1, 1, 1, 1, 1, 1, 1.06777, 1.02197, 1.06777, 1.02197, 1.06777, 1.02197, 0.91142, 0.96752, 1, 1.21261, 0.89903, 1, 1, 0.75155, 1.04745, 1.04745, 1.04745, 1.04394, 0.98633, 0.98633, 0.98633, 0.72959, 0.72959, 1.20502, 0.91406, 1.26514, 1.222, 1.02956, 1.03372, 1.03372, 0.96039, 1.24633, 1, 1.09125, 0.93327, 1.03336, 1.16541, 1.036, 1, 1, 1, 0.771, 1, 1, 1.15574, 1.15574, 1.15574, 1.15574, 0.86364, 0.94434, 0.86279, 0.94434, 0.86224, 1, 1, 1.16798, 1, 0.96085, 0.90068, 1.21237, 1.18416, 1.13904, 0.69825, 0.9716, 2.10339, 1.29004, 1.29004, 1.21339, 1.29004, 1.29004, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.42603, 1, 0.99862, 0.99862, 1, 0.87025, 0.87025, 0.87025, 0.87025, 1.18775, 1.42603, 1, 1.42603, 1.42603, 0.99862, 1, 1, 1, 1, 1, 1.2886, 1.04315, 1.15296, 1.34163, 1, 1, 1, 1.13269, 1.13269, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const SegoeuiBoldItalicMetrics = { + lineHeight: 1.33008, + lineGap: 0 +}; +const SegoeuiItalicFactors = [1.76738, 1, 1, 0.98946, 1.14763, 1.05365, 1.06234, 0.96927, 0.92586, 1.15373, 1.18414, 0.91349, 0.91349, 1.07403, 1.17308, 0.78383, 1.20088, 0.78383, 1.42531, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.78383, 0.78383, 1.17308, 1.17308, 1.17308, 0.77349, 0.94565, 0.94729, 0.85944, 0.88506, 0.9858, 0.74817, 0.80016, 0.88449, 0.98039, 0.95782, 0.69238, 0.89898, 0.83231, 0.98183, 1.03989, 0.96924, 0.86237, 0.96924, 0.80595, 0.74524, 0.86091, 0.95402, 0.94143, 0.98448, 0.8858, 0.83089, 0.93285, 1.0949, 1.39016, 1.0949, 1.45994, 0.74627, 1.04839, 0.97454, 0.97454, 0.87207, 0.97454, 0.87533, 1.06151, 0.97454, 1.00176, 1.16484, 1.08132, 0.98047, 1.16484, 1.02989, 1.01054, 0.96225, 0.97454, 0.97454, 1.06598, 0.79004, 1.16344, 1.00351, 0.94629, 0.9973, 0.91016, 0.96777, 0.9043, 0.91082, 0.92481, 0.91082, 1.17308, 0.95748, 0.96927, 0.96927, 1, 0.96927, 0.92481, 0.80597, 1.04839, 1.23393, 1.1781, 0.9245, 1.17308, 1.20808, 0.63218, 0.94261, 1.24822, 1.09971, 1.09971, 1.04839, 1, 0.85273, 0.78032, 1.04839, 1.09971, 1.22326, 0.9245, 1.09836, 1.13525, 1.15222, 0.70424, 0.94729, 0.94729, 0.94729, 0.94729, 0.94729, 0.94729, 0.85498, 0.88506, 0.74817, 0.74817, 0.74817, 0.74817, 0.95782, 0.95782, 0.95782, 0.95782, 0.9858, 1.03989, 0.96924, 0.96924, 0.96924, 0.96924, 0.96924, 1.17308, 0.96924, 0.95402, 0.95402, 0.95402, 0.95402, 0.83089, 0.86237, 0.88409, 0.97454, 0.97454, 0.97454, 0.97454, 0.97454, 0.97454, 0.92916, 0.87207, 0.87533, 0.87533, 0.87533, 0.87533, 0.93146, 0.93146, 0.93146, 0.93146, 0.93854, 1.01054, 0.96225, 0.96225, 0.96225, 0.96225, 0.96225, 1.24822, 0.8761, 1.00351, 1.00351, 1.00351, 1.00351, 0.96777, 0.97454, 0.96777, 0.94729, 0.97454, 0.94729, 0.97454, 0.94729, 0.97454, 0.88506, 0.87207, 0.88506, 0.87207, 0.88506, 0.87207, 0.88506, 0.87207, 0.9858, 0.95391, 0.9858, 0.97454, 0.74817, 0.87533, 0.74817, 0.87533, 0.74817, 0.87533, 0.74817, 0.87533, 0.74817, 0.87533, 0.88449, 0.97454, 0.88449, 0.97454, 0.88449, 0.97454, 1, 1, 0.98039, 1.00176, 0.98039, 1.00176, 0.95782, 0.93146, 0.95782, 0.93146, 0.95782, 0.93146, 0.95782, 1.16484, 0.95782, 0.93146, 0.84421, 1.12761, 0.69238, 1.08132, 1, 1, 0.98047, 0.83231, 1.16484, 1, 1, 0.84723, 1.04861, 0.84723, 0.78755, 0.83231, 1.23736, 1.03989, 1.01054, 1, 1, 1.03989, 1.01054, 0.9857, 1.03849, 1.01054, 0.96924, 0.96225, 0.96924, 0.96225, 0.96924, 0.96225, 0.92383, 0.90171, 0.80595, 1.06598, 1, 1, 0.80595, 1.06598, 0.74524, 0.79004, 0.74524, 0.79004, 0.74524, 0.79004, 0.74524, 0.79004, 1, 1, 0.86091, 1.02759, 0.85771, 1.16344, 0.95402, 1.00351, 0.95402, 1.00351, 0.95402, 1.00351, 0.95402, 1.00351, 0.95402, 1.00351, 0.95402, 1.00351, 0.98448, 0.9973, 0.83089, 0.96777, 0.83089, 0.93285, 0.9043, 0.93285, 0.9043, 0.93285, 0.9043, 1.31868, 0.96927, 0.94729, 0.97454, 0.85498, 0.92916, 0.96924, 0.8761, 1, 1, 0.86091, 1.16344, 1.04839, 1.04839, 1.04839, 1.04839, 1.04839, 1.04839, 1.04839, 1.04839, 1.04839, 0.81965, 0.81965, 0.94729, 0.78032, 0.71022, 0.90883, 0.84171, 0.99877, 0.77596, 1.05734, 1.2, 0.94729, 0.85944, 0.82791, 0.9607, 0.74817, 0.93285, 0.98039, 0.96924, 0.95782, 0.89898, 0.98316, 0.98183, 1.03989, 0.78614, 0.96924, 0.97642, 0.86237, 0.86075, 0.86091, 0.83089, 0.90082, 0.8858, 0.97296, 1.01284, 0.95782, 0.83089, 1.0976, 1.04, 1.03342, 1.2, 1.0675, 1.0976, 0.98205, 1.03809, 1.05097, 1.04, 0.95364, 1.03342, 1.05401, 1.2, 1.02148, 1.0119, 1.04724, 1.0127, 1.02732, 0.96225, 0.8965, 0.97783, 0.93574, 0.94818, 1.30679, 1.0675, 1.11826, 0.99821, 1.0557, 1.0326, 1.2, 1.0675, 0.96225, 1.0675, 1.0326, 0.74817, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.03754, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.87533, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.98705, 1, 1, 1, 1, 1, 1, 0.98448, 0.9973, 0.98448, 0.9973, 0.98448, 0.9973, 0.83089, 0.96777, 1, 1.20088, 0.89903, 1, 1, 0.75155, 0.94945, 0.94945, 0.94945, 0.94945, 1.12317, 1.12317, 1.12317, 0.67603, 0.67603, 1.15621, 0.73584, 1.21191, 1.22135, 1.06483, 0.94868, 0.94868, 0.95996, 1.24633, 1, 1.07497, 0.87709, 0.96927, 1.01473, 0.96927, 1, 1, 1, 0.77295, 1, 1, 1.09836, 1.09836, 1.09836, 1.01522, 0.86321, 0.94434, 0.8649, 0.94434, 0.86182, 1, 1, 1.083, 1, 0.91578, 0.86438, 1.17308, 1.18416, 1.14589, 0.69825, 0.97622, 1.96791, 1.24822, 1.24822, 1.17308, 1.24822, 1.24822, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.42603, 1, 0.99862, 0.99862, 1, 0.87025, 0.87025, 0.87025, 0.87025, 1.17984, 1.42603, 1, 1.42603, 1.42603, 0.99862, 1, 1, 1, 1, 1, 1.2886, 1.04315, 1.15296, 1.34163, 1, 1, 1, 1.10742, 1.10742, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const SegoeuiItalicMetrics = { + lineHeight: 1.33008, + lineGap: 0 +}; +const SegoeuiRegularFactors = [1.76738, 1, 1, 0.98594, 1.02285, 1.10454, 1.06234, 0.96927, 0.92037, 1.19985, 1.2046, 0.90616, 0.90616, 1.07152, 1.1714, 0.78032, 1.20088, 0.78032, 1.40246, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.96927, 0.78032, 0.78032, 1.1714, 1.1714, 1.1714, 0.80597, 0.94084, 0.96706, 0.85944, 0.85734, 0.97093, 0.75842, 0.79936, 0.88198, 0.9831, 0.95782, 0.71387, 0.86969, 0.84636, 1.07796, 1.03584, 0.96924, 0.83968, 0.96924, 0.82826, 0.79649, 0.85771, 0.95132, 0.93119, 0.98965, 0.88433, 0.8287, 0.93365, 1.08612, 1.3638, 1.08612, 1.45786, 0.74627, 0.80499, 0.91484, 1.05707, 0.92383, 1.05882, 0.9403, 1.12654, 1.05882, 1.01756, 1.09011, 1.09011, 0.99414, 1.09011, 1.034, 1.01756, 1.05356, 1.05707, 1.05882, 1.04399, 0.84863, 1.21968, 1.01756, 0.95801, 1.00068, 0.91797, 0.96777, 0.9043, 0.90351, 0.92105, 0.90351, 1.1714, 0.85337, 0.96927, 0.96927, 0.99912, 0.96927, 0.92105, 0.80597, 1.2434, 1.20808, 1.05937, 0.90957, 1.1714, 1.20808, 0.75155, 0.94261, 1.24644, 1.09971, 1.09971, 0.84751, 1, 0.85273, 0.78032, 0.61584, 1.05425, 1.17914, 0.90957, 1.08665, 1.11593, 1.14169, 0.73381, 0.96706, 0.96706, 0.96706, 0.96706, 0.96706, 0.96706, 0.86035, 0.85734, 0.75842, 0.75842, 0.75842, 0.75842, 0.95782, 0.95782, 0.95782, 0.95782, 0.97093, 1.03584, 0.96924, 0.96924, 0.96924, 0.96924, 0.96924, 1.1714, 0.96924, 0.95132, 0.95132, 0.95132, 0.95132, 0.8287, 0.83968, 0.89049, 0.91484, 0.91484, 0.91484, 0.91484, 0.91484, 0.91484, 0.93575, 0.92383, 0.9403, 0.9403, 0.9403, 0.9403, 0.8717, 0.8717, 0.8717, 0.8717, 1.00527, 1.01756, 1.05356, 1.05356, 1.05356, 1.05356, 1.05356, 1.24644, 0.95923, 1.01756, 1.01756, 1.01756, 1.01756, 0.96777, 1.05707, 0.96777, 0.96706, 0.91484, 0.96706, 0.91484, 0.96706, 0.91484, 0.85734, 0.92383, 0.85734, 0.92383, 0.85734, 0.92383, 0.85734, 0.92383, 0.97093, 1.0969, 0.97093, 1.05882, 0.75842, 0.9403, 0.75842, 0.9403, 0.75842, 0.9403, 0.75842, 0.9403, 0.75842, 0.9403, 0.88198, 1.05882, 0.88198, 1.05882, 0.88198, 1.05882, 1, 1, 0.9831, 1.01756, 0.9831, 1.01756, 0.95782, 0.8717, 0.95782, 0.8717, 0.95782, 0.8717, 0.95782, 1.09011, 0.95782, 0.8717, 0.84784, 1.11551, 0.71387, 1.09011, 1, 1, 0.99414, 0.84636, 1.09011, 1, 1, 0.84636, 1.0536, 0.84636, 0.94298, 0.84636, 1.23297, 1.03584, 1.01756, 1, 1, 1.03584, 1.01756, 1.00323, 1.03444, 1.01756, 0.96924, 1.05356, 0.96924, 1.05356, 0.96924, 1.05356, 0.93066, 0.98293, 0.82826, 1.04399, 1, 1, 0.82826, 1.04399, 0.79649, 0.84863, 0.79649, 0.84863, 0.79649, 0.84863, 0.79649, 0.84863, 1, 1, 0.85771, 1.17318, 0.85771, 1.21968, 0.95132, 1.01756, 0.95132, 1.01756, 0.95132, 1.01756, 0.95132, 1.01756, 0.95132, 1.01756, 0.95132, 1.01756, 0.98965, 1.00068, 0.8287, 0.96777, 0.8287, 0.93365, 0.9043, 0.93365, 0.9043, 0.93365, 0.9043, 1.08571, 0.96927, 0.96706, 0.91484, 0.86035, 0.93575, 0.96924, 0.95923, 1, 1, 0.85771, 1.21968, 1.11437, 1.11437, 0.93109, 0.91202, 0.60411, 0.84164, 0.55572, 1.01173, 0.97361, 0.81818, 0.81818, 0.96635, 0.78032, 0.72727, 0.92366, 0.98601, 1.03405, 0.77968, 1.09799, 1.2, 0.96706, 0.85944, 0.85638, 0.96491, 0.75842, 0.93365, 0.9831, 0.96924, 0.95782, 0.86969, 0.94152, 1.07796, 1.03584, 0.78437, 0.96924, 0.98715, 0.83968, 0.83491, 0.85771, 0.8287, 0.94492, 0.88433, 0.9287, 1.0098, 0.95782, 0.8287, 1.0625, 0.98248, 1.03424, 1.2, 1.01071, 1.0625, 0.95246, 1.03809, 1.04912, 0.98248, 1.00221, 1.03424, 1.05443, 1.2, 1.04785, 0.99609, 1.00169, 1.05176, 0.99346, 1.05356, 0.9087, 1.03004, 0.95542, 0.93117, 1.23362, 1.01071, 1.07831, 1.02512, 1.05205, 1.03502, 1.2, 1.01071, 1.05356, 1.01071, 1.03502, 0.75842, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.03719, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9403, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.04021, 1, 1, 1, 1, 1, 1, 0.98965, 1.00068, 0.98965, 1.00068, 0.98965, 1.00068, 0.8287, 0.96777, 1, 1.20088, 0.89903, 1, 1, 0.75155, 1.03077, 1.03077, 1.03077, 1.03077, 1.13196, 1.13196, 1.13196, 0.67428, 0.67428, 1.16039, 0.73291, 1.20996, 1.22135, 1.06483, 0.94868, 0.94868, 0.95996, 1.24633, 1, 1.07497, 0.87796, 0.96927, 1.01518, 0.96927, 1, 1, 1, 0.77295, 1, 1, 1.10539, 1.10539, 1.11358, 1.06967, 0.86279, 0.94434, 0.86279, 0.94434, 0.86182, 1, 1, 1.083, 1, 0.91578, 0.86507, 1.1714, 1.18416, 1.14589, 0.69825, 0.97622, 1.9697, 1.24822, 1.24822, 1.17238, 1.24822, 1.24822, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.42603, 1, 0.99862, 0.99862, 1, 0.87025, 0.87025, 0.87025, 0.87025, 1.18083, 1.42603, 1, 1.42603, 1.42603, 0.99862, 1, 1, 1, 1, 1, 1.2886, 1.04315, 1.15296, 1.34163, 1, 1, 1, 1.10938, 1.10938, 1, 1, 1, 1.05425, 1.09971, 1.09971, 1.09971, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +const SegoeuiRegularMetrics = { + lineHeight: 1.33008, + lineGap: 0 +}; + +;// ./src/core/xfa_fonts.js + + + + + + + + +const getXFAFontMap = getLookupTableFactory(function (t) { + t["MyriadPro-Regular"] = t["PdfJS-Fallback-Regular"] = { + name: "LiberationSans-Regular", + factors: MyriadProRegularFactors, + baseWidths: LiberationSansRegularWidths, + baseMapping: LiberationSansRegularMapping, + metrics: MyriadProRegularMetrics + }; + t["MyriadPro-Bold"] = t["PdfJS-Fallback-Bold"] = { + name: "LiberationSans-Bold", + factors: MyriadProBoldFactors, + baseWidths: LiberationSansBoldWidths, + baseMapping: LiberationSansBoldMapping, + metrics: MyriadProBoldMetrics + }; + t["MyriadPro-It"] = t["MyriadPro-Italic"] = t["PdfJS-Fallback-Italic"] = { + name: "LiberationSans-Italic", + factors: MyriadProItalicFactors, + baseWidths: LiberationSansItalicWidths, + baseMapping: LiberationSansItalicMapping, + metrics: MyriadProItalicMetrics + }; + t["MyriadPro-BoldIt"] = t["MyriadPro-BoldItalic"] = t["PdfJS-Fallback-BoldItalic"] = { + name: "LiberationSans-BoldItalic", + factors: MyriadProBoldItalicFactors, + baseWidths: LiberationSansBoldItalicWidths, + baseMapping: LiberationSansBoldItalicMapping, + metrics: MyriadProBoldItalicMetrics + }; + t.ArialMT = t.Arial = t["Arial-Regular"] = { + name: "LiberationSans-Regular", + baseWidths: LiberationSansRegularWidths, + baseMapping: LiberationSansRegularMapping + }; + t["Arial-BoldMT"] = t["Arial-Bold"] = { + name: "LiberationSans-Bold", + baseWidths: LiberationSansBoldWidths, + baseMapping: LiberationSansBoldMapping + }; + t["Arial-ItalicMT"] = t["Arial-Italic"] = { + name: "LiberationSans-Italic", + baseWidths: LiberationSansItalicWidths, + baseMapping: LiberationSansItalicMapping + }; + t["Arial-BoldItalicMT"] = t["Arial-BoldItalic"] = { + name: "LiberationSans-BoldItalic", + baseWidths: LiberationSansBoldItalicWidths, + baseMapping: LiberationSansBoldItalicMapping + }; + t["Calibri-Regular"] = { + name: "LiberationSans-Regular", + factors: CalibriRegularFactors, + baseWidths: LiberationSansRegularWidths, + baseMapping: LiberationSansRegularMapping, + metrics: CalibriRegularMetrics + }; + t["Calibri-Bold"] = { + name: "LiberationSans-Bold", + factors: CalibriBoldFactors, + baseWidths: LiberationSansBoldWidths, + baseMapping: LiberationSansBoldMapping, + metrics: CalibriBoldMetrics + }; + t["Calibri-Italic"] = { + name: "LiberationSans-Italic", + factors: CalibriItalicFactors, + baseWidths: LiberationSansItalicWidths, + baseMapping: LiberationSansItalicMapping, + metrics: CalibriItalicMetrics + }; + t["Calibri-BoldItalic"] = { + name: "LiberationSans-BoldItalic", + factors: CalibriBoldItalicFactors, + baseWidths: LiberationSansBoldItalicWidths, + baseMapping: LiberationSansBoldItalicMapping, + metrics: CalibriBoldItalicMetrics + }; + t["Segoeui-Regular"] = { + name: "LiberationSans-Regular", + factors: SegoeuiRegularFactors, + baseWidths: LiberationSansRegularWidths, + baseMapping: LiberationSansRegularMapping, + metrics: SegoeuiRegularMetrics + }; + t["Segoeui-Bold"] = { + name: "LiberationSans-Bold", + factors: SegoeuiBoldFactors, + baseWidths: LiberationSansBoldWidths, + baseMapping: LiberationSansBoldMapping, + metrics: SegoeuiBoldMetrics + }; + t["Segoeui-Italic"] = { + name: "LiberationSans-Italic", + factors: SegoeuiItalicFactors, + baseWidths: LiberationSansItalicWidths, + baseMapping: LiberationSansItalicMapping, + metrics: SegoeuiItalicMetrics + }; + t["Segoeui-BoldItalic"] = { + name: "LiberationSans-BoldItalic", + factors: SegoeuiBoldItalicFactors, + baseWidths: LiberationSansBoldItalicWidths, + baseMapping: LiberationSansBoldItalicMapping, + metrics: SegoeuiBoldItalicMetrics + }; + t["Helvetica-Regular"] = t.Helvetica = { + name: "LiberationSans-Regular", + factors: HelveticaRegularFactors, + baseWidths: LiberationSansRegularWidths, + baseMapping: LiberationSansRegularMapping, + metrics: HelveticaRegularMetrics + }; + t["Helvetica-Bold"] = { + name: "LiberationSans-Bold", + factors: HelveticaBoldFactors, + baseWidths: LiberationSansBoldWidths, + baseMapping: LiberationSansBoldMapping, + metrics: HelveticaBoldMetrics + }; + t["Helvetica-Italic"] = { + name: "LiberationSans-Italic", + factors: HelveticaItalicFactors, + baseWidths: LiberationSansItalicWidths, + baseMapping: LiberationSansItalicMapping, + metrics: HelveticaItalicMetrics + }; + t["Helvetica-BoldItalic"] = { + name: "LiberationSans-BoldItalic", + factors: HelveticaBoldItalicFactors, + baseWidths: LiberationSansBoldItalicWidths, + baseMapping: LiberationSansBoldItalicMapping, + metrics: HelveticaBoldItalicMetrics + }; +}); +function getXfaFontName(name) { + const fontName = normalizeFontName(name); + const fontMap = getXFAFontMap(); + return fontMap[fontName]; +} +function getXfaFontWidths(name) { + const info = getXfaFontName(name); + if (!info) { + return null; + } + const { + baseWidths, + baseMapping, + factors + } = info; + const rescaledBaseWidths = !factors ? baseWidths : baseWidths.map((w, i) => w * factors[i]); + let currentCode = -2; + let currentArray; + const newWidths = []; + for (const [unicode, glyphIndex] of baseMapping.map((charUnicode, index) => [charUnicode, index]).sort(([unicode1], [unicode2]) => unicode1 - unicode2)) { + if (unicode === -1) { + continue; + } + if (unicode === currentCode + 1) { + currentArray.push(rescaledBaseWidths[glyphIndex]); + currentCode += 1; + } else { + currentCode = unicode; + currentArray = [rescaledBaseWidths[glyphIndex]]; + newWidths.push(unicode, currentArray); + } + } + return newWidths; +} +function getXfaFontDict(name) { + const widths = getXfaFontWidths(name); + const dict = new Dict(null); + dict.set("BaseFont", Name.get(name)); + dict.set("Type", Name.get("Font")); + dict.set("Subtype", Name.get("CIDFontType2")); + dict.set("Encoding", Name.get("Identity-H")); + dict.set("CIDToGIDMap", Name.get("Identity")); + dict.set("W", widths); + dict.set("FirstChar", widths[0]); + dict.set("LastChar", widths.at(-2) + widths.at(-1).length - 1); + const descriptor = new Dict(null); + dict.set("FontDescriptor", descriptor); + const systemInfo = new Dict(null); + systemInfo.set("Ordering", "Identity"); + systemInfo.set("Registry", "Adobe"); + systemInfo.set("Supplement", 0); + dict.set("CIDSystemInfo", systemInfo); + return dict; +} + +;// ./src/core/ps_parser.js + + + +class PostScriptParser { + constructor(lexer) { + this.lexer = lexer; + this.operators = []; + this.token = null; + this.prev = null; + } + nextToken() { + this.prev = this.token; + this.token = this.lexer.getToken(); + } + accept(type) { + if (this.token.type === type) { + this.nextToken(); + return true; + } + return false; + } + expect(type) { + if (this.accept(type)) { + return true; + } + throw new FormatError(`Unexpected symbol: found ${this.token.type} expected ${type}.`); + } + parse() { + this.nextToken(); + this.expect(PostScriptTokenTypes.LBRACE); + this.parseBlock(); + this.expect(PostScriptTokenTypes.RBRACE); + return this.operators; + } + parseBlock() { + while (true) { + if (this.accept(PostScriptTokenTypes.NUMBER)) { + this.operators.push(this.prev.value); + } else if (this.accept(PostScriptTokenTypes.OPERATOR)) { + this.operators.push(this.prev.value); + } else if (this.accept(PostScriptTokenTypes.LBRACE)) { + this.parseCondition(); + } else { + return; + } + } + } + parseCondition() { + const conditionLocation = this.operators.length; + this.operators.push(null, null); + this.parseBlock(); + this.expect(PostScriptTokenTypes.RBRACE); + if (this.accept(PostScriptTokenTypes.IF)) { + this.operators[conditionLocation] = this.operators.length; + this.operators[conditionLocation + 1] = "jz"; + } else if (this.accept(PostScriptTokenTypes.LBRACE)) { + const jumpLocation = this.operators.length; + this.operators.push(null, null); + const endOfTrue = this.operators.length; + this.parseBlock(); + this.expect(PostScriptTokenTypes.RBRACE); + this.expect(PostScriptTokenTypes.IFELSE); + this.operators[jumpLocation] = this.operators.length; + this.operators[jumpLocation + 1] = "j"; + this.operators[conditionLocation] = endOfTrue; + this.operators[conditionLocation + 1] = "jz"; + } else { + throw new FormatError("PS Function: error parsing conditional."); + } + } +} +const PostScriptTokenTypes = { + LBRACE: 0, + RBRACE: 1, + NUMBER: 2, + OPERATOR: 3, + IF: 4, + IFELSE: 5 +}; +class PostScriptToken { + static get opCache() { + return shadow(this, "opCache", Object.create(null)); + } + constructor(type, value) { + this.type = type; + this.value = value; + } + static getOperator(op) { + return PostScriptToken.opCache[op] ||= new PostScriptToken(PostScriptTokenTypes.OPERATOR, op); + } + static get LBRACE() { + return shadow(this, "LBRACE", new PostScriptToken(PostScriptTokenTypes.LBRACE, "{")); + } + static get RBRACE() { + return shadow(this, "RBRACE", new PostScriptToken(PostScriptTokenTypes.RBRACE, "}")); + } + static get IF() { + return shadow(this, "IF", new PostScriptToken(PostScriptTokenTypes.IF, "IF")); + } + static get IFELSE() { + return shadow(this, "IFELSE", new PostScriptToken(PostScriptTokenTypes.IFELSE, "IFELSE")); + } +} +class PostScriptLexer { + constructor(stream) { + this.stream = stream; + this.nextChar(); + this.strBuf = []; + } + nextChar() { + return this.currentChar = this.stream.getByte(); + } + getToken() { + let comment = false; + let ch = this.currentChar; + while (true) { + if (ch < 0) { + return EOF; + } + if (comment) { + if (ch === 0x0a || ch === 0x0d) { + comment = false; + } + } else if (ch === 0x25) { + comment = true; + } else if (!isWhiteSpace(ch)) { + break; + } + ch = this.nextChar(); + } + switch (ch | 0) { + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x2b: + case 0x2d: + case 0x2e: + return new PostScriptToken(PostScriptTokenTypes.NUMBER, this.getNumber()); + case 0x7b: + this.nextChar(); + return PostScriptToken.LBRACE; + case 0x7d: + this.nextChar(); + return PostScriptToken.RBRACE; + } + const strBuf = this.strBuf; + strBuf.length = 0; + strBuf[0] = String.fromCharCode(ch); + while ((ch = this.nextChar()) >= 0 && (ch >= 0x41 && ch <= 0x5a || ch >= 0x61 && ch <= 0x7a)) { + strBuf.push(String.fromCharCode(ch)); + } + const str = strBuf.join(""); + switch (str.toLowerCase()) { + case "if": + return PostScriptToken.IF; + case "ifelse": + return PostScriptToken.IFELSE; + default: + return PostScriptToken.getOperator(str); + } + } + getNumber() { + let ch = this.currentChar; + const strBuf = this.strBuf; + strBuf.length = 0; + strBuf[0] = String.fromCharCode(ch); + while ((ch = this.nextChar()) >= 0) { + if (ch >= 0x30 && ch <= 0x39 || ch === 0x2d || ch === 0x2e) { + strBuf.push(String.fromCharCode(ch)); + } else { + break; + } + } + const value = parseFloat(strBuf.join("")); + if (isNaN(value)) { + throw new FormatError(`Invalid floating point number: ${value}`); + } + return value; + } +} + +;// ./src/core/image_utils.js + + +class BaseLocalCache { + constructor(options) { + this._onlyRefs = options?.onlyRefs === true; + if (!this._onlyRefs) { + this._nameRefMap = new Map(); + this._imageMap = new Map(); + } + this._imageCache = new RefSetCache(); + } + getByName(name) { + if (this._onlyRefs) { + unreachable("Should not call `getByName` method."); + } + const ref = this._nameRefMap.get(name); + if (ref) { + return this.getByRef(ref); + } + return this._imageMap.get(name) || null; + } + getByRef(ref) { + return this._imageCache.get(ref) || null; + } + set(name, ref, data) { + unreachable("Abstract method `set` called."); + } +} +class LocalImageCache extends BaseLocalCache { + set(name, ref = null, data) { + if (typeof name !== "string") { + throw new Error('LocalImageCache.set - expected "name" argument.'); + } + if (ref) { + if (this._imageCache.has(ref)) { + return; + } + this._nameRefMap.set(name, ref); + this._imageCache.put(ref, data); + return; + } + if (this._imageMap.has(name)) { + return; + } + this._imageMap.set(name, data); + } +} +class LocalColorSpaceCache extends BaseLocalCache { + set(name = null, ref = null, data) { + if (typeof name !== "string" && !ref) { + throw new Error('LocalColorSpaceCache.set - expected "name" and/or "ref" argument.'); + } + if (ref) { + if (this._imageCache.has(ref)) { + return; + } + if (name !== null) { + this._nameRefMap.set(name, ref); + } + this._imageCache.put(ref, data); + return; + } + if (this._imageMap.has(name)) { + return; + } + this._imageMap.set(name, data); + } +} +class LocalFunctionCache extends BaseLocalCache { + constructor(options) { + super({ + onlyRefs: true + }); + } + set(name = null, ref, data) { + if (!ref) { + throw new Error('LocalFunctionCache.set - expected "ref" argument.'); + } + if (this._imageCache.has(ref)) { + return; + } + this._imageCache.put(ref, data); + } +} +class LocalGStateCache extends BaseLocalCache { + set(name, ref = null, data) { + if (typeof name !== "string") { + throw new Error('LocalGStateCache.set - expected "name" argument.'); + } + if (ref) { + if (this._imageCache.has(ref)) { + return; + } + this._nameRefMap.set(name, ref); + this._imageCache.put(ref, data); + return; + } + if (this._imageMap.has(name)) { + return; + } + this._imageMap.set(name, data); + } +} +class LocalTilingPatternCache extends BaseLocalCache { + constructor(options) { + super({ + onlyRefs: true + }); + } + set(name = null, ref, data) { + if (!ref) { + throw new Error('LocalTilingPatternCache.set - expected "ref" argument.'); + } + if (this._imageCache.has(ref)) { + return; + } + this._imageCache.put(ref, data); + } +} +class RegionalImageCache extends BaseLocalCache { + constructor(options) { + super({ + onlyRefs: true + }); + } + set(name = null, ref, data) { + if (!ref) { + throw new Error('RegionalImageCache.set - expected "ref" argument.'); + } + if (this._imageCache.has(ref)) { + return; + } + this._imageCache.put(ref, data); + } +} +class GlobalImageCache { + static NUM_PAGES_THRESHOLD = 2; + static MIN_IMAGES_TO_CACHE = 10; + static MAX_BYTE_SIZE = 5 * MAX_IMAGE_SIZE_TO_CACHE; + #decodeFailedSet = new RefSet(); + constructor() { + this._refCache = new RefSetCache(); + this._imageCache = new RefSetCache(); + } + get #byteSize() { + let byteSize = 0; + for (const imageData of this._imageCache) { + byteSize += imageData.byteSize; + } + return byteSize; + } + get #cacheLimitReached() { + if (this._imageCache.size < GlobalImageCache.MIN_IMAGES_TO_CACHE) { + return false; + } + if (this.#byteSize < GlobalImageCache.MAX_BYTE_SIZE) { + return false; + } + return true; + } + shouldCache(ref, pageIndex) { + let pageIndexSet = this._refCache.get(ref); + if (!pageIndexSet) { + pageIndexSet = new Set(); + this._refCache.put(ref, pageIndexSet); + } + pageIndexSet.add(pageIndex); + if (pageIndexSet.size < GlobalImageCache.NUM_PAGES_THRESHOLD) { + return false; + } + if (!this._imageCache.has(ref) && this.#cacheLimitReached) { + return false; + } + return true; + } + addDecodeFailed(ref) { + this.#decodeFailedSet.put(ref); + } + hasDecodeFailed(ref) { + return this.#decodeFailedSet.has(ref); + } + addByteSize(ref, byteSize) { + const imageData = this._imageCache.get(ref); + if (!imageData) { + return; + } + if (imageData.byteSize) { + return; + } + imageData.byteSize = byteSize; + } + getData(ref, pageIndex) { + const pageIndexSet = this._refCache.get(ref); + if (!pageIndexSet) { + return null; + } + if (pageIndexSet.size < GlobalImageCache.NUM_PAGES_THRESHOLD) { + return null; + } + const imageData = this._imageCache.get(ref); + if (!imageData) { + return null; + } + pageIndexSet.add(pageIndex); + return imageData; + } + setData(ref, data) { + if (!this._refCache.has(ref)) { + throw new Error('GlobalImageCache.setData - expected "shouldCache" to have been called.'); + } + if (this._imageCache.has(ref)) { + return; + } + if (this.#cacheLimitReached) { + warn("GlobalImageCache.setData - cache limit reached."); + return; + } + this._imageCache.put(ref, data); + } + clear(onlyData = false) { + if (!onlyData) { + this.#decodeFailedSet.clear(); + this._refCache.clear(); + } + this._imageCache.clear(); + } +} + +;// ./src/core/function.js + + + + + + +class PDFFunctionFactory { + constructor({ + xref, + isEvalSupported = true + }) { + this.xref = xref; + this.isEvalSupported = isEvalSupported !== false; + } + create(fn) { + const cachedFunction = this.getCached(fn); + if (cachedFunction) { + return cachedFunction; + } + const parsedFunction = PDFFunction.parse({ + xref: this.xref, + isEvalSupported: this.isEvalSupported, + fn: fn instanceof Ref ? this.xref.fetch(fn) : fn + }); + this._cache(fn, parsedFunction); + return parsedFunction; + } + createFromArray(fnObj) { + const cachedFunction = this.getCached(fnObj); + if (cachedFunction) { + return cachedFunction; + } + const parsedFunction = PDFFunction.parseArray({ + xref: this.xref, + isEvalSupported: this.isEvalSupported, + fnObj: fnObj instanceof Ref ? this.xref.fetch(fnObj) : fnObj + }); + this._cache(fnObj, parsedFunction); + return parsedFunction; + } + getCached(cacheKey) { + let fnRef; + if (cacheKey instanceof Ref) { + fnRef = cacheKey; + } else if (cacheKey instanceof Dict) { + fnRef = cacheKey.objId; + } else if (cacheKey instanceof BaseStream) { + fnRef = cacheKey.dict?.objId; + } + if (fnRef) { + const localFunction = this._localFunctionCache.getByRef(fnRef); + if (localFunction) { + return localFunction; + } + } + return null; + } + _cache(cacheKey, parsedFunction) { + if (!parsedFunction) { + throw new Error('PDFFunctionFactory._cache - expected "parsedFunction" argument.'); + } + let fnRef; + if (cacheKey instanceof Ref) { + fnRef = cacheKey; + } else if (cacheKey instanceof Dict) { + fnRef = cacheKey.objId; + } else if (cacheKey instanceof BaseStream) { + fnRef = cacheKey.dict?.objId; + } + if (fnRef) { + this._localFunctionCache.set(null, fnRef, parsedFunction); + } + } + get _localFunctionCache() { + return shadow(this, "_localFunctionCache", new LocalFunctionCache()); + } +} +function toNumberArray(arr) { + if (!Array.isArray(arr)) { + return null; + } + if (!isNumberArray(arr, null)) { + return arr.map(x => +x); + } + return arr; +} +class PDFFunction { + static getSampleArray(size, outputSize, bps, stream) { + let i, ii; + let length = 1; + for (i = 0, ii = size.length; i < ii; i++) { + length *= size[i]; + } + length *= outputSize; + const array = new Array(length); + let codeSize = 0; + let codeBuf = 0; + const sampleMul = 1.0 / (2.0 ** bps - 1); + const strBytes = stream.getBytes((length * bps + 7) / 8); + let strIdx = 0; + for (i = 0; i < length; i++) { + while (codeSize < bps) { + codeBuf <<= 8; + codeBuf |= strBytes[strIdx++]; + codeSize += 8; + } + codeSize -= bps; + array[i] = (codeBuf >> codeSize) * sampleMul; + codeBuf &= (1 << codeSize) - 1; + } + return array; + } + static parse({ + xref, + isEvalSupported, + fn + }) { + const dict = fn.dict || fn; + const typeNum = dict.get("FunctionType"); + switch (typeNum) { + case 0: + return this.constructSampled({ + xref, + isEvalSupported, + fn, + dict + }); + case 1: + break; + case 2: + return this.constructInterpolated({ + xref, + isEvalSupported, + dict + }); + case 3: + return this.constructStiched({ + xref, + isEvalSupported, + dict + }); + case 4: + return this.constructPostScript({ + xref, + isEvalSupported, + fn, + dict + }); + } + throw new FormatError("Unknown type of function"); + } + static parseArray({ + xref, + isEvalSupported, + fnObj + }) { + if (!Array.isArray(fnObj)) { + return this.parse({ + xref, + isEvalSupported, + fn: fnObj + }); + } + const fnArray = []; + for (const fn of fnObj) { + fnArray.push(this.parse({ + xref, + isEvalSupported, + fn: xref.fetchIfRef(fn) + })); + } + return function (src, srcOffset, dest, destOffset) { + for (let i = 0, ii = fnArray.length; i < ii; i++) { + fnArray[i](src, srcOffset, dest, destOffset + i); + } + }; + } + static constructSampled({ + xref, + isEvalSupported, + fn, + dict + }) { + function toMultiArray(arr) { + const inputLength = arr.length; + const out = []; + let index = 0; + for (let i = 0; i < inputLength; i += 2) { + out[index++] = [arr[i], arr[i + 1]]; + } + return out; + } + function interpolate(x, xmin, xmax, ymin, ymax) { + return ymin + (x - xmin) * ((ymax - ymin) / (xmax - xmin)); + } + let domain = toNumberArray(dict.getArray("Domain")); + let range = toNumberArray(dict.getArray("Range")); + if (!domain || !range) { + throw new FormatError("No domain or range"); + } + const inputSize = domain.length / 2; + const outputSize = range.length / 2; + domain = toMultiArray(domain); + range = toMultiArray(range); + const size = toNumberArray(dict.getArray("Size")); + const bps = dict.get("BitsPerSample"); + const order = dict.get("Order") || 1; + if (order !== 1) { + info("No support for cubic spline interpolation: " + order); + } + let encode = toNumberArray(dict.getArray("Encode")); + if (!encode) { + encode = []; + for (let i = 0; i < inputSize; ++i) { + encode.push([0, size[i] - 1]); + } + } else { + encode = toMultiArray(encode); + } + let decode = toNumberArray(dict.getArray("Decode")); + decode = !decode ? range : toMultiArray(decode); + const samples = this.getSampleArray(size, outputSize, bps, fn); + return function constructSampledFn(src, srcOffset, dest, destOffset) { + const cubeVertices = 1 << inputSize; + const cubeN = new Float64Array(cubeVertices); + const cubeVertex = new Uint32Array(cubeVertices); + let i, j; + for (j = 0; j < cubeVertices; j++) { + cubeN[j] = 1; + } + let k = outputSize, + pos = 1; + for (i = 0; i < inputSize; ++i) { + const domain_2i = domain[i][0]; + const domain_2i_1 = domain[i][1]; + const xi = Math.min(Math.max(src[srcOffset + i], domain_2i), domain_2i_1); + let e = interpolate(xi, domain_2i, domain_2i_1, encode[i][0], encode[i][1]); + const size_i = size[i]; + e = Math.min(Math.max(e, 0), size_i - 1); + const e0 = e < size_i - 1 ? Math.floor(e) : e - 1; + const n0 = e0 + 1 - e; + const n1 = e - e0; + const offset0 = e0 * k; + const offset1 = offset0 + k; + for (j = 0; j < cubeVertices; j++) { + if (j & pos) { + cubeN[j] *= n1; + cubeVertex[j] += offset1; + } else { + cubeN[j] *= n0; + cubeVertex[j] += offset0; + } + } + k *= size_i; + pos <<= 1; + } + for (j = 0; j < outputSize; ++j) { + let rj = 0; + for (i = 0; i < cubeVertices; i++) { + rj += samples[cubeVertex[i] + j] * cubeN[i]; + } + rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]); + dest[destOffset + j] = Math.min(Math.max(rj, range[j][0]), range[j][1]); + } + }; + } + static constructInterpolated({ + xref, + isEvalSupported, + dict + }) { + const c0 = toNumberArray(dict.getArray("C0")) || [0]; + const c1 = toNumberArray(dict.getArray("C1")) || [1]; + const n = dict.get("N"); + const diff = []; + for (let i = 0, ii = c0.length; i < ii; ++i) { + diff.push(c1[i] - c0[i]); + } + const length = diff.length; + return function constructInterpolatedFn(src, srcOffset, dest, destOffset) { + const x = n === 1 ? src[srcOffset] : src[srcOffset] ** n; + for (let j = 0; j < length; ++j) { + dest[destOffset + j] = c0[j] + x * diff[j]; + } + }; + } + static constructStiched({ + xref, + isEvalSupported, + dict + }) { + const domain = toNumberArray(dict.getArray("Domain")); + if (!domain) { + throw new FormatError("No domain"); + } + const inputSize = domain.length / 2; + if (inputSize !== 1) { + throw new FormatError("Bad domain for stiched function"); + } + const fns = []; + for (const fn of dict.get("Functions")) { + fns.push(this.parse({ + xref, + isEvalSupported, + fn: xref.fetchIfRef(fn) + })); + } + const bounds = toNumberArray(dict.getArray("Bounds")); + const encode = toNumberArray(dict.getArray("Encode")); + const tmpBuf = new Float32Array(1); + return function constructStichedFn(src, srcOffset, dest, destOffset) { + const clip = function constructStichedFromIRClip(v, min, max) { + if (v > max) { + v = max; + } else if (v < min) { + v = min; + } + return v; + }; + const v = clip(src[srcOffset], domain[0], domain[1]); + const length = bounds.length; + let i; + for (i = 0; i < length; ++i) { + if (v < bounds[i]) { + break; + } + } + let dmin = domain[0]; + if (i > 0) { + dmin = bounds[i - 1]; + } + let dmax = domain[1]; + if (i < bounds.length) { + dmax = bounds[i]; + } + const rmin = encode[2 * i]; + const rmax = encode[2 * i + 1]; + tmpBuf[0] = dmin === dmax ? rmin : rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin); + fns[i](tmpBuf, 0, dest, destOffset); + }; + } + static constructPostScript({ + xref, + isEvalSupported, + fn, + dict + }) { + const domain = toNumberArray(dict.getArray("Domain")); + const range = toNumberArray(dict.getArray("Range")); + if (!domain) { + throw new FormatError("No domain."); + } + if (!range) { + throw new FormatError("No range."); + } + const lexer = new PostScriptLexer(fn); + const parser = new PostScriptParser(lexer); + const code = parser.parse(); + if (isEvalSupported && FeatureTest.isEvalSupported) { + const compiled = new PostScriptCompiler().compile(code, domain, range); + if (compiled) { + return new Function("src", "srcOffset", "dest", "destOffset", compiled); + } + } + info("Unable to compile PS function"); + const numOutputs = range.length >> 1; + const numInputs = domain.length >> 1; + const evaluator = new PostScriptEvaluator(code); + const cache = Object.create(null); + const MAX_CACHE_SIZE = 2048 * 4; + let cache_available = MAX_CACHE_SIZE; + const tmpBuf = new Float32Array(numInputs); + return function constructPostScriptFn(src, srcOffset, dest, destOffset) { + let i, value; + let key = ""; + const input = tmpBuf; + for (i = 0; i < numInputs; i++) { + value = src[srcOffset + i]; + input[i] = value; + key += value + "_"; + } + const cachedValue = cache[key]; + if (cachedValue !== undefined) { + dest.set(cachedValue, destOffset); + return; + } + const output = new Float32Array(numOutputs); + const stack = evaluator.execute(input); + const stackIndex = stack.length - numOutputs; + for (i = 0; i < numOutputs; i++) { + value = stack[stackIndex + i]; + let bound = range[i * 2]; + if (value < bound) { + value = bound; + } else { + bound = range[i * 2 + 1]; + if (value > bound) { + value = bound; + } + } + output[i] = value; + } + if (cache_available > 0) { + cache_available--; + cache[key] = output; + } + dest.set(output, destOffset); + }; + } +} +function isPDFFunction(v) { + let fnDict; + if (v instanceof Dict) { + fnDict = v; + } else if (v instanceof BaseStream) { + fnDict = v.dict; + } else { + return false; + } + return fnDict.has("FunctionType"); +} +class PostScriptStack { + static MAX_STACK_SIZE = 100; + constructor(initialStack) { + this.stack = initialStack ? Array.from(initialStack) : []; + } + push(value) { + if (this.stack.length >= PostScriptStack.MAX_STACK_SIZE) { + throw new Error("PostScript function stack overflow."); + } + this.stack.push(value); + } + pop() { + if (this.stack.length <= 0) { + throw new Error("PostScript function stack underflow."); + } + return this.stack.pop(); + } + copy(n) { + if (this.stack.length + n >= PostScriptStack.MAX_STACK_SIZE) { + throw new Error("PostScript function stack overflow."); + } + const stack = this.stack; + for (let i = stack.length - n, j = n - 1; j >= 0; j--, i++) { + stack.push(stack[i]); + } + } + index(n) { + this.push(this.stack[this.stack.length - n - 1]); + } + roll(n, p) { + const stack = this.stack; + const l = stack.length - n; + const r = stack.length - 1; + const c = l + (p - Math.floor(p / n) * n); + for (let i = l, j = r; i < j; i++, j--) { + const t = stack[i]; + stack[i] = stack[j]; + stack[j] = t; + } + for (let i = l, j = c - 1; i < j; i++, j--) { + const t = stack[i]; + stack[i] = stack[j]; + stack[j] = t; + } + for (let i = c, j = r; i < j; i++, j--) { + const t = stack[i]; + stack[i] = stack[j]; + stack[j] = t; + } + } +} +class PostScriptEvaluator { + constructor(operators) { + this.operators = operators; + } + execute(initialStack) { + const stack = new PostScriptStack(initialStack); + let counter = 0; + const operators = this.operators; + const length = operators.length; + let operator, a, b; + while (counter < length) { + operator = operators[counter++]; + if (typeof operator === "number") { + stack.push(operator); + continue; + } + switch (operator) { + case "jz": + b = stack.pop(); + a = stack.pop(); + if (!a) { + counter = b; + } + break; + case "j": + a = stack.pop(); + counter = a; + break; + case "abs": + a = stack.pop(); + stack.push(Math.abs(a)); + break; + case "add": + b = stack.pop(); + a = stack.pop(); + stack.push(a + b); + break; + case "and": + b = stack.pop(); + a = stack.pop(); + if (typeof a === "boolean" && typeof b === "boolean") { + stack.push(a && b); + } else { + stack.push(a & b); + } + break; + case "atan": + b = stack.pop(); + a = stack.pop(); + a = Math.atan2(a, b) / Math.PI * 180; + if (a < 0) { + a += 360; + } + stack.push(a); + break; + case "bitshift": + b = stack.pop(); + a = stack.pop(); + if (a > 0) { + stack.push(a << b); + } else { + stack.push(a >> b); + } + break; + case "ceiling": + a = stack.pop(); + stack.push(Math.ceil(a)); + break; + case "copy": + a = stack.pop(); + stack.copy(a); + break; + case "cos": + a = stack.pop(); + stack.push(Math.cos(a % 360 / 180 * Math.PI)); + break; + case "cvi": + a = stack.pop() | 0; + stack.push(a); + break; + case "cvr": + break; + case "div": + b = stack.pop(); + a = stack.pop(); + stack.push(a / b); + break; + case "dup": + stack.copy(1); + break; + case "eq": + b = stack.pop(); + a = stack.pop(); + stack.push(a === b); + break; + case "exch": + stack.roll(2, 1); + break; + case "exp": + b = stack.pop(); + a = stack.pop(); + stack.push(a ** b); + break; + case "false": + stack.push(false); + break; + case "floor": + a = stack.pop(); + stack.push(Math.floor(a)); + break; + case "ge": + b = stack.pop(); + a = stack.pop(); + stack.push(a >= b); + break; + case "gt": + b = stack.pop(); + a = stack.pop(); + stack.push(a > b); + break; + case "idiv": + b = stack.pop(); + a = stack.pop(); + stack.push(a / b | 0); + break; + case "index": + a = stack.pop(); + stack.index(a); + break; + case "le": + b = stack.pop(); + a = stack.pop(); + stack.push(a <= b); + break; + case "ln": + a = stack.pop(); + stack.push(Math.log(a)); + break; + case "log": + a = stack.pop(); + stack.push(Math.log10(a)); + break; + case "lt": + b = stack.pop(); + a = stack.pop(); + stack.push(a < b); + break; + case "mod": + b = stack.pop(); + a = stack.pop(); + stack.push(a % b); + break; + case "mul": + b = stack.pop(); + a = stack.pop(); + stack.push(a * b); + break; + case "ne": + b = stack.pop(); + a = stack.pop(); + stack.push(a !== b); + break; + case "neg": + a = stack.pop(); + stack.push(-a); + break; + case "not": + a = stack.pop(); + if (typeof a === "boolean") { + stack.push(!a); + } else { + stack.push(~a); + } + break; + case "or": + b = stack.pop(); + a = stack.pop(); + if (typeof a === "boolean" && typeof b === "boolean") { + stack.push(a || b); + } else { + stack.push(a | b); + } + break; + case "pop": + stack.pop(); + break; + case "roll": + b = stack.pop(); + a = stack.pop(); + stack.roll(a, b); + break; + case "round": + a = stack.pop(); + stack.push(Math.round(a)); + break; + case "sin": + a = stack.pop(); + stack.push(Math.sin(a % 360 / 180 * Math.PI)); + break; + case "sqrt": + a = stack.pop(); + stack.push(Math.sqrt(a)); + break; + case "sub": + b = stack.pop(); + a = stack.pop(); + stack.push(a - b); + break; + case "true": + stack.push(true); + break; + case "truncate": + a = stack.pop(); + a = a < 0 ? Math.ceil(a) : Math.floor(a); + stack.push(a); + break; + case "xor": + b = stack.pop(); + a = stack.pop(); + if (typeof a === "boolean" && typeof b === "boolean") { + stack.push(a !== b); + } else { + stack.push(a ^ b); + } + break; + default: + throw new FormatError(`Unknown operator ${operator}`); + } + } + return stack.stack; + } +} +class AstNode { + constructor(type) { + this.type = type; + } + visit(visitor) { + unreachable("abstract method"); + } +} +class AstArgument extends AstNode { + constructor(index, min, max) { + super("args"); + this.index = index; + this.min = min; + this.max = max; + } + visit(visitor) { + visitor.visitArgument(this); + } +} +class AstLiteral extends AstNode { + constructor(number) { + super("literal"); + this.number = number; + this.min = number; + this.max = number; + } + visit(visitor) { + visitor.visitLiteral(this); + } +} +class AstBinaryOperation extends AstNode { + constructor(op, arg1, arg2, min, max) { + super("binary"); + this.op = op; + this.arg1 = arg1; + this.arg2 = arg2; + this.min = min; + this.max = max; + } + visit(visitor) { + visitor.visitBinaryOperation(this); + } +} +class AstMin extends AstNode { + constructor(arg, max) { + super("max"); + this.arg = arg; + this.min = arg.min; + this.max = max; + } + visit(visitor) { + visitor.visitMin(this); + } +} +class AstVariable extends AstNode { + constructor(index, min, max) { + super("var"); + this.index = index; + this.min = min; + this.max = max; + } + visit(visitor) { + visitor.visitVariable(this); + } +} +class AstVariableDefinition extends AstNode { + constructor(variable, arg) { + super("definition"); + this.variable = variable; + this.arg = arg; + } + visit(visitor) { + visitor.visitVariableDefinition(this); + } +} +class ExpressionBuilderVisitor { + constructor() { + this.parts = []; + } + visitArgument(arg) { + this.parts.push("Math.max(", arg.min, ", Math.min(", arg.max, ", src[srcOffset + ", arg.index, "]))"); + } + visitVariable(variable) { + this.parts.push("v", variable.index); + } + visitLiteral(literal) { + this.parts.push(literal.number); + } + visitBinaryOperation(operation) { + this.parts.push("("); + operation.arg1.visit(this); + this.parts.push(" ", operation.op, " "); + operation.arg2.visit(this); + this.parts.push(")"); + } + visitVariableDefinition(definition) { + this.parts.push("var "); + definition.variable.visit(this); + this.parts.push(" = "); + definition.arg.visit(this); + this.parts.push(";"); + } + visitMin(max) { + this.parts.push("Math.min("); + max.arg.visit(this); + this.parts.push(", ", max.max, ")"); + } + toString() { + return this.parts.join(""); + } +} +function buildAddOperation(num1, num2) { + if (num2.type === "literal" && num2.number === 0) { + return num1; + } + if (num1.type === "literal" && num1.number === 0) { + return num2; + } + if (num2.type === "literal" && num1.type === "literal") { + return new AstLiteral(num1.number + num2.number); + } + return new AstBinaryOperation("+", num1, num2, num1.min + num2.min, num1.max + num2.max); +} +function buildMulOperation(num1, num2) { + if (num2.type === "literal") { + if (num2.number === 0) { + return new AstLiteral(0); + } else if (num2.number === 1) { + return num1; + } else if (num1.type === "literal") { + return new AstLiteral(num1.number * num2.number); + } + } + if (num1.type === "literal") { + if (num1.number === 0) { + return new AstLiteral(0); + } else if (num1.number === 1) { + return num2; + } + } + const min = Math.min(num1.min * num2.min, num1.min * num2.max, num1.max * num2.min, num1.max * num2.max); + const max = Math.max(num1.min * num2.min, num1.min * num2.max, num1.max * num2.min, num1.max * num2.max); + return new AstBinaryOperation("*", num1, num2, min, max); +} +function buildSubOperation(num1, num2) { + if (num2.type === "literal") { + if (num2.number === 0) { + return num1; + } else if (num1.type === "literal") { + return new AstLiteral(num1.number - num2.number); + } + } + if (num2.type === "binary" && num2.op === "-" && num1.type === "literal" && num1.number === 1 && num2.arg1.type === "literal" && num2.arg1.number === 1) { + return num2.arg2; + } + return new AstBinaryOperation("-", num1, num2, num1.min - num2.max, num1.max - num2.min); +} +function buildMinOperation(num1, max) { + if (num1.min >= max) { + return new AstLiteral(max); + } else if (num1.max <= max) { + return num1; + } + return new AstMin(num1, max); +} +class PostScriptCompiler { + compile(code, domain, range) { + const stack = []; + const instructions = []; + const inputSize = domain.length >> 1, + outputSize = range.length >> 1; + let lastRegister = 0; + let n, j; + let num1, num2, ast1, ast2, tmpVar, item; + for (let i = 0; i < inputSize; i++) { + stack.push(new AstArgument(i, domain[i * 2], domain[i * 2 + 1])); + } + for (let i = 0, ii = code.length; i < ii; i++) { + item = code[i]; + if (typeof item === "number") { + stack.push(new AstLiteral(item)); + continue; + } + switch (item) { + case "add": + if (stack.length < 2) { + return null; + } + num2 = stack.pop(); + num1 = stack.pop(); + stack.push(buildAddOperation(num1, num2)); + break; + case "cvr": + if (stack.length < 1) { + return null; + } + break; + case "mul": + if (stack.length < 2) { + return null; + } + num2 = stack.pop(); + num1 = stack.pop(); + stack.push(buildMulOperation(num1, num2)); + break; + case "sub": + if (stack.length < 2) { + return null; + } + num2 = stack.pop(); + num1 = stack.pop(); + stack.push(buildSubOperation(num1, num2)); + break; + case "exch": + if (stack.length < 2) { + return null; + } + ast1 = stack.pop(); + ast2 = stack.pop(); + stack.push(ast1, ast2); + break; + case "pop": + if (stack.length < 1) { + return null; + } + stack.pop(); + break; + case "index": + if (stack.length < 1) { + return null; + } + num1 = stack.pop(); + if (num1.type !== "literal") { + return null; + } + n = num1.number; + if (n < 0 || !Number.isInteger(n) || stack.length < n) { + return null; + } + ast1 = stack[stack.length - n - 1]; + if (ast1.type === "literal" || ast1.type === "var") { + stack.push(ast1); + break; + } + tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max); + stack[stack.length - n - 1] = tmpVar; + stack.push(tmpVar); + instructions.push(new AstVariableDefinition(tmpVar, ast1)); + break; + case "dup": + if (stack.length < 1) { + return null; + } + if (typeof code[i + 1] === "number" && code[i + 2] === "gt" && code[i + 3] === i + 7 && code[i + 4] === "jz" && code[i + 5] === "pop" && code[i + 6] === code[i + 1]) { + num1 = stack.pop(); + stack.push(buildMinOperation(num1, code[i + 1])); + i += 6; + break; + } + ast1 = stack.at(-1); + if (ast1.type === "literal" || ast1.type === "var") { + stack.push(ast1); + break; + } + tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max); + stack[stack.length - 1] = tmpVar; + stack.push(tmpVar); + instructions.push(new AstVariableDefinition(tmpVar, ast1)); + break; + case "roll": + if (stack.length < 2) { + return null; + } + num2 = stack.pop(); + num1 = stack.pop(); + if (num2.type !== "literal" || num1.type !== "literal") { + return null; + } + j = num2.number; + n = num1.number; + if (n <= 0 || !Number.isInteger(n) || !Number.isInteger(j) || stack.length < n) { + return null; + } + j = (j % n + n) % n; + if (j === 0) { + break; + } + stack.push(...stack.splice(stack.length - n, n - j)); + break; + default: + return null; + } + } + if (stack.length !== outputSize) { + return null; + } + const result = []; + for (const instruction of instructions) { + const statementBuilder = new ExpressionBuilderVisitor(); + instruction.visit(statementBuilder); + result.push(statementBuilder.toString()); + } + for (let i = 0, ii = stack.length; i < ii; i++) { + const expr = stack[i], + statementBuilder = new ExpressionBuilderVisitor(); + expr.visit(statementBuilder); + const min = range[i * 2], + max = range[i * 2 + 1]; + const out = [statementBuilder.toString()]; + if (min > expr.min) { + out.unshift("Math.max(", min, ", "); + out.push(")"); + } + if (max < expr.max) { + out.unshift("Math.min(", max, ", "); + out.push(")"); + } + out.unshift("dest[destOffset + ", i, "] = "); + out.push(";"); + result.push(out.join("")); + } + return result.join("\n"); + } +} + +;// ./src/core/bidi.js + +const baseTypes = ["BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "S", "B", "S", "WS", "B", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "B", "B", "B", "S", "WS", "ON", "ON", "ET", "ET", "ET", "ON", "ON", "ON", "ON", "ON", "ES", "CS", "ES", "CS", "CS", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "CS", "ON", "ON", "ON", "ON", "ON", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "ON", "ON", "ON", "ON", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "ON", "ON", "ON", "BN", "BN", "BN", "BN", "BN", "BN", "B", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "CS", "ON", "ET", "ET", "ET", "ET", "ON", "ON", "ON", "ON", "L", "ON", "ON", "BN", "ON", "ON", "ET", "ET", "EN", "EN", "ON", "L", "ON", "ON", "ON", "EN", "L", "ON", "ON", "ON", "ON", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "L", "L", "L", "L", "L", "L", "L", "L"]; +const arabicTypes = ["AN", "AN", "AN", "AN", "AN", "AN", "ON", "ON", "AL", "ET", "ET", "AL", "CS", "AL", "ON", "ON", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "ET", "AN", "AN", "AL", "AL", "AL", "NSM", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AN", "ON", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "NSM", "NSM", "ON", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "AL", "AL", "AL", "AL", "AL", "AL"]; +function isOdd(i) { + return (i & 1) !== 0; +} +function isEven(i) { + return (i & 1) === 0; +} +function findUnequal(arr, start, value) { + let j, jj; + for (j = start, jj = arr.length; j < jj; ++j) { + if (arr[j] !== value) { + return j; + } + } + return j; +} +function setValues(arr, start, end, value) { + for (let j = start; j < end; ++j) { + arr[j] = value; + } +} +function reverseValues(arr, start, end) { + for (let i = start, j = end - 1; i < j; ++i, --j) { + const temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } +} +function createBidiText(str, isLTR, vertical = false) { + let dir = "ltr"; + if (vertical) { + dir = "ttb"; + } else if (!isLTR) { + dir = "rtl"; + } + return { + str, + dir + }; +} +const chars = []; +const types = []; +function bidi(str, startLevel = -1, vertical = false) { + let isLTR = true; + const strLength = str.length; + if (strLength === 0 || vertical) { + return createBidiText(str, isLTR, vertical); + } + chars.length = strLength; + types.length = strLength; + let numBidi = 0; + let i, ii; + for (i = 0; i < strLength; ++i) { + chars[i] = str.charAt(i); + const charCode = str.charCodeAt(i); + let charType = "L"; + if (charCode <= 0x00ff) { + charType = baseTypes[charCode]; + } else if (0x0590 <= charCode && charCode <= 0x05f4) { + charType = "R"; + } else if (0x0600 <= charCode && charCode <= 0x06ff) { + charType = arabicTypes[charCode & 0xff]; + if (!charType) { + warn("Bidi: invalid Unicode character " + charCode.toString(16)); + } + } else if (0x0700 <= charCode && charCode <= 0x08ac || 0xfb50 <= charCode && charCode <= 0xfdff || 0xfe70 <= charCode && charCode <= 0xfeff) { + charType = "AL"; + } + if (charType === "R" || charType === "AL" || charType === "AN") { + numBidi++; + } + types[i] = charType; + } + if (numBidi === 0) { + isLTR = true; + return createBidiText(str, isLTR); + } + if (startLevel === -1) { + if (numBidi / strLength < 0.3 && strLength > 4) { + isLTR = true; + startLevel = 0; + } else { + isLTR = false; + startLevel = 1; + } + } + const levels = []; + for (i = 0; i < strLength; ++i) { + levels[i] = startLevel; + } + const e = isOdd(startLevel) ? "R" : "L"; + const sor = e; + const eor = sor; + let lastType = sor; + for (i = 0; i < strLength; ++i) { + if (types[i] === "NSM") { + types[i] = lastType; + } else { + lastType = types[i]; + } + } + lastType = sor; + let t; + for (i = 0; i < strLength; ++i) { + t = types[i]; + if (t === "EN") { + types[i] = lastType === "AL" ? "AN" : "EN"; + } else if (t === "R" || t === "L" || t === "AL") { + lastType = t; + } + } + for (i = 0; i < strLength; ++i) { + t = types[i]; + if (t === "AL") { + types[i] = "R"; + } + } + for (i = 1; i < strLength - 1; ++i) { + if (types[i] === "ES" && types[i - 1] === "EN" && types[i + 1] === "EN") { + types[i] = "EN"; + } + if (types[i] === "CS" && (types[i - 1] === "EN" || types[i - 1] === "AN") && types[i + 1] === types[i - 1]) { + types[i] = types[i - 1]; + } + } + for (i = 0; i < strLength; ++i) { + if (types[i] === "EN") { + for (let j = i - 1; j >= 0; --j) { + if (types[j] !== "ET") { + break; + } + types[j] = "EN"; + } + for (let j = i + 1; j < strLength; ++j) { + if (types[j] !== "ET") { + break; + } + types[j] = "EN"; + } + } + } + for (i = 0; i < strLength; ++i) { + t = types[i]; + if (t === "WS" || t === "ES" || t === "ET" || t === "CS") { + types[i] = "ON"; + } + } + lastType = sor; + for (i = 0; i < strLength; ++i) { + t = types[i]; + if (t === "EN") { + types[i] = lastType === "L" ? "L" : "EN"; + } else if (t === "R" || t === "L") { + lastType = t; + } + } + for (i = 0; i < strLength; ++i) { + if (types[i] === "ON") { + const end = findUnequal(types, i + 1, "ON"); + let before = sor; + if (i > 0) { + before = types[i - 1]; + } + let after = eor; + if (end + 1 < strLength) { + after = types[end + 1]; + } + if (before !== "L") { + before = "R"; + } + if (after !== "L") { + after = "R"; + } + if (before === after) { + setValues(types, i, end, before); + } + i = end - 1; + } + } + for (i = 0; i < strLength; ++i) { + if (types[i] === "ON") { + types[i] = e; + } + } + for (i = 0; i < strLength; ++i) { + t = types[i]; + if (isEven(levels[i])) { + if (t === "R") { + levels[i] += 1; + } else if (t === "AN" || t === "EN") { + levels[i] += 2; + } + } else if (t === "L" || t === "AN" || t === "EN") { + levels[i] += 1; + } + } + let highestLevel = -1; + let lowestOddLevel = 99; + let level; + for (i = 0, ii = levels.length; i < ii; ++i) { + level = levels[i]; + if (highestLevel < level) { + highestLevel = level; + } + if (lowestOddLevel > level && isOdd(level)) { + lowestOddLevel = level; + } + } + for (level = highestLevel; level >= lowestOddLevel; --level) { + let start = -1; + for (i = 0, ii = levels.length; i < ii; ++i) { + if (levels[i] < level) { + if (start >= 0) { + reverseValues(chars, start, i); + start = -1; + } + } else if (start < 0) { + start = i; + } + } + if (start >= 0) { + reverseValues(chars, start, levels.length); + } + } + for (i = 0, ii = chars.length; i < ii; ++i) { + const ch = chars[i]; + if (ch === "<" || ch === ">") { + chars[i] = ""; + } + } + return createBidiText(chars.join(""), isLTR); +} + +;// ./src/core/font_substitutions.js + + + +const NORMAL = { + style: "normal", + weight: "normal" +}; +const BOLD = { + style: "normal", + weight: "bold" +}; +const ITALIC = { + style: "italic", + weight: "normal" +}; +const BOLDITALIC = { + style: "italic", + weight: "bold" +}; +const substitutionMap = new Map([["Times-Roman", { + local: ["Times New Roman", "Times-Roman", "Times", "Liberation Serif", "Nimbus Roman", "Nimbus Roman L", "Tinos", "Thorndale", "TeX Gyre Termes", "FreeSerif", "Linux Libertine O", "Libertinus Serif", "DejaVu Serif", "Bitstream Vera Serif", "Ubuntu"], + style: NORMAL, + ultimate: "serif" +}], ["Times-Bold", { + alias: "Times-Roman", + style: BOLD, + ultimate: "serif" +}], ["Times-Italic", { + alias: "Times-Roman", + style: ITALIC, + ultimate: "serif" +}], ["Times-BoldItalic", { + alias: "Times-Roman", + style: BOLDITALIC, + ultimate: "serif" +}], ["Helvetica", { + local: ["Helvetica", "Helvetica Neue", "Arial", "Arial Nova", "Liberation Sans", "Arimo", "Nimbus Sans", "Nimbus Sans L", "A030", "TeX Gyre Heros", "FreeSans", "DejaVu Sans", "Albany", "Bitstream Vera Sans", "Arial Unicode MS", "Microsoft Sans Serif", "Apple Symbols", "Cantarell"], + path: "LiberationSans-Regular.ttf", + style: NORMAL, + ultimate: "sans-serif" +}], ["Helvetica-Bold", { + alias: "Helvetica", + path: "LiberationSans-Bold.ttf", + style: BOLD, + ultimate: "sans-serif" +}], ["Helvetica-Oblique", { + alias: "Helvetica", + path: "LiberationSans-Italic.ttf", + style: ITALIC, + ultimate: "sans-serif" +}], ["Helvetica-BoldOblique", { + alias: "Helvetica", + path: "LiberationSans-BoldItalic.ttf", + style: BOLDITALIC, + ultimate: "sans-serif" +}], ["Courier", { + local: ["Courier", "Courier New", "Liberation Mono", "Nimbus Mono", "Nimbus Mono L", "Cousine", "Cumberland", "TeX Gyre Cursor", "FreeMono", "Linux Libertine Mono O", "Libertinus Mono"], + style: NORMAL, + ultimate: "monospace" +}], ["Courier-Bold", { + alias: "Courier", + style: BOLD, + ultimate: "monospace" +}], ["Courier-Oblique", { + alias: "Courier", + style: ITALIC, + ultimate: "monospace" +}], ["Courier-BoldOblique", { + alias: "Courier", + style: BOLDITALIC, + ultimate: "monospace" +}], ["ArialBlack", { + local: ["Arial Black"], + style: { + style: "normal", + weight: "900" + }, + fallback: "Helvetica-Bold" +}], ["ArialBlack-Bold", { + alias: "ArialBlack" +}], ["ArialBlack-Italic", { + alias: "ArialBlack", + style: { + style: "italic", + weight: "900" + }, + fallback: "Helvetica-BoldOblique" +}], ["ArialBlack-BoldItalic", { + alias: "ArialBlack-Italic" +}], ["ArialNarrow", { + local: ["Arial Narrow", "Liberation Sans Narrow", "Helvetica Condensed", "Nimbus Sans Narrow", "TeX Gyre Heros Cn"], + style: NORMAL, + fallback: "Helvetica" +}], ["ArialNarrow-Bold", { + alias: "ArialNarrow", + style: BOLD, + fallback: "Helvetica-Bold" +}], ["ArialNarrow-Italic", { + alias: "ArialNarrow", + style: ITALIC, + fallback: "Helvetica-Oblique" +}], ["ArialNarrow-BoldItalic", { + alias: "ArialNarrow", + style: BOLDITALIC, + fallback: "Helvetica-BoldOblique" +}], ["Calibri", { + local: ["Calibri", "Carlito"], + style: NORMAL, + fallback: "Helvetica" +}], ["Calibri-Bold", { + alias: "Calibri", + style: BOLD, + fallback: "Helvetica-Bold" +}], ["Calibri-Italic", { + alias: "Calibri", + style: ITALIC, + fallback: "Helvetica-Oblique" +}], ["Calibri-BoldItalic", { + alias: "Calibri", + style: BOLDITALIC, + fallback: "Helvetica-BoldOblique" +}], ["Wingdings", { + local: ["Wingdings", "URW Dingbats"], + style: NORMAL +}], ["Wingdings-Regular", { + alias: "Wingdings" +}], ["Wingdings-Bold", { + alias: "Wingdings" +}]]); +const fontAliases = new Map([["Arial-Black", "ArialBlack"]]); +function getStyleToAppend(style) { + switch (style) { + case BOLD: + return "Bold"; + case ITALIC: + return "Italic"; + case BOLDITALIC: + return "Bold Italic"; + default: + if (style?.weight === "bold") { + return "Bold"; + } + if (style?.style === "italic") { + return "Italic"; + } + } + return ""; +} +function getFamilyName(str) { + const keywords = new Set(["thin", "extralight", "ultralight", "demilight", "semilight", "light", "book", "regular", "normal", "medium", "demibold", "semibold", "bold", "extrabold", "ultrabold", "black", "heavy", "extrablack", "ultrablack", "roman", "italic", "oblique", "ultracondensed", "extracondensed", "condensed", "semicondensed", "normal", "semiexpanded", "expanded", "extraexpanded", "ultraexpanded", "bolditalic"]); + return str.split(/[- ,+]+/g).filter(tok => !keywords.has(tok.toLowerCase())).join(" "); +} +function generateFont({ + alias, + local, + path, + fallback, + style, + ultimate +}, src, localFontPath, useFallback = true, usePath = true, append = "") { + const result = { + style: null, + ultimate: null + }; + if (local) { + const extra = append ? ` ${append}` : ""; + for (const name of local) { + src.push(`local(${name}${extra})`); + } + } + if (alias) { + const substitution = substitutionMap.get(alias); + const aliasAppend = append || getStyleToAppend(style); + Object.assign(result, generateFont(substitution, src, localFontPath, useFallback && !fallback, usePath && !path, aliasAppend)); + } + if (style) { + result.style = style; + } + if (ultimate) { + result.ultimate = ultimate; + } + if (useFallback && fallback) { + const fallbackInfo = substitutionMap.get(fallback); + const { + ultimate: fallbackUltimate + } = generateFont(fallbackInfo, src, localFontPath, useFallback, usePath && !path, append); + result.ultimate ||= fallbackUltimate; + } + if (usePath && path && localFontPath) { + src.push(`url(${localFontPath}${path})`); + } + return result; +} +function getFontSubstitution(systemFontCache, idFactory, localFontPath, baseFontName, standardFontName, type) { + if (baseFontName.startsWith("InvalidPDFjsFont_")) { + return null; + } + if ((type === "TrueType" || type === "Type1") && /^[A-Z]{6}\+/.test(baseFontName)) { + baseFontName = baseFontName.slice(7); + } + baseFontName = normalizeFontName(baseFontName); + const key = baseFontName; + let substitutionInfo = systemFontCache.get(key); + if (substitutionInfo) { + return substitutionInfo; + } + let substitution = substitutionMap.get(baseFontName); + if (!substitution) { + for (const [alias, subst] of fontAliases) { + if (baseFontName.startsWith(alias)) { + baseFontName = `${subst}${baseFontName.substring(alias.length)}`; + substitution = substitutionMap.get(baseFontName); + break; + } + } + } + let mustAddBaseFont = false; + if (!substitution) { + substitution = substitutionMap.get(standardFontName); + mustAddBaseFont = true; + } + const loadedName = `${idFactory.getDocId()}_s${idFactory.createFontId()}`; + if (!substitution) { + if (!validateFontName(baseFontName)) { + warn(`Cannot substitute the font because of its name: ${baseFontName}`); + systemFontCache.set(key, null); + return null; + } + const bold = /bold/gi.test(baseFontName); + const italic = /oblique|italic/gi.test(baseFontName); + const style = bold && italic && BOLDITALIC || bold && BOLD || italic && ITALIC || NORMAL; + substitutionInfo = { + css: `"${getFamilyName(baseFontName)}",${loadedName}`, + guessFallback: true, + loadedName, + baseFontName, + src: `local(${baseFontName})`, + style + }; + systemFontCache.set(key, substitutionInfo); + return substitutionInfo; + } + const src = []; + if (mustAddBaseFont && validateFontName(baseFontName)) { + src.push(`local(${baseFontName})`); + } + const { + style, + ultimate + } = generateFont(substitution, src, localFontPath); + const guessFallback = ultimate === null; + const fallback = guessFallback ? "" : `,${ultimate}`; + substitutionInfo = { + css: `"${getFamilyName(baseFontName)}",${loadedName}${fallback}`, + guessFallback, + loadedName, + baseFontName, + src: src.join(","), + style + }; + systemFontCache.set(key, substitutionInfo); + return substitutionInfo; +} + +;// ./src/core/image_resizer.js + + + +const MIN_IMAGE_DIM = 2048; +const MAX_IMAGE_DIM = 65537; +const MAX_ERROR = 128; +class ImageResizer { + static #goodSquareLength = MIN_IMAGE_DIM; + static #isImageDecoderSupported = FeatureTest.isImageDecoderSupported; + constructor(imgData, isMask) { + this._imgData = imgData; + this._isMask = isMask; + } + static get canUseImageDecoder() { + return shadow(this, "canUseImageDecoder", this.#isImageDecoderSupported ? ImageDecoder.isTypeSupported("image/bmp") : Promise.resolve(false)); + } + static needsToBeResized(width, height) { + if (width <= this.#goodSquareLength && height <= this.#goodSquareLength) { + return false; + } + const { + MAX_DIM + } = this; + if (width > MAX_DIM || height > MAX_DIM) { + return true; + } + const area = width * height; + if (this._hasMaxArea) { + return area > this.MAX_AREA; + } + if (area < this.#goodSquareLength ** 2) { + return false; + } + if (this._areGoodDims(width, height)) { + this.#goodSquareLength = Math.max(this.#goodSquareLength, Math.floor(Math.sqrt(width * height))); + return false; + } + this.#goodSquareLength = this._guessMax(this.#goodSquareLength, MAX_DIM, MAX_ERROR, 0); + const maxArea = this.MAX_AREA = this.#goodSquareLength ** 2; + return area > maxArea; + } + static get MAX_DIM() { + return shadow(this, "MAX_DIM", this._guessMax(MIN_IMAGE_DIM, MAX_IMAGE_DIM, 0, 1)); + } + static get MAX_AREA() { + this._hasMaxArea = true; + return shadow(this, "MAX_AREA", this._guessMax(this.#goodSquareLength, this.MAX_DIM, MAX_ERROR, 0) ** 2); + } + static set MAX_AREA(area) { + if (area >= 0) { + this._hasMaxArea = true; + shadow(this, "MAX_AREA", area); + } + } + static setOptions({ + canvasMaxAreaInBytes = -1, + isImageDecoderSupported = false + }) { + if (!this._hasMaxArea) { + this.MAX_AREA = canvasMaxAreaInBytes >> 2; + } + this.#isImageDecoderSupported = isImageDecoderSupported; + } + static _areGoodDims(width, height) { + try { + const canvas = new OffscreenCanvas(width, height); + const ctx = canvas.getContext("2d"); + ctx.fillRect(0, 0, 1, 1); + const opacity = ctx.getImageData(0, 0, 1, 1).data[3]; + canvas.width = canvas.height = 1; + return opacity !== 0; + } catch { + return false; + } + } + static _guessMax(start, end, tolerance, defaultHeight) { + while (start + tolerance + 1 < end) { + const middle = Math.floor((start + end) / 2); + const height = defaultHeight || middle; + if (this._areGoodDims(middle, height)) { + start = middle; + } else { + end = middle; + } + } + return start; + } + static async createImage(imgData, isMask = false) { + return new ImageResizer(imgData, isMask)._createImage(); + } + async _createImage() { + const { + _imgData: imgData + } = this; + const { + width, + height + } = imgData; + if (width * height * 4 > MAX_INT_32) { + const result = this.#rescaleImageData(); + if (result) { + return result; + } + } + const data = this._encodeBMP(); + let decoder, imagePromise; + if (await ImageResizer.canUseImageDecoder) { + decoder = new ImageDecoder({ + data, + type: "image/bmp", + preferAnimation: false, + transfer: [data.buffer] + }); + imagePromise = decoder.decode().catch(reason => { + warn(`BMP image decoding failed: ${reason}`); + return createImageBitmap(new Blob([this._encodeBMP().buffer], { + type: "image/bmp" + })); + }).finally(() => { + decoder.close(); + }); + } else { + imagePromise = createImageBitmap(new Blob([data.buffer], { + type: "image/bmp" + })); + } + const { + MAX_AREA, + MAX_DIM + } = ImageResizer; + const minFactor = Math.max(width / MAX_DIM, height / MAX_DIM, Math.sqrt(width * height / MAX_AREA)); + const firstFactor = Math.max(minFactor, 2); + const factor = Math.round(10 * (minFactor + 1.25)) / 10 / firstFactor; + const N = Math.floor(Math.log2(factor)); + const steps = new Array(N + 2).fill(2); + steps[0] = firstFactor; + steps.splice(-1, 1, factor / (1 << N)); + let newWidth = width; + let newHeight = height; + const result = await imagePromise; + let bitmap = result.image || result; + for (const step of steps) { + const prevWidth = newWidth; + const prevHeight = newHeight; + newWidth = Math.floor(newWidth / step) - 1; + newHeight = Math.floor(newHeight / step) - 1; + const canvas = new OffscreenCanvas(newWidth, newHeight); + const ctx = canvas.getContext("2d"); + ctx.drawImage(bitmap, 0, 0, prevWidth, prevHeight, 0, 0, newWidth, newHeight); + bitmap.close(); + bitmap = canvas.transferToImageBitmap(); + } + imgData.data = null; + imgData.bitmap = bitmap; + imgData.width = newWidth; + imgData.height = newHeight; + return imgData; + } + #rescaleImageData() { + const { + _imgData: imgData + } = this; + const { + data, + width, + height, + kind + } = imgData; + const rgbaSize = width * height * 4; + const K = Math.ceil(Math.log2(rgbaSize / MAX_INT_32)); + const newWidth = width >> K; + const newHeight = height >> K; + let rgbaData; + let maxHeight = height; + try { + rgbaData = new Uint8Array(rgbaSize); + } catch { + let n = Math.floor(Math.log2(rgbaSize + 1)); + while (true) { + try { + rgbaData = new Uint8Array(2 ** n - 1); + break; + } catch { + n -= 1; + } + } + maxHeight = Math.floor((2 ** n - 1) / (width * 4)); + const newSize = width * maxHeight * 4; + if (newSize < rgbaData.length) { + rgbaData = new Uint8Array(newSize); + } + } + const src32 = new Uint32Array(rgbaData.buffer); + const dest32 = new Uint32Array(newWidth * newHeight); + let srcPos = 0; + let newIndex = 0; + const step = Math.ceil(height / maxHeight); + const remainder = height % maxHeight === 0 ? height : height % maxHeight; + for (let k = 0; k < step; k++) { + const h = k < step - 1 ? maxHeight : remainder; + ({ + srcPos + } = convertToRGBA({ + kind, + src: data, + dest: src32, + width, + height: h, + inverseDecode: this._isMask, + srcPos + })); + for (let i = 0, ii = h >> K; i < ii; i++) { + const buf = src32.subarray((i << K) * width); + for (let j = 0; j < newWidth; j++) { + dest32[newIndex++] = buf[j << K]; + } + } + } + if (ImageResizer.needsToBeResized(newWidth, newHeight)) { + imgData.data = dest32; + imgData.width = newWidth; + imgData.height = newHeight; + imgData.kind = ImageKind.RGBA_32BPP; + return null; + } + const canvas = new OffscreenCanvas(newWidth, newHeight); + const ctx = canvas.getContext("2d", { + willReadFrequently: true + }); + ctx.putImageData(new ImageData(new Uint8ClampedArray(dest32.buffer), newWidth, newHeight), 0, 0); + imgData.data = null; + imgData.bitmap = canvas.transferToImageBitmap(); + imgData.width = newWidth; + imgData.height = newHeight; + return imgData; + } + _encodeBMP() { + const { + width, + height, + kind + } = this._imgData; + let data = this._imgData.data; + let bitPerPixel; + let colorTable = new Uint8Array(0); + let maskTable = colorTable; + let compression = 0; + switch (kind) { + case ImageKind.GRAYSCALE_1BPP: + { + bitPerPixel = 1; + colorTable = new Uint8Array(this._isMask ? [255, 255, 255, 255, 0, 0, 0, 0] : [0, 0, 0, 0, 255, 255, 255, 255]); + const rowLen = width + 7 >> 3; + const rowSize = rowLen + 3 & -4; + if (rowLen !== rowSize) { + const newData = new Uint8Array(rowSize * height); + let k = 0; + for (let i = 0, ii = height * rowLen; i < ii; i += rowLen, k += rowSize) { + newData.set(data.subarray(i, i + rowLen), k); + } + data = newData; + } + break; + } + case ImageKind.RGB_24BPP: + { + bitPerPixel = 24; + if (width & 3) { + const rowLen = 3 * width; + const rowSize = rowLen + 3 & -4; + const extraLen = rowSize - rowLen; + const newData = new Uint8Array(rowSize * height); + let k = 0; + for (let i = 0, ii = height * rowLen; i < ii; i += rowLen) { + const row = data.subarray(i, i + rowLen); + for (let j = 0; j < rowLen; j += 3) { + newData[k++] = row[j + 2]; + newData[k++] = row[j + 1]; + newData[k++] = row[j]; + } + k += extraLen; + } + data = newData; + } else { + for (let i = 0, ii = data.length; i < ii; i += 3) { + const tmp = data[i]; + data[i] = data[i + 2]; + data[i + 2] = tmp; + } + } + break; + } + case ImageKind.RGBA_32BPP: + bitPerPixel = 32; + compression = 3; + maskTable = new Uint8Array(4 + 4 + 4 + 4 + 52); + const view = new DataView(maskTable.buffer); + if (FeatureTest.isLittleEndian) { + view.setUint32(0, 0x000000ff, true); + view.setUint32(4, 0x0000ff00, true); + view.setUint32(8, 0x00ff0000, true); + view.setUint32(12, 0xff000000, true); + } else { + view.setUint32(0, 0xff000000, true); + view.setUint32(4, 0x00ff0000, true); + view.setUint32(8, 0x0000ff00, true); + view.setUint32(12, 0x000000ff, true); + } + break; + default: + throw new Error("invalid format"); + } + let i = 0; + const headerLength = 40 + maskTable.length; + const fileLength = 14 + headerLength + colorTable.length + data.length; + const bmpData = new Uint8Array(fileLength); + const view = new DataView(bmpData.buffer); + view.setUint16(i, 0x4d42, true); + i += 2; + view.setUint32(i, fileLength, true); + i += 4; + view.setUint32(i, 0, true); + i += 4; + view.setUint32(i, 14 + headerLength + colorTable.length, true); + i += 4; + view.setUint32(i, headerLength, true); + i += 4; + view.setInt32(i, width, true); + i += 4; + view.setInt32(i, -height, true); + i += 4; + view.setUint16(i, 1, true); + i += 2; + view.setUint16(i, bitPerPixel, true); + i += 2; + view.setUint32(i, compression, true); + i += 4; + view.setUint32(i, 0, true); + i += 4; + view.setInt32(i, 0, true); + i += 4; + view.setInt32(i, 0, true); + i += 4; + view.setUint32(i, colorTable.length / 4, true); + i += 4; + view.setUint32(i, 0, true); + i += 4; + bmpData.set(maskTable, i); + i += maskTable.length; + bmpData.set(colorTable, i); + i += colorTable.length; + bmpData.set(data, i); + return bmpData; + } +} + +;// ./src/shared/murmurhash3.js +const SEED = 0xc3d2e1f0; +const MASK_HIGH = 0xffff0000; +const MASK_LOW = 0xffff; +class MurmurHash3_64 { + constructor(seed) { + this.h1 = seed ? seed & 0xffffffff : SEED; + this.h2 = seed ? seed & 0xffffffff : SEED; + } + update(input) { + let data, length; + if (typeof input === "string") { + data = new Uint8Array(input.length * 2); + length = 0; + for (let i = 0, ii = input.length; i < ii; i++) { + const code = input.charCodeAt(i); + if (code <= 0xff) { + data[length++] = code; + } else { + data[length++] = code >>> 8; + data[length++] = code & 0xff; + } + } + } else if (ArrayBuffer.isView(input)) { + data = input.slice(); + length = data.byteLength; + } else { + throw new Error("Invalid data format, must be a string or TypedArray."); + } + const blockCounts = length >> 2; + const tailLength = length - blockCounts * 4; + const dataUint32 = new Uint32Array(data.buffer, 0, blockCounts); + let k1 = 0, + k2 = 0; + let h1 = this.h1, + h2 = this.h2; + const C1 = 0xcc9e2d51, + C2 = 0x1b873593; + const C1_LOW = C1 & MASK_LOW, + C2_LOW = C2 & MASK_LOW; + for (let i = 0; i < blockCounts; i++) { + if (i & 1) { + k1 = dataUint32[i]; + k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW; + k1 = k1 << 15 | k1 >>> 17; + k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW; + h1 ^= k1; + h1 = h1 << 13 | h1 >>> 19; + h1 = h1 * 5 + 0xe6546b64; + } else { + k2 = dataUint32[i]; + k2 = k2 * C1 & MASK_HIGH | k2 * C1_LOW & MASK_LOW; + k2 = k2 << 15 | k2 >>> 17; + k2 = k2 * C2 & MASK_HIGH | k2 * C2_LOW & MASK_LOW; + h2 ^= k2; + h2 = h2 << 13 | h2 >>> 19; + h2 = h2 * 5 + 0xe6546b64; + } + } + k1 = 0; + switch (tailLength) { + case 3: + k1 ^= data[blockCounts * 4 + 2] << 16; + case 2: + k1 ^= data[blockCounts * 4 + 1] << 8; + case 1: + k1 ^= data[blockCounts * 4]; + k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW; + k1 = k1 << 15 | k1 >>> 17; + k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW; + if (blockCounts & 1) { + h1 ^= k1; + } else { + h2 ^= k1; + } + } + this.h1 = h1; + this.h2 = h2; + } + hexdigest() { + let h1 = this.h1, + h2 = this.h2; + h1 ^= h2 >>> 1; + h1 = h1 * 0xed558ccd & MASK_HIGH | h1 * 0x8ccd & MASK_LOW; + h2 = h2 * 0xff51afd7 & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16; + h1 ^= h2 >>> 1; + h1 = h1 * 0x1a85ec53 & MASK_HIGH | h1 * 0xec53 & MASK_LOW; + h2 = h2 * 0xc4ceb9fe & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16; + h1 ^= h2 >>> 1; + return (h1 >>> 0).toString(16).padStart(8, "0") + (h2 >>> 0).toString(16).padStart(8, "0"); + } +} + +;// ./src/core/operator_list.js + +function addState(parentState, pattern, checkFn, iterateFn, processFn) { + let state = parentState; + for (let i = 0, ii = pattern.length - 1; i < ii; i++) { + const item = pattern[i]; + state = state[item] ||= []; + } + state[pattern.at(-1)] = { + checkFn, + iterateFn, + processFn + }; +} +const InitialState = []; +addState(InitialState, [OPS.save, OPS.transform, OPS.paintInlineImageXObject, OPS.restore], null, function iterateInlineImageGroup(context, i) { + const fnArray = context.fnArray; + const iFirstSave = context.iCurr - 3; + const pos = (i - iFirstSave) % 4; + switch (pos) { + case 0: + return fnArray[i] === OPS.save; + case 1: + return fnArray[i] === OPS.transform; + case 2: + return fnArray[i] === OPS.paintInlineImageXObject; + case 3: + return fnArray[i] === OPS.restore; + } + throw new Error(`iterateInlineImageGroup - invalid pos: ${pos}`); +}, function foundInlineImageGroup(context, i) { + const MIN_IMAGES_IN_INLINE_IMAGES_BLOCK = 10; + const MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200; + const MAX_WIDTH = 1000; + const IMAGE_PADDING = 1; + const fnArray = context.fnArray, + argsArray = context.argsArray; + const curr = context.iCurr; + const iFirstSave = curr - 3; + const iFirstTransform = curr - 2; + const iFirstPIIXO = curr - 1; + const count = Math.min(Math.floor((i - iFirstSave) / 4), MAX_IMAGES_IN_INLINE_IMAGES_BLOCK); + if (count < MIN_IMAGES_IN_INLINE_IMAGES_BLOCK) { + return i - (i - iFirstSave) % 4; + } + let maxX = 0; + const map = []; + let maxLineHeight = 0; + let currentX = IMAGE_PADDING, + currentY = IMAGE_PADDING; + for (let q = 0; q < count; q++) { + const transform = argsArray[iFirstTransform + (q << 2)]; + const img = argsArray[iFirstPIIXO + (q << 2)][0]; + if (currentX + img.width > MAX_WIDTH) { + maxX = Math.max(maxX, currentX); + currentY += maxLineHeight + 2 * IMAGE_PADDING; + currentX = 0; + maxLineHeight = 0; + } + map.push({ + transform, + x: currentX, + y: currentY, + w: img.width, + h: img.height + }); + currentX += img.width + 2 * IMAGE_PADDING; + maxLineHeight = Math.max(maxLineHeight, img.height); + } + const imgWidth = Math.max(maxX, currentX) + IMAGE_PADDING; + const imgHeight = currentY + maxLineHeight + IMAGE_PADDING; + const imgData = new Uint8Array(imgWidth * imgHeight * 4); + const imgRowSize = imgWidth << 2; + for (let q = 0; q < count; q++) { + const data = argsArray[iFirstPIIXO + (q << 2)][0].data; + const rowSize = map[q].w << 2; + let dataOffset = 0; + let offset = map[q].x + map[q].y * imgWidth << 2; + imgData.set(data.subarray(0, rowSize), offset - imgRowSize); + for (let k = 0, kk = map[q].h; k < kk; k++) { + imgData.set(data.subarray(dataOffset, dataOffset + rowSize), offset); + dataOffset += rowSize; + offset += imgRowSize; + } + imgData.set(data.subarray(dataOffset - rowSize, dataOffset), offset); + while (offset >= 0) { + data[offset - 4] = data[offset]; + data[offset - 3] = data[offset + 1]; + data[offset - 2] = data[offset + 2]; + data[offset - 1] = data[offset + 3]; + data[offset + rowSize] = data[offset + rowSize - 4]; + data[offset + rowSize + 1] = data[offset + rowSize - 3]; + data[offset + rowSize + 2] = data[offset + rowSize - 2]; + data[offset + rowSize + 3] = data[offset + rowSize - 1]; + offset -= imgRowSize; + } + } + const img = { + width: imgWidth, + height: imgHeight + }; + if (context.isOffscreenCanvasSupported) { + const canvas = new OffscreenCanvas(imgWidth, imgHeight); + const ctx = canvas.getContext("2d"); + ctx.putImageData(new ImageData(new Uint8ClampedArray(imgData.buffer), imgWidth, imgHeight), 0, 0); + img.bitmap = canvas.transferToImageBitmap(); + img.data = null; + } else { + img.kind = ImageKind.RGBA_32BPP; + img.data = imgData; + } + fnArray.splice(iFirstSave, count * 4, OPS.paintInlineImageXObjectGroup); + argsArray.splice(iFirstSave, count * 4, [img, map]); + return iFirstSave + 1; +}); +addState(InitialState, [OPS.save, OPS.transform, OPS.paintImageMaskXObject, OPS.restore], null, function iterateImageMaskGroup(context, i) { + const fnArray = context.fnArray; + const iFirstSave = context.iCurr - 3; + const pos = (i - iFirstSave) % 4; + switch (pos) { + case 0: + return fnArray[i] === OPS.save; + case 1: + return fnArray[i] === OPS.transform; + case 2: + return fnArray[i] === OPS.paintImageMaskXObject; + case 3: + return fnArray[i] === OPS.restore; + } + throw new Error(`iterateImageMaskGroup - invalid pos: ${pos}`); +}, function foundImageMaskGroup(context, i) { + const MIN_IMAGES_IN_MASKS_BLOCK = 10; + const MAX_IMAGES_IN_MASKS_BLOCK = 100; + const MAX_SAME_IMAGES_IN_MASKS_BLOCK = 1000; + const fnArray = context.fnArray, + argsArray = context.argsArray; + const curr = context.iCurr; + const iFirstSave = curr - 3; + const iFirstTransform = curr - 2; + const iFirstPIMXO = curr - 1; + let count = Math.floor((i - iFirstSave) / 4); + if (count < MIN_IMAGES_IN_MASKS_BLOCK) { + return i - (i - iFirstSave) % 4; + } + let isSameImage = false; + let iTransform, transformArgs; + const firstPIMXOArg0 = argsArray[iFirstPIMXO][0]; + const firstTransformArg0 = argsArray[iFirstTransform][0], + firstTransformArg1 = argsArray[iFirstTransform][1], + firstTransformArg2 = argsArray[iFirstTransform][2], + firstTransformArg3 = argsArray[iFirstTransform][3]; + if (firstTransformArg1 === firstTransformArg2) { + isSameImage = true; + iTransform = iFirstTransform + 4; + let iPIMXO = iFirstPIMXO + 4; + for (let q = 1; q < count; q++, iTransform += 4, iPIMXO += 4) { + transformArgs = argsArray[iTransform]; + if (argsArray[iPIMXO][0] !== firstPIMXOArg0 || transformArgs[0] !== firstTransformArg0 || transformArgs[1] !== firstTransformArg1 || transformArgs[2] !== firstTransformArg2 || transformArgs[3] !== firstTransformArg3) { + if (q < MIN_IMAGES_IN_MASKS_BLOCK) { + isSameImage = false; + } else { + count = q; + } + break; + } + } + } + if (isSameImage) { + count = Math.min(count, MAX_SAME_IMAGES_IN_MASKS_BLOCK); + const positions = new Float32Array(count * 2); + iTransform = iFirstTransform; + for (let q = 0; q < count; q++, iTransform += 4) { + transformArgs = argsArray[iTransform]; + positions[q << 1] = transformArgs[4]; + positions[(q << 1) + 1] = transformArgs[5]; + } + fnArray.splice(iFirstSave, count * 4, OPS.paintImageMaskXObjectRepeat); + argsArray.splice(iFirstSave, count * 4, [firstPIMXOArg0, firstTransformArg0, firstTransformArg1, firstTransformArg2, firstTransformArg3, positions]); + } else { + count = Math.min(count, MAX_IMAGES_IN_MASKS_BLOCK); + const images = []; + for (let q = 0; q < count; q++) { + transformArgs = argsArray[iFirstTransform + (q << 2)]; + const maskParams = argsArray[iFirstPIMXO + (q << 2)][0]; + images.push({ + data: maskParams.data, + width: maskParams.width, + height: maskParams.height, + interpolate: maskParams.interpolate, + count: maskParams.count, + transform: transformArgs + }); + } + fnArray.splice(iFirstSave, count * 4, OPS.paintImageMaskXObjectGroup); + argsArray.splice(iFirstSave, count * 4, [images]); + } + return iFirstSave + 1; +}); +addState(InitialState, [OPS.save, OPS.transform, OPS.paintImageXObject, OPS.restore], function (context) { + const argsArray = context.argsArray; + const iFirstTransform = context.iCurr - 2; + return argsArray[iFirstTransform][1] === 0 && argsArray[iFirstTransform][2] === 0; +}, function iterateImageGroup(context, i) { + const fnArray = context.fnArray, + argsArray = context.argsArray; + const iFirstSave = context.iCurr - 3; + const pos = (i - iFirstSave) % 4; + switch (pos) { + case 0: + return fnArray[i] === OPS.save; + case 1: + if (fnArray[i] !== OPS.transform) { + return false; + } + const iFirstTransform = context.iCurr - 2; + const firstTransformArg0 = argsArray[iFirstTransform][0]; + const firstTransformArg3 = argsArray[iFirstTransform][3]; + if (argsArray[i][0] !== firstTransformArg0 || argsArray[i][1] !== 0 || argsArray[i][2] !== 0 || argsArray[i][3] !== firstTransformArg3) { + return false; + } + return true; + case 2: + if (fnArray[i] !== OPS.paintImageXObject) { + return false; + } + const iFirstPIXO = context.iCurr - 1; + const firstPIXOArg0 = argsArray[iFirstPIXO][0]; + if (argsArray[i][0] !== firstPIXOArg0) { + return false; + } + return true; + case 3: + return fnArray[i] === OPS.restore; + } + throw new Error(`iterateImageGroup - invalid pos: ${pos}`); +}, function (context, i) { + const MIN_IMAGES_IN_BLOCK = 3; + const MAX_IMAGES_IN_BLOCK = 1000; + const fnArray = context.fnArray, + argsArray = context.argsArray; + const curr = context.iCurr; + const iFirstSave = curr - 3; + const iFirstTransform = curr - 2; + const iFirstPIXO = curr - 1; + const firstPIXOArg0 = argsArray[iFirstPIXO][0]; + const firstTransformArg0 = argsArray[iFirstTransform][0]; + const firstTransformArg3 = argsArray[iFirstTransform][3]; + const count = Math.min(Math.floor((i - iFirstSave) / 4), MAX_IMAGES_IN_BLOCK); + if (count < MIN_IMAGES_IN_BLOCK) { + return i - (i - iFirstSave) % 4; + } + const positions = new Float32Array(count * 2); + let iTransform = iFirstTransform; + for (let q = 0; q < count; q++, iTransform += 4) { + const transformArgs = argsArray[iTransform]; + positions[q << 1] = transformArgs[4]; + positions[(q << 1) + 1] = transformArgs[5]; + } + const args = [firstPIXOArg0, firstTransformArg0, firstTransformArg3, positions]; + fnArray.splice(iFirstSave, count * 4, OPS.paintImageXObjectRepeat); + argsArray.splice(iFirstSave, count * 4, args); + return iFirstSave + 1; +}); +addState(InitialState, [OPS.beginText, OPS.setFont, OPS.setTextMatrix, OPS.showText, OPS.endText], null, function iterateShowTextGroup(context, i) { + const fnArray = context.fnArray, + argsArray = context.argsArray; + const iFirstSave = context.iCurr - 4; + const pos = (i - iFirstSave) % 5; + switch (pos) { + case 0: + return fnArray[i] === OPS.beginText; + case 1: + return fnArray[i] === OPS.setFont; + case 2: + return fnArray[i] === OPS.setTextMatrix; + case 3: + if (fnArray[i] !== OPS.showText) { + return false; + } + const iFirstSetFont = context.iCurr - 3; + const firstSetFontArg0 = argsArray[iFirstSetFont][0]; + const firstSetFontArg1 = argsArray[iFirstSetFont][1]; + if (argsArray[i][0] !== firstSetFontArg0 || argsArray[i][1] !== firstSetFontArg1) { + return false; + } + return true; + case 4: + return fnArray[i] === OPS.endText; + } + throw new Error(`iterateShowTextGroup - invalid pos: ${pos}`); +}, function (context, i) { + const MIN_CHARS_IN_BLOCK = 3; + const MAX_CHARS_IN_BLOCK = 1000; + const fnArray = context.fnArray, + argsArray = context.argsArray; + const curr = context.iCurr; + const iFirstBeginText = curr - 4; + const iFirstSetFont = curr - 3; + const iFirstSetTextMatrix = curr - 2; + const iFirstShowText = curr - 1; + const iFirstEndText = curr; + const firstSetFontArg0 = argsArray[iFirstSetFont][0]; + const firstSetFontArg1 = argsArray[iFirstSetFont][1]; + let count = Math.min(Math.floor((i - iFirstBeginText) / 5), MAX_CHARS_IN_BLOCK); + if (count < MIN_CHARS_IN_BLOCK) { + return i - (i - iFirstBeginText) % 5; + } + let iFirst = iFirstBeginText; + if (iFirstBeginText >= 4 && fnArray[iFirstBeginText - 4] === fnArray[iFirstSetFont] && fnArray[iFirstBeginText - 3] === fnArray[iFirstSetTextMatrix] && fnArray[iFirstBeginText - 2] === fnArray[iFirstShowText] && fnArray[iFirstBeginText - 1] === fnArray[iFirstEndText] && argsArray[iFirstBeginText - 4][0] === firstSetFontArg0 && argsArray[iFirstBeginText - 4][1] === firstSetFontArg1) { + count++; + iFirst -= 5; + } + let iEndText = iFirst + 4; + for (let q = 1; q < count; q++) { + fnArray.splice(iEndText, 3); + argsArray.splice(iEndText, 3); + iEndText += 2; + } + return iEndText + 1; +}); +class NullOptimizer { + constructor(queue) { + this.queue = queue; + } + _optimize() {} + push(fn, args) { + this.queue.fnArray.push(fn); + this.queue.argsArray.push(args); + this._optimize(); + } + flush() {} + reset() {} +} +class QueueOptimizer extends NullOptimizer { + constructor(queue) { + super(queue); + this.state = null; + this.context = { + iCurr: 0, + fnArray: queue.fnArray, + argsArray: queue.argsArray, + isOffscreenCanvasSupported: false + }; + this.match = null; + this.lastProcessed = 0; + } + set isOffscreenCanvasSupported(value) { + this.context.isOffscreenCanvasSupported = value; + } + _optimize() { + const fnArray = this.queue.fnArray; + let i = this.lastProcessed, + ii = fnArray.length; + let state = this.state; + let match = this.match; + if (!state && !match && i + 1 === ii && !InitialState[fnArray[i]]) { + this.lastProcessed = ii; + return; + } + const context = this.context; + while (i < ii) { + if (match) { + const iterate = (0, match.iterateFn)(context, i); + if (iterate) { + i++; + continue; + } + i = (0, match.processFn)(context, i + 1); + ii = fnArray.length; + match = null; + state = null; + if (i >= ii) { + break; + } + } + state = (state || InitialState)[fnArray[i]]; + if (!state || Array.isArray(state)) { + i++; + continue; + } + context.iCurr = i; + i++; + if (state.checkFn && !(0, state.checkFn)(context)) { + state = null; + continue; + } + match = state; + state = null; + } + this.state = state; + this.match = match; + this.lastProcessed = i; + } + flush() { + while (this.match) { + const length = this.queue.fnArray.length; + this.lastProcessed = (0, this.match.processFn)(this.context, length); + this.match = null; + this.state = null; + this._optimize(); + } + } + reset() { + this.state = null; + this.match = null; + this.lastProcessed = 0; + } +} +class OperatorList { + static CHUNK_SIZE = 1000; + static CHUNK_SIZE_ABOUT = this.CHUNK_SIZE - 5; + constructor(intent = 0, streamSink) { + this._streamSink = streamSink; + this.fnArray = []; + this.argsArray = []; + this.optimizer = streamSink && !(intent & RenderingIntentFlag.OPLIST) ? new QueueOptimizer(this) : new NullOptimizer(this); + this.dependencies = new Set(); + this._totalLength = 0; + this.weight = 0; + this._resolved = streamSink ? null : Promise.resolve(); + } + set isOffscreenCanvasSupported(value) { + this.optimizer.isOffscreenCanvasSupported = value; + } + get length() { + return this.argsArray.length; + } + get ready() { + return this._resolved || this._streamSink.ready; + } + get totalLength() { + return this._totalLength + this.length; + } + addOp(fn, args) { + this.optimizer.push(fn, args); + this.weight++; + if (this._streamSink) { + if (this.weight >= OperatorList.CHUNK_SIZE) { + this.flush(); + } else if (this.weight >= OperatorList.CHUNK_SIZE_ABOUT && (fn === OPS.restore || fn === OPS.endText)) { + this.flush(); + } + } + } + addImageOps(fn, args, optionalContent, hasMask = false) { + if (hasMask) { + this.addOp(OPS.save); + this.addOp(OPS.setGState, [[["SMask", false]]]); + } + if (optionalContent !== undefined) { + this.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]); + } + this.addOp(fn, args); + if (optionalContent !== undefined) { + this.addOp(OPS.endMarkedContent, []); + } + if (hasMask) { + this.addOp(OPS.restore); + } + } + addDependency(dependency) { + if (this.dependencies.has(dependency)) { + return; + } + this.dependencies.add(dependency); + this.addOp(OPS.dependency, [dependency]); + } + addDependencies(dependencies) { + for (const dependency of dependencies) { + this.addDependency(dependency); + } + } + addOpList(opList) { + if (!(opList instanceof OperatorList)) { + warn('addOpList - ignoring invalid "opList" parameter.'); + return; + } + for (const dependency of opList.dependencies) { + this.dependencies.add(dependency); + } + for (let i = 0, ii = opList.length; i < ii; i++) { + this.addOp(opList.fnArray[i], opList.argsArray[i]); + } + } + getIR() { + return { + fnArray: this.fnArray, + argsArray: this.argsArray, + length: this.length + }; + } + get _transfers() { + const transfers = []; + const { + fnArray, + argsArray, + length + } = this; + for (let i = 0; i < length; i++) { + switch (fnArray[i]) { + case OPS.paintInlineImageXObject: + case OPS.paintInlineImageXObjectGroup: + case OPS.paintImageMaskXObject: + const arg = argsArray[i][0]; + if (!arg.cached && arg.data?.buffer instanceof ArrayBuffer) { + transfers.push(arg.data.buffer); + } + break; + } + } + return transfers; + } + flush(lastChunk = false, separateAnnots = null) { + this.optimizer.flush(); + const length = this.length; + this._totalLength += length; + this._streamSink.enqueue({ + fnArray: this.fnArray, + argsArray: this.argsArray, + lastChunk, + separateAnnots, + length + }, 1, this._transfers); + this.dependencies.clear(); + this.fnArray.length = 0; + this.argsArray.length = 0; + this.weight = 0; + this.optimizer.reset(); + } +} + +;// ./src/core/image.js + + + + + + + + + +function decodeAndClamp(value, addend, coefficient, max) { + value = addend + value * coefficient; + if (value < 0) { + value = 0; + } else if (value > max) { + value = max; + } + return value; +} +function resizeImageMask(src, bpc, w1, h1, w2, h2) { + const length = w2 * h2; + let dest; + if (bpc <= 8) { + dest = new Uint8Array(length); + } else if (bpc <= 16) { + dest = new Uint16Array(length); + } else { + dest = new Uint32Array(length); + } + const xRatio = w1 / w2; + const yRatio = h1 / h2; + let i, + j, + py, + newIndex = 0, + oldIndex; + const xScaled = new Uint16Array(w2); + const w1Scanline = w1; + for (i = 0; i < w2; i++) { + xScaled[i] = Math.floor(i * xRatio); + } + for (i = 0; i < h2; i++) { + py = Math.floor(i * yRatio) * w1Scanline; + for (j = 0; j < w2; j++) { + oldIndex = py + xScaled[j]; + dest[newIndex++] = src[oldIndex]; + } + } + return dest; +} +class PDFImage { + constructor({ + xref, + res, + image, + isInline = false, + smask = null, + mask = null, + isMask = false, + pdfFunctionFactory, + localColorSpaceCache + }) { + this.image = image; + const dict = image.dict; + const filter = dict.get("F", "Filter"); + let filterName; + if (filter instanceof Name) { + filterName = filter.name; + } else if (Array.isArray(filter)) { + const filterZero = xref.fetchIfRef(filter[0]); + if (filterZero instanceof Name) { + filterName = filterZero.name; + } + } + switch (filterName) { + case "JPXDecode": + ({ + width: image.width, + height: image.height, + componentsCount: image.numComps, + bitsPerComponent: image.bitsPerComponent + } = JpxImage.parseImageProperties(image.stream)); + image.stream.reset(); + this.jpxDecoderOptions = { + numComponents: 0, + isIndexedColormap: false, + smaskInData: dict.has("SMaskInData") + }; + break; + case "JBIG2Decode": + image.bitsPerComponent = 1; + image.numComps = 1; + break; + } + let width = dict.get("W", "Width"); + let height = dict.get("H", "Height"); + if (Number.isInteger(image.width) && image.width > 0 && Number.isInteger(image.height) && image.height > 0 && (image.width !== width || image.height !== height)) { + warn("PDFImage - using the Width/Height of the image data, " + "rather than the image dictionary."); + width = image.width; + height = image.height; + } + if (width < 1 || height < 1) { + throw new FormatError(`Invalid image width: ${width} or height: ${height}`); + } + this.width = width; + this.height = height; + this.interpolate = dict.get("I", "Interpolate"); + this.imageMask = dict.get("IM", "ImageMask") || false; + this.matte = dict.get("Matte") || false; + let bitsPerComponent = image.bitsPerComponent; + if (!bitsPerComponent) { + bitsPerComponent = dict.get("BPC", "BitsPerComponent"); + if (!bitsPerComponent) { + if (this.imageMask) { + bitsPerComponent = 1; + } else { + throw new FormatError(`Bits per component missing in image: ${this.imageMask}`); + } + } + } + this.bpc = bitsPerComponent; + if (!this.imageMask) { + let colorSpace = dict.getRaw("CS") || dict.getRaw("ColorSpace"); + const hasColorSpace = !!colorSpace; + if (!hasColorSpace) { + if (this.jpxDecoderOptions) { + colorSpace = Name.get("DeviceRGBA"); + } else { + switch (image.numComps) { + case 1: + colorSpace = Name.get("DeviceGray"); + break; + case 3: + colorSpace = Name.get("DeviceRGB"); + break; + case 4: + colorSpace = Name.get("DeviceCMYK"); + break; + default: + throw new Error(`Images with ${image.numComps} color components not supported.`); + } + } + } else if (this.jpxDecoderOptions?.smaskInData) { + colorSpace = Name.get("DeviceRGBA"); + } + this.colorSpace = ColorSpace.parse({ + cs: colorSpace, + xref, + resources: isInline ? res : null, + pdfFunctionFactory, + localColorSpaceCache + }); + this.numComps = this.colorSpace.numComps; + if (this.jpxDecoderOptions) { + this.jpxDecoderOptions.numComponents = hasColorSpace ? this.numComp : 0; + this.jpxDecoderOptions.isIndexedColormap = this.colorSpace.name === "Indexed"; + } + } + this.decode = dict.getArray("D", "Decode"); + this.needsDecode = false; + if (this.decode && (this.colorSpace && !this.colorSpace.isDefaultDecode(this.decode, bitsPerComponent) || isMask && !ColorSpace.isDefaultDecode(this.decode, 1))) { + this.needsDecode = true; + const max = (1 << bitsPerComponent) - 1; + this.decodeCoefficients = []; + this.decodeAddends = []; + const isIndexed = this.colorSpace?.name === "Indexed"; + for (let i = 0, j = 0; i < this.decode.length; i += 2, ++j) { + const dmin = this.decode[i]; + const dmax = this.decode[i + 1]; + this.decodeCoefficients[j] = isIndexed ? (dmax - dmin) / max : dmax - dmin; + this.decodeAddends[j] = isIndexed ? dmin : max * dmin; + } + } + if (smask) { + this.smask = new PDFImage({ + xref, + res, + image: smask, + isInline, + pdfFunctionFactory, + localColorSpaceCache + }); + } else if (mask) { + if (mask instanceof BaseStream) { + const maskDict = mask.dict, + imageMask = maskDict.get("IM", "ImageMask"); + if (!imageMask) { + warn("Ignoring /Mask in image without /ImageMask."); + } else { + this.mask = new PDFImage({ + xref, + res, + image: mask, + isInline, + isMask: true, + pdfFunctionFactory, + localColorSpaceCache + }); + } + } else { + this.mask = mask; + } + } + } + static async buildImage({ + xref, + res, + image, + isInline = false, + pdfFunctionFactory, + localColorSpaceCache + }) { + const imageData = image; + let smaskData = null; + let maskData = null; + const smask = image.dict.get("SMask"); + const mask = image.dict.get("Mask"); + if (smask) { + if (smask instanceof BaseStream) { + smaskData = smask; + } else { + warn("Unsupported /SMask format."); + } + } else if (mask) { + if (mask instanceof BaseStream || Array.isArray(mask)) { + maskData = mask; + } else { + warn("Unsupported /Mask format."); + } + } + return new PDFImage({ + xref, + res, + image: imageData, + isInline, + smask: smaskData, + mask: maskData, + pdfFunctionFactory, + localColorSpaceCache + }); + } + static createRawMask({ + imgArray, + width, + height, + imageIsFromDecodeStream, + inverseDecode, + interpolate + }) { + const computedLength = (width + 7 >> 3) * height; + const actualLength = imgArray.byteLength; + const haveFullData = computedLength === actualLength; + let data, i; + if (imageIsFromDecodeStream && (!inverseDecode || haveFullData)) { + data = imgArray; + } else if (!inverseDecode) { + data = new Uint8Array(imgArray); + } else { + data = new Uint8Array(computedLength); + data.set(imgArray); + data.fill(0xff, actualLength); + } + if (inverseDecode) { + for (i = 0; i < actualLength; i++) { + data[i] ^= 0xff; + } + } + return { + data, + width, + height, + interpolate + }; + } + static async createMask({ + imgArray, + width, + height, + imageIsFromDecodeStream, + inverseDecode, + interpolate, + isOffscreenCanvasSupported = false + }) { + const isSingleOpaquePixel = width === 1 && height === 1 && inverseDecode === (imgArray.length === 0 || !!(imgArray[0] & 128)); + if (isSingleOpaquePixel) { + return { + isSingleOpaquePixel + }; + } + if (isOffscreenCanvasSupported) { + if (ImageResizer.needsToBeResized(width, height)) { + const data = new Uint8ClampedArray(width * height * 4); + convertBlackAndWhiteToRGBA({ + src: imgArray, + dest: data, + width, + height, + nonBlackColor: 0, + inverseDecode + }); + return ImageResizer.createImage({ + kind: ImageKind.RGBA_32BPP, + data, + width, + height, + interpolate + }); + } + const canvas = new OffscreenCanvas(width, height); + const ctx = canvas.getContext("2d"); + const imgData = ctx.createImageData(width, height); + convertBlackAndWhiteToRGBA({ + src: imgArray, + dest: imgData.data, + width, + height, + nonBlackColor: 0, + inverseDecode + }); + ctx.putImageData(imgData, 0, 0); + const bitmap = canvas.transferToImageBitmap(); + return { + data: null, + width, + height, + interpolate, + bitmap + }; + } + return this.createRawMask({ + imgArray, + width, + height, + inverseDecode, + imageIsFromDecodeStream, + interpolate + }); + } + get drawWidth() { + return Math.max(this.width, this.smask?.width || 0, this.mask?.width || 0); + } + get drawHeight() { + return Math.max(this.height, this.smask?.height || 0, this.mask?.height || 0); + } + decodeBuffer(buffer) { + const bpc = this.bpc; + const numComps = this.numComps; + const decodeAddends = this.decodeAddends; + const decodeCoefficients = this.decodeCoefficients; + const max = (1 << bpc) - 1; + let i, ii; + if (bpc === 1) { + for (i = 0, ii = buffer.length; i < ii; i++) { + buffer[i] = +!buffer[i]; + } + return; + } + let index = 0; + for (i = 0, ii = this.width * this.height; i < ii; i++) { + for (let j = 0; j < numComps; j++) { + buffer[index] = decodeAndClamp(buffer[index], decodeAddends[j], decodeCoefficients[j], max); + index++; + } + } + } + getComponents(buffer) { + const bpc = this.bpc; + if (bpc === 8) { + return buffer; + } + const width = this.width; + const height = this.height; + const numComps = this.numComps; + const length = width * height * numComps; + let bufferPos = 0; + let output; + if (bpc <= 8) { + output = new Uint8Array(length); + } else if (bpc <= 16) { + output = new Uint16Array(length); + } else { + output = new Uint32Array(length); + } + const rowComps = width * numComps; + const max = (1 << bpc) - 1; + let i = 0, + ii, + buf; + if (bpc === 1) { + let mask, loop1End, loop2End; + for (let j = 0; j < height; j++) { + loop1End = i + (rowComps & ~7); + loop2End = i + rowComps; + while (i < loop1End) { + buf = buffer[bufferPos++]; + output[i] = buf >> 7 & 1; + output[i + 1] = buf >> 6 & 1; + output[i + 2] = buf >> 5 & 1; + output[i + 3] = buf >> 4 & 1; + output[i + 4] = buf >> 3 & 1; + output[i + 5] = buf >> 2 & 1; + output[i + 6] = buf >> 1 & 1; + output[i + 7] = buf & 1; + i += 8; + } + if (i < loop2End) { + buf = buffer[bufferPos++]; + mask = 128; + while (i < loop2End) { + output[i++] = +!!(buf & mask); + mask >>= 1; + } + } + } + } else { + let bits = 0; + buf = 0; + for (i = 0, ii = length; i < ii; ++i) { + if (i % rowComps === 0) { + buf = 0; + bits = 0; + } + while (bits < bpc) { + buf = buf << 8 | buffer[bufferPos++]; + bits += 8; + } + const remainingBits = bits - bpc; + let value = buf >> remainingBits; + if (value < 0) { + value = 0; + } else if (value > max) { + value = max; + } + output[i] = value; + buf &= (1 << remainingBits) - 1; + bits = remainingBits; + } + } + return output; + } + async fillOpacity(rgbaBuf, width, height, actualHeight, image) { + const smask = this.smask; + const mask = this.mask; + let alphaBuf, sw, sh, i, ii, j; + if (smask) { + sw = smask.width; + sh = smask.height; + alphaBuf = new Uint8ClampedArray(sw * sh); + await smask.fillGrayBuffer(alphaBuf); + if (sw !== width || sh !== height) { + alphaBuf = resizeImageMask(alphaBuf, smask.bpc, sw, sh, width, height); + } + } else if (mask) { + if (mask instanceof PDFImage) { + sw = mask.width; + sh = mask.height; + alphaBuf = new Uint8ClampedArray(sw * sh); + mask.numComps = 1; + await mask.fillGrayBuffer(alphaBuf); + for (i = 0, ii = sw * sh; i < ii; ++i) { + alphaBuf[i] = 255 - alphaBuf[i]; + } + if (sw !== width || sh !== height) { + alphaBuf = resizeImageMask(alphaBuf, mask.bpc, sw, sh, width, height); + } + } else if (Array.isArray(mask)) { + alphaBuf = new Uint8ClampedArray(width * height); + const numComps = this.numComps; + for (i = 0, ii = width * height; i < ii; ++i) { + let opacity = 0; + const imageOffset = i * numComps; + for (j = 0; j < numComps; ++j) { + const color = image[imageOffset + j]; + const maskOffset = j * 2; + if (color < mask[maskOffset] || color > mask[maskOffset + 1]) { + opacity = 255; + break; + } + } + alphaBuf[i] = opacity; + } + } else { + throw new FormatError("Unknown mask format."); + } + } + if (alphaBuf) { + for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) { + rgbaBuf[j] = alphaBuf[i]; + } + } else { + for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) { + rgbaBuf[j] = 255; + } + } + } + undoPreblend(buffer, width, height) { + const matte = this.smask?.matte; + if (!matte) { + return; + } + const matteRgb = this.colorSpace.getRgb(matte, 0); + const matteR = matteRgb[0]; + const matteG = matteRgb[1]; + const matteB = matteRgb[2]; + const length = width * height * 4; + for (let i = 0; i < length; i += 4) { + const alpha = buffer[i + 3]; + if (alpha === 0) { + buffer[i] = 255; + buffer[i + 1] = 255; + buffer[i + 2] = 255; + continue; + } + const k = 255 / alpha; + buffer[i] = (buffer[i] - matteR) * k + matteR; + buffer[i + 1] = (buffer[i + 1] - matteG) * k + matteG; + buffer[i + 2] = (buffer[i + 2] - matteB) * k + matteB; + } + } + async createImageData(forceRGBA = false, isOffscreenCanvasSupported = false) { + const drawWidth = this.drawWidth; + const drawHeight = this.drawHeight; + const imgData = { + width: drawWidth, + height: drawHeight, + interpolate: this.interpolate, + kind: 0, + data: null + }; + const numComps = this.numComps; + const originalWidth = this.width; + const originalHeight = this.height; + const bpc = this.bpc; + const rowBytes = originalWidth * numComps * bpc + 7 >> 3; + const mustBeResized = isOffscreenCanvasSupported && ImageResizer.needsToBeResized(drawWidth, drawHeight); + if (!this.smask && !this.mask && this.colorSpace.name === "DeviceRGBA") { + imgData.kind = ImageKind.RGBA_32BPP; + const imgArray = imgData.data = await this.getImageBytes(originalHeight * originalWidth * 4, {}); + if (isOffscreenCanvasSupported) { + if (!mustBeResized) { + return this.createBitmap(ImageKind.RGBA_32BPP, drawWidth, drawHeight, imgArray); + } + return ImageResizer.createImage(imgData, false); + } + return imgData; + } + if (!forceRGBA) { + let kind; + if (this.colorSpace.name === "DeviceGray" && bpc === 1) { + kind = ImageKind.GRAYSCALE_1BPP; + } else if (this.colorSpace.name === "DeviceRGB" && bpc === 8 && !this.needsDecode) { + kind = ImageKind.RGB_24BPP; + } + if (kind && !this.smask && !this.mask && drawWidth === originalWidth && drawHeight === originalHeight) { + const image = await this.#getImage(originalWidth, originalHeight); + if (image) { + return image; + } + const data = await this.getImageBytes(originalHeight * rowBytes, {}); + if (isOffscreenCanvasSupported) { + if (mustBeResized) { + return ImageResizer.createImage({ + data, + kind, + width: drawWidth, + height: drawHeight, + interpolate: this.interpolate + }, this.needsDecode); + } + return this.createBitmap(kind, originalWidth, originalHeight, data); + } + imgData.kind = kind; + imgData.data = data; + if (this.needsDecode) { + assert(kind === ImageKind.GRAYSCALE_1BPP, "PDFImage.createImageData: The image must be grayscale."); + const buffer = imgData.data; + for (let i = 0, ii = buffer.length; i < ii; i++) { + buffer[i] ^= 0xff; + } + } + return imgData; + } + if (this.image instanceof JpegStream && !this.smask && !this.mask && !this.needsDecode) { + let imageLength = originalHeight * rowBytes; + if (isOffscreenCanvasSupported && !mustBeResized) { + let isHandled = false; + switch (this.colorSpace.name) { + case "DeviceGray": + imageLength *= 4; + isHandled = true; + break; + case "DeviceRGB": + imageLength = imageLength / 3 * 4; + isHandled = true; + break; + case "DeviceCMYK": + isHandled = true; + break; + } + if (isHandled) { + const image = await this.#getImage(drawWidth, drawHeight); + if (image) { + return image; + } + const rgba = await this.getImageBytes(imageLength, { + drawWidth, + drawHeight, + forceRGBA: true + }); + return this.createBitmap(ImageKind.RGBA_32BPP, drawWidth, drawHeight, rgba); + } + } else { + switch (this.colorSpace.name) { + case "DeviceGray": + imageLength *= 3; + case "DeviceRGB": + case "DeviceCMYK": + imgData.kind = ImageKind.RGB_24BPP; + imgData.data = await this.getImageBytes(imageLength, { + drawWidth, + drawHeight, + forceRGB: true + }); + if (mustBeResized) { + return ImageResizer.createImage(imgData); + } + return imgData; + } + } + } + } + const imgArray = await this.getImageBytes(originalHeight * rowBytes, { + internal: true + }); + const actualHeight = 0 | imgArray.length / rowBytes * drawHeight / originalHeight; + const comps = this.getComponents(imgArray); + let alpha01, maybeUndoPreblend; + let canvas, ctx, canvasImgData, data; + if (isOffscreenCanvasSupported && !mustBeResized) { + canvas = new OffscreenCanvas(drawWidth, drawHeight); + ctx = canvas.getContext("2d"); + canvasImgData = ctx.createImageData(drawWidth, drawHeight); + data = canvasImgData.data; + } + imgData.kind = ImageKind.RGBA_32BPP; + if (!forceRGBA && !this.smask && !this.mask) { + if (!isOffscreenCanvasSupported || mustBeResized) { + imgData.kind = ImageKind.RGB_24BPP; + data = new Uint8ClampedArray(drawWidth * drawHeight * 3); + alpha01 = 0; + } else { + const arr = new Uint32Array(data.buffer); + arr.fill(FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff); + alpha01 = 1; + } + maybeUndoPreblend = false; + } else { + if (!isOffscreenCanvasSupported || mustBeResized) { + data = new Uint8ClampedArray(drawWidth * drawHeight * 4); + } + alpha01 = 1; + maybeUndoPreblend = true; + await this.fillOpacity(data, drawWidth, drawHeight, actualHeight, comps); + } + if (this.needsDecode) { + this.decodeBuffer(comps); + } + this.colorSpace.fillRgb(data, originalWidth, originalHeight, drawWidth, drawHeight, actualHeight, bpc, comps, alpha01); + if (maybeUndoPreblend) { + this.undoPreblend(data, drawWidth, actualHeight); + } + if (isOffscreenCanvasSupported && !mustBeResized) { + ctx.putImageData(canvasImgData, 0, 0); + const bitmap = canvas.transferToImageBitmap(); + return { + data: null, + width: drawWidth, + height: drawHeight, + bitmap, + interpolate: this.interpolate + }; + } + imgData.data = data; + if (mustBeResized) { + return ImageResizer.createImage(imgData); + } + return imgData; + } + async fillGrayBuffer(buffer) { + const numComps = this.numComps; + if (numComps !== 1) { + throw new FormatError(`Reading gray scale from a color image: ${numComps}`); + } + const width = this.width; + const height = this.height; + const bpc = this.bpc; + const rowBytes = width * numComps * bpc + 7 >> 3; + const imgArray = await this.getImageBytes(height * rowBytes, { + internal: true + }); + const comps = this.getComponents(imgArray); + let i, length; + if (bpc === 1) { + length = width * height; + if (this.needsDecode) { + for (i = 0; i < length; ++i) { + buffer[i] = comps[i] - 1 & 255; + } + } else { + for (i = 0; i < length; ++i) { + buffer[i] = -comps[i] & 255; + } + } + return; + } + if (this.needsDecode) { + this.decodeBuffer(comps); + } + length = width * height; + const scale = 255 / ((1 << bpc) - 1); + for (i = 0; i < length; ++i) { + buffer[i] = scale * comps[i]; + } + } + createBitmap(kind, width, height, src) { + const canvas = new OffscreenCanvas(width, height); + const ctx = canvas.getContext("2d"); + let imgData; + if (kind === ImageKind.RGBA_32BPP) { + imgData = new ImageData(src, width, height); + } else { + imgData = ctx.createImageData(width, height); + convertToRGBA({ + kind, + src, + dest: new Uint32Array(imgData.data.buffer), + width, + height, + inverseDecode: this.needsDecode + }); + } + ctx.putImageData(imgData, 0, 0); + const bitmap = canvas.transferToImageBitmap(); + return { + data: null, + width, + height, + bitmap, + interpolate: this.interpolate + }; + } + async #getImage(width, height) { + const bitmap = await this.image.getTransferableImage(); + if (!bitmap) { + return null; + } + return { + data: null, + width, + height, + bitmap, + interpolate: this.interpolate + }; + } + async getImageBytes(length, { + drawWidth, + drawHeight, + forceRGBA = false, + forceRGB = false, + internal = false + }) { + this.image.reset(); + this.image.drawWidth = drawWidth || this.width; + this.image.drawHeight = drawHeight || this.height; + this.image.forceRGBA = !!forceRGBA; + this.image.forceRGB = !!forceRGB; + const imageBytes = await this.image.getImageData(length, this.jpxDecoderOptions); + if (internal || this.image instanceof DecodeStream) { + return imageBytes; + } + assert(imageBytes instanceof Uint8Array, 'PDFImage.getImageBytes: Unsupported "imageBytes" type.'); + return new Uint8Array(imageBytes); + } +} + +;// ./src/core/evaluator.js + + + + + + + + + + + + + + + + + + + + + + + + + + + + +const DefaultPartialEvaluatorOptions = Object.freeze({ + maxImageSize: -1, + disableFontFace: false, + ignoreErrors: false, + isEvalSupported: true, + isOffscreenCanvasSupported: false, + isImageDecoderSupported: false, + canvasMaxAreaInBytes: -1, + fontExtraProperties: false, + useSystemFonts: true, + cMapUrl: null, + standardFontDataUrl: null +}); +const PatternType = { + TILING: 1, + SHADING: 2 +}; +const TEXT_CHUNK_BATCH_SIZE = 10; +const deferred = Promise.resolve(); +function normalizeBlendMode(value, parsingArray = false) { + if (Array.isArray(value)) { + for (const val of value) { + const maybeBM = normalizeBlendMode(val, true); + if (maybeBM) { + return maybeBM; + } + } + warn(`Unsupported blend mode Array: ${value}`); + return "source-over"; + } + if (!(value instanceof Name)) { + if (parsingArray) { + return null; + } + return "source-over"; + } + switch (value.name) { + case "Normal": + case "Compatible": + return "source-over"; + case "Multiply": + return "multiply"; + case "Screen": + return "screen"; + case "Overlay": + return "overlay"; + case "Darken": + return "darken"; + case "Lighten": + return "lighten"; + case "ColorDodge": + return "color-dodge"; + case "ColorBurn": + return "color-burn"; + case "HardLight": + return "hard-light"; + case "SoftLight": + return "soft-light"; + case "Difference": + return "difference"; + case "Exclusion": + return "exclusion"; + case "Hue": + return "hue"; + case "Saturation": + return "saturation"; + case "Color": + return "color"; + case "Luminosity": + return "luminosity"; + } + if (parsingArray) { + return null; + } + warn(`Unsupported blend mode: ${value.name}`); + return "source-over"; +} +function addLocallyCachedImageOps(opList, data) { + if (data.objId) { + opList.addDependency(data.objId); + } + opList.addImageOps(data.fn, data.args, data.optionalContent, data.hasMask); + if (data.fn === OPS.paintImageMaskXObject && data.args[0]?.count > 0) { + data.args[0].count++; + } +} +class TimeSlotManager { + static TIME_SLOT_DURATION_MS = 20; + static CHECK_TIME_EVERY = 100; + constructor() { + this.reset(); + } + check() { + if (++this.checked < TimeSlotManager.CHECK_TIME_EVERY) { + return false; + } + this.checked = 0; + return this.endTime <= Date.now(); + } + reset() { + this.endTime = Date.now() + TimeSlotManager.TIME_SLOT_DURATION_MS; + this.checked = 0; + } +} +class PartialEvaluator { + constructor({ + xref, + handler, + pageIndex, + idFactory, + fontCache, + builtInCMapCache, + standardFontDataCache, + globalImageCache, + systemFontCache, + options = null + }) { + this.xref = xref; + this.handler = handler; + this.pageIndex = pageIndex; + this.idFactory = idFactory; + this.fontCache = fontCache; + this.builtInCMapCache = builtInCMapCache; + this.standardFontDataCache = standardFontDataCache; + this.globalImageCache = globalImageCache; + this.systemFontCache = systemFontCache; + this.options = options || DefaultPartialEvaluatorOptions; + this.type3FontRefs = null; + this._regionalImageCache = new RegionalImageCache(); + this._fetchBuiltInCMapBound = this.fetchBuiltInCMap.bind(this); + ImageResizer.setOptions(this.options); + JpegStream.setOptions(this.options); + } + get _pdfFunctionFactory() { + const pdfFunctionFactory = new PDFFunctionFactory({ + xref: this.xref, + isEvalSupported: this.options.isEvalSupported + }); + return shadow(this, "_pdfFunctionFactory", pdfFunctionFactory); + } + get parsingType3Font() { + return !!this.type3FontRefs; + } + clone(newOptions = null) { + const newEvaluator = Object.create(this); + newEvaluator.options = Object.assign(Object.create(null), this.options, newOptions); + return newEvaluator; + } + hasBlendModes(resources, nonBlendModesSet) { + if (!(resources instanceof Dict)) { + return false; + } + if (resources.objId && nonBlendModesSet.has(resources.objId)) { + return false; + } + const processed = new RefSet(nonBlendModesSet); + if (resources.objId) { + processed.put(resources.objId); + } + const nodes = [resources], + xref = this.xref; + while (nodes.length) { + const node = nodes.shift(); + const graphicStates = node.get("ExtGState"); + if (graphicStates instanceof Dict) { + for (let graphicState of graphicStates.getRawValues()) { + if (graphicState instanceof Ref) { + if (processed.has(graphicState)) { + continue; + } + try { + graphicState = xref.fetch(graphicState); + } catch (ex) { + processed.put(graphicState); + info(`hasBlendModes - ignoring ExtGState: "${ex}".`); + continue; + } + } + if (!(graphicState instanceof Dict)) { + continue; + } + if (graphicState.objId) { + processed.put(graphicState.objId); + } + const bm = graphicState.get("BM"); + if (bm instanceof Name) { + if (bm.name !== "Normal") { + return true; + } + continue; + } + if (bm !== undefined && Array.isArray(bm)) { + for (const element of bm) { + if (element instanceof Name && element.name !== "Normal") { + return true; + } + } + } + } + } + const xObjects = node.get("XObject"); + if (!(xObjects instanceof Dict)) { + continue; + } + for (let xObject of xObjects.getRawValues()) { + if (xObject instanceof Ref) { + if (processed.has(xObject)) { + continue; + } + try { + xObject = xref.fetch(xObject); + } catch (ex) { + processed.put(xObject); + info(`hasBlendModes - ignoring XObject: "${ex}".`); + continue; + } + } + if (!(xObject instanceof BaseStream)) { + continue; + } + if (xObject.dict.objId) { + processed.put(xObject.dict.objId); + } + const xResources = xObject.dict.get("Resources"); + if (!(xResources instanceof Dict)) { + continue; + } + if (xResources.objId && processed.has(xResources.objId)) { + continue; + } + nodes.push(xResources); + if (xResources.objId) { + processed.put(xResources.objId); + } + } + } + for (const ref of processed) { + nonBlendModesSet.put(ref); + } + return false; + } + async #fetchData(url) { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to fetch file "${url}" with "${response.statusText}".`); + } + return new Uint8Array(await response.arrayBuffer()); + } + async fetchBuiltInCMap(name) { + const cachedData = this.builtInCMapCache.get(name); + if (cachedData) { + return cachedData; + } + let data; + if (this.options.cMapUrl !== null) { + const cMapData = await this.#fetchData(`${this.options.cMapUrl}${name}.bcmap`); + data = { + cMapData, + isCompressed: true + }; + } else { + data = await this.handler.sendWithPromise("FetchBuiltInCMap", { + name + }); + } + this.builtInCMapCache.set(name, data); + return data; + } + async fetchStandardFontData(name) { + const cachedData = this.standardFontDataCache.get(name); + if (cachedData) { + return new Stream(cachedData); + } + if (this.options.useSystemFonts && name !== "Symbol" && name !== "ZapfDingbats") { + return null; + } + const standardFontNameToFileName = getFontNameToFileMap(), + filename = standardFontNameToFileName[name]; + let data; + try { + if (this.options.standardFontDataUrl !== null) { + data = await this.#fetchData(`${this.options.standardFontDataUrl}${filename}`); + } else { + data = await this.handler.sendWithPromise("FetchStandardFontData", { + filename + }); + } + } catch (ex) { + warn(ex); + return null; + } + this.standardFontDataCache.set(name, data); + return new Stream(data); + } + async buildFormXObject(resources, xobj, smask, operatorList, task, initialState, localColorSpaceCache) { + const dict = xobj.dict; + const matrix = lookupMatrix(dict.getArray("Matrix"), null); + const bbox = lookupNormalRect(dict.getArray("BBox"), null); + let optionalContent, groupOptions; + if (dict.has("OC")) { + optionalContent = await this.parseMarkedContentProps(dict.get("OC"), resources); + } + if (optionalContent !== undefined) { + operatorList.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]); + } + const group = dict.get("Group"); + if (group) { + groupOptions = { + matrix, + bbox, + smask, + isolated: false, + knockout: false + }; + const groupSubtype = group.get("S"); + let colorSpace = null; + if (isName(groupSubtype, "Transparency")) { + groupOptions.isolated = group.get("I") || false; + groupOptions.knockout = group.get("K") || false; + if (group.has("CS")) { + const cs = group.getRaw("CS"); + const cachedColorSpace = ColorSpace.getCached(cs, this.xref, localColorSpaceCache); + if (cachedColorSpace) { + colorSpace = cachedColorSpace; + } else { + colorSpace = await this.parseColorSpace({ + cs, + resources, + localColorSpaceCache + }); + } + } + } + if (smask?.backdrop) { + colorSpace ||= ColorSpace.singletons.rgb; + smask.backdrop = colorSpace.getRgb(smask.backdrop, 0); + } + operatorList.addOp(OPS.beginGroup, [groupOptions]); + } + const args = group ? [matrix, null] : [matrix, bbox]; + operatorList.addOp(OPS.paintFormXObjectBegin, args); + await this.getOperatorList({ + stream: xobj, + task, + resources: dict.get("Resources") || resources, + operatorList, + initialState + }); + operatorList.addOp(OPS.paintFormXObjectEnd, []); + if (group) { + operatorList.addOp(OPS.endGroup, [groupOptions]); + } + if (optionalContent !== undefined) { + operatorList.addOp(OPS.endMarkedContent, []); + } + } + _sendImgData(objId, imgData, cacheGlobally = false) { + const transfers = imgData ? [imgData.bitmap || imgData.data.buffer] : null; + if (this.parsingType3Font || cacheGlobally) { + return this.handler.send("commonobj", [objId, "Image", imgData], transfers); + } + return this.handler.send("obj", [objId, this.pageIndex, "Image", imgData], transfers); + } + async buildPaintImageXObject({ + resources, + image, + isInline = false, + operatorList, + cacheKey, + localImageCache, + localColorSpaceCache + }) { + const dict = image.dict; + const imageRef = dict.objId; + const w = dict.get("W", "Width"); + const h = dict.get("H", "Height"); + if (!(w && typeof w === "number") || !(h && typeof h === "number")) { + warn("Image dimensions are missing, or not numbers."); + return; + } + const maxImageSize = this.options.maxImageSize; + if (maxImageSize !== -1 && w * h > maxImageSize) { + const msg = "Image exceeded maximum allowed size and was removed."; + if (this.options.ignoreErrors) { + warn(msg); + return; + } + throw new Error(msg); + } + let optionalContent; + if (dict.has("OC")) { + optionalContent = await this.parseMarkedContentProps(dict.get("OC"), resources); + } + const imageMask = dict.get("IM", "ImageMask") || false; + let imgData, args; + if (imageMask) { + const interpolate = dict.get("I", "Interpolate"); + const bitStrideLength = w + 7 >> 3; + const imgArray = image.getBytes(bitStrideLength * h); + const decode = dict.getArray("D", "Decode"); + if (this.parsingType3Font) { + imgData = PDFImage.createRawMask({ + imgArray, + width: w, + height: h, + imageIsFromDecodeStream: image instanceof DecodeStream, + inverseDecode: decode?.[0] > 0, + interpolate + }); + imgData.cached = !!cacheKey; + args = [imgData]; + operatorList.addImageOps(OPS.paintImageMaskXObject, args, optionalContent); + if (cacheKey) { + const cacheData = { + fn: OPS.paintImageMaskXObject, + args, + optionalContent + }; + localImageCache.set(cacheKey, imageRef, cacheData); + if (imageRef) { + this._regionalImageCache.set(null, imageRef, cacheData); + } + } + return; + } + imgData = await PDFImage.createMask({ + imgArray, + width: w, + height: h, + imageIsFromDecodeStream: image instanceof DecodeStream, + inverseDecode: decode?.[0] > 0, + interpolate, + isOffscreenCanvasSupported: this.options.isOffscreenCanvasSupported + }); + if (imgData.isSingleOpaquePixel) { + operatorList.addImageOps(OPS.paintSolidColorImageMask, [], optionalContent); + if (cacheKey) { + const cacheData = { + fn: OPS.paintSolidColorImageMask, + args: [], + optionalContent + }; + localImageCache.set(cacheKey, imageRef, cacheData); + if (imageRef) { + this._regionalImageCache.set(null, imageRef, cacheData); + } + } + return; + } + const objId = `mask_${this.idFactory.createObjId()}`; + operatorList.addDependency(objId); + imgData.dataLen = imgData.bitmap ? imgData.width * imgData.height * 4 : imgData.data.length; + this._sendImgData(objId, imgData); + args = [{ + data: objId, + width: imgData.width, + height: imgData.height, + interpolate: imgData.interpolate, + count: 1 + }]; + operatorList.addImageOps(OPS.paintImageMaskXObject, args, optionalContent); + if (cacheKey) { + const cacheData = { + objId, + fn: OPS.paintImageMaskXObject, + args, + optionalContent + }; + localImageCache.set(cacheKey, imageRef, cacheData); + if (imageRef) { + this._regionalImageCache.set(null, imageRef, cacheData); + } + } + return; + } + const SMALL_IMAGE_DIMENSIONS = 200; + const hasMask = dict.has("SMask") || dict.has("Mask"); + if (isInline && w + h < SMALL_IMAGE_DIMENSIONS && !hasMask) { + try { + const imageObj = new PDFImage({ + xref: this.xref, + res: resources, + image, + isInline, + pdfFunctionFactory: this._pdfFunctionFactory, + localColorSpaceCache + }); + imgData = await imageObj.createImageData(true, false); + operatorList.isOffscreenCanvasSupported = this.options.isOffscreenCanvasSupported; + operatorList.addImageOps(OPS.paintInlineImageXObject, [imgData], optionalContent); + } catch (reason) { + const msg = `Unable to decode inline image: "${reason}".`; + if (!this.options.ignoreErrors) { + throw new Error(msg); + } + warn(msg); + } + return; + } + let objId = `img_${this.idFactory.createObjId()}`, + cacheGlobally = false; + if (this.parsingType3Font) { + objId = `${this.idFactory.getDocId()}_type3_${objId}`; + } else if (cacheKey && imageRef) { + cacheGlobally = this.globalImageCache.shouldCache(imageRef, this.pageIndex); + if (cacheGlobally) { + assert(!isInline, "Cannot cache an inline image globally."); + objId = `${this.idFactory.getDocId()}_${objId}`; + } + } + operatorList.addDependency(objId); + args = [objId, w, h]; + operatorList.addImageOps(OPS.paintImageXObject, args, optionalContent, hasMask); + if (cacheGlobally) { + if (this.globalImageCache.hasDecodeFailed(imageRef)) { + this.globalImageCache.setData(imageRef, { + objId, + fn: OPS.paintImageXObject, + args, + optionalContent, + hasMask, + byteSize: 0 + }); + this._sendImgData(objId, null, cacheGlobally); + return; + } + if (w * h > 250000 || hasMask) { + const localLength = await this.handler.sendWithPromise("commonobj", [objId, "CopyLocalImage", { + imageRef + }]); + if (localLength) { + this.globalImageCache.setData(imageRef, { + objId, + fn: OPS.paintImageXObject, + args, + optionalContent, + hasMask, + byteSize: 0 + }); + this.globalImageCache.addByteSize(imageRef, localLength); + return; + } + } + } + PDFImage.buildImage({ + xref: this.xref, + res: resources, + image, + isInline, + pdfFunctionFactory: this._pdfFunctionFactory, + localColorSpaceCache + }).then(async imageObj => { + imgData = await imageObj.createImageData(false, this.options.isOffscreenCanvasSupported); + imgData.dataLen = imgData.bitmap ? imgData.width * imgData.height * 4 : imgData.data.length; + imgData.ref = imageRef; + if (cacheGlobally) { + this.globalImageCache.addByteSize(imageRef, imgData.dataLen); + } + return this._sendImgData(objId, imgData, cacheGlobally); + }).catch(reason => { + warn(`Unable to decode image "${objId}": "${reason}".`); + if (imageRef) { + this.globalImageCache.addDecodeFailed(imageRef); + } + return this._sendImgData(objId, null, cacheGlobally); + }); + if (cacheKey) { + const cacheData = { + objId, + fn: OPS.paintImageXObject, + args, + optionalContent, + hasMask + }; + localImageCache.set(cacheKey, imageRef, cacheData); + if (imageRef) { + this._regionalImageCache.set(null, imageRef, cacheData); + if (cacheGlobally) { + this.globalImageCache.setData(imageRef, { + objId, + fn: OPS.paintImageXObject, + args, + optionalContent, + hasMask, + byteSize: 0 + }); + } + } + } + } + handleSMask(smask, resources, operatorList, task, stateManager, localColorSpaceCache) { + const smaskContent = smask.get("G"); + const smaskOptions = { + subtype: smask.get("S").name, + backdrop: smask.get("BC") + }; + const transferObj = smask.get("TR"); + if (isPDFFunction(transferObj)) { + const transferFn = this._pdfFunctionFactory.create(transferObj); + const transferMap = new Uint8Array(256); + const tmp = new Float32Array(1); + for (let i = 0; i < 256; i++) { + tmp[0] = i / 255; + transferFn(tmp, 0, tmp, 0); + transferMap[i] = tmp[0] * 255 | 0; + } + smaskOptions.transferMap = transferMap; + } + return this.buildFormXObject(resources, smaskContent, smaskOptions, operatorList, task, stateManager.state.clone(), localColorSpaceCache); + } + handleTransferFunction(tr) { + let transferArray; + if (Array.isArray(tr)) { + transferArray = tr; + } else if (isPDFFunction(tr)) { + transferArray = [tr]; + } else { + return null; + } + const transferMaps = []; + let numFns = 0, + numEffectfulFns = 0; + for (const entry of transferArray) { + const transferObj = this.xref.fetchIfRef(entry); + numFns++; + if (isName(transferObj, "Identity")) { + transferMaps.push(null); + continue; + } else if (!isPDFFunction(transferObj)) { + return null; + } + const transferFn = this._pdfFunctionFactory.create(transferObj); + const transferMap = new Uint8Array(256), + tmp = new Float32Array(1); + for (let j = 0; j < 256; j++) { + tmp[0] = j / 255; + transferFn(tmp, 0, tmp, 0); + transferMap[j] = tmp[0] * 255 | 0; + } + transferMaps.push(transferMap); + numEffectfulFns++; + } + if (!(numFns === 1 || numFns === 4)) { + return null; + } + if (numEffectfulFns === 0) { + return null; + } + return transferMaps; + } + handleTilingType(fn, color, resources, pattern, patternDict, operatorList, task, localTilingPatternCache) { + const tilingOpList = new OperatorList(); + const patternResources = Dict.merge({ + xref: this.xref, + dictArray: [patternDict.get("Resources"), resources] + }); + return this.getOperatorList({ + stream: pattern, + task, + resources: patternResources, + operatorList: tilingOpList + }).then(function () { + const operatorListIR = tilingOpList.getIR(); + const tilingPatternIR = getTilingPatternIR(operatorListIR, patternDict, color); + operatorList.addDependencies(tilingOpList.dependencies); + operatorList.addOp(fn, tilingPatternIR); + if (patternDict.objId) { + localTilingPatternCache.set(null, patternDict.objId, { + operatorListIR, + dict: patternDict + }); + } + }).catch(reason => { + if (reason instanceof AbortException) { + return; + } + if (this.options.ignoreErrors) { + warn(`handleTilingType - ignoring pattern: "${reason}".`); + return; + } + throw reason; + }); + } + async handleSetFont(resources, fontArgs, fontRef, operatorList, task, state, fallbackFontDict = null, cssFontInfo = null) { + const fontName = fontArgs?.[0] instanceof Name ? fontArgs[0].name : null; + let translated = await this.loadFont(fontName, fontRef, resources, fallbackFontDict, cssFontInfo); + if (translated.font.isType3Font) { + try { + await translated.loadType3Data(this, resources, task); + operatorList.addDependencies(translated.type3Dependencies); + } catch (reason) { + translated = new TranslatedFont({ + loadedName: "g_font_error", + font: new ErrorFont(`Type3 font load error: ${reason}`), + dict: translated.font, + evaluatorOptions: this.options + }); + } + } + state.font = translated.font; + translated.send(this.handler); + return translated.loadedName; + } + handleText(chars, state) { + const font = state.font; + const glyphs = font.charsToGlyphs(chars); + if (font.data) { + const isAddToPathSet = !!(state.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG); + if (isAddToPathSet || state.fillColorSpace.name === "Pattern" || font.disableFontFace || this.options.disableFontFace) { + PartialEvaluator.buildFontPaths(font, glyphs, this.handler, this.options); + } + } + return glyphs; + } + ensureStateFont(state) { + if (state.font) { + return; + } + const reason = new FormatError("Missing setFont (Tf) operator before text rendering operator."); + if (this.options.ignoreErrors) { + warn(`ensureStateFont: "${reason}".`); + return; + } + throw reason; + } + async setGState({ + resources, + gState, + operatorList, + cacheKey, + task, + stateManager, + localGStateCache, + localColorSpaceCache + }) { + const gStateRef = gState.objId; + let isSimpleGState = true; + const gStateObj = []; + let promise = Promise.resolve(); + for (const key of gState.getKeys()) { + const value = gState.get(key); + switch (key) { + case "Type": + break; + case "LW": + case "LC": + case "LJ": + case "ML": + case "D": + case "RI": + case "FL": + case "CA": + case "ca": + gStateObj.push([key, value]); + break; + case "Font": + isSimpleGState = false; + promise = promise.then(() => this.handleSetFont(resources, null, value[0], operatorList, task, stateManager.state).then(function (loadedName) { + operatorList.addDependency(loadedName); + gStateObj.push([key, [loadedName, value[1]]]); + })); + break; + case "BM": + gStateObj.push([key, normalizeBlendMode(value)]); + break; + case "SMask": + if (isName(value, "None")) { + gStateObj.push([key, false]); + break; + } + if (value instanceof Dict) { + isSimpleGState = false; + promise = promise.then(() => this.handleSMask(value, resources, operatorList, task, stateManager, localColorSpaceCache)); + gStateObj.push([key, true]); + } else { + warn("Unsupported SMask type"); + } + break; + case "TR": + const transferMaps = this.handleTransferFunction(value); + gStateObj.push([key, transferMaps]); + break; + case "OP": + case "op": + case "OPM": + case "BG": + case "BG2": + case "UCR": + case "UCR2": + case "TR2": + case "HT": + case "SM": + case "SA": + case "AIS": + case "TK": + info("graphic state operator " + key); + break; + default: + info("Unknown graphic state operator " + key); + break; + } + } + await promise; + if (gStateObj.length > 0) { + operatorList.addOp(OPS.setGState, [gStateObj]); + } + if (isSimpleGState) { + localGStateCache.set(cacheKey, gStateRef, gStateObj); + } + } + loadFont(fontName, font, resources, fallbackFontDict = null, cssFontInfo = null) { + const errorFont = async () => { + return new TranslatedFont({ + loadedName: "g_font_error", + font: new ErrorFont(`Font "${fontName}" is not available.`), + dict: font, + evaluatorOptions: this.options + }); + }; + let fontRef; + if (font) { + if (font instanceof Ref) { + fontRef = font; + } + } else { + const fontRes = resources.get("Font"); + if (fontRes) { + fontRef = fontRes.getRaw(fontName); + } + } + if (fontRef) { + if (this.type3FontRefs?.has(fontRef)) { + return errorFont(); + } + if (this.fontCache.has(fontRef)) { + return this.fontCache.get(fontRef); + } + try { + font = this.xref.fetchIfRef(fontRef); + } catch (ex) { + warn(`loadFont - lookup failed: "${ex}".`); + } + } + if (!(font instanceof Dict)) { + if (!this.options.ignoreErrors && !this.parsingType3Font) { + warn(`Font "${fontName}" is not available.`); + return errorFont(); + } + warn(`Font "${fontName}" is not available -- attempting to fallback to a default font.`); + font = fallbackFontDict || PartialEvaluator.fallbackFontDict; + } + if (font.cacheKey && this.fontCache.has(font.cacheKey)) { + return this.fontCache.get(font.cacheKey); + } + const { + promise, + resolve + } = Promise.withResolvers(); + let preEvaluatedFont; + try { + preEvaluatedFont = this.preEvaluateFont(font); + preEvaluatedFont.cssFontInfo = cssFontInfo; + } catch (reason) { + warn(`loadFont - preEvaluateFont failed: "${reason}".`); + return errorFont(); + } + const { + descriptor, + hash + } = preEvaluatedFont; + const fontRefIsRef = fontRef instanceof Ref; + let fontID; + if (hash && descriptor instanceof Dict) { + const fontAliases = descriptor.fontAliases ||= Object.create(null); + if (fontAliases[hash]) { + const aliasFontRef = fontAliases[hash].aliasRef; + if (fontRefIsRef && aliasFontRef && this.fontCache.has(aliasFontRef)) { + this.fontCache.putAlias(fontRef, aliasFontRef); + return this.fontCache.get(fontRef); + } + } else { + fontAliases[hash] = { + fontID: this.idFactory.createFontId() + }; + } + if (fontRefIsRef) { + fontAliases[hash].aliasRef = fontRef; + } + fontID = fontAliases[hash].fontID; + } else { + fontID = this.idFactory.createFontId(); + } + assert(fontID?.startsWith("f"), 'The "fontID" must be (correctly) defined.'); + if (fontRefIsRef) { + this.fontCache.put(fontRef, promise); + } else { + font.cacheKey = `cacheKey_${fontID}`; + this.fontCache.put(font.cacheKey, promise); + } + font.loadedName = `${this.idFactory.getDocId()}_${fontID}`; + this.translateFont(preEvaluatedFont).then(translatedFont => { + resolve(new TranslatedFont({ + loadedName: font.loadedName, + font: translatedFont, + dict: font, + evaluatorOptions: this.options + })); + }).catch(reason => { + warn(`loadFont - translateFont failed: "${reason}".`); + resolve(new TranslatedFont({ + loadedName: font.loadedName, + font: new ErrorFont(reason instanceof Error ? reason.message : reason), + dict: font, + evaluatorOptions: this.options + })); + }); + return promise; + } + buildPath(operatorList, fn, args, parsingText = false) { + const lastIndex = operatorList.length - 1; + if (!args) { + args = []; + } + if (lastIndex < 0 || operatorList.fnArray[lastIndex] !== OPS.constructPath) { + if (parsingText) { + warn(`Encountered path operator "${fn}" inside of a text object.`); + operatorList.addOp(OPS.save, null); + } + let minMax; + switch (fn) { + case OPS.rectangle: + const x = args[0] + args[2]; + const y = args[1] + args[3]; + minMax = [Math.min(args[0], x), Math.min(args[1], y), Math.max(args[0], x), Math.max(args[1], y)]; + break; + case OPS.moveTo: + case OPS.lineTo: + minMax = [args[0], args[1], args[0], args[1]]; + break; + default: + minMax = [Infinity, Infinity, -Infinity, -Infinity]; + break; + } + operatorList.addOp(OPS.constructPath, [[fn], args, minMax]); + if (parsingText) { + operatorList.addOp(OPS.restore, null); + } + } else { + const opArgs = operatorList.argsArray[lastIndex]; + opArgs[0].push(fn); + opArgs[1].push(...args); + const minMax = opArgs[2]; + switch (fn) { + case OPS.rectangle: + const x = args[0] + args[2]; + const y = args[1] + args[3]; + minMax[0] = Math.min(minMax[0], args[0], x); + minMax[1] = Math.min(minMax[1], args[1], y); + minMax[2] = Math.max(minMax[2], args[0], x); + minMax[3] = Math.max(minMax[3], args[1], y); + break; + case OPS.moveTo: + case OPS.lineTo: + minMax[0] = Math.min(minMax[0], args[0]); + minMax[1] = Math.min(minMax[1], args[1]); + minMax[2] = Math.max(minMax[2], args[0]); + minMax[3] = Math.max(minMax[3], args[1]); + break; + } + } + } + parseColorSpace({ + cs, + resources, + localColorSpaceCache + }) { + return ColorSpace.parseAsync({ + cs, + xref: this.xref, + resources, + pdfFunctionFactory: this._pdfFunctionFactory, + localColorSpaceCache + }).catch(reason => { + if (reason instanceof AbortException) { + return null; + } + if (this.options.ignoreErrors) { + warn(`parseColorSpace - ignoring ColorSpace: "${reason}".`); + return null; + } + throw reason; + }); + } + parseShading({ + shading, + resources, + localColorSpaceCache, + localShadingPatternCache + }) { + let id = localShadingPatternCache.get(shading); + if (id) { + return id; + } + let patternIR; + try { + const shadingFill = Pattern.parseShading(shading, this.xref, resources, this._pdfFunctionFactory, localColorSpaceCache); + patternIR = shadingFill.getIR(); + } catch (reason) { + if (reason instanceof AbortException) { + return null; + } + if (this.options.ignoreErrors) { + warn(`parseShading - ignoring shading: "${reason}".`); + localShadingPatternCache.set(shading, null); + return null; + } + throw reason; + } + id = `pattern_${this.idFactory.createObjId()}`; + if (this.parsingType3Font) { + id = `${this.idFactory.getDocId()}_type3_${id}`; + } + localShadingPatternCache.set(shading, id); + if (this.parsingType3Font) { + this.handler.send("commonobj", [id, "Pattern", patternIR]); + } else { + this.handler.send("obj", [id, this.pageIndex, "Pattern", patternIR]); + } + return id; + } + handleColorN(operatorList, fn, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache, localShadingPatternCache) { + const patternName = args.pop(); + if (patternName instanceof Name) { + const rawPattern = patterns.getRaw(patternName.name); + const localTilingPattern = rawPattern instanceof Ref && localTilingPatternCache.getByRef(rawPattern); + if (localTilingPattern) { + try { + const color = cs.base ? cs.base.getRgb(args, 0) : null; + const tilingPatternIR = getTilingPatternIR(localTilingPattern.operatorListIR, localTilingPattern.dict, color); + operatorList.addOp(fn, tilingPatternIR); + return undefined; + } catch {} + } + const pattern = this.xref.fetchIfRef(rawPattern); + if (pattern) { + const dict = pattern instanceof BaseStream ? pattern.dict : pattern; + const typeNum = dict.get("PatternType"); + if (typeNum === PatternType.TILING) { + const color = cs.base ? cs.base.getRgb(args, 0) : null; + return this.handleTilingType(fn, color, resources, pattern, dict, operatorList, task, localTilingPatternCache); + } else if (typeNum === PatternType.SHADING) { + const shading = dict.get("Shading"); + const objId = this.parseShading({ + shading, + resources, + localColorSpaceCache, + localShadingPatternCache + }); + if (objId) { + const matrix = lookupMatrix(dict.getArray("Matrix"), null); + operatorList.addOp(fn, ["Shading", objId, matrix]); + } + return undefined; + } + throw new FormatError(`Unknown PatternType: ${typeNum}`); + } + } + throw new FormatError(`Unknown PatternName: ${patternName}`); + } + _parseVisibilityExpression(array, nestingCounter, currentResult) { + const MAX_NESTING = 10; + if (++nestingCounter > MAX_NESTING) { + warn("Visibility expression is too deeply nested"); + return; + } + const length = array.length; + const operator = this.xref.fetchIfRef(array[0]); + if (length < 2 || !(operator instanceof Name)) { + warn("Invalid visibility expression"); + return; + } + switch (operator.name) { + case "And": + case "Or": + case "Not": + currentResult.push(operator.name); + break; + default: + warn(`Invalid operator ${operator.name} in visibility expression`); + return; + } + for (let i = 1; i < length; i++) { + const raw = array[i]; + const object = this.xref.fetchIfRef(raw); + if (Array.isArray(object)) { + const nestedResult = []; + currentResult.push(nestedResult); + this._parseVisibilityExpression(object, nestingCounter, nestedResult); + } else if (raw instanceof Ref) { + currentResult.push(raw.toString()); + } + } + } + async parseMarkedContentProps(contentProperties, resources) { + let optionalContent; + if (contentProperties instanceof Name) { + const properties = resources.get("Properties"); + optionalContent = properties.get(contentProperties.name); + } else if (contentProperties instanceof Dict) { + optionalContent = contentProperties; + } else { + throw new FormatError("Optional content properties malformed."); + } + const optionalContentType = optionalContent.get("Type")?.name; + if (optionalContentType === "OCG") { + return { + type: optionalContentType, + id: optionalContent.objId + }; + } else if (optionalContentType === "OCMD") { + const expression = optionalContent.get("VE"); + if (Array.isArray(expression)) { + const result = []; + this._parseVisibilityExpression(expression, 0, result); + if (result.length > 0) { + return { + type: "OCMD", + expression: result + }; + } + } + const optionalContentGroups = optionalContent.get("OCGs"); + if (Array.isArray(optionalContentGroups) || optionalContentGroups instanceof Dict) { + const groupIds = []; + if (Array.isArray(optionalContentGroups)) { + for (const ocg of optionalContentGroups) { + groupIds.push(ocg.toString()); + } + } else { + groupIds.push(optionalContentGroups.objId); + } + return { + type: optionalContentType, + ids: groupIds, + policy: optionalContent.get("P") instanceof Name ? optionalContent.get("P").name : null, + expression: null + }; + } else if (optionalContentGroups instanceof Ref) { + return { + type: optionalContentType, + id: optionalContentGroups.toString() + }; + } + } + return null; + } + getOperatorList({ + stream, + task, + resources, + operatorList, + initialState = null, + fallbackFontDict = null + }) { + resources ||= Dict.empty; + initialState ||= new EvalState(); + if (!operatorList) { + throw new Error('getOperatorList: missing "operatorList" parameter'); + } + const self = this; + const xref = this.xref; + let parsingText = false; + const localImageCache = new LocalImageCache(); + const localColorSpaceCache = new LocalColorSpaceCache(); + const localGStateCache = new LocalGStateCache(); + const localTilingPatternCache = new LocalTilingPatternCache(); + const localShadingPatternCache = new Map(); + const xobjs = resources.get("XObject") || Dict.empty; + const patterns = resources.get("Pattern") || Dict.empty; + const stateManager = new StateManager(initialState); + const preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager); + const timeSlotManager = new TimeSlotManager(); + function closePendingRestoreOPS(argument) { + for (let i = 0, ii = preprocessor.savedStatesDepth; i < ii; i++) { + operatorList.addOp(OPS.restore, []); + } + } + return new Promise(function promiseBody(resolve, reject) { + const next = function (promise) { + Promise.all([promise, operatorList.ready]).then(function () { + try { + promiseBody(resolve, reject); + } catch (ex) { + reject(ex); + } + }, reject); + }; + task.ensureNotTerminated(); + timeSlotManager.reset(); + const operation = {}; + let stop, i, ii, cs, name, isValidName; + while (!(stop = timeSlotManager.check())) { + operation.args = null; + if (!preprocessor.read(operation)) { + break; + } + let args = operation.args; + let fn = operation.fn; + switch (fn | 0) { + case OPS.paintXObject: + isValidName = args[0] instanceof Name; + name = args[0].name; + if (isValidName) { + const localImage = localImageCache.getByName(name); + if (localImage) { + addLocallyCachedImageOps(operatorList, localImage); + args = null; + continue; + } + } + next(new Promise(function (resolveXObject, rejectXObject) { + if (!isValidName) { + throw new FormatError("XObject must be referred to by name."); + } + let xobj = xobjs.getRaw(name); + if (xobj instanceof Ref) { + const localImage = localImageCache.getByRef(xobj) || self._regionalImageCache.getByRef(xobj); + if (localImage) { + addLocallyCachedImageOps(operatorList, localImage); + resolveXObject(); + return; + } + const globalImage = self.globalImageCache.getData(xobj, self.pageIndex); + if (globalImage) { + operatorList.addDependency(globalImage.objId); + operatorList.addImageOps(globalImage.fn, globalImage.args, globalImage.optionalContent, globalImage.hasMask); + resolveXObject(); + return; + } + xobj = xref.fetch(xobj); + } + if (!(xobj instanceof BaseStream)) { + throw new FormatError("XObject should be a stream"); + } + const type = xobj.dict.get("Subtype"); + if (!(type instanceof Name)) { + throw new FormatError("XObject should have a Name subtype"); + } + if (type.name === "Form") { + stateManager.save(); + self.buildFormXObject(resources, xobj, null, operatorList, task, stateManager.state.clone(), localColorSpaceCache).then(function () { + stateManager.restore(); + resolveXObject(); + }, rejectXObject); + return; + } else if (type.name === "Image") { + self.buildPaintImageXObject({ + resources, + image: xobj, + operatorList, + cacheKey: name, + localImageCache, + localColorSpaceCache + }).then(resolveXObject, rejectXObject); + return; + } else if (type.name === "PS") { + info("Ignored XObject subtype PS"); + } else { + throw new FormatError(`Unhandled XObject subtype ${type.name}`); + } + resolveXObject(); + }).catch(function (reason) { + if (reason instanceof AbortException) { + return; + } + if (self.options.ignoreErrors) { + warn(`getOperatorList - ignoring XObject: "${reason}".`); + return; + } + throw reason; + })); + return; + case OPS.setFont: + const fontSize = args[1]; + next(self.handleSetFont(resources, args, null, operatorList, task, stateManager.state, fallbackFontDict).then(function (loadedName) { + operatorList.addDependency(loadedName); + operatorList.addOp(OPS.setFont, [loadedName, fontSize]); + })); + return; + case OPS.beginText: + parsingText = true; + break; + case OPS.endText: + parsingText = false; + break; + case OPS.endInlineImage: + const cacheKey = args[0].cacheKey; + if (cacheKey) { + const localImage = localImageCache.getByName(cacheKey); + if (localImage) { + addLocallyCachedImageOps(operatorList, localImage); + args = null; + continue; + } + } + next(self.buildPaintImageXObject({ + resources, + image: args[0], + isInline: true, + operatorList, + cacheKey, + localImageCache, + localColorSpaceCache + })); + return; + case OPS.showText: + if (!stateManager.state.font) { + self.ensureStateFont(stateManager.state); + continue; + } + args[0] = self.handleText(args[0], stateManager.state); + break; + case OPS.showSpacedText: + if (!stateManager.state.font) { + self.ensureStateFont(stateManager.state); + continue; + } + const combinedGlyphs = [], + state = stateManager.state; + for (const arrItem of args[0]) { + if (typeof arrItem === "string") { + combinedGlyphs.push(...self.handleText(arrItem, state)); + } else if (typeof arrItem === "number") { + combinedGlyphs.push(arrItem); + } + } + args[0] = combinedGlyphs; + fn = OPS.showText; + break; + case OPS.nextLineShowText: + if (!stateManager.state.font) { + self.ensureStateFont(stateManager.state); + continue; + } + operatorList.addOp(OPS.nextLine); + args[0] = self.handleText(args[0], stateManager.state); + fn = OPS.showText; + break; + case OPS.nextLineSetSpacingShowText: + if (!stateManager.state.font) { + self.ensureStateFont(stateManager.state); + continue; + } + operatorList.addOp(OPS.nextLine); + operatorList.addOp(OPS.setWordSpacing, [args.shift()]); + operatorList.addOp(OPS.setCharSpacing, [args.shift()]); + args[0] = self.handleText(args[0], stateManager.state); + fn = OPS.showText; + break; + case OPS.setTextRenderingMode: + stateManager.state.textRenderingMode = args[0]; + break; + case OPS.setFillColorSpace: + { + const cachedColorSpace = ColorSpace.getCached(args[0], xref, localColorSpaceCache); + if (cachedColorSpace) { + stateManager.state.fillColorSpace = cachedColorSpace; + continue; + } + next(self.parseColorSpace({ + cs: args[0], + resources, + localColorSpaceCache + }).then(function (colorSpace) { + stateManager.state.fillColorSpace = colorSpace || ColorSpace.singletons.gray; + })); + return; + } + case OPS.setStrokeColorSpace: + { + const cachedColorSpace = ColorSpace.getCached(args[0], xref, localColorSpaceCache); + if (cachedColorSpace) { + stateManager.state.strokeColorSpace = cachedColorSpace; + continue; + } + next(self.parseColorSpace({ + cs: args[0], + resources, + localColorSpaceCache + }).then(function (colorSpace) { + stateManager.state.strokeColorSpace = colorSpace || ColorSpace.singletons.gray; + })); + return; + } + case OPS.setFillColor: + cs = stateManager.state.fillColorSpace; + args = cs.getRgb(args, 0); + fn = OPS.setFillRGBColor; + break; + case OPS.setStrokeColor: + cs = stateManager.state.strokeColorSpace; + args = cs.getRgb(args, 0); + fn = OPS.setStrokeRGBColor; + break; + case OPS.setFillGray: + stateManager.state.fillColorSpace = ColorSpace.singletons.gray; + args = ColorSpace.singletons.gray.getRgb(args, 0); + fn = OPS.setFillRGBColor; + break; + case OPS.setStrokeGray: + stateManager.state.strokeColorSpace = ColorSpace.singletons.gray; + args = ColorSpace.singletons.gray.getRgb(args, 0); + fn = OPS.setStrokeRGBColor; + break; + case OPS.setFillCMYKColor: + stateManager.state.fillColorSpace = ColorSpace.singletons.cmyk; + args = ColorSpace.singletons.cmyk.getRgb(args, 0); + fn = OPS.setFillRGBColor; + break; + case OPS.setStrokeCMYKColor: + stateManager.state.strokeColorSpace = ColorSpace.singletons.cmyk; + args = ColorSpace.singletons.cmyk.getRgb(args, 0); + fn = OPS.setStrokeRGBColor; + break; + case OPS.setFillRGBColor: + stateManager.state.fillColorSpace = ColorSpace.singletons.rgb; + args = ColorSpace.singletons.rgb.getRgb(args, 0); + break; + case OPS.setStrokeRGBColor: + stateManager.state.strokeColorSpace = ColorSpace.singletons.rgb; + args = ColorSpace.singletons.rgb.getRgb(args, 0); + break; + case OPS.setFillColorN: + cs = stateManager.state.patternFillColorSpace; + if (!cs) { + if (isNumberArray(args, null)) { + args = ColorSpace.singletons.gray.getRgb(args, 0); + fn = OPS.setFillRGBColor; + break; + } + args = []; + fn = OPS.setFillTransparent; + break; + } + if (cs.name === "Pattern") { + next(self.handleColorN(operatorList, OPS.setFillColorN, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache, localShadingPatternCache)); + return; + } + args = cs.getRgb(args, 0); + fn = OPS.setFillRGBColor; + break; + case OPS.setStrokeColorN: + cs = stateManager.state.patternStrokeColorSpace; + if (!cs) { + if (isNumberArray(args, null)) { + args = ColorSpace.singletons.gray.getRgb(args, 0); + fn = OPS.setStrokeRGBColor; + break; + } + args = []; + fn = OPS.setStrokeTransparent; + break; + } + if (cs.name === "Pattern") { + next(self.handleColorN(operatorList, OPS.setStrokeColorN, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache, localShadingPatternCache)); + return; + } + args = cs.getRgb(args, 0); + fn = OPS.setStrokeRGBColor; + break; + case OPS.shadingFill: + let shading; + try { + const shadingRes = resources.get("Shading"); + if (!shadingRes) { + throw new FormatError("No shading resource found"); + } + shading = shadingRes.get(args[0].name); + if (!shading) { + throw new FormatError("No shading object found"); + } + } catch (reason) { + if (reason instanceof AbortException) { + continue; + } + if (self.options.ignoreErrors) { + warn(`getOperatorList - ignoring Shading: "${reason}".`); + continue; + } + throw reason; + } + const patternId = self.parseShading({ + shading, + resources, + localColorSpaceCache, + localShadingPatternCache + }); + if (!patternId) { + continue; + } + args = [patternId]; + fn = OPS.shadingFill; + break; + case OPS.setGState: + isValidName = args[0] instanceof Name; + name = args[0].name; + if (isValidName) { + const localGStateObj = localGStateCache.getByName(name); + if (localGStateObj) { + if (localGStateObj.length > 0) { + operatorList.addOp(OPS.setGState, [localGStateObj]); + } + args = null; + continue; + } + } + next(new Promise(function (resolveGState, rejectGState) { + if (!isValidName) { + throw new FormatError("GState must be referred to by name."); + } + const extGState = resources.get("ExtGState"); + if (!(extGState instanceof Dict)) { + throw new FormatError("ExtGState should be a dictionary."); + } + const gState = extGState.get(name); + if (!(gState instanceof Dict)) { + throw new FormatError("GState should be a dictionary."); + } + self.setGState({ + resources, + gState, + operatorList, + cacheKey: name, + task, + stateManager, + localGStateCache, + localColorSpaceCache + }).then(resolveGState, rejectGState); + }).catch(function (reason) { + if (reason instanceof AbortException) { + return; + } + if (self.options.ignoreErrors) { + warn(`getOperatorList - ignoring ExtGState: "${reason}".`); + return; + } + throw reason; + })); + return; + case OPS.moveTo: + case OPS.lineTo: + case OPS.curveTo: + case OPS.curveTo2: + case OPS.curveTo3: + case OPS.closePath: + case OPS.rectangle: + self.buildPath(operatorList, fn, args, parsingText); + continue; + case OPS.markPoint: + case OPS.markPointProps: + case OPS.beginCompat: + case OPS.endCompat: + continue; + case OPS.beginMarkedContentProps: + if (!(args[0] instanceof Name)) { + warn(`Expected name for beginMarkedContentProps arg0=${args[0]}`); + operatorList.addOp(OPS.beginMarkedContentProps, ["OC", null]); + continue; + } + if (args[0].name === "OC") { + next(self.parseMarkedContentProps(args[1], resources).then(data => { + operatorList.addOp(OPS.beginMarkedContentProps, ["OC", data]); + }).catch(reason => { + if (reason instanceof AbortException) { + return; + } + if (self.options.ignoreErrors) { + warn(`getOperatorList - ignoring beginMarkedContentProps: "${reason}".`); + operatorList.addOp(OPS.beginMarkedContentProps, ["OC", null]); + return; + } + throw reason; + })); + return; + } + args = [args[0].name, args[1] instanceof Dict ? args[1].get("MCID") : null]; + break; + case OPS.beginMarkedContent: + case OPS.endMarkedContent: + default: + if (args !== null) { + for (i = 0, ii = args.length; i < ii; i++) { + if (args[i] instanceof Dict) { + break; + } + } + if (i < ii) { + warn("getOperatorList - ignoring operator: " + fn); + continue; + } + } + } + operatorList.addOp(fn, args); + } + if (stop) { + next(deferred); + return; + } + closePendingRestoreOPS(); + resolve(); + }).catch(reason => { + if (reason instanceof AbortException) { + return; + } + if (this.options.ignoreErrors) { + warn(`getOperatorList - ignoring errors during "${task.name}" ` + `task: "${reason}".`); + closePendingRestoreOPS(); + return; + } + throw reason; + }); + } + getTextContent({ + stream, + task, + resources, + stateManager = null, + includeMarkedContent = false, + sink, + seenStyles = new Set(), + viewBox, + lang = null, + markedContentData = null, + disableNormalization = false, + keepWhiteSpace = false + }) { + resources ||= Dict.empty; + stateManager ||= new StateManager(new TextState()); + if (includeMarkedContent) { + markedContentData ||= { + level: 0 + }; + } + const textContent = { + items: [], + styles: Object.create(null), + lang + }; + const textContentItem = { + initialized: false, + str: [], + totalWidth: 0, + totalHeight: 0, + width: 0, + height: 0, + vertical: false, + prevTransform: null, + textAdvanceScale: 0, + spaceInFlowMin: 0, + spaceInFlowMax: 0, + trackingSpaceMin: Infinity, + negativeSpaceMax: -Infinity, + notASpace: -Infinity, + transform: null, + fontName: null, + hasEOL: false + }; + const twoLastChars = [" ", " "]; + let twoLastCharsPos = 0; + function saveLastChar(char) { + const nextPos = (twoLastCharsPos + 1) % 2; + const ret = twoLastChars[twoLastCharsPos] !== " " && twoLastChars[nextPos] === " "; + twoLastChars[twoLastCharsPos] = char; + twoLastCharsPos = nextPos; + return !keepWhiteSpace && ret; + } + function shouldAddWhitepsace() { + return !keepWhiteSpace && twoLastChars[twoLastCharsPos] !== " " && twoLastChars[(twoLastCharsPos + 1) % 2] === " "; + } + function resetLastChars() { + twoLastChars[0] = twoLastChars[1] = " "; + twoLastCharsPos = 0; + } + const TRACKING_SPACE_FACTOR = 0.102; + const NOT_A_SPACE_FACTOR = 0.03; + const NEGATIVE_SPACE_FACTOR = -0.2; + const SPACE_IN_FLOW_MIN_FACTOR = 0.102; + const SPACE_IN_FLOW_MAX_FACTOR = 0.6; + const VERTICAL_SHIFT_RATIO = 0.25; + const self = this; + const xref = this.xref; + const showSpacedTextBuffer = []; + let xobjs = null; + const emptyXObjectCache = new LocalImageCache(); + const emptyGStateCache = new LocalGStateCache(); + const preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager); + let textState; + function pushWhitespace({ + width = 0, + height = 0, + transform = textContentItem.prevTransform, + fontName = textContentItem.fontName + }) { + textContent.items.push({ + str: " ", + dir: "ltr", + width, + height, + transform, + fontName, + hasEOL: false + }); + } + function getCurrentTextTransform() { + const font = textState.font; + const tsm = [textState.fontSize * textState.textHScale, 0, 0, textState.fontSize, 0, textState.textRise]; + if (font.isType3Font && (textState.fontSize <= 1 || font.isCharBBox) && !isArrayEqual(textState.fontMatrix, FONT_IDENTITY_MATRIX)) { + const glyphHeight = font.bbox[3] - font.bbox[1]; + if (glyphHeight > 0) { + tsm[3] *= glyphHeight * textState.fontMatrix[3]; + } + } + return Util.transform(textState.ctm, Util.transform(textState.textMatrix, tsm)); + } + function ensureTextContentItem() { + if (textContentItem.initialized) { + return textContentItem; + } + const { + font, + loadedName + } = textState; + if (!seenStyles.has(loadedName)) { + seenStyles.add(loadedName); + textContent.styles[loadedName] = { + fontFamily: font.fallbackName, + ascent: font.ascent, + descent: font.descent, + vertical: font.vertical + }; + if (self.options.fontExtraProperties && font.systemFontInfo) { + const style = textContent.styles[loadedName]; + style.fontSubstitution = font.systemFontInfo.css; + style.fontSubstitutionLoadedName = font.systemFontInfo.loadedName; + } + } + textContentItem.fontName = loadedName; + const trm = textContentItem.transform = getCurrentTextTransform(); + if (!font.vertical) { + textContentItem.width = textContentItem.totalWidth = 0; + textContentItem.height = textContentItem.totalHeight = Math.hypot(trm[2], trm[3]); + textContentItem.vertical = false; + } else { + textContentItem.width = textContentItem.totalWidth = Math.hypot(trm[0], trm[1]); + textContentItem.height = textContentItem.totalHeight = 0; + textContentItem.vertical = true; + } + const scaleLineX = Math.hypot(textState.textLineMatrix[0], textState.textLineMatrix[1]); + const scaleCtmX = Math.hypot(textState.ctm[0], textState.ctm[1]); + textContentItem.textAdvanceScale = scaleCtmX * scaleLineX; + const { + fontSize + } = textState; + textContentItem.trackingSpaceMin = fontSize * TRACKING_SPACE_FACTOR; + textContentItem.notASpace = fontSize * NOT_A_SPACE_FACTOR; + textContentItem.negativeSpaceMax = fontSize * NEGATIVE_SPACE_FACTOR; + textContentItem.spaceInFlowMin = fontSize * SPACE_IN_FLOW_MIN_FACTOR; + textContentItem.spaceInFlowMax = fontSize * SPACE_IN_FLOW_MAX_FACTOR; + textContentItem.hasEOL = false; + textContentItem.initialized = true; + return textContentItem; + } + function updateAdvanceScale() { + if (!textContentItem.initialized) { + return; + } + const scaleLineX = Math.hypot(textState.textLineMatrix[0], textState.textLineMatrix[1]); + const scaleCtmX = Math.hypot(textState.ctm[0], textState.ctm[1]); + const scaleFactor = scaleCtmX * scaleLineX; + if (scaleFactor === textContentItem.textAdvanceScale) { + return; + } + if (!textContentItem.vertical) { + textContentItem.totalWidth += textContentItem.width * textContentItem.textAdvanceScale; + textContentItem.width = 0; + } else { + textContentItem.totalHeight += textContentItem.height * textContentItem.textAdvanceScale; + textContentItem.height = 0; + } + textContentItem.textAdvanceScale = scaleFactor; + } + function runBidiTransform(textChunk) { + let text = textChunk.str.join(""); + if (!disableNormalization) { + text = normalizeUnicode(text); + } + const bidiResult = bidi(text, -1, textChunk.vertical); + return { + str: bidiResult.str, + dir: bidiResult.dir, + width: Math.abs(textChunk.totalWidth), + height: Math.abs(textChunk.totalHeight), + transform: textChunk.transform, + fontName: textChunk.fontName, + hasEOL: textChunk.hasEOL + }; + } + async function handleSetFont(fontName, fontRef) { + const translated = await self.loadFont(fontName, fontRef, resources); + if (translated.font.isType3Font) { + try { + await translated.loadType3Data(self, resources, task); + } catch {} + } + textState.loadedName = translated.loadedName; + textState.font = translated.font; + textState.fontMatrix = translated.font.fontMatrix || FONT_IDENTITY_MATRIX; + } + function applyInverseRotation(x, y, matrix) { + const scale = Math.hypot(matrix[0], matrix[1]); + return [(matrix[0] * x + matrix[1] * y) / scale, (matrix[2] * x + matrix[3] * y) / scale]; + } + function compareWithLastPosition(glyphWidth) { + const currentTransform = getCurrentTextTransform(); + let posX = currentTransform[4]; + let posY = currentTransform[5]; + if (textState.font?.vertical) { + if (posX < viewBox[0] || posX > viewBox[2] || posY + glyphWidth < viewBox[1] || posY > viewBox[3]) { + return false; + } + } else if (posX + glyphWidth < viewBox[0] || posX > viewBox[2] || posY < viewBox[1] || posY > viewBox[3]) { + return false; + } + if (!textState.font || !textContentItem.prevTransform) { + return true; + } + let lastPosX = textContentItem.prevTransform[4]; + let lastPosY = textContentItem.prevTransform[5]; + if (lastPosX === posX && lastPosY === posY) { + return true; + } + let rotate = -1; + if (currentTransform[0] && currentTransform[1] === 0 && currentTransform[2] === 0) { + rotate = currentTransform[0] > 0 ? 0 : 180; + } else if (currentTransform[1] && currentTransform[0] === 0 && currentTransform[3] === 0) { + rotate = currentTransform[1] > 0 ? 90 : 270; + } + switch (rotate) { + case 0: + break; + case 90: + [posX, posY] = [posY, posX]; + [lastPosX, lastPosY] = [lastPosY, lastPosX]; + break; + case 180: + [posX, posY, lastPosX, lastPosY] = [-posX, -posY, -lastPosX, -lastPosY]; + break; + case 270: + [posX, posY] = [-posY, -posX]; + [lastPosX, lastPosY] = [-lastPosY, -lastPosX]; + break; + default: + [posX, posY] = applyInverseRotation(posX, posY, currentTransform); + [lastPosX, lastPosY] = applyInverseRotation(lastPosX, lastPosY, textContentItem.prevTransform); + } + if (textState.font.vertical) { + const advanceY = (lastPosY - posY) / textContentItem.textAdvanceScale; + const advanceX = posX - lastPosX; + const textOrientation = Math.sign(textContentItem.height); + if (advanceY < textOrientation * textContentItem.negativeSpaceMax) { + if (Math.abs(advanceX) > 0.5 * textContentItem.width) { + appendEOL(); + return true; + } + resetLastChars(); + flushTextContentItem(); + return true; + } + if (Math.abs(advanceX) > textContentItem.width) { + appendEOL(); + return true; + } + if (advanceY <= textOrientation * textContentItem.notASpace) { + resetLastChars(); + } + if (advanceY <= textOrientation * textContentItem.trackingSpaceMin) { + if (shouldAddWhitepsace()) { + resetLastChars(); + flushTextContentItem(); + pushWhitespace({ + height: Math.abs(advanceY) + }); + } else { + textContentItem.height += advanceY; + } + } else if (!addFakeSpaces(advanceY, textContentItem.prevTransform, textOrientation)) { + if (textContentItem.str.length === 0) { + resetLastChars(); + pushWhitespace({ + height: Math.abs(advanceY) + }); + } else { + textContentItem.height += advanceY; + } + } + if (Math.abs(advanceX) > textContentItem.width * VERTICAL_SHIFT_RATIO) { + flushTextContentItem(); + } + return true; + } + const advanceX = (posX - lastPosX) / textContentItem.textAdvanceScale; + const advanceY = posY - lastPosY; + const textOrientation = Math.sign(textContentItem.width); + if (advanceX < textOrientation * textContentItem.negativeSpaceMax) { + if (Math.abs(advanceY) > 0.5 * textContentItem.height) { + appendEOL(); + return true; + } + resetLastChars(); + flushTextContentItem(); + return true; + } + if (Math.abs(advanceY) > textContentItem.height) { + appendEOL(); + return true; + } + if (advanceX <= textOrientation * textContentItem.notASpace) { + resetLastChars(); + } + if (advanceX <= textOrientation * textContentItem.trackingSpaceMin) { + if (shouldAddWhitepsace()) { + resetLastChars(); + flushTextContentItem(); + pushWhitespace({ + width: Math.abs(advanceX) + }); + } else { + textContentItem.width += advanceX; + } + } else if (!addFakeSpaces(advanceX, textContentItem.prevTransform, textOrientation)) { + if (textContentItem.str.length === 0) { + resetLastChars(); + pushWhitespace({ + width: Math.abs(advanceX) + }); + } else { + textContentItem.width += advanceX; + } + } + if (Math.abs(advanceY) > textContentItem.height * VERTICAL_SHIFT_RATIO) { + flushTextContentItem(); + } + return true; + } + function buildTextContentItem({ + chars, + extraSpacing + }) { + const font = textState.font; + if (!chars) { + const charSpacing = textState.charSpacing + extraSpacing; + if (charSpacing) { + if (!font.vertical) { + textState.translateTextMatrix(charSpacing * textState.textHScale, 0); + } else { + textState.translateTextMatrix(0, -charSpacing); + } + } + if (keepWhiteSpace) { + compareWithLastPosition(0); + } + return; + } + const glyphs = font.charsToGlyphs(chars); + const scale = textState.fontMatrix[0] * textState.fontSize; + for (let i = 0, ii = glyphs.length; i < ii; i++) { + const glyph = glyphs[i]; + const { + category + } = glyph; + if (category.isInvisibleFormatMark) { + continue; + } + let charSpacing = textState.charSpacing + (i + 1 === ii ? extraSpacing : 0); + let glyphWidth = glyph.width; + if (font.vertical) { + glyphWidth = glyph.vmetric ? glyph.vmetric[0] : -glyphWidth; + } + let scaledDim = glyphWidth * scale; + if (!keepWhiteSpace && category.isWhitespace) { + if (!font.vertical) { + charSpacing += scaledDim + textState.wordSpacing; + textState.translateTextMatrix(charSpacing * textState.textHScale, 0); + } else { + charSpacing += -scaledDim + textState.wordSpacing; + textState.translateTextMatrix(0, -charSpacing); + } + saveLastChar(" "); + continue; + } + if (!category.isZeroWidthDiacritic && !compareWithLastPosition(scaledDim)) { + if (!font.vertical) { + textState.translateTextMatrix(scaledDim * textState.textHScale, 0); + } else { + textState.translateTextMatrix(0, scaledDim); + } + continue; + } + const textChunk = ensureTextContentItem(); + if (category.isZeroWidthDiacritic) { + scaledDim = 0; + } + if (!font.vertical) { + scaledDim *= textState.textHScale; + textState.translateTextMatrix(scaledDim, 0); + textChunk.width += scaledDim; + } else { + textState.translateTextMatrix(0, scaledDim); + scaledDim = Math.abs(scaledDim); + textChunk.height += scaledDim; + } + if (scaledDim) { + textChunk.prevTransform = getCurrentTextTransform(); + } + const glyphUnicode = glyph.unicode; + if (saveLastChar(glyphUnicode)) { + textChunk.str.push(" "); + } + textChunk.str.push(glyphUnicode); + if (charSpacing) { + if (!font.vertical) { + textState.translateTextMatrix(charSpacing * textState.textHScale, 0); + } else { + textState.translateTextMatrix(0, -charSpacing); + } + } + } + } + function appendEOL() { + resetLastChars(); + if (textContentItem.initialized) { + textContentItem.hasEOL = true; + flushTextContentItem(); + } else { + textContent.items.push({ + str: "", + dir: "ltr", + width: 0, + height: 0, + transform: getCurrentTextTransform(), + fontName: textState.loadedName, + hasEOL: true + }); + } + } + function addFakeSpaces(width, transf, textOrientation) { + if (textOrientation * textContentItem.spaceInFlowMin <= width && width <= textOrientation * textContentItem.spaceInFlowMax) { + if (textContentItem.initialized) { + resetLastChars(); + textContentItem.str.push(" "); + } + return false; + } + const fontName = textContentItem.fontName; + let height = 0; + if (textContentItem.vertical) { + height = width; + width = 0; + } + flushTextContentItem(); + resetLastChars(); + pushWhitespace({ + width: Math.abs(width), + height: Math.abs(height), + transform: transf || getCurrentTextTransform(), + fontName + }); + return true; + } + function flushTextContentItem() { + if (!textContentItem.initialized || !textContentItem.str) { + return; + } + if (!textContentItem.vertical) { + textContentItem.totalWidth += textContentItem.width * textContentItem.textAdvanceScale; + } else { + textContentItem.totalHeight += textContentItem.height * textContentItem.textAdvanceScale; + } + textContent.items.push(runBidiTransform(textContentItem)); + textContentItem.initialized = false; + textContentItem.str.length = 0; + } + function enqueueChunk(batch = false) { + const length = textContent.items.length; + if (length === 0) { + return; + } + if (batch && length < TEXT_CHUNK_BATCH_SIZE) { + return; + } + sink.enqueue(textContent, length); + textContent.items = []; + textContent.styles = Object.create(null); + } + const timeSlotManager = new TimeSlotManager(); + return new Promise(function promiseBody(resolve, reject) { + const next = function (promise) { + enqueueChunk(true); + Promise.all([promise, sink.ready]).then(function () { + try { + promiseBody(resolve, reject); + } catch (ex) { + reject(ex); + } + }, reject); + }; + task.ensureNotTerminated(); + timeSlotManager.reset(); + const operation = {}; + let stop, + name, + isValidName, + args = []; + while (!(stop = timeSlotManager.check())) { + args.length = 0; + operation.args = args; + if (!preprocessor.read(operation)) { + break; + } + const previousState = textState; + textState = stateManager.state; + const fn = operation.fn; + args = operation.args; + switch (fn | 0) { + case OPS.setFont: + const fontNameArg = args[0].name, + fontSizeArg = args[1]; + if (textState.font && fontNameArg === textState.fontName && fontSizeArg === textState.fontSize) { + break; + } + flushTextContentItem(); + textState.fontName = fontNameArg; + textState.fontSize = fontSizeArg; + next(handleSetFont(fontNameArg, null)); + return; + case OPS.setTextRise: + textState.textRise = args[0]; + break; + case OPS.setHScale: + textState.textHScale = args[0] / 100; + break; + case OPS.setLeading: + textState.leading = args[0]; + break; + case OPS.moveText: + textState.translateTextLineMatrix(args[0], args[1]); + textState.textMatrix = textState.textLineMatrix.slice(); + break; + case OPS.setLeadingMoveText: + textState.leading = -args[1]; + textState.translateTextLineMatrix(args[0], args[1]); + textState.textMatrix = textState.textLineMatrix.slice(); + break; + case OPS.nextLine: + textState.carriageReturn(); + break; + case OPS.setTextMatrix: + textState.setTextMatrix(args[0], args[1], args[2], args[3], args[4], args[5]); + textState.setTextLineMatrix(args[0], args[1], args[2], args[3], args[4], args[5]); + updateAdvanceScale(); + break; + case OPS.setCharSpacing: + textState.charSpacing = args[0]; + break; + case OPS.setWordSpacing: + textState.wordSpacing = args[0]; + break; + case OPS.beginText: + textState.textMatrix = IDENTITY_MATRIX.slice(); + textState.textLineMatrix = IDENTITY_MATRIX.slice(); + break; + case OPS.showSpacedText: + if (!stateManager.state.font) { + self.ensureStateFont(stateManager.state); + continue; + } + const spaceFactor = (textState.font.vertical ? 1 : -1) * textState.fontSize / 1000; + const elements = args[0]; + for (let i = 0, ii = elements.length; i < ii; i++) { + const item = elements[i]; + if (typeof item === "string") { + showSpacedTextBuffer.push(item); + } else if (typeof item === "number" && item !== 0) { + const str = showSpacedTextBuffer.join(""); + showSpacedTextBuffer.length = 0; + buildTextContentItem({ + chars: str, + extraSpacing: item * spaceFactor + }); + } + } + if (showSpacedTextBuffer.length > 0) { + const str = showSpacedTextBuffer.join(""); + showSpacedTextBuffer.length = 0; + buildTextContentItem({ + chars: str, + extraSpacing: 0 + }); + } + break; + case OPS.showText: + if (!stateManager.state.font) { + self.ensureStateFont(stateManager.state); + continue; + } + buildTextContentItem({ + chars: args[0], + extraSpacing: 0 + }); + break; + case OPS.nextLineShowText: + if (!stateManager.state.font) { + self.ensureStateFont(stateManager.state); + continue; + } + textState.carriageReturn(); + buildTextContentItem({ + chars: args[0], + extraSpacing: 0 + }); + break; + case OPS.nextLineSetSpacingShowText: + if (!stateManager.state.font) { + self.ensureStateFont(stateManager.state); + continue; + } + textState.wordSpacing = args[0]; + textState.charSpacing = args[1]; + textState.carriageReturn(); + buildTextContentItem({ + chars: args[2], + extraSpacing: 0 + }); + break; + case OPS.paintXObject: + flushTextContentItem(); + xobjs ??= resources.get("XObject") || Dict.empty; + isValidName = args[0] instanceof Name; + name = args[0].name; + if (isValidName && emptyXObjectCache.getByName(name)) { + break; + } + next(new Promise(function (resolveXObject, rejectXObject) { + if (!isValidName) { + throw new FormatError("XObject must be referred to by name."); + } + let xobj = xobjs.getRaw(name); + if (xobj instanceof Ref) { + if (emptyXObjectCache.getByRef(xobj)) { + resolveXObject(); + return; + } + const globalImage = self.globalImageCache.getData(xobj, self.pageIndex); + if (globalImage) { + resolveXObject(); + return; + } + xobj = xref.fetch(xobj); + } + if (!(xobj instanceof BaseStream)) { + throw new FormatError("XObject should be a stream"); + } + const type = xobj.dict.get("Subtype"); + if (!(type instanceof Name)) { + throw new FormatError("XObject should have a Name subtype"); + } + if (type.name !== "Form") { + emptyXObjectCache.set(name, xobj.dict.objId, true); + resolveXObject(); + return; + } + const currentState = stateManager.state.clone(); + const xObjStateManager = new StateManager(currentState); + const matrix = lookupMatrix(xobj.dict.getArray("Matrix"), null); + if (matrix) { + xObjStateManager.transform(matrix); + } + enqueueChunk(); + const sinkWrapper = { + enqueueInvoked: false, + enqueue(chunk, size) { + this.enqueueInvoked = true; + sink.enqueue(chunk, size); + }, + get desiredSize() { + return sink.desiredSize; + }, + get ready() { + return sink.ready; + } + }; + self.getTextContent({ + stream: xobj, + task, + resources: xobj.dict.get("Resources") || resources, + stateManager: xObjStateManager, + includeMarkedContent, + sink: sinkWrapper, + seenStyles, + viewBox, + lang, + markedContentData, + disableNormalization, + keepWhiteSpace + }).then(function () { + if (!sinkWrapper.enqueueInvoked) { + emptyXObjectCache.set(name, xobj.dict.objId, true); + } + resolveXObject(); + }, rejectXObject); + }).catch(function (reason) { + if (reason instanceof AbortException) { + return; + } + if (self.options.ignoreErrors) { + warn(`getTextContent - ignoring XObject: "${reason}".`); + return; + } + throw reason; + })); + return; + case OPS.setGState: + isValidName = args[0] instanceof Name; + name = args[0].name; + if (isValidName && emptyGStateCache.getByName(name)) { + break; + } + next(new Promise(function (resolveGState, rejectGState) { + if (!isValidName) { + throw new FormatError("GState must be referred to by name."); + } + const extGState = resources.get("ExtGState"); + if (!(extGState instanceof Dict)) { + throw new FormatError("ExtGState should be a dictionary."); + } + const gState = extGState.get(name); + if (!(gState instanceof Dict)) { + throw new FormatError("GState should be a dictionary."); + } + const gStateFont = gState.get("Font"); + if (!gStateFont) { + emptyGStateCache.set(name, gState.objId, true); + resolveGState(); + return; + } + flushTextContentItem(); + textState.fontName = null; + textState.fontSize = gStateFont[1]; + handleSetFont(null, gStateFont[0]).then(resolveGState, rejectGState); + }).catch(function (reason) { + if (reason instanceof AbortException) { + return; + } + if (self.options.ignoreErrors) { + warn(`getTextContent - ignoring ExtGState: "${reason}".`); + return; + } + throw reason; + })); + return; + case OPS.beginMarkedContent: + flushTextContentItem(); + if (includeMarkedContent) { + markedContentData.level++; + textContent.items.push({ + type: "beginMarkedContent", + tag: args[0] instanceof Name ? args[0].name : null + }); + } + break; + case OPS.beginMarkedContentProps: + flushTextContentItem(); + if (includeMarkedContent) { + markedContentData.level++; + let mcid = null; + if (args[1] instanceof Dict) { + mcid = args[1].get("MCID"); + } + textContent.items.push({ + type: "beginMarkedContentProps", + id: Number.isInteger(mcid) ? `${self.idFactory.getPageObjId()}_mc${mcid}` : null, + tag: args[0] instanceof Name ? args[0].name : null + }); + } + break; + case OPS.endMarkedContent: + flushTextContentItem(); + if (includeMarkedContent) { + if (markedContentData.level === 0) { + break; + } + markedContentData.level--; + textContent.items.push({ + type: "endMarkedContent" + }); + } + break; + case OPS.restore: + if (previousState && (previousState.font !== textState.font || previousState.fontSize !== textState.fontSize || previousState.fontName !== textState.fontName)) { + flushTextContentItem(); + } + break; + } + if (textContent.items.length >= sink.desiredSize) { + stop = true; + break; + } + } + if (stop) { + next(deferred); + return; + } + flushTextContentItem(); + enqueueChunk(); + resolve(); + }).catch(reason => { + if (reason instanceof AbortException) { + return; + } + if (this.options.ignoreErrors) { + warn(`getTextContent - ignoring errors during "${task.name}" ` + `task: "${reason}".`); + flushTextContentItem(); + enqueueChunk(); + return; + } + throw reason; + }); + } + async extractDataStructures(dict, properties) { + const xref = this.xref; + let cidToGidBytes; + const toUnicodePromise = this.readToUnicode(properties.toUnicode); + if (properties.composite) { + const cidSystemInfo = dict.get("CIDSystemInfo"); + if (cidSystemInfo instanceof Dict) { + properties.cidSystemInfo = { + registry: stringToPDFString(cidSystemInfo.get("Registry")), + ordering: stringToPDFString(cidSystemInfo.get("Ordering")), + supplement: cidSystemInfo.get("Supplement") + }; + } + try { + const cidToGidMap = dict.get("CIDToGIDMap"); + if (cidToGidMap instanceof BaseStream) { + cidToGidBytes = cidToGidMap.getBytes(); + } + } catch (ex) { + if (!this.options.ignoreErrors) { + throw ex; + } + warn(`extractDataStructures - ignoring CIDToGIDMap data: "${ex}".`); + } + } + const differences = []; + let baseEncodingName = null; + let encoding; + if (dict.has("Encoding")) { + encoding = dict.get("Encoding"); + if (encoding instanceof Dict) { + baseEncodingName = encoding.get("BaseEncoding"); + baseEncodingName = baseEncodingName instanceof Name ? baseEncodingName.name : null; + if (encoding.has("Differences")) { + const diffEncoding = encoding.get("Differences"); + let index = 0; + for (const entry of diffEncoding) { + const data = xref.fetchIfRef(entry); + if (typeof data === "number") { + index = data; + } else if (data instanceof Name) { + differences[index++] = data.name; + } else { + throw new FormatError(`Invalid entry in 'Differences' array: ${data}`); + } + } + } + } else if (encoding instanceof Name) { + baseEncodingName = encoding.name; + } else { + const msg = "Encoding is not a Name nor a Dict"; + if (!this.options.ignoreErrors) { + throw new FormatError(msg); + } + warn(msg); + } + if (baseEncodingName !== "MacRomanEncoding" && baseEncodingName !== "MacExpertEncoding" && baseEncodingName !== "WinAnsiEncoding") { + baseEncodingName = null; + } + } + const nonEmbeddedFont = !properties.file || properties.isInternalFont, + isSymbolsFontName = getSymbolsFonts()[properties.name]; + if (baseEncodingName && nonEmbeddedFont && isSymbolsFontName) { + baseEncodingName = null; + } + if (baseEncodingName) { + properties.defaultEncoding = getEncoding(baseEncodingName); + } else { + const isSymbolicFont = !!(properties.flags & FontFlags.Symbolic); + const isNonsymbolicFont = !!(properties.flags & FontFlags.Nonsymbolic); + encoding = StandardEncoding; + if (properties.type === "TrueType" && !isNonsymbolicFont) { + encoding = WinAnsiEncoding; + } + if (isSymbolicFont || isSymbolsFontName) { + encoding = MacRomanEncoding; + if (nonEmbeddedFont) { + if (/Symbol/i.test(properties.name)) { + encoding = SymbolSetEncoding; + } else if (/Dingbats/i.test(properties.name)) { + encoding = ZapfDingbatsEncoding; + } else if (/Wingdings/i.test(properties.name)) { + encoding = WinAnsiEncoding; + } + } + } + properties.defaultEncoding = encoding; + } + properties.differences = differences; + properties.baseEncodingName = baseEncodingName; + properties.hasEncoding = !!baseEncodingName || differences.length > 0; + properties.dict = dict; + properties.toUnicode = await toUnicodePromise; + const builtToUnicode = await this.buildToUnicode(properties); + properties.toUnicode = builtToUnicode; + if (cidToGidBytes) { + properties.cidToGidMap = this.readCidToGidMap(cidToGidBytes, builtToUnicode); + } + return properties; + } + _simpleFontToUnicode(properties, forceGlyphs = false) { + assert(!properties.composite, "Must be a simple font."); + const toUnicode = []; + const encoding = properties.defaultEncoding.slice(); + const baseEncodingName = properties.baseEncodingName; + const differences = properties.differences; + for (const charcode in differences) { + const glyphName = differences[charcode]; + if (glyphName === ".notdef") { + continue; + } + encoding[charcode] = glyphName; + } + const glyphsUnicodeMap = getGlyphsUnicode(); + for (const charcode in encoding) { + let glyphName = encoding[charcode]; + if (glyphName === "") { + continue; + } + let unicode = glyphsUnicodeMap[glyphName]; + if (unicode !== undefined) { + toUnicode[charcode] = String.fromCharCode(unicode); + continue; + } + let code = 0; + switch (glyphName[0]) { + case "G": + if (glyphName.length === 3) { + code = parseInt(glyphName.substring(1), 16); + } + break; + case "g": + if (glyphName.length === 5) { + code = parseInt(glyphName.substring(1), 16); + } + break; + case "C": + case "c": + if (glyphName.length >= 3 && glyphName.length <= 4) { + const codeStr = glyphName.substring(1); + if (forceGlyphs) { + code = parseInt(codeStr, 16); + break; + } + code = +codeStr; + if (Number.isNaN(code) && Number.isInteger(parseInt(codeStr, 16))) { + return this._simpleFontToUnicode(properties, true); + } + } + break; + case "u": + unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap); + if (unicode !== -1) { + code = unicode; + } + break; + default: + switch (glyphName) { + case "f_h": + case "f_t": + case "T_h": + toUnicode[charcode] = glyphName.replaceAll("_", ""); + continue; + } + break; + } + if (code > 0 && code <= 0x10ffff && Number.isInteger(code)) { + if (baseEncodingName && code === +charcode) { + const baseEncoding = getEncoding(baseEncodingName); + if (baseEncoding && (glyphName = baseEncoding[charcode])) { + toUnicode[charcode] = String.fromCharCode(glyphsUnicodeMap[glyphName]); + continue; + } + } + toUnicode[charcode] = String.fromCodePoint(code); + } + } + return toUnicode; + } + async buildToUnicode(properties) { + properties.hasIncludedToUnicodeMap = properties.toUnicode?.length > 0; + if (properties.hasIncludedToUnicodeMap) { + if (!properties.composite && properties.hasEncoding) { + properties.fallbackToUnicode = this._simpleFontToUnicode(properties); + } + return properties.toUnicode; + } + if (!properties.composite) { + return new ToUnicodeMap(this._simpleFontToUnicode(properties)); + } + if (properties.composite && (properties.cMap.builtInCMap && !(properties.cMap instanceof IdentityCMap) || properties.cidSystemInfo?.registry === "Adobe" && (properties.cidSystemInfo.ordering === "GB1" || properties.cidSystemInfo.ordering === "CNS1" || properties.cidSystemInfo.ordering === "Japan1" || properties.cidSystemInfo.ordering === "Korea1"))) { + const { + registry, + ordering + } = properties.cidSystemInfo; + const ucs2CMapName = Name.get(`${registry}-${ordering}-UCS2`); + const ucs2CMap = await CMapFactory.create({ + encoding: ucs2CMapName, + fetchBuiltInCMap: this._fetchBuiltInCMapBound, + useCMap: null + }); + const toUnicode = [], + buf = []; + properties.cMap.forEach(function (charcode, cid) { + if (cid > 0xffff) { + throw new FormatError("Max size of CID is 65,535"); + } + const ucs2 = ucs2CMap.lookup(cid); + if (ucs2) { + buf.length = 0; + for (let i = 0, ii = ucs2.length; i < ii; i += 2) { + buf.push((ucs2.charCodeAt(i) << 8) + ucs2.charCodeAt(i + 1)); + } + toUnicode[charcode] = String.fromCharCode(...buf); + } + }); + return new ToUnicodeMap(toUnicode); + } + return new IdentityToUnicodeMap(properties.firstChar, properties.lastChar); + } + async readToUnicode(cmapObj) { + if (!cmapObj) { + return null; + } + if (cmapObj instanceof Name) { + const cmap = await CMapFactory.create({ + encoding: cmapObj, + fetchBuiltInCMap: this._fetchBuiltInCMapBound, + useCMap: null + }); + if (cmap instanceof IdentityCMap) { + return new IdentityToUnicodeMap(0, 0xffff); + } + return new ToUnicodeMap(cmap.getMap()); + } + if (cmapObj instanceof BaseStream) { + try { + const cmap = await CMapFactory.create({ + encoding: cmapObj, + fetchBuiltInCMap: this._fetchBuiltInCMapBound, + useCMap: null + }); + if (cmap instanceof IdentityCMap) { + return new IdentityToUnicodeMap(0, 0xffff); + } + const map = new Array(cmap.length); + cmap.forEach(function (charCode, token) { + if (typeof token === "number") { + map[charCode] = String.fromCodePoint(token); + return; + } + if (token.length % 2 !== 0) { + token = "\u0000" + token; + } + const str = []; + for (let k = 0; k < token.length; k += 2) { + const w1 = token.charCodeAt(k) << 8 | token.charCodeAt(k + 1); + if ((w1 & 0xf800) !== 0xd800) { + str.push(w1); + continue; + } + k += 2; + const w2 = token.charCodeAt(k) << 8 | token.charCodeAt(k + 1); + str.push(((w1 & 0x3ff) << 10) + (w2 & 0x3ff) + 0x10000); + } + map[charCode] = String.fromCodePoint(...str); + }); + return new ToUnicodeMap(map); + } catch (reason) { + if (reason instanceof AbortException) { + return null; + } + if (this.options.ignoreErrors) { + warn(`readToUnicode - ignoring ToUnicode data: "${reason}".`); + return null; + } + throw reason; + } + } + return null; + } + readCidToGidMap(glyphsData, toUnicode) { + const result = []; + for (let j = 0, jj = glyphsData.length; j < jj; j++) { + const glyphID = glyphsData[j++] << 8 | glyphsData[j]; + const code = j >> 1; + if (glyphID === 0 && !toUnicode.has(code)) { + continue; + } + result[code] = glyphID; + } + return result; + } + extractWidths(dict, descriptor, properties) { + const xref = this.xref; + let glyphsWidths = []; + let defaultWidth = 0; + const glyphsVMetrics = []; + let defaultVMetrics; + if (properties.composite) { + const dw = dict.get("DW"); + defaultWidth = typeof dw === "number" ? Math.ceil(dw) : 1000; + const widths = dict.get("W"); + if (Array.isArray(widths)) { + for (let i = 0, ii = widths.length; i < ii; i++) { + let start = xref.fetchIfRef(widths[i++]); + if (!Number.isInteger(start)) { + break; + } + const code = xref.fetchIfRef(widths[i]); + if (Array.isArray(code)) { + for (const c of code) { + const width = xref.fetchIfRef(c); + if (typeof width === "number") { + glyphsWidths[start] = width; + } + start++; + } + } else if (Number.isInteger(code)) { + const width = xref.fetchIfRef(widths[++i]); + if (typeof width !== "number") { + continue; + } + for (let j = start; j <= code; j++) { + glyphsWidths[j] = width; + } + } else { + break; + } + } + } + if (properties.vertical) { + const dw2 = dict.getArray("DW2"); + let vmetrics = isNumberArray(dw2, 2) ? dw2 : [880, -1000]; + defaultVMetrics = [vmetrics[1], defaultWidth * 0.5, vmetrics[0]]; + vmetrics = dict.get("W2"); + if (Array.isArray(vmetrics)) { + for (let i = 0, ii = vmetrics.length; i < ii; i++) { + let start = xref.fetchIfRef(vmetrics[i++]); + if (!Number.isInteger(start)) { + break; + } + const code = xref.fetchIfRef(vmetrics[i]); + if (Array.isArray(code)) { + for (let j = 0, jj = code.length; j < jj; j++) { + const vmetric = [xref.fetchIfRef(code[j++]), xref.fetchIfRef(code[j++]), xref.fetchIfRef(code[j])]; + if (isNumberArray(vmetric, null)) { + glyphsVMetrics[start] = vmetric; + } + start++; + } + } else if (Number.isInteger(code)) { + const vmetric = [xref.fetchIfRef(vmetrics[++i]), xref.fetchIfRef(vmetrics[++i]), xref.fetchIfRef(vmetrics[++i])]; + if (!isNumberArray(vmetric, null)) { + continue; + } + for (let j = start; j <= code; j++) { + glyphsVMetrics[j] = vmetric; + } + } else { + break; + } + } + } + } + } else { + const widths = dict.get("Widths"); + if (Array.isArray(widths)) { + let j = properties.firstChar; + for (const w of widths) { + const width = xref.fetchIfRef(w); + if (typeof width === "number") { + glyphsWidths[j] = width; + } + j++; + } + const missingWidth = descriptor.get("MissingWidth"); + defaultWidth = typeof missingWidth === "number" ? missingWidth : 0; + } else { + const baseFontName = dict.get("BaseFont"); + if (baseFontName instanceof Name) { + const metrics = this.getBaseFontMetrics(baseFontName.name); + glyphsWidths = this.buildCharCodeToWidth(metrics.widths, properties); + defaultWidth = metrics.defaultWidth; + } + } + } + let isMonospace = true; + let firstWidth = defaultWidth; + for (const glyph in glyphsWidths) { + const glyphWidth = glyphsWidths[glyph]; + if (!glyphWidth) { + continue; + } + if (!firstWidth) { + firstWidth = glyphWidth; + continue; + } + if (firstWidth !== glyphWidth) { + isMonospace = false; + break; + } + } + if (isMonospace) { + properties.flags |= FontFlags.FixedPitch; + } else { + properties.flags &= ~FontFlags.FixedPitch; + } + properties.defaultWidth = defaultWidth; + properties.widths = glyphsWidths; + properties.defaultVMetrics = defaultVMetrics; + properties.vmetrics = glyphsVMetrics; + } + isSerifFont(baseFontName) { + const fontNameWoStyle = baseFontName.split("-", 1)[0]; + return fontNameWoStyle in getSerifFonts() || /serif/gi.test(fontNameWoStyle); + } + getBaseFontMetrics(name) { + let defaultWidth = 0; + let widths = Object.create(null); + let monospace = false; + const stdFontMap = getStdFontMap(); + let lookupName = stdFontMap[name] || name; + const Metrics = getMetrics(); + if (!(lookupName in Metrics)) { + lookupName = this.isSerifFont(name) ? "Times-Roman" : "Helvetica"; + } + const glyphWidths = Metrics[lookupName]; + if (typeof glyphWidths === "number") { + defaultWidth = glyphWidths; + monospace = true; + } else { + widths = glyphWidths(); + } + return { + defaultWidth, + monospace, + widths + }; + } + buildCharCodeToWidth(widthsByGlyphName, properties) { + const widths = Object.create(null); + const differences = properties.differences; + const encoding = properties.defaultEncoding; + for (let charCode = 0; charCode < 256; charCode++) { + if (charCode in differences && widthsByGlyphName[differences[charCode]]) { + widths[charCode] = widthsByGlyphName[differences[charCode]]; + continue; + } + if (charCode in encoding && widthsByGlyphName[encoding[charCode]]) { + widths[charCode] = widthsByGlyphName[encoding[charCode]]; + continue; + } + } + return widths; + } + preEvaluateFont(dict) { + const baseDict = dict; + let type = dict.get("Subtype"); + if (!(type instanceof Name)) { + throw new FormatError("invalid font Subtype"); + } + let composite = false; + let hash; + if (type.name === "Type0") { + const df = dict.get("DescendantFonts"); + if (!df) { + throw new FormatError("Descendant fonts are not specified"); + } + dict = Array.isArray(df) ? this.xref.fetchIfRef(df[0]) : df; + if (!(dict instanceof Dict)) { + throw new FormatError("Descendant font is not a dictionary."); + } + type = dict.get("Subtype"); + if (!(type instanceof Name)) { + throw new FormatError("invalid font Subtype"); + } + composite = true; + } + let firstChar = dict.get("FirstChar"); + if (!Number.isInteger(firstChar)) { + firstChar = 0; + } + let lastChar = dict.get("LastChar"); + if (!Number.isInteger(lastChar)) { + lastChar = composite ? 0xffff : 0xff; + } + const descriptor = dict.get("FontDescriptor"); + const toUnicode = dict.get("ToUnicode") || baseDict.get("ToUnicode"); + if (descriptor) { + hash = new MurmurHash3_64(); + const encoding = baseDict.getRaw("Encoding"); + if (encoding instanceof Name) { + hash.update(encoding.name); + } else if (encoding instanceof Ref) { + hash.update(encoding.toString()); + } else if (encoding instanceof Dict) { + for (const entry of encoding.getRawValues()) { + if (entry instanceof Name) { + hash.update(entry.name); + } else if (entry instanceof Ref) { + hash.update(entry.toString()); + } else if (Array.isArray(entry)) { + const diffLength = entry.length, + diffBuf = new Array(diffLength); + for (let j = 0; j < diffLength; j++) { + const diffEntry = entry[j]; + if (diffEntry instanceof Name) { + diffBuf[j] = diffEntry.name; + } else if (typeof diffEntry === "number" || diffEntry instanceof Ref) { + diffBuf[j] = diffEntry.toString(); + } + } + hash.update(diffBuf.join()); + } + } + } + hash.update(`${firstChar}-${lastChar}`); + if (toUnicode instanceof BaseStream) { + const stream = toUnicode.str || toUnicode; + const uint8array = stream.buffer ? new Uint8Array(stream.buffer.buffer, 0, stream.bufferLength) : new Uint8Array(stream.bytes.buffer, stream.start, stream.end - stream.start); + hash.update(uint8array); + } else if (toUnicode instanceof Name) { + hash.update(toUnicode.name); + } + const widths = dict.get("Widths") || baseDict.get("Widths"); + if (Array.isArray(widths)) { + const widthsBuf = []; + for (const entry of widths) { + if (typeof entry === "number" || entry instanceof Ref) { + widthsBuf.push(entry.toString()); + } + } + hash.update(widthsBuf.join()); + } + if (composite) { + hash.update("compositeFont"); + const compositeWidths = dict.get("W") || baseDict.get("W"); + if (Array.isArray(compositeWidths)) { + const widthsBuf = []; + for (const entry of compositeWidths) { + if (typeof entry === "number" || entry instanceof Ref) { + widthsBuf.push(entry.toString()); + } else if (Array.isArray(entry)) { + const subWidthsBuf = []; + for (const element of entry) { + if (typeof element === "number" || element instanceof Ref) { + subWidthsBuf.push(element.toString()); + } + } + widthsBuf.push(`[${subWidthsBuf.join()}]`); + } + } + hash.update(widthsBuf.join()); + } + const cidToGidMap = dict.getRaw("CIDToGIDMap") || baseDict.getRaw("CIDToGIDMap"); + if (cidToGidMap instanceof Name) { + hash.update(cidToGidMap.name); + } else if (cidToGidMap instanceof Ref) { + hash.update(cidToGidMap.toString()); + } else if (cidToGidMap instanceof BaseStream) { + hash.update(cidToGidMap.peekBytes()); + } + } + } + return { + descriptor, + dict, + baseDict, + composite, + type: type.name, + firstChar, + lastChar, + toUnicode, + hash: hash ? hash.hexdigest() : "" + }; + } + async translateFont({ + descriptor, + dict, + baseDict, + composite, + type, + firstChar, + lastChar, + toUnicode, + cssFontInfo + }) { + const isType3Font = type === "Type3"; + if (!descriptor) { + if (isType3Font) { + const bbox = lookupNormalRect(dict.getArray("FontBBox"), [0, 0, 0, 0]); + descriptor = new Dict(null); + descriptor.set("FontName", Name.get(type)); + descriptor.set("FontBBox", bbox); + } else { + let baseFontName = dict.get("BaseFont"); + if (!(baseFontName instanceof Name)) { + throw new FormatError("Base font is not specified"); + } + baseFontName = baseFontName.name.replaceAll(/[,_]/g, "-"); + const metrics = this.getBaseFontMetrics(baseFontName); + const fontNameWoStyle = baseFontName.split("-", 1)[0]; + const flags = (this.isSerifFont(fontNameWoStyle) ? FontFlags.Serif : 0) | (metrics.monospace ? FontFlags.FixedPitch : 0) | (getSymbolsFonts()[fontNameWoStyle] ? FontFlags.Symbolic : FontFlags.Nonsymbolic); + const properties = { + type, + name: baseFontName, + loadedName: baseDict.loadedName, + systemFontInfo: null, + widths: metrics.widths, + defaultWidth: metrics.defaultWidth, + isSimulatedFlags: true, + flags, + firstChar, + lastChar, + toUnicode, + xHeight: 0, + capHeight: 0, + italicAngle: 0, + isType3Font + }; + const widths = dict.get("Widths"); + const standardFontName = getStandardFontName(baseFontName); + let file = null; + if (standardFontName) { + file = await this.fetchStandardFontData(standardFontName); + properties.isInternalFont = !!file; + } + if (!properties.isInternalFont && this.options.useSystemFonts) { + properties.systemFontInfo = getFontSubstitution(this.systemFontCache, this.idFactory, this.options.standardFontDataUrl, baseFontName, standardFontName, type); + } + const newProperties = await this.extractDataStructures(dict, properties); + if (Array.isArray(widths)) { + const glyphWidths = []; + let j = firstChar; + for (const w of widths) { + const width = this.xref.fetchIfRef(w); + if (typeof width === "number") { + glyphWidths[j] = width; + } + j++; + } + newProperties.widths = glyphWidths; + } else { + newProperties.widths = this.buildCharCodeToWidth(metrics.widths, newProperties); + } + return new Font(baseFontName, file, newProperties); + } + } + let fontName = descriptor.get("FontName"); + let baseFont = dict.get("BaseFont"); + if (typeof fontName === "string") { + fontName = Name.get(fontName); + } + if (typeof baseFont === "string") { + baseFont = Name.get(baseFont); + } + const fontNameStr = fontName?.name; + const baseFontStr = baseFont?.name; + if (!isType3Font && fontNameStr !== baseFontStr) { + info(`The FontDescriptor's FontName is "${fontNameStr}" but ` + `should be the same as the Font's BaseFont "${baseFontStr}".`); + if (fontNameStr && baseFontStr && (baseFontStr.startsWith(fontNameStr) || !isKnownFontName(fontNameStr) && isKnownFontName(baseFontStr))) { + fontName = null; + } + } + fontName ||= baseFont; + if (!(fontName instanceof Name)) { + throw new FormatError("invalid font name"); + } + let fontFile, subtype, length1, length2, length3; + try { + fontFile = descriptor.get("FontFile", "FontFile2", "FontFile3"); + if (fontFile) { + if (!(fontFile instanceof BaseStream)) { + throw new FormatError("FontFile should be a stream"); + } else if (fontFile.isEmpty) { + throw new FormatError("FontFile is empty"); + } + } + } catch (ex) { + if (!this.options.ignoreErrors) { + throw ex; + } + warn(`translateFont - fetching "${fontName.name}" font file: "${ex}".`); + fontFile = null; + } + let isInternalFont = false; + let glyphScaleFactors = null; + let systemFontInfo = null; + if (fontFile) { + if (fontFile.dict) { + const subtypeEntry = fontFile.dict.get("Subtype"); + if (subtypeEntry instanceof Name) { + subtype = subtypeEntry.name; + } + length1 = fontFile.dict.get("Length1"); + length2 = fontFile.dict.get("Length2"); + length3 = fontFile.dict.get("Length3"); + } + } else if (cssFontInfo) { + const standardFontName = getXfaFontName(fontName.name); + if (standardFontName) { + cssFontInfo.fontFamily = `${cssFontInfo.fontFamily}-PdfJS-XFA`; + cssFontInfo.metrics = standardFontName.metrics || null; + glyphScaleFactors = standardFontName.factors || null; + fontFile = await this.fetchStandardFontData(standardFontName.name); + isInternalFont = !!fontFile; + baseDict = dict = getXfaFontDict(fontName.name); + composite = true; + } + } else if (!isType3Font) { + const standardFontName = getStandardFontName(fontName.name); + if (standardFontName) { + fontFile = await this.fetchStandardFontData(standardFontName); + isInternalFont = !!fontFile; + } + if (!isInternalFont && this.options.useSystemFonts) { + systemFontInfo = getFontSubstitution(this.systemFontCache, this.idFactory, this.options.standardFontDataUrl, fontName.name, standardFontName, type); + } + } + const fontMatrix = lookupMatrix(dict.getArray("FontMatrix"), FONT_IDENTITY_MATRIX); + const bbox = lookupNormalRect(descriptor.getArray("FontBBox") || dict.getArray("FontBBox"), undefined); + let ascent = descriptor.get("Ascent"); + if (typeof ascent !== "number") { + ascent = undefined; + } + let descent = descriptor.get("Descent"); + if (typeof descent !== "number") { + descent = undefined; + } + let xHeight = descriptor.get("XHeight"); + if (typeof xHeight !== "number") { + xHeight = 0; + } + let capHeight = descriptor.get("CapHeight"); + if (typeof capHeight !== "number") { + capHeight = 0; + } + let flags = descriptor.get("Flags"); + if (!Number.isInteger(flags)) { + flags = 0; + } + let italicAngle = descriptor.get("ItalicAngle"); + if (typeof italicAngle !== "number") { + italicAngle = 0; + } + const properties = { + type, + name: fontName.name, + subtype, + file: fontFile, + length1, + length2, + length3, + isInternalFont, + loadedName: baseDict.loadedName, + composite, + fixedPitch: false, + fontMatrix, + firstChar, + lastChar, + toUnicode, + bbox, + ascent, + descent, + xHeight, + capHeight, + flags, + italicAngle, + isType3Font, + cssFontInfo, + scaleFactors: glyphScaleFactors, + systemFontInfo + }; + if (composite) { + const cidEncoding = baseDict.get("Encoding"); + if (cidEncoding instanceof Name) { + properties.cidEncoding = cidEncoding.name; + } + const cMap = await CMapFactory.create({ + encoding: cidEncoding, + fetchBuiltInCMap: this._fetchBuiltInCMapBound, + useCMap: null + }); + properties.cMap = cMap; + properties.vertical = properties.cMap.vertical; + } + const newProperties = await this.extractDataStructures(dict, properties); + this.extractWidths(dict, descriptor, newProperties); + return new Font(fontName.name, fontFile, newProperties); + } + static buildFontPaths(font, glyphs, handler, evaluatorOptions) { + function buildPath(fontChar) { + const glyphName = `${font.loadedName}_path_${fontChar}`; + try { + if (font.renderer.hasBuiltPath(fontChar)) { + return; + } + handler.send("commonobj", [glyphName, "FontPath", font.renderer.getPathJs(fontChar)]); + } catch (reason) { + if (evaluatorOptions.ignoreErrors) { + warn(`buildFontPaths - ignoring ${glyphName} glyph: "${reason}".`); + return; + } + throw reason; + } + } + for (const glyph of glyphs) { + buildPath(glyph.fontChar); + const accent = glyph.accent; + if (accent?.fontChar) { + buildPath(accent.fontChar); + } + } + } + static get fallbackFontDict() { + const dict = new Dict(); + dict.set("BaseFont", Name.get("Helvetica")); + dict.set("Type", Name.get("FallbackType")); + dict.set("Subtype", Name.get("FallbackType")); + dict.set("Encoding", Name.get("WinAnsiEncoding")); + return shadow(this, "fallbackFontDict", dict); + } +} +class TranslatedFont { + constructor({ + loadedName, + font, + dict, + evaluatorOptions + }) { + this.loadedName = loadedName; + this.font = font; + this.dict = dict; + this._evaluatorOptions = evaluatorOptions || DefaultPartialEvaluatorOptions; + this.type3Loaded = null; + this.type3Dependencies = font.isType3Font ? new Set() : null; + this.sent = false; + } + send(handler) { + if (this.sent) { + return; + } + this.sent = true; + handler.send("commonobj", [this.loadedName, "Font", this.font.exportData(this._evaluatorOptions.fontExtraProperties)]); + } + fallback(handler) { + if (!this.font.data) { + return; + } + this.font.disableFontFace = true; + PartialEvaluator.buildFontPaths(this.font, this.font.glyphCacheValues, handler, this._evaluatorOptions); + } + loadType3Data(evaluator, resources, task) { + if (this.type3Loaded) { + return this.type3Loaded; + } + if (!this.font.isType3Font) { + throw new Error("Must be a Type3 font."); + } + const type3Evaluator = evaluator.clone({ + ignoreErrors: false + }); + const type3FontRefs = new RefSet(evaluator.type3FontRefs); + if (this.dict.objId && !type3FontRefs.has(this.dict.objId)) { + type3FontRefs.put(this.dict.objId); + } + type3Evaluator.type3FontRefs = type3FontRefs; + const translatedFont = this.font, + type3Dependencies = this.type3Dependencies; + let loadCharProcsPromise = Promise.resolve(); + const charProcs = this.dict.get("CharProcs"); + const fontResources = this.dict.get("Resources") || resources; + const charProcOperatorList = Object.create(null); + const fontBBox = Util.normalizeRect(translatedFont.bbox || [0, 0, 0, 0]), + width = fontBBox[2] - fontBBox[0], + height = fontBBox[3] - fontBBox[1]; + const fontBBoxSize = Math.hypot(width, height); + for (const key of charProcs.getKeys()) { + loadCharProcsPromise = loadCharProcsPromise.then(() => { + const glyphStream = charProcs.get(key); + const operatorList = new OperatorList(); + return type3Evaluator.getOperatorList({ + stream: glyphStream, + task, + resources: fontResources, + operatorList + }).then(() => { + if (operatorList.fnArray[0] === OPS.setCharWidthAndBounds) { + this._removeType3ColorOperators(operatorList, fontBBoxSize); + } + charProcOperatorList[key] = operatorList.getIR(); + for (const dependency of operatorList.dependencies) { + type3Dependencies.add(dependency); + } + }).catch(function (reason) { + warn(`Type3 font resource "${key}" is not available.`); + const dummyOperatorList = new OperatorList(); + charProcOperatorList[key] = dummyOperatorList.getIR(); + }); + }); + } + this.type3Loaded = loadCharProcsPromise.then(() => { + translatedFont.charProcOperatorList = charProcOperatorList; + if (this._bbox) { + translatedFont.isCharBBox = true; + translatedFont.bbox = this._bbox; + } + }); + return this.type3Loaded; + } + _removeType3ColorOperators(operatorList, fontBBoxSize = NaN) { + const charBBox = Util.normalizeRect(operatorList.argsArray[0].slice(2)), + width = charBBox[2] - charBBox[0], + height = charBBox[3] - charBBox[1]; + const charBBoxSize = Math.hypot(width, height); + if (width === 0 || height === 0) { + operatorList.fnArray.splice(0, 1); + operatorList.argsArray.splice(0, 1); + } else if (fontBBoxSize === 0 || Math.round(charBBoxSize / fontBBoxSize) >= 10) { + if (!this._bbox) { + this._bbox = [Infinity, Infinity, -Infinity, -Infinity]; + } + this._bbox[0] = Math.min(this._bbox[0], charBBox[0]); + this._bbox[1] = Math.min(this._bbox[1], charBBox[1]); + this._bbox[2] = Math.max(this._bbox[2], charBBox[2]); + this._bbox[3] = Math.max(this._bbox[3], charBBox[3]); + } + let i = 0, + ii = operatorList.length; + while (i < ii) { + switch (operatorList.fnArray[i]) { + case OPS.setCharWidthAndBounds: + break; + case OPS.setStrokeColorSpace: + case OPS.setFillColorSpace: + case OPS.setStrokeColor: + case OPS.setStrokeColorN: + case OPS.setFillColor: + case OPS.setFillColorN: + case OPS.setStrokeGray: + case OPS.setFillGray: + case OPS.setStrokeRGBColor: + case OPS.setFillRGBColor: + case OPS.setStrokeCMYKColor: + case OPS.setFillCMYKColor: + case OPS.shadingFill: + case OPS.setRenderingIntent: + operatorList.fnArray.splice(i, 1); + operatorList.argsArray.splice(i, 1); + ii--; + continue; + case OPS.setGState: + const [gStateObj] = operatorList.argsArray[i]; + let j = 0, + jj = gStateObj.length; + while (j < jj) { + const [gStateKey] = gStateObj[j]; + switch (gStateKey) { + case "TR": + case "TR2": + case "HT": + case "BG": + case "BG2": + case "UCR": + case "UCR2": + gStateObj.splice(j, 1); + jj--; + continue; + } + j++; + } + break; + } + i++; + } + } +} +class StateManager { + constructor(initialState = new EvalState()) { + this.state = initialState; + this.stateStack = []; + } + save() { + const old = this.state; + this.stateStack.push(this.state); + this.state = old.clone(); + } + restore() { + const prev = this.stateStack.pop(); + if (prev) { + this.state = prev; + } + } + transform(args) { + this.state.ctm = Util.transform(this.state.ctm, args); + } +} +class TextState { + constructor() { + this.ctm = new Float32Array(IDENTITY_MATRIX); + this.fontName = null; + this.fontSize = 0; + this.loadedName = null; + this.font = null; + this.fontMatrix = FONT_IDENTITY_MATRIX; + this.textMatrix = IDENTITY_MATRIX.slice(); + this.textLineMatrix = IDENTITY_MATRIX.slice(); + this.charSpacing = 0; + this.wordSpacing = 0; + this.leading = 0; + this.textHScale = 1; + this.textRise = 0; + } + setTextMatrix(a, b, c, d, e, f) { + const m = this.textMatrix; + m[0] = a; + m[1] = b; + m[2] = c; + m[3] = d; + m[4] = e; + m[5] = f; + } + setTextLineMatrix(a, b, c, d, e, f) { + const m = this.textLineMatrix; + m[0] = a; + m[1] = b; + m[2] = c; + m[3] = d; + m[4] = e; + m[5] = f; + } + translateTextMatrix(x, y) { + const m = this.textMatrix; + m[4] = m[0] * x + m[2] * y + m[4]; + m[5] = m[1] * x + m[3] * y + m[5]; + } + translateTextLineMatrix(x, y) { + const m = this.textLineMatrix; + m[4] = m[0] * x + m[2] * y + m[4]; + m[5] = m[1] * x + m[3] * y + m[5]; + } + carriageReturn() { + this.translateTextLineMatrix(0, -this.leading); + this.textMatrix = this.textLineMatrix.slice(); + } + clone() { + const clone = Object.create(this); + clone.textMatrix = this.textMatrix.slice(); + clone.textLineMatrix = this.textLineMatrix.slice(); + clone.fontMatrix = this.fontMatrix.slice(); + return clone; + } +} +class EvalState { + constructor() { + this.ctm = new Float32Array(IDENTITY_MATRIX); + this.font = null; + this.textRenderingMode = TextRenderingMode.FILL; + this._fillColorSpace = ColorSpace.singletons.gray; + this._strokeColorSpace = ColorSpace.singletons.gray; + this.patternFillColorSpace = null; + this.patternStrokeColorSpace = null; + } + get fillColorSpace() { + return this._fillColorSpace; + } + set fillColorSpace(colorSpace) { + this._fillColorSpace = this.patternFillColorSpace = colorSpace; + } + get strokeColorSpace() { + return this._strokeColorSpace; + } + set strokeColorSpace(colorSpace) { + this._strokeColorSpace = this.patternStrokeColorSpace = colorSpace; + } + clone() { + return Object.create(this); + } +} +class EvaluatorPreprocessor { + static get opMap() { + return shadow(this, "opMap", Object.assign(Object.create(null), { + w: { + id: OPS.setLineWidth, + numArgs: 1, + variableArgs: false + }, + J: { + id: OPS.setLineCap, + numArgs: 1, + variableArgs: false + }, + j: { + id: OPS.setLineJoin, + numArgs: 1, + variableArgs: false + }, + M: { + id: OPS.setMiterLimit, + numArgs: 1, + variableArgs: false + }, + d: { + id: OPS.setDash, + numArgs: 2, + variableArgs: false + }, + ri: { + id: OPS.setRenderingIntent, + numArgs: 1, + variableArgs: false + }, + i: { + id: OPS.setFlatness, + numArgs: 1, + variableArgs: false + }, + gs: { + id: OPS.setGState, + numArgs: 1, + variableArgs: false + }, + q: { + id: OPS.save, + numArgs: 0, + variableArgs: false + }, + Q: { + id: OPS.restore, + numArgs: 0, + variableArgs: false + }, + cm: { + id: OPS.transform, + numArgs: 6, + variableArgs: false + }, + m: { + id: OPS.moveTo, + numArgs: 2, + variableArgs: false + }, + l: { + id: OPS.lineTo, + numArgs: 2, + variableArgs: false + }, + c: { + id: OPS.curveTo, + numArgs: 6, + variableArgs: false + }, + v: { + id: OPS.curveTo2, + numArgs: 4, + variableArgs: false + }, + y: { + id: OPS.curveTo3, + numArgs: 4, + variableArgs: false + }, + h: { + id: OPS.closePath, + numArgs: 0, + variableArgs: false + }, + re: { + id: OPS.rectangle, + numArgs: 4, + variableArgs: false + }, + S: { + id: OPS.stroke, + numArgs: 0, + variableArgs: false + }, + s: { + id: OPS.closeStroke, + numArgs: 0, + variableArgs: false + }, + f: { + id: OPS.fill, + numArgs: 0, + variableArgs: false + }, + F: { + id: OPS.fill, + numArgs: 0, + variableArgs: false + }, + "f*": { + id: OPS.eoFill, + numArgs: 0, + variableArgs: false + }, + B: { + id: OPS.fillStroke, + numArgs: 0, + variableArgs: false + }, + "B*": { + id: OPS.eoFillStroke, + numArgs: 0, + variableArgs: false + }, + b: { + id: OPS.closeFillStroke, + numArgs: 0, + variableArgs: false + }, + "b*": { + id: OPS.closeEOFillStroke, + numArgs: 0, + variableArgs: false + }, + n: { + id: OPS.endPath, + numArgs: 0, + variableArgs: false + }, + W: { + id: OPS.clip, + numArgs: 0, + variableArgs: false + }, + "W*": { + id: OPS.eoClip, + numArgs: 0, + variableArgs: false + }, + BT: { + id: OPS.beginText, + numArgs: 0, + variableArgs: false + }, + ET: { + id: OPS.endText, + numArgs: 0, + variableArgs: false + }, + Tc: { + id: OPS.setCharSpacing, + numArgs: 1, + variableArgs: false + }, + Tw: { + id: OPS.setWordSpacing, + numArgs: 1, + variableArgs: false + }, + Tz: { + id: OPS.setHScale, + numArgs: 1, + variableArgs: false + }, + TL: { + id: OPS.setLeading, + numArgs: 1, + variableArgs: false + }, + Tf: { + id: OPS.setFont, + numArgs: 2, + variableArgs: false + }, + Tr: { + id: OPS.setTextRenderingMode, + numArgs: 1, + variableArgs: false + }, + Ts: { + id: OPS.setTextRise, + numArgs: 1, + variableArgs: false + }, + Td: { + id: OPS.moveText, + numArgs: 2, + variableArgs: false + }, + TD: { + id: OPS.setLeadingMoveText, + numArgs: 2, + variableArgs: false + }, + Tm: { + id: OPS.setTextMatrix, + numArgs: 6, + variableArgs: false + }, + "T*": { + id: OPS.nextLine, + numArgs: 0, + variableArgs: false + }, + Tj: { + id: OPS.showText, + numArgs: 1, + variableArgs: false + }, + TJ: { + id: OPS.showSpacedText, + numArgs: 1, + variableArgs: false + }, + "'": { + id: OPS.nextLineShowText, + numArgs: 1, + variableArgs: false + }, + '"': { + id: OPS.nextLineSetSpacingShowText, + numArgs: 3, + variableArgs: false + }, + d0: { + id: OPS.setCharWidth, + numArgs: 2, + variableArgs: false + }, + d1: { + id: OPS.setCharWidthAndBounds, + numArgs: 6, + variableArgs: false + }, + CS: { + id: OPS.setStrokeColorSpace, + numArgs: 1, + variableArgs: false + }, + cs: { + id: OPS.setFillColorSpace, + numArgs: 1, + variableArgs: false + }, + SC: { + id: OPS.setStrokeColor, + numArgs: 4, + variableArgs: true + }, + SCN: { + id: OPS.setStrokeColorN, + numArgs: 33, + variableArgs: true + }, + sc: { + id: OPS.setFillColor, + numArgs: 4, + variableArgs: true + }, + scn: { + id: OPS.setFillColorN, + numArgs: 33, + variableArgs: true + }, + G: { + id: OPS.setStrokeGray, + numArgs: 1, + variableArgs: false + }, + g: { + id: OPS.setFillGray, + numArgs: 1, + variableArgs: false + }, + RG: { + id: OPS.setStrokeRGBColor, + numArgs: 3, + variableArgs: false + }, + rg: { + id: OPS.setFillRGBColor, + numArgs: 3, + variableArgs: false + }, + K: { + id: OPS.setStrokeCMYKColor, + numArgs: 4, + variableArgs: false + }, + k: { + id: OPS.setFillCMYKColor, + numArgs: 4, + variableArgs: false + }, + sh: { + id: OPS.shadingFill, + numArgs: 1, + variableArgs: false + }, + BI: { + id: OPS.beginInlineImage, + numArgs: 0, + variableArgs: false + }, + ID: { + id: OPS.beginImageData, + numArgs: 0, + variableArgs: false + }, + EI: { + id: OPS.endInlineImage, + numArgs: 1, + variableArgs: false + }, + Do: { + id: OPS.paintXObject, + numArgs: 1, + variableArgs: false + }, + MP: { + id: OPS.markPoint, + numArgs: 1, + variableArgs: false + }, + DP: { + id: OPS.markPointProps, + numArgs: 2, + variableArgs: false + }, + BMC: { + id: OPS.beginMarkedContent, + numArgs: 1, + variableArgs: false + }, + BDC: { + id: OPS.beginMarkedContentProps, + numArgs: 2, + variableArgs: false + }, + EMC: { + id: OPS.endMarkedContent, + numArgs: 0, + variableArgs: false + }, + BX: { + id: OPS.beginCompat, + numArgs: 0, + variableArgs: false + }, + EX: { + id: OPS.endCompat, + numArgs: 0, + variableArgs: false + }, + BM: null, + BD: null, + true: null, + fa: null, + fal: null, + fals: null, + false: null, + nu: null, + nul: null, + null: null + })); + } + static MAX_INVALID_PATH_OPS = 10; + constructor(stream, xref, stateManager = new StateManager()) { + this.parser = new Parser({ + lexer: new Lexer(stream, EvaluatorPreprocessor.opMap), + xref + }); + this.stateManager = stateManager; + this.nonProcessedArgs = []; + this._isPathOp = false; + this._numInvalidPathOPS = 0; + } + get savedStatesDepth() { + return this.stateManager.stateStack.length; + } + read(operation) { + let args = operation.args; + while (true) { + const obj = this.parser.getObj(); + if (obj instanceof Cmd) { + const cmd = obj.cmd; + const opSpec = EvaluatorPreprocessor.opMap[cmd]; + if (!opSpec) { + warn(`Unknown command "${cmd}".`); + continue; + } + const fn = opSpec.id; + const numArgs = opSpec.numArgs; + let argsLength = args !== null ? args.length : 0; + if (!this._isPathOp) { + this._numInvalidPathOPS = 0; + } + this._isPathOp = fn >= OPS.moveTo && fn <= OPS.endPath; + if (!opSpec.variableArgs) { + if (argsLength !== numArgs) { + const nonProcessedArgs = this.nonProcessedArgs; + while (argsLength > numArgs) { + nonProcessedArgs.push(args.shift()); + argsLength--; + } + while (argsLength < numArgs && nonProcessedArgs.length !== 0) { + if (args === null) { + args = []; + } + args.unshift(nonProcessedArgs.pop()); + argsLength++; + } + } + if (argsLength < numArgs) { + const partialMsg = `command ${cmd}: expected ${numArgs} args, ` + `but received ${argsLength} args.`; + if (this._isPathOp && ++this._numInvalidPathOPS > EvaluatorPreprocessor.MAX_INVALID_PATH_OPS) { + throw new FormatError(`Invalid ${partialMsg}`); + } + warn(`Skipping ${partialMsg}`); + if (args !== null) { + args.length = 0; + } + continue; + } + } else if (argsLength > numArgs) { + info(`Command ${cmd}: expected [0, ${numArgs}] args, ` + `but received ${argsLength} args.`); + } + this.preprocessCommand(fn, args); + operation.fn = fn; + operation.args = args; + return true; + } + if (obj === EOF) { + return false; + } + if (obj !== null) { + if (args === null) { + args = []; + } + args.push(obj); + if (args.length > 33) { + throw new FormatError("Too many arguments"); + } + } + } + } + preprocessCommand(fn, args) { + switch (fn | 0) { + case OPS.save: + this.stateManager.save(); + break; + case OPS.restore: + this.stateManager.restore(); + break; + case OPS.transform: + this.stateManager.transform(args); + break; + } + } +} + +;// ./src/core/default_appearance.js + + + + + + + + +class DefaultAppearanceEvaluator extends EvaluatorPreprocessor { + constructor(str) { + super(new StringStream(str)); + } + parse() { + const operation = { + fn: 0, + args: [] + }; + const result = { + fontSize: 0, + fontName: "", + fontColor: new Uint8ClampedArray(3) + }; + try { + while (true) { + operation.args.length = 0; + if (!this.read(operation)) { + break; + } + if (this.savedStatesDepth !== 0) { + continue; + } + const { + fn, + args + } = operation; + switch (fn | 0) { + case OPS.setFont: + const [fontName, fontSize] = args; + if (fontName instanceof Name) { + result.fontName = fontName.name; + } + if (typeof fontSize === "number" && fontSize > 0) { + result.fontSize = fontSize; + } + break; + case OPS.setFillRGBColor: + ColorSpace.singletons.rgb.getRgbItem(args, 0, result.fontColor, 0); + break; + case OPS.setFillGray: + ColorSpace.singletons.gray.getRgbItem(args, 0, result.fontColor, 0); + break; + case OPS.setFillCMYKColor: + ColorSpace.singletons.cmyk.getRgbItem(args, 0, result.fontColor, 0); + break; + } + } + } catch (reason) { + warn(`parseDefaultAppearance - ignoring errors: "${reason}".`); + } + return result; + } +} +function parseDefaultAppearance(str) { + return new DefaultAppearanceEvaluator(str).parse(); +} +class AppearanceStreamEvaluator extends EvaluatorPreprocessor { + constructor(stream, evaluatorOptions, xref) { + super(stream); + this.stream = stream; + this.evaluatorOptions = evaluatorOptions; + this.xref = xref; + this.resources = stream.dict?.get("Resources"); + } + parse() { + const operation = { + fn: 0, + args: [] + }; + let result = { + scaleFactor: 1, + fontSize: 0, + fontName: "", + fontColor: new Uint8ClampedArray(3), + fillColorSpace: ColorSpace.singletons.gray + }; + let breakLoop = false; + const stack = []; + try { + while (true) { + operation.args.length = 0; + if (breakLoop || !this.read(operation)) { + break; + } + const { + fn, + args + } = operation; + switch (fn | 0) { + case OPS.save: + stack.push({ + scaleFactor: result.scaleFactor, + fontSize: result.fontSize, + fontName: result.fontName, + fontColor: result.fontColor.slice(), + fillColorSpace: result.fillColorSpace + }); + break; + case OPS.restore: + result = stack.pop() || result; + break; + case OPS.setTextMatrix: + result.scaleFactor *= Math.hypot(args[0], args[1]); + break; + case OPS.setFont: + const [fontName, fontSize] = args; + if (fontName instanceof Name) { + result.fontName = fontName.name; + } + if (typeof fontSize === "number" && fontSize > 0) { + result.fontSize = fontSize * result.scaleFactor; + } + break; + case OPS.setFillColorSpace: + result.fillColorSpace = ColorSpace.parse({ + cs: args[0], + xref: this.xref, + resources: this.resources, + pdfFunctionFactory: this._pdfFunctionFactory, + localColorSpaceCache: this._localColorSpaceCache + }); + break; + case OPS.setFillColor: + const cs = result.fillColorSpace; + cs.getRgbItem(args, 0, result.fontColor, 0); + break; + case OPS.setFillRGBColor: + ColorSpace.singletons.rgb.getRgbItem(args, 0, result.fontColor, 0); + break; + case OPS.setFillGray: + ColorSpace.singletons.gray.getRgbItem(args, 0, result.fontColor, 0); + break; + case OPS.setFillCMYKColor: + ColorSpace.singletons.cmyk.getRgbItem(args, 0, result.fontColor, 0); + break; + case OPS.showText: + case OPS.showSpacedText: + case OPS.nextLineShowText: + case OPS.nextLineSetSpacingShowText: + breakLoop = true; + break; + } + } + } catch (reason) { + warn(`parseAppearanceStream - ignoring errors: "${reason}".`); + } + this.stream.reset(); + delete result.scaleFactor; + delete result.fillColorSpace; + return result; + } + get _localColorSpaceCache() { + return shadow(this, "_localColorSpaceCache", new LocalColorSpaceCache()); + } + get _pdfFunctionFactory() { + const pdfFunctionFactory = new PDFFunctionFactory({ + xref: this.xref, + isEvalSupported: this.evaluatorOptions.isEvalSupported + }); + return shadow(this, "_pdfFunctionFactory", pdfFunctionFactory); + } +} +function parseAppearanceStream(stream, evaluatorOptions, xref) { + return new AppearanceStreamEvaluator(stream, evaluatorOptions, xref).parse(); +} +function getPdfColor(color, isFill) { + if (color[0] === color[1] && color[1] === color[2]) { + const gray = color[0] / 255; + return `${numberToString(gray)} ${isFill ? "g" : "G"}`; + } + return Array.from(color, c => numberToString(c / 255)).join(" ") + ` ${isFill ? "rg" : "RG"}`; +} +function createDefaultAppearance({ + fontSize, + fontName, + fontColor +}) { + return `/${escapePDFName(fontName)} ${fontSize} Tf ${getPdfColor(fontColor, true)}`; +} +class FakeUnicodeFont { + constructor(xref, fontFamily) { + this.xref = xref; + this.widths = null; + this.firstChar = Infinity; + this.lastChar = -Infinity; + this.fontFamily = fontFamily; + const canvas = new OffscreenCanvas(1, 1); + this.ctxMeasure = canvas.getContext("2d", { + willReadFrequently: true + }); + if (!FakeUnicodeFont._fontNameId) { + FakeUnicodeFont._fontNameId = 1; + } + this.fontName = Name.get(`InvalidPDFjsFont_${fontFamily}_${FakeUnicodeFont._fontNameId++}`); + } + get fontDescriptorRef() { + if (!FakeUnicodeFont._fontDescriptorRef) { + const fontDescriptor = new Dict(this.xref); + fontDescriptor.set("Type", Name.get("FontDescriptor")); + fontDescriptor.set("FontName", this.fontName); + fontDescriptor.set("FontFamily", "MyriadPro Regular"); + fontDescriptor.set("FontBBox", [0, 0, 0, 0]); + fontDescriptor.set("FontStretch", Name.get("Normal")); + fontDescriptor.set("FontWeight", 400); + fontDescriptor.set("ItalicAngle", 0); + FakeUnicodeFont._fontDescriptorRef = this.xref.getNewPersistentRef(fontDescriptor); + } + return FakeUnicodeFont._fontDescriptorRef; + } + get descendantFontRef() { + const descendantFont = new Dict(this.xref); + descendantFont.set("BaseFont", this.fontName); + descendantFont.set("Type", Name.get("Font")); + descendantFont.set("Subtype", Name.get("CIDFontType0")); + descendantFont.set("CIDToGIDMap", Name.get("Identity")); + descendantFont.set("FirstChar", this.firstChar); + descendantFont.set("LastChar", this.lastChar); + descendantFont.set("FontDescriptor", this.fontDescriptorRef); + descendantFont.set("DW", 1000); + const widths = []; + const chars = [...this.widths.entries()].sort(); + let currentChar = null; + let currentWidths = null; + for (const [char, width] of chars) { + if (!currentChar) { + currentChar = char; + currentWidths = [width]; + continue; + } + if (char === currentChar + currentWidths.length) { + currentWidths.push(width); + } else { + widths.push(currentChar, currentWidths); + currentChar = char; + currentWidths = [width]; + } + } + if (currentChar) { + widths.push(currentChar, currentWidths); + } + descendantFont.set("W", widths); + const cidSystemInfo = new Dict(this.xref); + cidSystemInfo.set("Ordering", "Identity"); + cidSystemInfo.set("Registry", "Adobe"); + cidSystemInfo.set("Supplement", 0); + descendantFont.set("CIDSystemInfo", cidSystemInfo); + return this.xref.getNewPersistentRef(descendantFont); + } + get baseFontRef() { + const baseFont = new Dict(this.xref); + baseFont.set("BaseFont", this.fontName); + baseFont.set("Type", Name.get("Font")); + baseFont.set("Subtype", Name.get("Type0")); + baseFont.set("Encoding", Name.get("Identity-H")); + baseFont.set("DescendantFonts", [this.descendantFontRef]); + baseFont.set("ToUnicode", Name.get("Identity-H")); + return this.xref.getNewPersistentRef(baseFont); + } + get resources() { + const resources = new Dict(this.xref); + const font = new Dict(this.xref); + font.set(this.fontName.name, this.baseFontRef); + resources.set("Font", font); + return resources; + } + _createContext() { + this.widths = new Map(); + this.ctxMeasure.font = `1000px ${this.fontFamily}`; + return this.ctxMeasure; + } + createFontResources(text) { + const ctx = this._createContext(); + for (const line of text.split(/\r\n?|\n/)) { + for (const char of line.split("")) { + const code = char.charCodeAt(0); + if (this.widths.has(code)) { + continue; + } + const metrics = ctx.measureText(char); + const width = Math.ceil(metrics.width); + this.widths.set(code, width); + this.firstChar = Math.min(code, this.firstChar); + this.lastChar = Math.max(code, this.lastChar); + } + } + return this.resources; + } + static getFirstPositionInfo(rect, rotation, fontSize) { + const [x1, y1, x2, y2] = rect; + let w = x2 - x1; + let h = y2 - y1; + if (rotation % 180 !== 0) { + [w, h] = [h, w]; + } + const lineHeight = LINE_FACTOR * fontSize; + const lineDescent = LINE_DESCENT_FACTOR * fontSize; + return { + coords: [0, h + lineDescent - lineHeight], + bbox: [0, 0, w, h], + matrix: rotation !== 0 ? getRotationMatrix(rotation, h, lineHeight) : undefined + }; + } + createAppearance(text, rect, rotation, fontSize, bgColor, strokeAlpha) { + const ctx = this._createContext(); + const lines = []; + let maxWidth = -Infinity; + for (const line of text.split(/\r\n?|\n/)) { + lines.push(line); + const lineWidth = ctx.measureText(line).width; + maxWidth = Math.max(maxWidth, lineWidth); + for (const code of codePointIter(line)) { + const char = String.fromCodePoint(code); + let width = this.widths.get(code); + if (width === undefined) { + const metrics = ctx.measureText(char); + width = Math.ceil(metrics.width); + this.widths.set(code, width); + this.firstChar = Math.min(code, this.firstChar); + this.lastChar = Math.max(code, this.lastChar); + } + } + } + maxWidth *= fontSize / 1000; + const [x1, y1, x2, y2] = rect; + let w = x2 - x1; + let h = y2 - y1; + if (rotation % 180 !== 0) { + [w, h] = [h, w]; + } + let hscale = 1; + if (maxWidth > w) { + hscale = w / maxWidth; + } + let vscale = 1; + const lineHeight = LINE_FACTOR * fontSize; + const lineDescent = LINE_DESCENT_FACTOR * fontSize; + const maxHeight = lineHeight * lines.length; + if (maxHeight > h) { + vscale = h / maxHeight; + } + const fscale = Math.min(hscale, vscale); + const newFontSize = fontSize * fscale; + const buffer = ["q", `0 0 ${numberToString(w)} ${numberToString(h)} re W n`, `BT`, `1 0 0 1 0 ${numberToString(h + lineDescent)} Tm 0 Tc ${getPdfColor(bgColor, true)}`, `/${this.fontName.name} ${numberToString(newFontSize)} Tf`]; + const { + resources + } = this; + strokeAlpha = typeof strokeAlpha === "number" && strokeAlpha >= 0 && strokeAlpha <= 1 ? strokeAlpha : 1; + if (strokeAlpha !== 1) { + buffer.push("/R0 gs"); + const extGState = new Dict(this.xref); + const r0 = new Dict(this.xref); + r0.set("ca", strokeAlpha); + r0.set("CA", strokeAlpha); + r0.set("Type", Name.get("ExtGState")); + extGState.set("R0", r0); + resources.set("ExtGState", extGState); + } + const vShift = numberToString(lineHeight); + for (const line of lines) { + buffer.push(`0 -${vShift} Td <${stringToUTF16HexString(line)}> Tj`); + } + buffer.push("ET", "Q"); + const appearance = buffer.join("\n"); + const appearanceStreamDict = new Dict(this.xref); + appearanceStreamDict.set("Subtype", Name.get("Form")); + appearanceStreamDict.set("Type", Name.get("XObject")); + appearanceStreamDict.set("BBox", [0, 0, w, h]); + appearanceStreamDict.set("Length", appearance.length); + appearanceStreamDict.set("Resources", resources); + if (rotation) { + const matrix = getRotationMatrix(rotation, w, h); + appearanceStreamDict.set("Matrix", matrix); + } + const ap = new StringStream(appearance); + ap.dict = appearanceStreamDict; + return ap; + } +} + +;// ./src/core/name_number_tree.js + + +class NameOrNumberTree { + constructor(root, xref, type) { + this.root = root; + this.xref = xref; + this._type = type; + } + getAll() { + const map = new Map(); + if (!this.root) { + return map; + } + const xref = this.xref; + const processed = new RefSet(); + processed.put(this.root); + const queue = [this.root]; + while (queue.length > 0) { + const obj = xref.fetchIfRef(queue.shift()); + if (!(obj instanceof Dict)) { + continue; + } + if (obj.has("Kids")) { + const kids = obj.get("Kids"); + if (!Array.isArray(kids)) { + continue; + } + for (const kid of kids) { + if (processed.has(kid)) { + throw new FormatError(`Duplicate entry in "${this._type}" tree.`); + } + queue.push(kid); + processed.put(kid); + } + continue; + } + const entries = obj.get(this._type); + if (!Array.isArray(entries)) { + continue; + } + for (let i = 0, ii = entries.length; i < ii; i += 2) { + map.set(xref.fetchIfRef(entries[i]), xref.fetchIfRef(entries[i + 1])); + } + } + return map; + } + getRaw(key) { + if (!this.root) { + return null; + } + const xref = this.xref; + let kidsOrEntries = xref.fetchIfRef(this.root); + let loopCount = 0; + const MAX_LEVELS = 10; + while (kidsOrEntries.has("Kids")) { + if (++loopCount > MAX_LEVELS) { + warn(`Search depth limit reached for "${this._type}" tree.`); + return null; + } + const kids = kidsOrEntries.get("Kids"); + if (!Array.isArray(kids)) { + return null; + } + let l = 0, + r = kids.length - 1; + while (l <= r) { + const m = l + r >> 1; + const kid = xref.fetchIfRef(kids[m]); + const limits = kid.get("Limits"); + if (key < xref.fetchIfRef(limits[0])) { + r = m - 1; + } else if (key > xref.fetchIfRef(limits[1])) { + l = m + 1; + } else { + kidsOrEntries = kid; + break; + } + } + if (l > r) { + return null; + } + } + const entries = kidsOrEntries.get(this._type); + if (Array.isArray(entries)) { + let l = 0, + r = entries.length - 2; + while (l <= r) { + const tmp = l + r >> 1, + m = tmp + (tmp & 1); + const currentKey = xref.fetchIfRef(entries[m]); + if (key < currentKey) { + r = m - 2; + } else if (key > currentKey) { + l = m + 2; + } else { + return entries[m + 1]; + } + } + } + return null; + } + get(key) { + return this.xref.fetchIfRef(this.getRaw(key)); + } +} +class NameTree extends NameOrNumberTree { + constructor(root, xref) { + super(root, xref, "Names"); + } +} +class NumberTree extends NameOrNumberTree { + constructor(root, xref) { + super(root, xref, "Nums"); + } +} + +;// ./src/core/cleanup_helper.js + + + + +function clearGlobalCaches() { + clearPatternCaches(); + clearPrimitiveCaches(); + clearUnicodeCaches(); + JpxImage.cleanup(); +} + +;// ./src/core/file_spec.js + + + +function pickPlatformItem(dict) { + if (!(dict instanceof Dict)) { + return null; + } + if (dict.has("UF")) { + return dict.get("UF"); + } else if (dict.has("F")) { + return dict.get("F"); + } else if (dict.has("Unix")) { + return dict.get("Unix"); + } else if (dict.has("Mac")) { + return dict.get("Mac"); + } else if (dict.has("DOS")) { + return dict.get("DOS"); + } + return null; +} +function stripPath(str) { + return str.substring(str.lastIndexOf("/") + 1); +} +class FileSpec { + #contentAvailable = false; + constructor(root, xref, skipContent = false) { + if (!(root instanceof Dict)) { + return; + } + this.xref = xref; + this.root = root; + if (root.has("FS")) { + this.fs = root.get("FS"); + } + if (root.has("RF")) { + warn("Related file specifications are not supported"); + } + if (!skipContent) { + if (root.has("EF")) { + this.#contentAvailable = true; + } else { + warn("Non-embedded file specifications are not supported"); + } + } + } + get filename() { + let filename = ""; + const item = pickPlatformItem(this.root); + if (item && typeof item === "string") { + filename = stringToPDFString(item).replaceAll("\\\\", "\\").replaceAll("\\/", "/").replaceAll("\\", "/"); + } + return shadow(this, "filename", filename || "unnamed"); + } + get content() { + if (!this.#contentAvailable) { + return null; + } + this._contentRef ||= pickPlatformItem(this.root?.get("EF")); + let content = null; + if (this._contentRef) { + const fileObj = this.xref.fetchIfRef(this._contentRef); + if (fileObj instanceof BaseStream) { + content = fileObj.getBytes(); + } else { + warn("Embedded file specification points to non-existing/invalid content"); + } + } else { + warn("Embedded file specification does not have any content"); + } + return content; + } + get description() { + let description = ""; + const desc = this.root?.get("Desc"); + if (desc && typeof desc === "string") { + description = stringToPDFString(desc); + } + return shadow(this, "description", description); + } + get serializable() { + return { + rawFilename: this.filename, + filename: stripPath(this.filename), + content: this.content, + description: this.description + }; + } +} + +;// ./src/core/xml_parser.js + +const XMLParserErrorCode = { + NoError: 0, + EndOfDocument: -1, + UnterminatedCdat: -2, + UnterminatedXmlDeclaration: -3, + UnterminatedDoctypeDeclaration: -4, + UnterminatedComment: -5, + MalformedElement: -6, + OutOfMemory: -7, + UnterminatedAttributeValue: -8, + UnterminatedElement: -9, + ElementNeverBegun: -10 +}; +function isWhitespace(s, index) { + const ch = s[index]; + return ch === " " || ch === "\n" || ch === "\r" || ch === "\t"; +} +function isWhitespaceString(s) { + for (let i = 0, ii = s.length; i < ii; i++) { + if (!isWhitespace(s, i)) { + return false; + } + } + return true; +} +class XMLParserBase { + _resolveEntities(s) { + return s.replaceAll(/&([^;]+);/g, (all, entity) => { + if (entity.substring(0, 2) === "#x") { + return String.fromCodePoint(parseInt(entity.substring(2), 16)); + } else if (entity.substring(0, 1) === "#") { + return String.fromCodePoint(parseInt(entity.substring(1), 10)); + } + switch (entity) { + case "lt": + return "<"; + case "gt": + return ">"; + case "amp": + return "&"; + case "quot": + return '"'; + case "apos": + return "'"; + } + return this.onResolveEntity(entity); + }); + } + _parseContent(s, start) { + const attributes = []; + let pos = start; + function skipWs() { + while (pos < s.length && isWhitespace(s, pos)) { + ++pos; + } + } + while (pos < s.length && !isWhitespace(s, pos) && s[pos] !== ">" && s[pos] !== "/") { + ++pos; + } + const name = s.substring(start, pos); + skipWs(); + while (pos < s.length && s[pos] !== ">" && s[pos] !== "/" && s[pos] !== "?") { + skipWs(); + let attrName = "", + attrValue = ""; + while (pos < s.length && !isWhitespace(s, pos) && s[pos] !== "=") { + attrName += s[pos]; + ++pos; + } + skipWs(); + if (s[pos] !== "=") { + return null; + } + ++pos; + skipWs(); + const attrEndChar = s[pos]; + if (attrEndChar !== '"' && attrEndChar !== "'") { + return null; + } + const attrEndIndex = s.indexOf(attrEndChar, ++pos); + if (attrEndIndex < 0) { + return null; + } + attrValue = s.substring(pos, attrEndIndex); + attributes.push({ + name: attrName, + value: this._resolveEntities(attrValue) + }); + pos = attrEndIndex + 1; + skipWs(); + } + return { + name, + attributes, + parsed: pos - start + }; + } + _parseProcessingInstruction(s, start) { + let pos = start; + function skipWs() { + while (pos < s.length && isWhitespace(s, pos)) { + ++pos; + } + } + while (pos < s.length && !isWhitespace(s, pos) && s[pos] !== ">" && s[pos] !== "?" && s[pos] !== "/") { + ++pos; + } + const name = s.substring(start, pos); + skipWs(); + const attrStart = pos; + while (pos < s.length && (s[pos] !== "?" || s[pos + 1] !== ">")) { + ++pos; + } + const value = s.substring(attrStart, pos); + return { + name, + value, + parsed: pos - start + }; + } + parseXml(s) { + let i = 0; + while (i < s.length) { + const ch = s[i]; + let j = i; + if (ch === "<") { + ++j; + const ch2 = s[j]; + let q; + switch (ch2) { + case "/": + ++j; + q = s.indexOf(">", j); + if (q < 0) { + this.onError(XMLParserErrorCode.UnterminatedElement); + return; + } + this.onEndElement(s.substring(j, q)); + j = q + 1; + break; + case "?": + ++j; + const pi = this._parseProcessingInstruction(s, j); + if (s.substring(j + pi.parsed, j + pi.parsed + 2) !== "?>") { + this.onError(XMLParserErrorCode.UnterminatedXmlDeclaration); + return; + } + this.onPi(pi.name, pi.value); + j += pi.parsed + 2; + break; + case "!": + if (s.substring(j + 1, j + 3) === "--") { + q = s.indexOf("-->", j + 3); + if (q < 0) { + this.onError(XMLParserErrorCode.UnterminatedComment); + return; + } + this.onComment(s.substring(j + 3, q)); + j = q + 3; + } else if (s.substring(j + 1, j + 8) === "[CDATA[") { + q = s.indexOf("]]>", j + 8); + if (q < 0) { + this.onError(XMLParserErrorCode.UnterminatedCdat); + return; + } + this.onCdata(s.substring(j + 8, q)); + j = q + 3; + } else if (s.substring(j + 1, j + 8) === "DOCTYPE") { + const q2 = s.indexOf("[", j + 8); + let complexDoctype = false; + q = s.indexOf(">", j + 8); + if (q < 0) { + this.onError(XMLParserErrorCode.UnterminatedDoctypeDeclaration); + return; + } + if (q2 > 0 && q > q2) { + q = s.indexOf("]>", j + 8); + if (q < 0) { + this.onError(XMLParserErrorCode.UnterminatedDoctypeDeclaration); + return; + } + complexDoctype = true; + } + const doctypeContent = s.substring(j + 8, q + (complexDoctype ? 1 : 0)); + this.onDoctype(doctypeContent); + j = q + (complexDoctype ? 2 : 1); + } else { + this.onError(XMLParserErrorCode.MalformedElement); + return; + } + break; + default: + const content = this._parseContent(s, j); + if (content === null) { + this.onError(XMLParserErrorCode.MalformedElement); + return; + } + let isClosed = false; + if (s.substring(j + content.parsed, j + content.parsed + 2) === "/>") { + isClosed = true; + } else if (s.substring(j + content.parsed, j + content.parsed + 1) !== ">") { + this.onError(XMLParserErrorCode.UnterminatedElement); + return; + } + this.onBeginElement(content.name, content.attributes, isClosed); + j += content.parsed + (isClosed ? 2 : 1); + break; + } + } else { + while (j < s.length && s[j] !== "<") { + j++; + } + const text = s.substring(i, j); + this.onText(this._resolveEntities(text)); + } + i = j; + } + } + onResolveEntity(name) { + return `&${name};`; + } + onPi(name, value) {} + onComment(text) {} + onCdata(text) {} + onDoctype(doctypeContent) {} + onText(text) {} + onBeginElement(name, attributes, isEmpty) {} + onEndElement(name) {} + onError(code) {} +} +class SimpleDOMNode { + constructor(nodeName, nodeValue) { + this.nodeName = nodeName; + this.nodeValue = nodeValue; + Object.defineProperty(this, "parentNode", { + value: null, + writable: true + }); + } + get firstChild() { + return this.childNodes?.[0]; + } + get nextSibling() { + const childNodes = this.parentNode.childNodes; + if (!childNodes) { + return undefined; + } + const index = childNodes.indexOf(this); + if (index === -1) { + return undefined; + } + return childNodes[index + 1]; + } + get textContent() { + if (!this.childNodes) { + return this.nodeValue || ""; + } + return this.childNodes.map(function (child) { + return child.textContent; + }).join(""); + } + get children() { + return this.childNodes || []; + } + hasChildNodes() { + return this.childNodes?.length > 0; + } + searchNode(paths, pos) { + if (pos >= paths.length) { + return this; + } + const component = paths[pos]; + if (component.name.startsWith("#") && pos < paths.length - 1) { + return this.searchNode(paths, pos + 1); + } + const stack = []; + let node = this; + while (true) { + if (component.name === node.nodeName) { + if (component.pos === 0) { + const res = node.searchNode(paths, pos + 1); + if (res !== null) { + return res; + } + } else if (stack.length === 0) { + return null; + } else { + const [parent] = stack.pop(); + let siblingPos = 0; + for (const child of parent.childNodes) { + if (component.name === child.nodeName) { + if (siblingPos === component.pos) { + return child.searchNode(paths, pos + 1); + } + siblingPos++; + } + } + return node.searchNode(paths, pos + 1); + } + } + if (node.childNodes?.length > 0) { + stack.push([node, 0]); + node = node.childNodes[0]; + } else if (stack.length === 0) { + return null; + } else { + while (stack.length !== 0) { + const [parent, currentPos] = stack.pop(); + const newPos = currentPos + 1; + if (newPos < parent.childNodes.length) { + stack.push([parent, newPos]); + node = parent.childNodes[newPos]; + break; + } + } + if (stack.length === 0) { + return null; + } + } + } + } + dump(buffer) { + if (this.nodeName === "#text") { + buffer.push(encodeToXmlString(this.nodeValue)); + return; + } + buffer.push(`<${this.nodeName}`); + if (this.attributes) { + for (const attribute of this.attributes) { + buffer.push(` ${attribute.name}="${encodeToXmlString(attribute.value)}"`); + } + } + if (this.hasChildNodes()) { + buffer.push(">"); + for (const child of this.childNodes) { + child.dump(buffer); + } + buffer.push(``); + } else if (this.nodeValue) { + buffer.push(`>${encodeToXmlString(this.nodeValue)}`); + } else { + buffer.push("/>"); + } + } +} +class SimpleXMLParser extends XMLParserBase { + constructor({ + hasAttributes = false, + lowerCaseName = false + }) { + super(); + this._currentFragment = null; + this._stack = null; + this._errorCode = XMLParserErrorCode.NoError; + this._hasAttributes = hasAttributes; + this._lowerCaseName = lowerCaseName; + } + parseFromString(data) { + this._currentFragment = []; + this._stack = []; + this._errorCode = XMLParserErrorCode.NoError; + this.parseXml(data); + if (this._errorCode !== XMLParserErrorCode.NoError) { + return undefined; + } + const [documentElement] = this._currentFragment; + if (!documentElement) { + return undefined; + } + return { + documentElement + }; + } + onText(text) { + if (isWhitespaceString(text)) { + return; + } + const node = new SimpleDOMNode("#text", text); + this._currentFragment.push(node); + } + onCdata(text) { + const node = new SimpleDOMNode("#text", text); + this._currentFragment.push(node); + } + onBeginElement(name, attributes, isEmpty) { + if (this._lowerCaseName) { + name = name.toLowerCase(); + } + const node = new SimpleDOMNode(name); + node.childNodes = []; + if (this._hasAttributes) { + node.attributes = attributes; + } + this._currentFragment.push(node); + if (isEmpty) { + return; + } + this._stack.push(this._currentFragment); + this._currentFragment = node.childNodes; + } + onEndElement(name) { + this._currentFragment = this._stack.pop() || []; + const lastElement = this._currentFragment.at(-1); + if (!lastElement) { + return null; + } + for (const childNode of lastElement.childNodes) { + childNode.parentNode = lastElement; + } + return lastElement; + } + onError(code) { + this._errorCode = code; + } +} + +;// ./src/core/metadata_parser.js + +class MetadataParser { + constructor(data) { + data = this._repair(data); + const parser = new SimpleXMLParser({ + lowerCaseName: true + }); + const xmlDocument = parser.parseFromString(data); + this._metadataMap = new Map(); + this._data = data; + if (xmlDocument) { + this._parse(xmlDocument); + } + } + _repair(data) { + return data.replace(/^[^<]+/, "").replaceAll(/>\\376\\377([^<]+)/g, function (all, codes) { + const bytes = codes.replaceAll(/\\([0-3])([0-7])([0-7])/g, function (code, d1, d2, d3) { + return String.fromCharCode(d1 * 64 + d2 * 8 + d3 * 1); + }).replaceAll(/&(amp|apos|gt|lt|quot);/g, function (str, name) { + switch (name) { + case "amp": + return "&"; + case "apos": + return "'"; + case "gt": + return ">"; + case "lt": + return "<"; + case "quot": + return '"'; + } + throw new Error(`_repair: ${name} isn't defined.`); + }); + const charBuf = [">"]; + for (let i = 0, ii = bytes.length; i < ii; i += 2) { + const code = bytes.charCodeAt(i) * 256 + bytes.charCodeAt(i + 1); + if (code >= 32 && code < 127 && code !== 60 && code !== 62 && code !== 38) { + charBuf.push(String.fromCharCode(code)); + } else { + charBuf.push("&#x" + (0x10000 + code).toString(16).substring(1) + ";"); + } + } + return charBuf.join(""); + }); + } + _getSequence(entry) { + const name = entry.nodeName; + if (name !== "rdf:bag" && name !== "rdf:seq" && name !== "rdf:alt") { + return null; + } + return entry.childNodes.filter(node => node.nodeName === "rdf:li"); + } + _parseArray(entry) { + if (!entry.hasChildNodes()) { + return; + } + const [seqNode] = entry.childNodes; + const sequence = this._getSequence(seqNode) || []; + this._metadataMap.set(entry.nodeName, sequence.map(node => node.textContent.trim())); + } + _parse(xmlDocument) { + let rdf = xmlDocument.documentElement; + if (rdf.nodeName !== "rdf:rdf") { + rdf = rdf.firstChild; + while (rdf && rdf.nodeName !== "rdf:rdf") { + rdf = rdf.nextSibling; + } + } + if (!rdf || rdf.nodeName !== "rdf:rdf" || !rdf.hasChildNodes()) { + return; + } + for (const desc of rdf.childNodes) { + if (desc.nodeName !== "rdf:description") { + continue; + } + for (const entry of desc.childNodes) { + const name = entry.nodeName; + switch (name) { + case "#text": + continue; + case "dc:creator": + case "dc:subject": + this._parseArray(entry); + continue; + } + this._metadataMap.set(name, entry.textContent.trim()); + } + } + } + get serializable() { + return { + parsedData: this._metadataMap, + rawData: this._data + }; + } +} + +;// ./src/core/struct_tree.js + + + + +const MAX_DEPTH = 40; +const StructElementType = { + PAGE_CONTENT: 1, + STREAM_CONTENT: 2, + OBJECT: 3, + ANNOTATION: 4, + ELEMENT: 5 +}; +class StructTreeRoot { + constructor(rootDict, rootRef) { + this.dict = rootDict; + this.ref = rootRef instanceof Ref ? rootRef : null; + this.roleMap = new Map(); + this.structParentIds = null; + } + init() { + this.readRoleMap(); + } + #addIdToPage(pageRef, id, type) { + if (!(pageRef instanceof Ref) || id < 0) { + return; + } + this.structParentIds ||= new RefSetCache(); + let ids = this.structParentIds.get(pageRef); + if (!ids) { + ids = []; + this.structParentIds.put(pageRef, ids); + } + ids.push([id, type]); + } + addAnnotationIdToPage(pageRef, id) { + this.#addIdToPage(pageRef, id, StructElementType.ANNOTATION); + } + readRoleMap() { + const roleMapDict = this.dict.get("RoleMap"); + if (!(roleMapDict instanceof Dict)) { + return; + } + for (const [key, value] of roleMapDict) { + if (value instanceof Name) { + this.roleMap.set(key, value.name); + } + } + } + static async canCreateStructureTree({ + catalogRef, + pdfManager, + newAnnotationsByPage + }) { + if (!(catalogRef instanceof Ref)) { + warn("Cannot save the struct tree: no catalog reference."); + return false; + } + let nextKey = 0; + let hasNothingToUpdate = true; + for (const [pageIndex, elements] of newAnnotationsByPage) { + const { + ref: pageRef + } = await pdfManager.getPage(pageIndex); + if (!(pageRef instanceof Ref)) { + warn(`Cannot save the struct tree: page ${pageIndex} has no ref.`); + hasNothingToUpdate = true; + break; + } + for (const element of elements) { + if (element.accessibilityData?.type) { + element.parentTreeId = nextKey++; + hasNothingToUpdate = false; + } + } + } + if (hasNothingToUpdate) { + for (const elements of newAnnotationsByPage.values()) { + for (const element of elements) { + delete element.parentTreeId; + } + } + return false; + } + return true; + } + static async createStructureTree({ + newAnnotationsByPage, + xref, + catalogRef, + pdfManager, + changes + }) { + const root = pdfManager.catalog.cloneDict(); + const cache = new RefSetCache(); + cache.put(catalogRef, root); + const structTreeRootRef = xref.getNewTemporaryRef(); + root.set("StructTreeRoot", structTreeRootRef); + const structTreeRoot = new Dict(xref); + structTreeRoot.set("Type", Name.get("StructTreeRoot")); + const parentTreeRef = xref.getNewTemporaryRef(); + structTreeRoot.set("ParentTree", parentTreeRef); + const kids = []; + structTreeRoot.set("K", kids); + cache.put(structTreeRootRef, structTreeRoot); + const parentTree = new Dict(xref); + const nums = []; + parentTree.set("Nums", nums); + const nextKey = await this.#writeKids({ + newAnnotationsByPage, + structTreeRootRef, + structTreeRoot: null, + kids, + nums, + xref, + pdfManager, + changes, + cache + }); + structTreeRoot.set("ParentTreeNextKey", nextKey); + cache.put(parentTreeRef, parentTree); + for (const [ref, obj] of cache.items()) { + changes.put(ref, { + data: obj + }); + } + } + async canUpdateStructTree({ + pdfManager, + xref, + newAnnotationsByPage + }) { + if (!this.ref) { + warn("Cannot update the struct tree: no root reference."); + return false; + } + let nextKey = this.dict.get("ParentTreeNextKey"); + if (!Number.isInteger(nextKey) || nextKey < 0) { + warn("Cannot update the struct tree: invalid next key."); + return false; + } + const parentTree = this.dict.get("ParentTree"); + if (!(parentTree instanceof Dict)) { + warn("Cannot update the struct tree: ParentTree isn't a dict."); + return false; + } + const nums = parentTree.get("Nums"); + if (!Array.isArray(nums)) { + warn("Cannot update the struct tree: nums isn't an array."); + return false; + } + const numberTree = new NumberTree(parentTree, xref); + for (const pageIndex of newAnnotationsByPage.keys()) { + const { + pageDict + } = await pdfManager.getPage(pageIndex); + if (!pageDict.has("StructParents")) { + continue; + } + const id = pageDict.get("StructParents"); + if (!Number.isInteger(id) || !Array.isArray(numberTree.get(id))) { + warn(`Cannot save the struct tree: page ${pageIndex} has a wrong id.`); + return false; + } + } + let hasNothingToUpdate = true; + for (const [pageIndex, elements] of newAnnotationsByPage) { + const { + pageDict + } = await pdfManager.getPage(pageIndex); + StructTreeRoot.#collectParents({ + elements, + xref: this.dict.xref, + pageDict, + numberTree + }); + for (const element of elements) { + if (element.accessibilityData?.type) { + if (!(element.accessibilityData.structParent >= 0)) { + element.parentTreeId = nextKey++; + } + hasNothingToUpdate = false; + } + } + } + if (hasNothingToUpdate) { + for (const elements of newAnnotationsByPage.values()) { + for (const element of elements) { + delete element.parentTreeId; + delete element.structTreeParent; + } + } + return false; + } + return true; + } + async updateStructureTree({ + newAnnotationsByPage, + pdfManager, + changes + }) { + const xref = this.dict.xref; + const structTreeRoot = this.dict.clone(); + const structTreeRootRef = this.ref; + const cache = new RefSetCache(); + cache.put(structTreeRootRef, structTreeRoot); + let parentTreeRef = structTreeRoot.getRaw("ParentTree"); + let parentTree; + if (parentTreeRef instanceof Ref) { + parentTree = xref.fetch(parentTreeRef); + } else { + parentTree = parentTreeRef; + parentTreeRef = xref.getNewTemporaryRef(); + structTreeRoot.set("ParentTree", parentTreeRef); + } + parentTree = parentTree.clone(); + cache.put(parentTreeRef, parentTree); + let nums = parentTree.getRaw("Nums"); + let numsRef = null; + if (nums instanceof Ref) { + numsRef = nums; + nums = xref.fetch(numsRef); + } + nums = nums.slice(); + if (!numsRef) { + parentTree.set("Nums", nums); + } + const newNextKey = await StructTreeRoot.#writeKids({ + newAnnotationsByPage, + structTreeRootRef, + structTreeRoot: this, + kids: null, + nums, + xref, + pdfManager, + changes, + cache + }); + if (newNextKey === -1) { + return; + } + structTreeRoot.set("ParentTreeNextKey", newNextKey); + if (numsRef) { + cache.put(numsRef, nums); + } + for (const [ref, obj] of cache.items()) { + changes.put(ref, { + data: obj + }); + } + } + static async #writeKids({ + newAnnotationsByPage, + structTreeRootRef, + structTreeRoot, + kids, + nums, + xref, + pdfManager, + changes, + cache + }) { + const objr = Name.get("OBJR"); + let nextKey = -1; + let structTreePageObjs; + for (const [pageIndex, elements] of newAnnotationsByPage) { + const page = await pdfManager.getPage(pageIndex); + const { + ref: pageRef + } = page; + const isPageRef = pageRef instanceof Ref; + for (const { + accessibilityData, + ref, + parentTreeId, + structTreeParent + } of elements) { + if (!accessibilityData?.type) { + continue; + } + const { + structParent + } = accessibilityData; + if (structTreeRoot && Number.isInteger(structParent) && structParent >= 0) { + let objs = (structTreePageObjs ||= new Map()).get(pageIndex); + if (objs === undefined) { + const structTreePage = new StructTreePage(structTreeRoot, page.pageDict); + objs = structTreePage.collectObjects(pageRef); + structTreePageObjs.set(pageIndex, objs); + } + const objRef = objs?.get(structParent); + if (objRef) { + const tagDict = xref.fetch(objRef).clone(); + StructTreeRoot.#writeProperties(tagDict, accessibilityData); + changes.put(objRef, { + data: tagDict + }); + continue; + } + } + nextKey = Math.max(nextKey, parentTreeId); + const tagRef = xref.getNewTemporaryRef(); + const tagDict = new Dict(xref); + StructTreeRoot.#writeProperties(tagDict, accessibilityData); + await this.#updateParentTag({ + structTreeParent, + tagDict, + newTagRef: tagRef, + structTreeRootRef, + fallbackKids: kids, + xref, + cache + }); + const objDict = new Dict(xref); + tagDict.set("K", objDict); + objDict.set("Type", objr); + if (isPageRef) { + objDict.set("Pg", pageRef); + } + objDict.set("Obj", ref); + cache.put(tagRef, tagDict); + nums.push(parentTreeId, tagRef); + } + } + return nextKey + 1; + } + static #writeProperties(tagDict, { + type, + title, + lang, + alt, + expanded, + actualText + }) { + tagDict.set("S", Name.get(type)); + if (title) { + tagDict.set("T", stringToAsciiOrUTF16BE(title)); + } + if (lang) { + tagDict.set("Lang", stringToAsciiOrUTF16BE(lang)); + } + if (alt) { + tagDict.set("Alt", stringToAsciiOrUTF16BE(alt)); + } + if (expanded) { + tagDict.set("E", stringToAsciiOrUTF16BE(expanded)); + } + if (actualText) { + tagDict.set("ActualText", stringToAsciiOrUTF16BE(actualText)); + } + } + static #collectParents({ + elements, + xref, + pageDict, + numberTree + }) { + const idToElements = new Map(); + for (const element of elements) { + if (element.structTreeParentId) { + const id = parseInt(element.structTreeParentId.split("_mc")[1], 10); + let elems = idToElements.get(id); + if (!elems) { + elems = []; + idToElements.set(id, elems); + } + elems.push(element); + } + } + const id = pageDict.get("StructParents"); + if (!Number.isInteger(id)) { + return; + } + const parentArray = numberTree.get(id); + const updateElement = (kid, pageKid, kidRef) => { + const elems = idToElements.get(kid); + if (elems) { + const parentRef = pageKid.getRaw("P"); + const parentDict = xref.fetchIfRef(parentRef); + if (parentRef instanceof Ref && parentDict instanceof Dict) { + const params = { + ref: kidRef, + dict: pageKid + }; + for (const element of elems) { + element.structTreeParent = params; + } + } + return true; + } + return false; + }; + for (const kidRef of parentArray) { + if (!(kidRef instanceof Ref)) { + continue; + } + const pageKid = xref.fetch(kidRef); + const k = pageKid.get("K"); + if (Number.isInteger(k)) { + updateElement(k, pageKid, kidRef); + continue; + } + if (!Array.isArray(k)) { + continue; + } + for (let kid of k) { + kid = xref.fetchIfRef(kid); + if (Number.isInteger(kid) && updateElement(kid, pageKid, kidRef)) { + break; + } + if (!(kid instanceof Dict)) { + continue; + } + if (!isName(kid.get("Type"), "MCR")) { + break; + } + const mcid = kid.get("MCID"); + if (Number.isInteger(mcid) && updateElement(mcid, pageKid, kidRef)) { + break; + } + } + } + } + static async #updateParentTag({ + structTreeParent, + tagDict, + newTagRef, + structTreeRootRef, + fallbackKids, + xref, + cache + }) { + let ref = null; + let parentRef; + if (structTreeParent) { + ({ + ref + } = structTreeParent); + parentRef = structTreeParent.dict.getRaw("P") || structTreeRootRef; + } else { + parentRef = structTreeRootRef; + } + tagDict.set("P", parentRef); + const parentDict = xref.fetchIfRef(parentRef); + if (!parentDict) { + fallbackKids.push(newTagRef); + return; + } + let cachedParentDict = cache.get(parentRef); + if (!cachedParentDict) { + cachedParentDict = parentDict.clone(); + cache.put(parentRef, cachedParentDict); + } + const parentKidsRaw = cachedParentDict.getRaw("K"); + let cachedParentKids = parentKidsRaw instanceof Ref ? cache.get(parentKidsRaw) : null; + if (!cachedParentKids) { + cachedParentKids = xref.fetchIfRef(parentKidsRaw); + cachedParentKids = Array.isArray(cachedParentKids) ? cachedParentKids.slice() : [parentKidsRaw]; + const parentKidsRef = xref.getNewTemporaryRef(); + cachedParentDict.set("K", parentKidsRef); + cache.put(parentKidsRef, cachedParentKids); + } + const index = cachedParentKids.indexOf(ref); + cachedParentKids.splice(index >= 0 ? index + 1 : cachedParentKids.length, 0, newTagRef); + } +} +class StructElementNode { + constructor(tree, dict) { + this.tree = tree; + this.dict = dict; + this.kids = []; + this.parseKids(); + } + get role() { + const nameObj = this.dict.get("S"); + const name = nameObj instanceof Name ? nameObj.name : ""; + const { + root + } = this.tree; + if (root.roleMap.has(name)) { + return root.roleMap.get(name); + } + return name; + } + parseKids() { + let pageObjId = null; + const objRef = this.dict.getRaw("Pg"); + if (objRef instanceof Ref) { + pageObjId = objRef.toString(); + } + const kids = this.dict.get("K"); + if (Array.isArray(kids)) { + for (const kid of kids) { + const element = this.parseKid(pageObjId, kid); + if (element) { + this.kids.push(element); + } + } + } else { + const element = this.parseKid(pageObjId, kids); + if (element) { + this.kids.push(element); + } + } + } + parseKid(pageObjId, kid) { + if (Number.isInteger(kid)) { + if (this.tree.pageDict.objId !== pageObjId) { + return null; + } + return new StructElement({ + type: StructElementType.PAGE_CONTENT, + mcid: kid, + pageObjId + }); + } + let kidDict = null; + if (kid instanceof Ref) { + kidDict = this.dict.xref.fetch(kid); + } else if (kid instanceof Dict) { + kidDict = kid; + } + if (!kidDict) { + return null; + } + const pageRef = kidDict.getRaw("Pg"); + if (pageRef instanceof Ref) { + pageObjId = pageRef.toString(); + } + const type = kidDict.get("Type") instanceof Name ? kidDict.get("Type").name : null; + if (type === "MCR") { + if (this.tree.pageDict.objId !== pageObjId) { + return null; + } + const kidRef = kidDict.getRaw("Stm"); + return new StructElement({ + type: StructElementType.STREAM_CONTENT, + refObjId: kidRef instanceof Ref ? kidRef.toString() : null, + pageObjId, + mcid: kidDict.get("MCID") + }); + } + if (type === "OBJR") { + if (this.tree.pageDict.objId !== pageObjId) { + return null; + } + const kidRef = kidDict.getRaw("Obj"); + return new StructElement({ + type: StructElementType.OBJECT, + refObjId: kidRef instanceof Ref ? kidRef.toString() : null, + pageObjId + }); + } + return new StructElement({ + type: StructElementType.ELEMENT, + dict: kidDict + }); + } +} +class StructElement { + constructor({ + type, + dict = null, + mcid = null, + pageObjId = null, + refObjId = null + }) { + this.type = type; + this.dict = dict; + this.mcid = mcid; + this.pageObjId = pageObjId; + this.refObjId = refObjId; + this.parentNode = null; + } +} +class StructTreePage { + constructor(structTreeRoot, pageDict) { + this.root = structTreeRoot; + this.rootDict = structTreeRoot ? structTreeRoot.dict : null; + this.pageDict = pageDict; + this.nodes = []; + } + collectObjects(pageRef) { + if (!this.root || !this.rootDict || !(pageRef instanceof Ref)) { + return null; + } + const parentTree = this.rootDict.get("ParentTree"); + if (!parentTree) { + return null; + } + const ids = this.root.structParentIds?.get(pageRef); + if (!ids) { + return null; + } + const map = new Map(); + const numberTree = new NumberTree(parentTree, this.rootDict.xref); + for (const [elemId] of ids) { + const obj = numberTree.getRaw(elemId); + if (obj instanceof Ref) { + map.set(elemId, obj); + } + } + return map; + } + parse(pageRef) { + if (!this.root || !this.rootDict || !(pageRef instanceof Ref)) { + return; + } + const parentTree = this.rootDict.get("ParentTree"); + if (!parentTree) { + return; + } + const id = this.pageDict.get("StructParents"); + const ids = this.root.structParentIds?.get(pageRef); + if (!Number.isInteger(id) && !ids) { + return; + } + const map = new Map(); + const numberTree = new NumberTree(parentTree, this.rootDict.xref); + if (Number.isInteger(id)) { + const parentArray = numberTree.get(id); + if (Array.isArray(parentArray)) { + for (const ref of parentArray) { + if (ref instanceof Ref) { + this.addNode(this.rootDict.xref.fetch(ref), map); + } + } + } + } + if (!ids) { + return; + } + for (const [elemId, type] of ids) { + const obj = numberTree.get(elemId); + if (obj) { + const elem = this.addNode(this.rootDict.xref.fetchIfRef(obj), map); + if (elem?.kids?.length === 1 && elem.kids[0].type === StructElementType.OBJECT) { + elem.kids[0].type = type; + } + } + } + } + addNode(dict, map, level = 0) { + if (level > MAX_DEPTH) { + warn("StructTree MAX_DEPTH reached."); + return null; + } + if (!(dict instanceof Dict)) { + return null; + } + if (map.has(dict)) { + return map.get(dict); + } + const element = new StructElementNode(this, dict); + map.set(dict, element); + const parent = dict.get("P"); + if (!parent || isName(parent.get("Type"), "StructTreeRoot")) { + if (!this.addTopLevelNode(dict, element)) { + map.delete(dict); + } + return element; + } + const parentNode = this.addNode(parent, map, level + 1); + if (!parentNode) { + return element; + } + let save = false; + for (const kid of parentNode.kids) { + if (kid.type === StructElementType.ELEMENT && kid.dict === dict) { + kid.parentNode = element; + save = true; + } + } + if (!save) { + map.delete(dict); + } + return element; + } + addTopLevelNode(dict, element) { + const obj = this.rootDict.get("K"); + if (!obj) { + return false; + } + if (obj instanceof Dict) { + if (obj.objId !== dict.objId) { + return false; + } + this.nodes[0] = element; + return true; + } + if (!Array.isArray(obj)) { + return true; + } + let save = false; + for (let i = 0; i < obj.length; i++) { + const kidRef = obj[i]; + if (kidRef?.toString() === dict.objId) { + this.nodes[i] = element; + save = true; + } + } + return save; + } + get serializable() { + function nodeToSerializable(node, parent, level = 0) { + if (level > MAX_DEPTH) { + warn("StructTree too deep to be fully serialized."); + return; + } + const obj = Object.create(null); + obj.role = node.role; + obj.children = []; + parent.children.push(obj); + let alt = node.dict.get("Alt"); + if (typeof alt !== "string") { + alt = node.dict.get("ActualText"); + } + if (typeof alt === "string") { + obj.alt = stringToPDFString(alt); + } + const a = node.dict.get("A"); + if (a instanceof Dict) { + const bbox = lookupNormalRect(a.getArray("BBox"), null); + if (bbox) { + obj.bbox = bbox; + } else { + const width = a.get("Width"); + const height = a.get("Height"); + if (typeof width === "number" && width > 0 && typeof height === "number" && height > 0) { + obj.bbox = [0, 0, width, height]; + } + } + } + const lang = node.dict.get("Lang"); + if (typeof lang === "string") { + obj.lang = stringToPDFString(lang); + } + for (const kid of node.kids) { + const kidElement = kid.type === StructElementType.ELEMENT ? kid.parentNode : null; + if (kidElement) { + nodeToSerializable(kidElement, obj, level + 1); + continue; + } else if (kid.type === StructElementType.PAGE_CONTENT || kid.type === StructElementType.STREAM_CONTENT) { + obj.children.push({ + type: "content", + id: `p${kid.pageObjId}_mc${kid.mcid}` + }); + } else if (kid.type === StructElementType.OBJECT) { + obj.children.push({ + type: "object", + id: kid.refObjId + }); + } else if (kid.type === StructElementType.ANNOTATION) { + obj.children.push({ + type: "annotation", + id: `${AnnotationPrefix}${kid.refObjId}` + }); + } + } + } + const root = Object.create(null); + root.children = []; + root.role = "Root"; + for (const child of this.nodes) { + if (!child) { + continue; + } + nodeToSerializable(child, root); + } + return root; + } +} + +;// ./src/core/catalog.js + + + + + + + + + + + +function isValidExplicitDest(dest) { + if (!Array.isArray(dest) || dest.length < 2) { + return false; + } + const [page, zoom, ...args] = dest; + if (!(page instanceof Ref) && !Number.isInteger(page)) { + return false; + } + if (!(zoom instanceof Name)) { + return false; + } + const argsLen = args.length; + let allowNull = true; + switch (zoom.name) { + case "XYZ": + if (argsLen < 2 || argsLen > 3) { + return false; + } + break; + case "Fit": + case "FitB": + return argsLen === 0; + case "FitH": + case "FitBH": + case "FitV": + case "FitBV": + if (argsLen > 1) { + return false; + } + break; + case "FitR": + if (argsLen !== 4) { + return false; + } + allowNull = false; + break; + default: + return false; + } + for (const arg of args) { + if (!(typeof arg === "number" || allowNull && arg === null)) { + return false; + } + } + return true; +} +function fetchDest(dest) { + if (dest instanceof Dict) { + dest = dest.get("D"); + } + return isValidExplicitDest(dest) ? dest : null; +} +function fetchRemoteDest(action) { + let dest = action.get("D"); + if (dest) { + if (dest instanceof Name) { + dest = dest.name; + } + if (typeof dest === "string") { + return stringToPDFString(dest); + } else if (isValidExplicitDest(dest)) { + return JSON.stringify(dest); + } + } + return null; +} +class Catalog { + constructor(pdfManager, xref) { + this.pdfManager = pdfManager; + this.xref = xref; + this._catDict = xref.getCatalogObj(); + if (!(this._catDict instanceof Dict)) { + throw new FormatError("Catalog object is not a dictionary."); + } + this.toplevelPagesDict; + this._actualNumPages = null; + this.fontCache = new RefSetCache(); + this.builtInCMapCache = new Map(); + this.standardFontDataCache = new Map(); + this.globalImageCache = new GlobalImageCache(); + this.pageKidsCountCache = new RefSetCache(); + this.pageIndexCache = new RefSetCache(); + this.pageDictCache = new RefSetCache(); + this.nonBlendModesSet = new RefSet(); + this.systemFontCache = new Map(); + } + cloneDict() { + return this._catDict.clone(); + } + get version() { + const version = this._catDict.get("Version"); + if (version instanceof Name) { + if (PDF_VERSION_REGEXP.test(version.name)) { + return shadow(this, "version", version.name); + } + warn(`Invalid PDF catalog version: ${version.name}`); + } + return shadow(this, "version", null); + } + get lang() { + const lang = this._catDict.get("Lang"); + return shadow(this, "lang", lang && typeof lang === "string" ? stringToPDFString(lang) : null); + } + get needsRendering() { + const needsRendering = this._catDict.get("NeedsRendering"); + return shadow(this, "needsRendering", typeof needsRendering === "boolean" ? needsRendering : false); + } + get collection() { + let collection = null; + try { + const obj = this._catDict.get("Collection"); + if (obj instanceof Dict && obj.size > 0) { + collection = obj; + } + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + info("Cannot fetch Collection entry; assuming no collection is present."); + } + return shadow(this, "collection", collection); + } + get acroForm() { + let acroForm = null; + try { + const obj = this._catDict.get("AcroForm"); + if (obj instanceof Dict && obj.size > 0) { + acroForm = obj; + } + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + info("Cannot fetch AcroForm entry; assuming no forms are present."); + } + return shadow(this, "acroForm", acroForm); + } + get acroFormRef() { + const value = this._catDict.getRaw("AcroForm"); + return shadow(this, "acroFormRef", value instanceof Ref ? value : null); + } + get metadata() { + const streamRef = this._catDict.getRaw("Metadata"); + if (!(streamRef instanceof Ref)) { + return shadow(this, "metadata", null); + } + let metadata = null; + try { + const stream = this.xref.fetch(streamRef, !this.xref.encrypt?.encryptMetadata); + if (stream instanceof BaseStream && stream.dict instanceof Dict) { + const type = stream.dict.get("Type"); + const subtype = stream.dict.get("Subtype"); + if (isName(type, "Metadata") && isName(subtype, "XML")) { + const data = stringToUTF8String(stream.getString()); + if (data) { + metadata = new MetadataParser(data).serializable; + } + } + } + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + info(`Skipping invalid Metadata: "${ex}".`); + } + return shadow(this, "metadata", metadata); + } + get markInfo() { + let markInfo = null; + try { + markInfo = this._readMarkInfo(); + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn("Unable to read mark info."); + } + return shadow(this, "markInfo", markInfo); + } + _readMarkInfo() { + const obj = this._catDict.get("MarkInfo"); + if (!(obj instanceof Dict)) { + return null; + } + const markInfo = { + Marked: false, + UserProperties: false, + Suspects: false + }; + for (const key in markInfo) { + const value = obj.get(key); + if (typeof value === "boolean") { + markInfo[key] = value; + } + } + return markInfo; + } + get structTreeRoot() { + let structTree = null; + try { + structTree = this._readStructTreeRoot(); + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn("Unable read to structTreeRoot info."); + } + return shadow(this, "structTreeRoot", structTree); + } + _readStructTreeRoot() { + const rawObj = this._catDict.getRaw("StructTreeRoot"); + const obj = this.xref.fetchIfRef(rawObj); + if (!(obj instanceof Dict)) { + return null; + } + const root = new StructTreeRoot(obj, rawObj); + root.init(); + return root; + } + get toplevelPagesDict() { + const pagesObj = this._catDict.get("Pages"); + if (!(pagesObj instanceof Dict)) { + throw new FormatError("Invalid top-level pages dictionary."); + } + return shadow(this, "toplevelPagesDict", pagesObj); + } + get documentOutline() { + let obj = null; + try { + obj = this._readDocumentOutline(); + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn("Unable to read document outline."); + } + return shadow(this, "documentOutline", obj); + } + _readDocumentOutline() { + let obj = this._catDict.get("Outlines"); + if (!(obj instanceof Dict)) { + return null; + } + obj = obj.getRaw("First"); + if (!(obj instanceof Ref)) { + return null; + } + const root = { + items: [] + }; + const queue = [{ + obj, + parent: root + }]; + const processed = new RefSet(); + processed.put(obj); + const xref = this.xref, + blackColor = new Uint8ClampedArray(3); + while (queue.length > 0) { + const i = queue.shift(); + const outlineDict = xref.fetchIfRef(i.obj); + if (outlineDict === null) { + continue; + } + if (!outlineDict.has("Title")) { + warn("Invalid outline item encountered."); + } + const data = { + url: null, + dest: null, + action: null + }; + Catalog.parseDestDictionary({ + destDict: outlineDict, + resultObj: data, + docBaseUrl: this.baseUrl, + docAttachments: this.attachments + }); + const title = outlineDict.get("Title"); + const flags = outlineDict.get("F") || 0; + const color = outlineDict.getArray("C"); + const count = outlineDict.get("Count"); + let rgbColor = blackColor; + if (isNumberArray(color, 3) && (color[0] !== 0 || color[1] !== 0 || color[2] !== 0)) { + rgbColor = ColorSpace.singletons.rgb.getRgb(color, 0); + } + const outlineItem = { + action: data.action, + attachment: data.attachment, + dest: data.dest, + url: data.url, + unsafeUrl: data.unsafeUrl, + newWindow: data.newWindow, + setOCGState: data.setOCGState, + title: typeof title === "string" ? stringToPDFString(title) : "", + color: rgbColor, + count: Number.isInteger(count) ? count : undefined, + bold: !!(flags & 2), + italic: !!(flags & 1), + items: [] + }; + i.parent.items.push(outlineItem); + obj = outlineDict.getRaw("First"); + if (obj instanceof Ref && !processed.has(obj)) { + queue.push({ + obj, + parent: outlineItem + }); + processed.put(obj); + } + obj = outlineDict.getRaw("Next"); + if (obj instanceof Ref && !processed.has(obj)) { + queue.push({ + obj, + parent: i.parent + }); + processed.put(obj); + } + } + return root.items.length > 0 ? root.items : null; + } + get permissions() { + let permissions = null; + try { + permissions = this._readPermissions(); + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn("Unable to read permissions."); + } + return shadow(this, "permissions", permissions); + } + _readPermissions() { + const encrypt = this.xref.trailer.get("Encrypt"); + if (!(encrypt instanceof Dict)) { + return null; + } + let flags = encrypt.get("P"); + if (typeof flags !== "number") { + return null; + } + flags += 2 ** 32; + const permissions = []; + for (const key in PermissionFlag) { + const value = PermissionFlag[key]; + if (flags & value) { + permissions.push(value); + } + } + return permissions; + } + get optionalContentConfig() { + let config = null; + try { + const properties = this._catDict.get("OCProperties"); + if (!properties) { + return shadow(this, "optionalContentConfig", null); + } + const defaultConfig = properties.get("D"); + if (!defaultConfig) { + return shadow(this, "optionalContentConfig", null); + } + const groupsData = properties.get("OCGs"); + if (!Array.isArray(groupsData)) { + return shadow(this, "optionalContentConfig", null); + } + const groupRefCache = new RefSetCache(); + for (const groupRef of groupsData) { + if (!(groupRef instanceof Ref) || groupRefCache.has(groupRef)) { + continue; + } + groupRefCache.put(groupRef, this.#readOptionalContentGroup(groupRef)); + } + config = this.#readOptionalContentConfig(defaultConfig, groupRefCache); + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn(`Unable to read optional content config: ${ex}`); + } + return shadow(this, "optionalContentConfig", config); + } + #readOptionalContentGroup(groupRef) { + const group = this.xref.fetch(groupRef); + const obj = { + id: groupRef.toString(), + name: null, + intent: null, + usage: { + print: null, + view: null + }, + rbGroups: [] + }; + const name = group.get("Name"); + if (typeof name === "string") { + obj.name = stringToPDFString(name); + } + let intent = group.getArray("Intent"); + if (!Array.isArray(intent)) { + intent = [intent]; + } + if (intent.every(i => i instanceof Name)) { + obj.intent = intent.map(i => i.name); + } + const usage = group.get("Usage"); + if (!(usage instanceof Dict)) { + return obj; + } + const usageObj = obj.usage; + const print = usage.get("Print"); + if (print instanceof Dict) { + const printState = print.get("PrintState"); + if (printState instanceof Name) { + switch (printState.name) { + case "ON": + case "OFF": + usageObj.print = { + printState: printState.name + }; + } + } + } + const view = usage.get("View"); + if (view instanceof Dict) { + const viewState = view.get("ViewState"); + if (viewState instanceof Name) { + switch (viewState.name) { + case "ON": + case "OFF": + usageObj.view = { + viewState: viewState.name + }; + } + } + } + return obj; + } + #readOptionalContentConfig(config, groupRefCache) { + function parseOnOff(refs) { + const onParsed = []; + if (Array.isArray(refs)) { + for (const value of refs) { + if (value instanceof Ref && groupRefCache.has(value)) { + onParsed.push(value.toString()); + } + } + } + return onParsed; + } + function parseOrder(refs, nestedLevels = 0) { + if (!Array.isArray(refs)) { + return null; + } + const order = []; + for (const value of refs) { + if (value instanceof Ref && groupRefCache.has(value)) { + parsedOrderRefs.put(value); + order.push(value.toString()); + continue; + } + const nestedOrder = parseNestedOrder(value, nestedLevels); + if (nestedOrder) { + order.push(nestedOrder); + } + } + if (nestedLevels > 0) { + return order; + } + const hiddenGroups = []; + for (const [groupRef] of groupRefCache.items()) { + if (parsedOrderRefs.has(groupRef)) { + continue; + } + hiddenGroups.push(groupRef.toString()); + } + if (hiddenGroups.length) { + order.push({ + name: null, + order: hiddenGroups + }); + } + return order; + } + function parseNestedOrder(ref, nestedLevels) { + if (++nestedLevels > MAX_NESTED_LEVELS) { + warn("parseNestedOrder - reached MAX_NESTED_LEVELS."); + return null; + } + const value = xref.fetchIfRef(ref); + if (!Array.isArray(value)) { + return null; + } + const nestedName = xref.fetchIfRef(value[0]); + if (typeof nestedName !== "string") { + return null; + } + const nestedOrder = parseOrder(value.slice(1), nestedLevels); + if (!nestedOrder?.length) { + return null; + } + return { + name: stringToPDFString(nestedName), + order: nestedOrder + }; + } + function parseRBGroups(rbGroups) { + if (!Array.isArray(rbGroups)) { + return; + } + for (const value of rbGroups) { + const rbGroup = xref.fetchIfRef(value); + if (!Array.isArray(rbGroup) || !rbGroup.length) { + continue; + } + const parsedRbGroup = new Set(); + for (const ref of rbGroup) { + if (ref instanceof Ref && groupRefCache.has(ref) && !parsedRbGroup.has(ref.toString())) { + parsedRbGroup.add(ref.toString()); + groupRefCache.get(ref).rbGroups.push(parsedRbGroup); + } + } + } + } + const xref = this.xref, + parsedOrderRefs = new RefSet(), + MAX_NESTED_LEVELS = 10; + parseRBGroups(config.get("RBGroups")); + return { + name: typeof config.get("Name") === "string" ? stringToPDFString(config.get("Name")) : null, + creator: typeof config.get("Creator") === "string" ? stringToPDFString(config.get("Creator")) : null, + baseState: config.get("BaseState") instanceof Name ? config.get("BaseState").name : null, + on: parseOnOff(config.get("ON")), + off: parseOnOff(config.get("OFF")), + order: parseOrder(config.get("Order")), + groups: [...groupRefCache] + }; + } + setActualNumPages(num = null) { + this._actualNumPages = num; + } + get hasActualNumPages() { + return this._actualNumPages !== null; + } + get _pagesCount() { + const obj = this.toplevelPagesDict.get("Count"); + if (!Number.isInteger(obj)) { + throw new FormatError("Page count in top-level pages dictionary is not an integer."); + } + return shadow(this, "_pagesCount", obj); + } + get numPages() { + return this.hasActualNumPages ? this._actualNumPages : this._pagesCount; + } + get destinations() { + const obj = this._readDests(), + dests = Object.create(null); + if (obj instanceof NameTree) { + for (const [key, value] of obj.getAll()) { + const dest = fetchDest(value); + if (dest) { + dests[stringToPDFString(key)] = dest; + } + } + } else if (obj instanceof Dict) { + for (const [key, value] of obj) { + const dest = fetchDest(value); + if (dest) { + dests[key] = dest; + } + } + } + return shadow(this, "destinations", dests); + } + getDestination(id) { + const obj = this._readDests(); + if (obj instanceof NameTree) { + const dest = fetchDest(obj.get(id)); + if (dest) { + return dest; + } + const allDest = this.destinations[id]; + if (allDest) { + warn(`Found "${id}" at an incorrect position in the NameTree.`); + return allDest; + } + } else if (obj instanceof Dict) { + const dest = fetchDest(obj.get(id)); + if (dest) { + return dest; + } + } + return null; + } + _readDests() { + const obj = this._catDict.get("Names"); + if (obj?.has("Dests")) { + return new NameTree(obj.getRaw("Dests"), this.xref); + } else if (this._catDict.has("Dests")) { + return this._catDict.get("Dests"); + } + return undefined; + } + get pageLabels() { + let obj = null; + try { + obj = this._readPageLabels(); + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn("Unable to read page labels."); + } + return shadow(this, "pageLabels", obj); + } + _readPageLabels() { + const obj = this._catDict.getRaw("PageLabels"); + if (!obj) { + return null; + } + const pageLabels = new Array(this.numPages); + let style = null, + prefix = ""; + const numberTree = new NumberTree(obj, this.xref); + const nums = numberTree.getAll(); + let currentLabel = "", + currentIndex = 1; + for (let i = 0, ii = this.numPages; i < ii; i++) { + const labelDict = nums.get(i); + if (labelDict !== undefined) { + if (!(labelDict instanceof Dict)) { + throw new FormatError("PageLabel is not a dictionary."); + } + if (labelDict.has("Type") && !isName(labelDict.get("Type"), "PageLabel")) { + throw new FormatError("Invalid type in PageLabel dictionary."); + } + if (labelDict.has("S")) { + const s = labelDict.get("S"); + if (!(s instanceof Name)) { + throw new FormatError("Invalid style in PageLabel dictionary."); + } + style = s.name; + } else { + style = null; + } + if (labelDict.has("P")) { + const p = labelDict.get("P"); + if (typeof p !== "string") { + throw new FormatError("Invalid prefix in PageLabel dictionary."); + } + prefix = stringToPDFString(p); + } else { + prefix = ""; + } + if (labelDict.has("St")) { + const st = labelDict.get("St"); + if (!(Number.isInteger(st) && st >= 1)) { + throw new FormatError("Invalid start in PageLabel dictionary."); + } + currentIndex = st; + } else { + currentIndex = 1; + } + } + switch (style) { + case "D": + currentLabel = currentIndex; + break; + case "R": + case "r": + currentLabel = toRomanNumerals(currentIndex, style === "r"); + break; + case "A": + case "a": + const LIMIT = 26; + const A_UPPER_CASE = 0x41, + A_LOWER_CASE = 0x61; + const baseCharCode = style === "a" ? A_LOWER_CASE : A_UPPER_CASE; + const letterIndex = currentIndex - 1; + const character = String.fromCharCode(baseCharCode + letterIndex % LIMIT); + currentLabel = character.repeat(Math.floor(letterIndex / LIMIT) + 1); + break; + default: + if (style) { + throw new FormatError(`Invalid style "${style}" in PageLabel dictionary.`); + } + currentLabel = ""; + } + pageLabels[i] = prefix + currentLabel; + currentIndex++; + } + return pageLabels; + } + get pageLayout() { + const obj = this._catDict.get("PageLayout"); + let pageLayout = ""; + if (obj instanceof Name) { + switch (obj.name) { + case "SinglePage": + case "OneColumn": + case "TwoColumnLeft": + case "TwoColumnRight": + case "TwoPageLeft": + case "TwoPageRight": + pageLayout = obj.name; + } + } + return shadow(this, "pageLayout", pageLayout); + } + get pageMode() { + const obj = this._catDict.get("PageMode"); + let pageMode = "UseNone"; + if (obj instanceof Name) { + switch (obj.name) { + case "UseNone": + case "UseOutlines": + case "UseThumbs": + case "FullScreen": + case "UseOC": + case "UseAttachments": + pageMode = obj.name; + } + } + return shadow(this, "pageMode", pageMode); + } + get viewerPreferences() { + const obj = this._catDict.get("ViewerPreferences"); + if (!(obj instanceof Dict)) { + return shadow(this, "viewerPreferences", null); + } + let prefs = null; + for (const key of obj.getKeys()) { + const value = obj.get(key); + let prefValue; + switch (key) { + case "HideToolbar": + case "HideMenubar": + case "HideWindowUI": + case "FitWindow": + case "CenterWindow": + case "DisplayDocTitle": + case "PickTrayByPDFSize": + if (typeof value === "boolean") { + prefValue = value; + } + break; + case "NonFullScreenPageMode": + if (value instanceof Name) { + switch (value.name) { + case "UseNone": + case "UseOutlines": + case "UseThumbs": + case "UseOC": + prefValue = value.name; + break; + default: + prefValue = "UseNone"; + } + } + break; + case "Direction": + if (value instanceof Name) { + switch (value.name) { + case "L2R": + case "R2L": + prefValue = value.name; + break; + default: + prefValue = "L2R"; + } + } + break; + case "ViewArea": + case "ViewClip": + case "PrintArea": + case "PrintClip": + if (value instanceof Name) { + switch (value.name) { + case "MediaBox": + case "CropBox": + case "BleedBox": + case "TrimBox": + case "ArtBox": + prefValue = value.name; + break; + default: + prefValue = "CropBox"; + } + } + break; + case "PrintScaling": + if (value instanceof Name) { + switch (value.name) { + case "None": + case "AppDefault": + prefValue = value.name; + break; + default: + prefValue = "AppDefault"; + } + } + break; + case "Duplex": + if (value instanceof Name) { + switch (value.name) { + case "Simplex": + case "DuplexFlipShortEdge": + case "DuplexFlipLongEdge": + prefValue = value.name; + break; + default: + prefValue = "None"; + } + } + break; + case "PrintPageRange": + if (Array.isArray(value) && value.length % 2 === 0) { + const isValid = value.every((page, i, arr) => Number.isInteger(page) && page > 0 && (i === 0 || page >= arr[i - 1]) && page <= this.numPages); + if (isValid) { + prefValue = value; + } + } + break; + case "NumCopies": + if (Number.isInteger(value) && value > 0) { + prefValue = value; + } + break; + default: + warn(`Ignoring non-standard key in ViewerPreferences: ${key}.`); + continue; + } + if (prefValue === undefined) { + warn(`Bad value, for key "${key}", in ViewerPreferences: ${value}.`); + continue; + } + if (!prefs) { + prefs = Object.create(null); + } + prefs[key] = prefValue; + } + return shadow(this, "viewerPreferences", prefs); + } + get openAction() { + const obj = this._catDict.get("OpenAction"); + const openAction = Object.create(null); + if (obj instanceof Dict) { + const destDict = new Dict(this.xref); + destDict.set("A", obj); + const resultObj = { + url: null, + dest: null, + action: null + }; + Catalog.parseDestDictionary({ + destDict, + resultObj + }); + if (Array.isArray(resultObj.dest)) { + openAction.dest = resultObj.dest; + } else if (resultObj.action) { + openAction.action = resultObj.action; + } + } else if (Array.isArray(obj)) { + openAction.dest = obj; + } + return shadow(this, "openAction", objectSize(openAction) > 0 ? openAction : null); + } + get attachments() { + const obj = this._catDict.get("Names"); + let attachments = null; + if (obj instanceof Dict && obj.has("EmbeddedFiles")) { + const nameTree = new NameTree(obj.getRaw("EmbeddedFiles"), this.xref); + for (const [key, value] of nameTree.getAll()) { + const fs = new FileSpec(value, this.xref); + if (!attachments) { + attachments = Object.create(null); + } + attachments[stringToPDFString(key)] = fs.serializable; + } + } + return shadow(this, "attachments", attachments); + } + get xfaImages() { + const obj = this._catDict.get("Names"); + let xfaImages = null; + if (obj instanceof Dict && obj.has("XFAImages")) { + const nameTree = new NameTree(obj.getRaw("XFAImages"), this.xref); + for (const [key, value] of nameTree.getAll()) { + if (!xfaImages) { + xfaImages = new Dict(this.xref); + } + xfaImages.set(stringToPDFString(key), value); + } + } + return shadow(this, "xfaImages", xfaImages); + } + _collectJavaScript() { + const obj = this._catDict.get("Names"); + let javaScript = null; + function appendIfJavaScriptDict(name, jsDict) { + if (!(jsDict instanceof Dict)) { + return; + } + if (!isName(jsDict.get("S"), "JavaScript")) { + return; + } + let js = jsDict.get("JS"); + if (js instanceof BaseStream) { + js = js.getString(); + } else if (typeof js !== "string") { + return; + } + js = stringToPDFString(js).replaceAll("\x00", ""); + if (js) { + (javaScript ||= new Map()).set(name, js); + } + } + if (obj instanceof Dict && obj.has("JavaScript")) { + const nameTree = new NameTree(obj.getRaw("JavaScript"), this.xref); + for (const [key, value] of nameTree.getAll()) { + appendIfJavaScriptDict(stringToPDFString(key), value); + } + } + const openAction = this._catDict.get("OpenAction"); + if (openAction) { + appendIfJavaScriptDict("OpenAction", openAction); + } + return javaScript; + } + get jsActions() { + const javaScript = this._collectJavaScript(); + let actions = collectActions(this.xref, this._catDict, DocumentActionEventType); + if (javaScript) { + actions ||= Object.create(null); + for (const [key, val] of javaScript) { + if (key in actions) { + actions[key].push(val); + } else { + actions[key] = [val]; + } + } + } + return shadow(this, "jsActions", actions); + } + async fontFallback(id, handler) { + const translatedFonts = await Promise.all(this.fontCache); + for (const translatedFont of translatedFonts) { + if (translatedFont.loadedName === id) { + translatedFont.fallback(handler); + return; + } + } + } + async cleanup(manuallyTriggered = false) { + clearGlobalCaches(); + this.globalImageCache.clear(manuallyTriggered); + this.pageKidsCountCache.clear(); + this.pageIndexCache.clear(); + this.pageDictCache.clear(); + this.nonBlendModesSet.clear(); + const translatedFonts = await Promise.all(this.fontCache); + for (const { + dict + } of translatedFonts) { + delete dict.cacheKey; + } + this.fontCache.clear(); + this.builtInCMapCache.clear(); + this.standardFontDataCache.clear(); + this.systemFontCache.clear(); + } + async getPageDict(pageIndex) { + const nodesToVisit = [this.toplevelPagesDict]; + const visitedNodes = new RefSet(); + const pagesRef = this._catDict.getRaw("Pages"); + if (pagesRef instanceof Ref) { + visitedNodes.put(pagesRef); + } + const xref = this.xref, + pageKidsCountCache = this.pageKidsCountCache, + pageIndexCache = this.pageIndexCache, + pageDictCache = this.pageDictCache; + let currentPageIndex = 0; + while (nodesToVisit.length) { + const currentNode = nodesToVisit.pop(); + if (currentNode instanceof Ref) { + const count = pageKidsCountCache.get(currentNode); + if (count >= 0 && currentPageIndex + count <= pageIndex) { + currentPageIndex += count; + continue; + } + if (visitedNodes.has(currentNode)) { + throw new FormatError("Pages tree contains circular reference."); + } + visitedNodes.put(currentNode); + const obj = await (pageDictCache.get(currentNode) || xref.fetchAsync(currentNode)); + if (obj instanceof Dict) { + let type = obj.getRaw("Type"); + if (type instanceof Ref) { + type = await xref.fetchAsync(type); + } + if (isName(type, "Page") || !obj.has("Kids")) { + if (!pageKidsCountCache.has(currentNode)) { + pageKidsCountCache.put(currentNode, 1); + } + if (!pageIndexCache.has(currentNode)) { + pageIndexCache.put(currentNode, currentPageIndex); + } + if (currentPageIndex === pageIndex) { + return [obj, currentNode]; + } + currentPageIndex++; + continue; + } + } + nodesToVisit.push(obj); + continue; + } + if (!(currentNode instanceof Dict)) { + throw new FormatError("Page dictionary kid reference points to wrong type of object."); + } + const { + objId + } = currentNode; + let count = currentNode.getRaw("Count"); + if (count instanceof Ref) { + count = await xref.fetchAsync(count); + } + if (Number.isInteger(count) && count >= 0) { + if (objId && !pageKidsCountCache.has(objId)) { + pageKidsCountCache.put(objId, count); + } + if (currentPageIndex + count <= pageIndex) { + currentPageIndex += count; + continue; + } + } + let kids = currentNode.getRaw("Kids"); + if (kids instanceof Ref) { + kids = await xref.fetchAsync(kids); + } + if (!Array.isArray(kids)) { + let type = currentNode.getRaw("Type"); + if (type instanceof Ref) { + type = await xref.fetchAsync(type); + } + if (isName(type, "Page") || !currentNode.has("Kids")) { + if (currentPageIndex === pageIndex) { + return [currentNode, null]; + } + currentPageIndex++; + continue; + } + throw new FormatError("Page dictionary kids object is not an array."); + } + for (let last = kids.length - 1; last >= 0; last--) { + const lastKid = kids[last]; + nodesToVisit.push(lastKid); + if (currentNode === this.toplevelPagesDict && lastKid instanceof Ref && !pageDictCache.has(lastKid)) { + pageDictCache.put(lastKid, xref.fetchAsync(lastKid)); + } + } + } + throw new Error(`Page index ${pageIndex} not found.`); + } + async getAllPageDicts(recoveryMode = false) { + const { + ignoreErrors + } = this.pdfManager.evaluatorOptions; + const queue = [{ + currentNode: this.toplevelPagesDict, + posInKids: 0 + }]; + const visitedNodes = new RefSet(); + const pagesRef = this._catDict.getRaw("Pages"); + if (pagesRef instanceof Ref) { + visitedNodes.put(pagesRef); + } + const map = new Map(), + xref = this.xref, + pageIndexCache = this.pageIndexCache; + let pageIndex = 0; + function addPageDict(pageDict, pageRef) { + if (pageRef && !pageIndexCache.has(pageRef)) { + pageIndexCache.put(pageRef, pageIndex); + } + map.set(pageIndex++, [pageDict, pageRef]); + } + function addPageError(error) { + if (error instanceof XRefEntryException && !recoveryMode) { + throw error; + } + if (recoveryMode && ignoreErrors && pageIndex === 0) { + warn(`getAllPageDicts - Skipping invalid first page: "${error}".`); + error = Dict.empty; + } + map.set(pageIndex++, [error, null]); + } + while (queue.length > 0) { + const queueItem = queue.at(-1); + const { + currentNode, + posInKids + } = queueItem; + let kids = currentNode.getRaw("Kids"); + if (kids instanceof Ref) { + try { + kids = await xref.fetchAsync(kids); + } catch (ex) { + addPageError(ex); + break; + } + } + if (!Array.isArray(kids)) { + addPageError(new FormatError("Page dictionary kids object is not an array.")); + break; + } + if (posInKids >= kids.length) { + queue.pop(); + continue; + } + const kidObj = kids[posInKids]; + let obj; + if (kidObj instanceof Ref) { + if (visitedNodes.has(kidObj)) { + addPageError(new FormatError("Pages tree contains circular reference.")); + break; + } + visitedNodes.put(kidObj); + try { + obj = await xref.fetchAsync(kidObj); + } catch (ex) { + addPageError(ex); + break; + } + } else { + obj = kidObj; + } + if (!(obj instanceof Dict)) { + addPageError(new FormatError("Page dictionary kid reference points to wrong type of object.")); + break; + } + let type = obj.getRaw("Type"); + if (type instanceof Ref) { + try { + type = await xref.fetchAsync(type); + } catch (ex) { + addPageError(ex); + break; + } + } + if (isName(type, "Page") || !obj.has("Kids")) { + addPageDict(obj, kidObj instanceof Ref ? kidObj : null); + } else { + queue.push({ + currentNode: obj, + posInKids: 0 + }); + } + queueItem.posInKids++; + } + return map; + } + getPageIndex(pageRef) { + const cachedPageIndex = this.pageIndexCache.get(pageRef); + if (cachedPageIndex !== undefined) { + return Promise.resolve(cachedPageIndex); + } + const xref = this.xref; + function pagesBeforeRef(kidRef) { + let total = 0, + parentRef; + return xref.fetchAsync(kidRef).then(function (node) { + if (isRefsEqual(kidRef, pageRef) && !isDict(node, "Page") && !(node instanceof Dict && !node.has("Type") && node.has("Contents"))) { + throw new FormatError("The reference does not point to a /Page dictionary."); + } + if (!node) { + return null; + } + if (!(node instanceof Dict)) { + throw new FormatError("Node must be a dictionary."); + } + parentRef = node.getRaw("Parent"); + return node.getAsync("Parent"); + }).then(function (parent) { + if (!parent) { + return null; + } + if (!(parent instanceof Dict)) { + throw new FormatError("Parent must be a dictionary."); + } + return parent.getAsync("Kids"); + }).then(function (kids) { + if (!kids) { + return null; + } + const kidPromises = []; + let found = false; + for (const kid of kids) { + if (!(kid instanceof Ref)) { + throw new FormatError("Kid must be a reference."); + } + if (isRefsEqual(kid, kidRef)) { + found = true; + break; + } + kidPromises.push(xref.fetchAsync(kid).then(function (obj) { + if (!(obj instanceof Dict)) { + throw new FormatError("Kid node must be a dictionary."); + } + if (obj.has("Count")) { + total += obj.get("Count"); + } else { + total++; + } + })); + } + if (!found) { + throw new FormatError("Kid reference not found in parent's kids."); + } + return Promise.all(kidPromises).then(function () { + return [total, parentRef]; + }); + }); + } + let total = 0; + const next = ref => pagesBeforeRef(ref).then(args => { + if (!args) { + this.pageIndexCache.put(pageRef, total); + return total; + } + const [count, parentRef] = args; + total += count; + return next(parentRef); + }); + return next(pageRef); + } + get baseUrl() { + const uri = this._catDict.get("URI"); + if (uri instanceof Dict) { + const base = uri.get("Base"); + if (typeof base === "string") { + const absoluteUrl = createValidAbsoluteUrl(base, null, { + tryConvertEncoding: true + }); + if (absoluteUrl) { + return shadow(this, "baseUrl", absoluteUrl.href); + } + } + } + return shadow(this, "baseUrl", this.pdfManager.docBaseUrl); + } + static parseDestDictionary({ + destDict, + resultObj, + docBaseUrl = null, + docAttachments = null + }) { + if (!(destDict instanceof Dict)) { + warn("parseDestDictionary: `destDict` must be a dictionary."); + return; + } + let action = destDict.get("A"), + url, + dest; + if (!(action instanceof Dict)) { + if (destDict.has("Dest")) { + action = destDict.get("Dest"); + } else { + action = destDict.get("AA"); + if (action instanceof Dict) { + if (action.has("D")) { + action = action.get("D"); + } else if (action.has("U")) { + action = action.get("U"); + } + } + } + } + if (action instanceof Dict) { + const actionType = action.get("S"); + if (!(actionType instanceof Name)) { + warn("parseDestDictionary: Invalid type in Action dictionary."); + return; + } + const actionName = actionType.name; + switch (actionName) { + case "ResetForm": + const flags = action.get("Flags"); + const include = ((typeof flags === "number" ? flags : 0) & 1) === 0; + const fields = []; + const refs = []; + for (const obj of action.get("Fields") || []) { + if (obj instanceof Ref) { + refs.push(obj.toString()); + } else if (typeof obj === "string") { + fields.push(stringToPDFString(obj)); + } + } + resultObj.resetForm = { + fields, + refs, + include + }; + break; + case "URI": + url = action.get("URI"); + if (url instanceof Name) { + url = "/" + url.name; + } + break; + case "GoTo": + dest = action.get("D"); + break; + case "Launch": + case "GoToR": + const urlDict = action.get("F"); + if (urlDict instanceof Dict) { + const fs = new FileSpec(urlDict, null, true); + const { + rawFilename + } = fs.serializable; + url = rawFilename; + } else if (typeof urlDict === "string") { + url = urlDict; + } + const remoteDest = fetchRemoteDest(action); + if (remoteDest && typeof url === "string") { + url = url.split("#", 1)[0] + "#" + remoteDest; + } + const newWindow = action.get("NewWindow"); + if (typeof newWindow === "boolean") { + resultObj.newWindow = newWindow; + } + break; + case "GoToE": + const target = action.get("T"); + let attachment; + if (docAttachments && target instanceof Dict) { + const relationship = target.get("R"); + const name = target.get("N"); + if (isName(relationship, "C") && typeof name === "string") { + attachment = docAttachments[stringToPDFString(name)]; + } + } + if (attachment) { + resultObj.attachment = attachment; + const attachmentDest = fetchRemoteDest(action); + if (attachmentDest) { + resultObj.attachmentDest = attachmentDest; + } + } else { + warn(`parseDestDictionary - unimplemented "GoToE" action.`); + } + break; + case "Named": + const namedAction = action.get("N"); + if (namedAction instanceof Name) { + resultObj.action = namedAction.name; + } + break; + case "SetOCGState": + const state = action.get("State"); + const preserveRB = action.get("PreserveRB"); + if (!Array.isArray(state) || state.length === 0) { + break; + } + const stateArr = []; + for (const elem of state) { + if (elem instanceof Name) { + switch (elem.name) { + case "ON": + case "OFF": + case "Toggle": + stateArr.push(elem.name); + break; + } + } else if (elem instanceof Ref) { + stateArr.push(elem.toString()); + } + } + if (stateArr.length !== state.length) { + break; + } + resultObj.setOCGState = { + state: stateArr, + preserveRB: typeof preserveRB === "boolean" ? preserveRB : true + }; + break; + case "JavaScript": + const jsAction = action.get("JS"); + let js; + if (jsAction instanceof BaseStream) { + js = jsAction.getString(); + } else if (typeof jsAction === "string") { + js = jsAction; + } + const jsURL = js && recoverJsURL(stringToPDFString(js)); + if (jsURL) { + url = jsURL.url; + resultObj.newWindow = jsURL.newWindow; + break; + } + default: + if (actionName === "JavaScript" || actionName === "SubmitForm") { + break; + } + warn(`parseDestDictionary - unsupported action: "${actionName}".`); + break; + } + } else if (destDict.has("Dest")) { + dest = destDict.get("Dest"); + } + if (typeof url === "string") { + const absoluteUrl = createValidAbsoluteUrl(url, docBaseUrl, { + addDefaultProtocol: true, + tryConvertEncoding: true + }); + if (absoluteUrl) { + resultObj.url = absoluteUrl.href; + } + resultObj.unsafeUrl = url; + } + if (dest) { + if (dest instanceof Name) { + dest = dest.name; + } + if (typeof dest === "string") { + resultObj.dest = stringToPDFString(dest); + } else if (isValidExplicitDest(dest)) { + resultObj.dest = dest; + } + } + } +} + +;// ./src/core/object_loader.js + + + + +function mayHaveChildren(value) { + return value instanceof Ref || value instanceof Dict || value instanceof BaseStream || Array.isArray(value); +} +function addChildren(node, nodesToVisit) { + if (node instanceof Dict) { + node = node.getRawValues(); + } else if (node instanceof BaseStream) { + node = node.dict.getRawValues(); + } else if (!Array.isArray(node)) { + return; + } + for (const rawValue of node) { + if (mayHaveChildren(rawValue)) { + nodesToVisit.push(rawValue); + } + } +} +class ObjectLoader { + constructor(dict, keys, xref) { + this.dict = dict; + this.keys = keys; + this.xref = xref; + this.refSet = null; + } + async load() { + if (this.xref.stream.isDataLoaded) { + return undefined; + } + const { + keys, + dict + } = this; + this.refSet = new RefSet(); + const nodesToVisit = []; + for (const key of keys) { + const rawValue = dict.getRaw(key); + if (rawValue !== undefined) { + nodesToVisit.push(rawValue); + } + } + return this._walk(nodesToVisit); + } + async _walk(nodesToVisit) { + const nodesToRevisit = []; + const pendingRequests = []; + while (nodesToVisit.length) { + let currentNode = nodesToVisit.pop(); + if (currentNode instanceof Ref) { + if (this.refSet.has(currentNode)) { + continue; + } + try { + this.refSet.put(currentNode); + currentNode = this.xref.fetch(currentNode); + } catch (ex) { + if (!(ex instanceof MissingDataException)) { + warn(`ObjectLoader._walk - requesting all data: "${ex}".`); + this.refSet = null; + const { + manager + } = this.xref.stream; + return manager.requestAllChunks(); + } + nodesToRevisit.push(currentNode); + pendingRequests.push({ + begin: ex.begin, + end: ex.end + }); + } + } + if (currentNode instanceof BaseStream) { + const baseStreams = currentNode.getBaseStreams(); + if (baseStreams) { + let foundMissingData = false; + for (const stream of baseStreams) { + if (stream.isDataLoaded) { + continue; + } + foundMissingData = true; + pendingRequests.push({ + begin: stream.start, + end: stream.end + }); + } + if (foundMissingData) { + nodesToRevisit.push(currentNode); + } + } + } + addChildren(currentNode, nodesToVisit); + } + if (pendingRequests.length) { + await this.xref.stream.manager.requestRanges(pendingRequests); + for (const node of nodesToRevisit) { + if (node instanceof Ref) { + this.refSet.remove(node); + } + } + return this._walk(nodesToRevisit); + } + this.refSet = null; + return undefined; + } +} + +;// ./src/core/xfa/symbol_utils.js +const $acceptWhitespace = Symbol(); +const $addHTML = Symbol(); +const $appendChild = Symbol(); +const $childrenToHTML = Symbol(); +const $clean = Symbol(); +const $cleanPage = Symbol(); +const $cleanup = Symbol(); +const $clone = Symbol(); +const $consumed = Symbol(); +const $content = Symbol("content"); +const $data = Symbol("data"); +const $dump = Symbol(); +const $extra = Symbol("extra"); +const $finalize = Symbol(); +const $flushHTML = Symbol(); +const $getAttributeIt = Symbol(); +const $getAttributes = Symbol(); +const $getAvailableSpace = Symbol(); +const $getChildrenByClass = Symbol(); +const $getChildrenByName = Symbol(); +const $getChildrenByNameIt = Symbol(); +const $getDataValue = Symbol(); +const $getExtra = Symbol(); +const $getRealChildrenByNameIt = Symbol(); +const $getChildren = Symbol(); +const $getContainedChildren = Symbol(); +const $getNextPage = Symbol(); +const $getSubformParent = Symbol(); +const $getParent = Symbol(); +const $getTemplateRoot = Symbol(); +const $globalData = Symbol(); +const $hasSettableValue = Symbol(); +const $ids = Symbol(); +const $indexOf = Symbol(); +const $insertAt = Symbol(); +const $isCDATAXml = Symbol(); +const $isBindable = Symbol(); +const $isDataValue = Symbol(); +const $isDescendent = Symbol(); +const $isNsAgnostic = Symbol(); +const $isSplittable = Symbol(); +const $isThereMoreWidth = Symbol(); +const $isTransparent = Symbol(); +const $isUsable = Symbol(); +const $lastAttribute = Symbol(); +const $namespaceId = Symbol("namespaceId"); +const $nodeName = Symbol("nodeName"); +const $nsAttributes = Symbol(); +const $onChild = Symbol(); +const $onChildCheck = Symbol(); +const $onText = Symbol(); +const $pushGlyphs = Symbol(); +const $popPara = Symbol(); +const $pushPara = Symbol(); +const $removeChild = Symbol(); +const $root = Symbol("root"); +const $resolvePrototypes = Symbol(); +const $searchNode = Symbol(); +const $setId = Symbol(); +const $setSetAttributes = Symbol(); +const $setValue = Symbol(); +const $tabIndex = Symbol(); +const $text = Symbol(); +const $toPages = Symbol(); +const $toHTML = Symbol(); +const $toString = Symbol(); +const $toStyle = Symbol(); +const $uid = Symbol("uid"); + +;// ./src/core/xfa/namespaces.js +const $buildXFAObject = Symbol(); +const NamespaceIds = { + config: { + id: 0, + check: ns => ns.startsWith("http://www.xfa.org/schema/xci/") + }, + connectionSet: { + id: 1, + check: ns => ns.startsWith("http://www.xfa.org/schema/xfa-connection-set/") + }, + datasets: { + id: 2, + check: ns => ns.startsWith("http://www.xfa.org/schema/xfa-data/") + }, + form: { + id: 3, + check: ns => ns.startsWith("http://www.xfa.org/schema/xfa-form/") + }, + localeSet: { + id: 4, + check: ns => ns.startsWith("http://www.xfa.org/schema/xfa-locale-set/") + }, + pdf: { + id: 5, + check: ns => ns === "http://ns.adobe.com/xdp/pdf/" + }, + signature: { + id: 6, + check: ns => ns === "http://www.w3.org/2000/09/xmldsig#" + }, + sourceSet: { + id: 7, + check: ns => ns.startsWith("http://www.xfa.org/schema/xfa-source-set/") + }, + stylesheet: { + id: 8, + check: ns => ns === "http://www.w3.org/1999/XSL/Transform" + }, + template: { + id: 9, + check: ns => ns.startsWith("http://www.xfa.org/schema/xfa-template/") + }, + xdc: { + id: 10, + check: ns => ns.startsWith("http://www.xfa.org/schema/xdc/") + }, + xdp: { + id: 11, + check: ns => ns === "http://ns.adobe.com/xdp/" + }, + xfdf: { + id: 12, + check: ns => ns === "http://ns.adobe.com/xfdf/" + }, + xhtml: { + id: 13, + check: ns => ns === "http://www.w3.org/1999/xhtml" + }, + xmpmeta: { + id: 14, + check: ns => ns === "http://ns.adobe.com/xmpmeta/" + } +}; + +;// ./src/core/xfa/utils.js + +const dimConverters = { + pt: x => x, + cm: x => x / 2.54 * 72, + mm: x => x / (10 * 2.54) * 72, + in: x => x * 72, + px: x => x +}; +const measurementPattern = /([+-]?\d+\.?\d*)(.*)/; +function stripQuotes(str) { + if (str.startsWith("'") || str.startsWith('"')) { + return str.slice(1, -1); + } + return str; +} +function getInteger({ + data, + defaultValue, + validate +}) { + if (!data) { + return defaultValue; + } + data = data.trim(); + const n = parseInt(data, 10); + if (!isNaN(n) && validate(n)) { + return n; + } + return defaultValue; +} +function getFloat({ + data, + defaultValue, + validate +}) { + if (!data) { + return defaultValue; + } + data = data.trim(); + const n = parseFloat(data); + if (!isNaN(n) && validate(n)) { + return n; + } + return defaultValue; +} +function getKeyword({ + data, + defaultValue, + validate +}) { + if (!data) { + return defaultValue; + } + data = data.trim(); + if (validate(data)) { + return data; + } + return defaultValue; +} +function getStringOption(data, options) { + return getKeyword({ + data, + defaultValue: options[0], + validate: k => options.includes(k) + }); +} +function getMeasurement(str, def = "0") { + def ||= "0"; + if (!str) { + return getMeasurement(def); + } + const match = str.trim().match(measurementPattern); + if (!match) { + return getMeasurement(def); + } + const [, valueStr, unit] = match; + const value = parseFloat(valueStr); + if (isNaN(value)) { + return getMeasurement(def); + } + if (value === 0) { + return 0; + } + const conv = dimConverters[unit]; + if (conv) { + return conv(value); + } + return value; +} +function getRatio(data) { + if (!data) { + return { + num: 1, + den: 1 + }; + } + const ratio = data.trim().split(/\s*:\s*/).map(x => parseFloat(x)).filter(x => !isNaN(x)); + if (ratio.length === 1) { + ratio.push(1); + } + if (ratio.length === 0) { + return { + num: 1, + den: 1 + }; + } + const [num, den] = ratio; + return { + num, + den + }; +} +function getRelevant(data) { + if (!data) { + return []; + } + return data.trim().split(/\s+/).map(e => ({ + excluded: e[0] === "-", + viewname: e.substring(1) + })); +} +function getColor(data, def = [0, 0, 0]) { + let [r, g, b] = def; + if (!data) { + return { + r, + g, + b + }; + } + const color = data.trim().split(/\s*,\s*/).map(c => Math.min(Math.max(0, parseInt(c.trim(), 10)), 255)).map(c => isNaN(c) ? 0 : c); + if (color.length < 3) { + return { + r, + g, + b + }; + } + [r, g, b] = color; + return { + r, + g, + b + }; +} +function getBBox(data) { + const def = -1; + if (!data) { + return { + x: def, + y: def, + width: def, + height: def + }; + } + const bbox = data.trim().split(/\s*,\s*/).map(m => getMeasurement(m, "-1")); + if (bbox.length < 4 || bbox[2] < 0 || bbox[3] < 0) { + return { + x: def, + y: def, + width: def, + height: def + }; + } + const [x, y, width, height] = bbox; + return { + x, + y, + width, + height + }; +} +class HTMLResult { + static get FAILURE() { + return shadow(this, "FAILURE", new HTMLResult(false, null, null, null)); + } + static get EMPTY() { + return shadow(this, "EMPTY", new HTMLResult(true, null, null, null)); + } + constructor(success, html, bbox, breakNode) { + this.success = success; + this.html = html; + this.bbox = bbox; + this.breakNode = breakNode; + } + isBreak() { + return !!this.breakNode; + } + static breakNode(node) { + return new HTMLResult(false, null, null, node); + } + static success(html, bbox = null) { + return new HTMLResult(true, html, bbox, null); + } +} + +;// ./src/core/xfa/fonts.js + + + +class FontFinder { + constructor(pdfFonts) { + this.fonts = new Map(); + this.cache = new Map(); + this.warned = new Set(); + this.defaultFont = null; + this.add(pdfFonts); + } + add(pdfFonts, reallyMissingFonts = null) { + for (const pdfFont of pdfFonts) { + this.addPdfFont(pdfFont); + } + for (const pdfFont of this.fonts.values()) { + if (!pdfFont.regular) { + pdfFont.regular = pdfFont.italic || pdfFont.bold || pdfFont.bolditalic; + } + } + if (!reallyMissingFonts || reallyMissingFonts.size === 0) { + return; + } + const myriad = this.fonts.get("PdfJS-Fallback-PdfJS-XFA"); + for (const missing of reallyMissingFonts) { + this.fonts.set(missing, myriad); + } + } + addPdfFont(pdfFont) { + const cssFontInfo = pdfFont.cssFontInfo; + const name = cssFontInfo.fontFamily; + let font = this.fonts.get(name); + if (!font) { + font = Object.create(null); + this.fonts.set(name, font); + if (!this.defaultFont) { + this.defaultFont = font; + } + } + let property = ""; + const fontWeight = parseFloat(cssFontInfo.fontWeight); + if (parseFloat(cssFontInfo.italicAngle) !== 0) { + property = fontWeight >= 700 ? "bolditalic" : "italic"; + } else if (fontWeight >= 700) { + property = "bold"; + } + if (!property) { + if (pdfFont.name.includes("Bold") || pdfFont.psName?.includes("Bold")) { + property = "bold"; + } + if (pdfFont.name.includes("Italic") || pdfFont.name.endsWith("It") || pdfFont.psName?.includes("Italic") || pdfFont.psName?.endsWith("It")) { + property += "italic"; + } + } + if (!property) { + property = "regular"; + } + font[property] = pdfFont; + } + getDefault() { + return this.defaultFont; + } + find(fontName, mustWarn = true) { + let font = this.fonts.get(fontName) || this.cache.get(fontName); + if (font) { + return font; + } + const pattern = /,|-|_| |bolditalic|bold|italic|regular|it/gi; + let name = fontName.replaceAll(pattern, ""); + font = this.fonts.get(name); + if (font) { + this.cache.set(fontName, font); + return font; + } + name = name.toLowerCase(); + const maybe = []; + for (const [family, pdfFont] of this.fonts.entries()) { + if (family.replaceAll(pattern, "").toLowerCase().startsWith(name)) { + maybe.push(pdfFont); + } + } + if (maybe.length === 0) { + for (const [, pdfFont] of this.fonts.entries()) { + if (pdfFont.regular.name?.replaceAll(pattern, "").toLowerCase().startsWith(name)) { + maybe.push(pdfFont); + } + } + } + if (maybe.length === 0) { + name = name.replaceAll(/psmt|mt/gi, ""); + for (const [family, pdfFont] of this.fonts.entries()) { + if (family.replaceAll(pattern, "").toLowerCase().startsWith(name)) { + maybe.push(pdfFont); + } + } + } + if (maybe.length === 0) { + for (const pdfFont of this.fonts.values()) { + if (pdfFont.regular.name?.replaceAll(pattern, "").toLowerCase().startsWith(name)) { + maybe.push(pdfFont); + } + } + } + if (maybe.length >= 1) { + if (maybe.length !== 1 && mustWarn) { + warn(`XFA - Too many choices to guess the correct font: ${fontName}`); + } + this.cache.set(fontName, maybe[0]); + return maybe[0]; + } + if (mustWarn && !this.warned.has(fontName)) { + this.warned.add(fontName); + warn(`XFA - Cannot find the font: ${fontName}`); + } + return null; + } +} +function selectFont(xfaFont, typeface) { + if (xfaFont.posture === "italic") { + if (xfaFont.weight === "bold") { + return typeface.bolditalic; + } + return typeface.italic; + } else if (xfaFont.weight === "bold") { + return typeface.bold; + } + return typeface.regular; +} +function fonts_getMetrics(xfaFont, real = false) { + let pdfFont = null; + if (xfaFont) { + const name = stripQuotes(xfaFont.typeface); + const typeface = xfaFont[$globalData].fontFinder.find(name); + pdfFont = selectFont(xfaFont, typeface); + } + if (!pdfFont) { + return { + lineHeight: 12, + lineGap: 2, + lineNoGap: 10 + }; + } + const size = xfaFont.size || 10; + const lineHeight = pdfFont.lineHeight ? Math.max(real ? 0 : 1.2, pdfFont.lineHeight) : 1.2; + const lineGap = pdfFont.lineGap === undefined ? 0.2 : pdfFont.lineGap; + return { + lineHeight: lineHeight * size, + lineGap: lineGap * size, + lineNoGap: Math.max(1, lineHeight - lineGap) * size + }; +} + +;// ./src/core/xfa/text.js + +const WIDTH_FACTOR = 1.02; +class FontInfo { + constructor(xfaFont, margin, lineHeight, fontFinder) { + this.lineHeight = lineHeight; + this.paraMargin = margin || { + top: 0, + bottom: 0, + left: 0, + right: 0 + }; + if (!xfaFont) { + [this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder); + return; + } + this.xfaFont = { + typeface: xfaFont.typeface, + posture: xfaFont.posture, + weight: xfaFont.weight, + size: xfaFont.size, + letterSpacing: xfaFont.letterSpacing + }; + const typeface = fontFinder.find(xfaFont.typeface); + if (!typeface) { + [this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder); + return; + } + this.pdfFont = selectFont(xfaFont, typeface); + if (!this.pdfFont) { + [this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder); + } + } + defaultFont(fontFinder) { + const font = fontFinder.find("Helvetica", false) || fontFinder.find("Myriad Pro", false) || fontFinder.find("Arial", false) || fontFinder.getDefault(); + if (font?.regular) { + const pdfFont = font.regular; + const info = pdfFont.cssFontInfo; + const xfaFont = { + typeface: info.fontFamily, + posture: "normal", + weight: "normal", + size: 10, + letterSpacing: 0 + }; + return [pdfFont, xfaFont]; + } + const xfaFont = { + typeface: "Courier", + posture: "normal", + weight: "normal", + size: 10, + letterSpacing: 0 + }; + return [null, xfaFont]; + } +} +class FontSelector { + constructor(defaultXfaFont, defaultParaMargin, defaultLineHeight, fontFinder) { + this.fontFinder = fontFinder; + this.stack = [new FontInfo(defaultXfaFont, defaultParaMargin, defaultLineHeight, fontFinder)]; + } + pushData(xfaFont, margin, lineHeight) { + const lastFont = this.stack.at(-1); + for (const name of ["typeface", "posture", "weight", "size", "letterSpacing"]) { + if (!xfaFont[name]) { + xfaFont[name] = lastFont.xfaFont[name]; + } + } + for (const name of ["top", "bottom", "left", "right"]) { + if (isNaN(margin[name])) { + margin[name] = lastFont.paraMargin[name]; + } + } + const fontInfo = new FontInfo(xfaFont, margin, lineHeight || lastFont.lineHeight, this.fontFinder); + if (!fontInfo.pdfFont) { + fontInfo.pdfFont = lastFont.pdfFont; + } + this.stack.push(fontInfo); + } + popFont() { + this.stack.pop(); + } + topFont() { + return this.stack.at(-1); + } +} +class TextMeasure { + constructor(defaultXfaFont, defaultParaMargin, defaultLineHeight, fonts) { + this.glyphs = []; + this.fontSelector = new FontSelector(defaultXfaFont, defaultParaMargin, defaultLineHeight, fonts); + this.extraHeight = 0; + } + pushData(xfaFont, margin, lineHeight) { + this.fontSelector.pushData(xfaFont, margin, lineHeight); + } + popFont(xfaFont) { + return this.fontSelector.popFont(); + } + addPara() { + const lastFont = this.fontSelector.topFont(); + this.extraHeight += lastFont.paraMargin.top + lastFont.paraMargin.bottom; + } + addString(str) { + if (!str) { + return; + } + const lastFont = this.fontSelector.topFont(); + const fontSize = lastFont.xfaFont.size; + if (lastFont.pdfFont) { + const letterSpacing = lastFont.xfaFont.letterSpacing; + const pdfFont = lastFont.pdfFont; + const fontLineHeight = pdfFont.lineHeight || 1.2; + const lineHeight = lastFont.lineHeight || Math.max(1.2, fontLineHeight) * fontSize; + const lineGap = pdfFont.lineGap === undefined ? 0.2 : pdfFont.lineGap; + const noGap = fontLineHeight - lineGap; + const firstLineHeight = Math.max(1, noGap) * fontSize; + const scale = fontSize / 1000; + const fallbackWidth = pdfFont.defaultWidth || pdfFont.charsToGlyphs(" ")[0].width; + for (const line of str.split(/[\u2029\n]/)) { + const encodedLine = pdfFont.encodeString(line).join(""); + const glyphs = pdfFont.charsToGlyphs(encodedLine); + for (const glyph of glyphs) { + const width = glyph.width || fallbackWidth; + this.glyphs.push([width * scale + letterSpacing, lineHeight, firstLineHeight, glyph.unicode, false]); + } + this.glyphs.push([0, 0, 0, "\n", true]); + } + this.glyphs.pop(); + return; + } + for (const line of str.split(/[\u2029\n]/)) { + for (const char of line.split("")) { + this.glyphs.push([fontSize, 1.2 * fontSize, fontSize, char, false]); + } + this.glyphs.push([0, 0, 0, "\n", true]); + } + this.glyphs.pop(); + } + compute(maxWidth) { + let lastSpacePos = -1, + lastSpaceWidth = 0, + width = 0, + height = 0, + currentLineWidth = 0, + currentLineHeight = 0; + let isBroken = false; + let isFirstLine = true; + for (let i = 0, ii = this.glyphs.length; i < ii; i++) { + const [glyphWidth, lineHeight, firstLineHeight, char, isEOL] = this.glyphs[i]; + const isSpace = char === " "; + const glyphHeight = isFirstLine ? firstLineHeight : lineHeight; + if (isEOL) { + width = Math.max(width, currentLineWidth); + currentLineWidth = 0; + height += currentLineHeight; + currentLineHeight = glyphHeight; + lastSpacePos = -1; + lastSpaceWidth = 0; + isFirstLine = false; + continue; + } + if (isSpace) { + if (currentLineWidth + glyphWidth > maxWidth) { + width = Math.max(width, currentLineWidth); + currentLineWidth = 0; + height += currentLineHeight; + currentLineHeight = glyphHeight; + lastSpacePos = -1; + lastSpaceWidth = 0; + isBroken = true; + isFirstLine = false; + } else { + currentLineHeight = Math.max(glyphHeight, currentLineHeight); + lastSpaceWidth = currentLineWidth; + currentLineWidth += glyphWidth; + lastSpacePos = i; + } + continue; + } + if (currentLineWidth + glyphWidth > maxWidth) { + height += currentLineHeight; + currentLineHeight = glyphHeight; + if (lastSpacePos !== -1) { + i = lastSpacePos; + width = Math.max(width, lastSpaceWidth); + currentLineWidth = 0; + lastSpacePos = -1; + lastSpaceWidth = 0; + } else { + width = Math.max(width, currentLineWidth); + currentLineWidth = glyphWidth; + } + isBroken = true; + isFirstLine = false; + continue; + } + currentLineWidth += glyphWidth; + currentLineHeight = Math.max(glyphHeight, currentLineHeight); + } + width = Math.max(width, currentLineWidth); + height += currentLineHeight + this.extraHeight; + return { + width: WIDTH_FACTOR * width, + height, + isBroken + }; + } +} + +;// ./src/core/xfa/som.js + + +const namePattern = /^[^.[]+/; +const indexPattern = /^[^\]]+/; +const operators = { + dot: 0, + dotDot: 1, + dotHash: 2, + dotBracket: 3, + dotParen: 4 +}; +const shortcuts = new Map([["$data", (root, current) => root.datasets ? root.datasets.data : root], ["$record", (root, current) => (root.datasets ? root.datasets.data : root)[$getChildren]()[0]], ["$template", (root, current) => root.template], ["$connectionSet", (root, current) => root.connectionSet], ["$form", (root, current) => root.form], ["$layout", (root, current) => root.layout], ["$host", (root, current) => root.host], ["$dataWindow", (root, current) => root.dataWindow], ["$event", (root, current) => root.event], ["!", (root, current) => root.datasets], ["$xfa", (root, current) => root], ["xfa", (root, current) => root], ["$", (root, current) => current]]); +const somCache = new WeakMap(); +function parseIndex(index) { + index = index.trim(); + if (index === "*") { + return Infinity; + } + return parseInt(index, 10) || 0; +} +function parseExpression(expr, dotDotAllowed, noExpr = true) { + let match = expr.match(namePattern); + if (!match) { + return null; + } + let [name] = match; + const parsed = [{ + name, + cacheName: "." + name, + index: 0, + js: null, + formCalc: null, + operator: operators.dot + }]; + let pos = name.length; + while (pos < expr.length) { + const spos = pos; + const char = expr.charAt(pos++); + if (char === "[") { + match = expr.slice(pos).match(indexPattern); + if (!match) { + warn("XFA - Invalid index in SOM expression"); + return null; + } + parsed.at(-1).index = parseIndex(match[0]); + pos += match[0].length + 1; + continue; + } + let operator; + switch (expr.charAt(pos)) { + case ".": + if (!dotDotAllowed) { + return null; + } + pos++; + operator = operators.dotDot; + break; + case "#": + pos++; + operator = operators.dotHash; + break; + case "[": + if (noExpr) { + warn("XFA - SOM expression contains a FormCalc subexpression which is not supported for now."); + return null; + } + operator = operators.dotBracket; + break; + case "(": + if (noExpr) { + warn("XFA - SOM expression contains a JavaScript subexpression which is not supported for now."); + return null; + } + operator = operators.dotParen; + break; + default: + operator = operators.dot; + break; + } + match = expr.slice(pos).match(namePattern); + if (!match) { + break; + } + [name] = match; + pos += name.length; + parsed.push({ + name, + cacheName: expr.slice(spos, pos), + operator, + index: 0, + js: null, + formCalc: null + }); + } + return parsed; +} +function searchNode(root, container, expr, dotDotAllowed = true, useCache = true) { + const parsed = parseExpression(expr, dotDotAllowed); + if (!parsed) { + return null; + } + const fn = shortcuts.get(parsed[0].name); + let i = 0; + let isQualified; + if (fn) { + isQualified = true; + root = [fn(root, container)]; + i = 1; + } else { + isQualified = container === null; + root = [container || root]; + } + for (let ii = parsed.length; i < ii; i++) { + const { + name, + cacheName, + operator, + index + } = parsed[i]; + const nodes = []; + for (const node of root) { + if (!node.isXFAObject) { + continue; + } + let children, cached; + if (useCache) { + cached = somCache.get(node); + if (!cached) { + cached = new Map(); + somCache.set(node, cached); + } + children = cached.get(cacheName); + } + if (!children) { + switch (operator) { + case operators.dot: + children = node[$getChildrenByName](name, false); + break; + case operators.dotDot: + children = node[$getChildrenByName](name, true); + break; + case operators.dotHash: + children = node[$getChildrenByClass](name); + children = children.isXFAObjectArray ? children.children : [children]; + break; + default: + break; + } + if (useCache) { + cached.set(cacheName, children); + } + } + if (children.length > 0) { + nodes.push(children); + } + } + if (nodes.length === 0 && !isQualified && i === 0) { + const parent = container[$getParent](); + container = parent; + if (!container) { + return null; + } + i = -1; + root = [container]; + continue; + } + root = isFinite(index) ? nodes.filter(node => index < node.length).map(node => node[index]) : nodes.flat(); + } + if (root.length === 0) { + return null; + } + return root; +} +function createDataNode(root, container, expr) { + const parsed = parseExpression(expr); + if (!parsed) { + return null; + } + if (parsed.some(x => x.operator === operators.dotDot)) { + return null; + } + const fn = shortcuts.get(parsed[0].name); + let i = 0; + if (fn) { + root = fn(root, container); + i = 1; + } else { + root = container || root; + } + for (let ii = parsed.length; i < ii; i++) { + const { + name, + operator, + index + } = parsed[i]; + if (!isFinite(index)) { + parsed[i].index = 0; + return root.createNodes(parsed.slice(i)); + } + let children; + switch (operator) { + case operators.dot: + children = root[$getChildrenByName](name, false); + break; + case operators.dotDot: + children = root[$getChildrenByName](name, true); + break; + case operators.dotHash: + children = root[$getChildrenByClass](name); + children = children.isXFAObjectArray ? children.children : [children]; + break; + default: + break; + } + if (children.length === 0) { + return root.createNodes(parsed.slice(i)); + } + if (index < children.length) { + const child = children[index]; + if (!child.isXFAObject) { + warn(`XFA - Cannot create a node.`); + return null; + } + root = child; + } else { + parsed[i].index = index - children.length; + return root.createNodes(parsed.slice(i)); + } + } + return null; +} + +;// ./src/core/xfa/xfa_object.js + + + + + + +const _applyPrototype = Symbol(); +const _attributes = Symbol(); +const _attributeNames = Symbol(); +const _children = Symbol("_children"); +const _cloneAttribute = Symbol(); +const _dataValue = Symbol(); +const _defaultValue = Symbol(); +const _filteredChildrenGenerator = Symbol(); +const _getPrototype = Symbol(); +const _getUnsetAttributes = Symbol(); +const _hasChildren = Symbol(); +const _max = Symbol(); +const _options = Symbol(); +const _parent = Symbol("parent"); +const _resolvePrototypesHelper = Symbol(); +const _setAttributes = Symbol(); +const _validator = Symbol(); +let uid = 0; +const NS_DATASETS = NamespaceIds.datasets.id; +class XFAObject { + constructor(nsId, name, hasChildren = false) { + this[$namespaceId] = nsId; + this[$nodeName] = name; + this[_hasChildren] = hasChildren; + this[_parent] = null; + this[_children] = []; + this[$uid] = `${name}${uid++}`; + this[$globalData] = null; + } + get isXFAObject() { + return true; + } + get isXFAObjectArray() { + return false; + } + createNodes(path) { + let root = this, + node = null; + for (const { + name, + index + } of path) { + for (let i = 0, ii = isFinite(index) ? index : 0; i <= ii; i++) { + const nsId = root[$namespaceId] === NS_DATASETS ? -1 : root[$namespaceId]; + node = new XmlObject(nsId, name); + root[$appendChild](node); + } + root = node; + } + return node; + } + [$onChild](child) { + if (!this[_hasChildren] || !this[$onChildCheck](child)) { + return false; + } + const name = child[$nodeName]; + const node = this[name]; + if (node instanceof XFAObjectArray) { + if (node.push(child)) { + this[$appendChild](child); + return true; + } + } else { + if (node !== null) { + this[$removeChild](node); + } + this[name] = child; + this[$appendChild](child); + return true; + } + let id = ""; + if (this.id) { + id = ` (id: ${this.id})`; + } else if (this.name) { + id = ` (name: ${this.name} ${this.h.value})`; + } + warn(`XFA - node "${this[$nodeName]}"${id} has already enough "${name}"!`); + return false; + } + [$onChildCheck](child) { + return this.hasOwnProperty(child[$nodeName]) && child[$namespaceId] === this[$namespaceId]; + } + [$isNsAgnostic]() { + return false; + } + [$acceptWhitespace]() { + return false; + } + [$isCDATAXml]() { + return false; + } + [$isBindable]() { + return false; + } + [$popPara]() { + if (this.para) { + this[$getTemplateRoot]()[$extra].paraStack.pop(); + } + } + [$pushPara]() { + this[$getTemplateRoot]()[$extra].paraStack.push(this.para); + } + [$setId](ids) { + if (this.id && this[$namespaceId] === NamespaceIds.template.id) { + ids.set(this.id, this); + } + } + [$getTemplateRoot]() { + return this[$globalData].template; + } + [$isSplittable]() { + return false; + } + [$isThereMoreWidth]() { + return false; + } + [$appendChild](child) { + child[_parent] = this; + this[_children].push(child); + if (!child[$globalData] && this[$globalData]) { + child[$globalData] = this[$globalData]; + } + } + [$removeChild](child) { + const i = this[_children].indexOf(child); + this[_children].splice(i, 1); + } + [$hasSettableValue]() { + return this.hasOwnProperty("value"); + } + [$setValue](_) {} + [$onText](_) {} + [$finalize]() {} + [$clean](builder) { + delete this[_hasChildren]; + if (this[$cleanup]) { + builder.clean(this[$cleanup]); + delete this[$cleanup]; + } + } + [$indexOf](child) { + return this[_children].indexOf(child); + } + [$insertAt](i, child) { + child[_parent] = this; + this[_children].splice(i, 0, child); + if (!child[$globalData] && this[$globalData]) { + child[$globalData] = this[$globalData]; + } + } + [$isTransparent]() { + return !this.name; + } + [$lastAttribute]() { + return ""; + } + [$text]() { + if (this[_children].length === 0) { + return this[$content]; + } + return this[_children].map(c => c[$text]()).join(""); + } + get [_attributeNames]() { + const proto = Object.getPrototypeOf(this); + if (!proto._attributes) { + const attributes = proto._attributes = new Set(); + for (const name of Object.getOwnPropertyNames(this)) { + if (this[name] === null || this[name] instanceof XFAObject || this[name] instanceof XFAObjectArray) { + break; + } + attributes.add(name); + } + } + return shadow(this, _attributeNames, proto._attributes); + } + [$isDescendent](parent) { + let node = this; + while (node) { + if (node === parent) { + return true; + } + node = node[$getParent](); + } + return false; + } + [$getParent]() { + return this[_parent]; + } + [$getSubformParent]() { + return this[$getParent](); + } + [$getChildren](name = null) { + if (!name) { + return this[_children]; + } + return this[name]; + } + [$dump]() { + const dumped = Object.create(null); + if (this[$content]) { + dumped.$content = this[$content]; + } + for (const name of Object.getOwnPropertyNames(this)) { + const value = this[name]; + if (value === null) { + continue; + } + if (value instanceof XFAObject) { + dumped[name] = value[$dump](); + } else if (value instanceof XFAObjectArray) { + if (!value.isEmpty()) { + dumped[name] = value.dump(); + } + } else { + dumped[name] = value; + } + } + return dumped; + } + [$toStyle]() { + return null; + } + [$toHTML]() { + return HTMLResult.EMPTY; + } + *[$getContainedChildren]() { + for (const node of this[$getChildren]()) { + yield node; + } + } + *[_filteredChildrenGenerator](filter, include) { + for (const node of this[$getContainedChildren]()) { + if (!filter || include === filter.has(node[$nodeName])) { + const availableSpace = this[$getAvailableSpace](); + const res = node[$toHTML](availableSpace); + if (!res.success) { + this[$extra].failingNode = node; + } + yield res; + } + } + } + [$flushHTML]() { + return null; + } + [$addHTML](html, bbox) { + this[$extra].children.push(html); + } + [$getAvailableSpace]() {} + [$childrenToHTML]({ + filter = null, + include = true + }) { + if (!this[$extra].generator) { + this[$extra].generator = this[_filteredChildrenGenerator](filter, include); + } else { + const availableSpace = this[$getAvailableSpace](); + const res = this[$extra].failingNode[$toHTML](availableSpace); + if (!res.success) { + return res; + } + if (res.html) { + this[$addHTML](res.html, res.bbox); + } + delete this[$extra].failingNode; + } + while (true) { + const gen = this[$extra].generator.next(); + if (gen.done) { + break; + } + const res = gen.value; + if (!res.success) { + return res; + } + if (res.html) { + this[$addHTML](res.html, res.bbox); + } + } + this[$extra].generator = null; + return HTMLResult.EMPTY; + } + [$setSetAttributes](attributes) { + this[_setAttributes] = new Set(Object.keys(attributes)); + } + [_getUnsetAttributes](protoAttributes) { + const allAttr = this[_attributeNames]; + const setAttr = this[_setAttributes]; + return [...protoAttributes].filter(x => allAttr.has(x) && !setAttr.has(x)); + } + [$resolvePrototypes](ids, ancestors = new Set()) { + for (const child of this[_children]) { + child[_resolvePrototypesHelper](ids, ancestors); + } + } + [_resolvePrototypesHelper](ids, ancestors) { + const proto = this[_getPrototype](ids, ancestors); + if (proto) { + this[_applyPrototype](proto, ids, ancestors); + } else { + this[$resolvePrototypes](ids, ancestors); + } + } + [_getPrototype](ids, ancestors) { + const { + use, + usehref + } = this; + if (!use && !usehref) { + return null; + } + let proto = null; + let somExpression = null; + let id = null; + let ref = use; + if (usehref) { + ref = usehref; + if (usehref.startsWith("#som(") && usehref.endsWith(")")) { + somExpression = usehref.slice("#som(".length, -1); + } else if (usehref.startsWith(".#som(") && usehref.endsWith(")")) { + somExpression = usehref.slice(".#som(".length, -1); + } else if (usehref.startsWith("#")) { + id = usehref.slice(1); + } else if (usehref.startsWith(".#")) { + id = usehref.slice(2); + } + } else if (use.startsWith("#")) { + id = use.slice(1); + } else { + somExpression = use; + } + this.use = this.usehref = ""; + if (id) { + proto = ids.get(id); + } else { + proto = searchNode(ids.get($root), this, somExpression, true, false); + if (proto) { + proto = proto[0]; + } + } + if (!proto) { + warn(`XFA - Invalid prototype reference: ${ref}.`); + return null; + } + if (proto[$nodeName] !== this[$nodeName]) { + warn(`XFA - Incompatible prototype: ${proto[$nodeName]} !== ${this[$nodeName]}.`); + return null; + } + if (ancestors.has(proto)) { + warn(`XFA - Cycle detected in prototypes use.`); + return null; + } + ancestors.add(proto); + const protoProto = proto[_getPrototype](ids, ancestors); + if (protoProto) { + proto[_applyPrototype](protoProto, ids, ancestors); + } + proto[$resolvePrototypes](ids, ancestors); + ancestors.delete(proto); + return proto; + } + [_applyPrototype](proto, ids, ancestors) { + if (ancestors.has(proto)) { + warn(`XFA - Cycle detected in prototypes use.`); + return; + } + if (!this[$content] && proto[$content]) { + this[$content] = proto[$content]; + } + const newAncestors = new Set(ancestors); + newAncestors.add(proto); + for (const unsetAttrName of this[_getUnsetAttributes](proto[_setAttributes])) { + this[unsetAttrName] = proto[unsetAttrName]; + if (this[_setAttributes]) { + this[_setAttributes].add(unsetAttrName); + } + } + for (const name of Object.getOwnPropertyNames(this)) { + if (this[_attributeNames].has(name)) { + continue; + } + const value = this[name]; + const protoValue = proto[name]; + if (value instanceof XFAObjectArray) { + for (const child of value[_children]) { + child[_resolvePrototypesHelper](ids, ancestors); + } + for (let i = value[_children].length, ii = protoValue[_children].length; i < ii; i++) { + const child = proto[_children][i][$clone](); + if (value.push(child)) { + child[_parent] = this; + this[_children].push(child); + child[_resolvePrototypesHelper](ids, ancestors); + } else { + break; + } + } + continue; + } + if (value !== null) { + value[$resolvePrototypes](ids, ancestors); + if (protoValue) { + value[_applyPrototype](protoValue, ids, ancestors); + } + continue; + } + if (protoValue !== null) { + const child = protoValue[$clone](); + child[_parent] = this; + this[name] = child; + this[_children].push(child); + child[_resolvePrototypesHelper](ids, ancestors); + } + } + } + static [_cloneAttribute](obj) { + if (Array.isArray(obj)) { + return obj.map(x => XFAObject[_cloneAttribute](x)); + } + if (typeof obj === "object" && obj !== null) { + return Object.assign({}, obj); + } + return obj; + } + [$clone]() { + const clone = Object.create(Object.getPrototypeOf(this)); + for (const $symbol of Object.getOwnPropertySymbols(this)) { + try { + clone[$symbol] = this[$symbol]; + } catch { + shadow(clone, $symbol, this[$symbol]); + } + } + clone[$uid] = `${clone[$nodeName]}${uid++}`; + clone[_children] = []; + for (const name of Object.getOwnPropertyNames(this)) { + if (this[_attributeNames].has(name)) { + clone[name] = XFAObject[_cloneAttribute](this[name]); + continue; + } + const value = this[name]; + clone[name] = value instanceof XFAObjectArray ? new XFAObjectArray(value[_max]) : null; + } + for (const child of this[_children]) { + const name = child[$nodeName]; + const clonedChild = child[$clone](); + clone[_children].push(clonedChild); + clonedChild[_parent] = clone; + if (clone[name] === null) { + clone[name] = clonedChild; + } else { + clone[name][_children].push(clonedChild); + } + } + return clone; + } + [$getChildren](name = null) { + if (!name) { + return this[_children]; + } + return this[_children].filter(c => c[$nodeName] === name); + } + [$getChildrenByClass](name) { + return this[name]; + } + [$getChildrenByName](name, allTransparent, first = true) { + return Array.from(this[$getChildrenByNameIt](name, allTransparent, first)); + } + *[$getChildrenByNameIt](name, allTransparent, first = true) { + if (name === "parent") { + yield this[_parent]; + return; + } + for (const child of this[_children]) { + if (child[$nodeName] === name) { + yield child; + } + if (child.name === name) { + yield child; + } + if (allTransparent || child[$isTransparent]()) { + yield* child[$getChildrenByNameIt](name, allTransparent, false); + } + } + if (first && this[_attributeNames].has(name)) { + yield new XFAAttribute(this, name, this[name]); + } + } +} +class XFAObjectArray { + constructor(max = Infinity) { + this[_max] = max; + this[_children] = []; + } + get isXFAObject() { + return false; + } + get isXFAObjectArray() { + return true; + } + push(child) { + const len = this[_children].length; + if (len <= this[_max]) { + this[_children].push(child); + return true; + } + warn(`XFA - node "${child[$nodeName]}" accepts no more than ${this[_max]} children`); + return false; + } + isEmpty() { + return this[_children].length === 0; + } + dump() { + return this[_children].length === 1 ? this[_children][0][$dump]() : this[_children].map(x => x[$dump]()); + } + [$clone]() { + const clone = new XFAObjectArray(this[_max]); + clone[_children] = this[_children].map(c => c[$clone]()); + return clone; + } + get children() { + return this[_children]; + } + clear() { + this[_children].length = 0; + } +} +class XFAAttribute { + constructor(node, name, value) { + this[_parent] = node; + this[$nodeName] = name; + this[$content] = value; + this[$consumed] = false; + this[$uid] = `attribute${uid++}`; + } + [$getParent]() { + return this[_parent]; + } + [$isDataValue]() { + return true; + } + [$getDataValue]() { + return this[$content].trim(); + } + [$setValue](value) { + value = value.value || ""; + this[$content] = value.toString(); + } + [$text]() { + return this[$content]; + } + [$isDescendent](parent) { + return this[_parent] === parent || this[_parent][$isDescendent](parent); + } +} +class XmlObject extends XFAObject { + constructor(nsId, name, attributes = {}) { + super(nsId, name); + this[$content] = ""; + this[_dataValue] = null; + if (name !== "#text") { + const map = new Map(); + this[_attributes] = map; + for (const [attrName, value] of Object.entries(attributes)) { + map.set(attrName, new XFAAttribute(this, attrName, value)); + } + if (attributes.hasOwnProperty($nsAttributes)) { + const dataNode = attributes[$nsAttributes].xfa.dataNode; + if (dataNode !== undefined) { + if (dataNode === "dataGroup") { + this[_dataValue] = false; + } else if (dataNode === "dataValue") { + this[_dataValue] = true; + } + } + } + } + this[$consumed] = false; + } + [$toString](buf) { + const tagName = this[$nodeName]; + if (tagName === "#text") { + buf.push(encodeToXmlString(this[$content])); + return; + } + const utf8TagName = utf8StringToString(tagName); + const prefix = this[$namespaceId] === NS_DATASETS ? "xfa:" : ""; + buf.push(`<${prefix}${utf8TagName}`); + for (const [name, value] of this[_attributes].entries()) { + const utf8Name = utf8StringToString(name); + buf.push(` ${utf8Name}="${encodeToXmlString(value[$content])}"`); + } + if (this[_dataValue] !== null) { + if (this[_dataValue]) { + buf.push(` xfa:dataNode="dataValue"`); + } else { + buf.push(` xfa:dataNode="dataGroup"`); + } + } + if (!this[$content] && this[_children].length === 0) { + buf.push("/>"); + return; + } + buf.push(">"); + if (this[$content]) { + if (typeof this[$content] === "string") { + buf.push(encodeToXmlString(this[$content])); + } else { + this[$content][$toString](buf); + } + } else { + for (const child of this[_children]) { + child[$toString](buf); + } + } + buf.push(``); + } + [$onChild](child) { + if (this[$content]) { + const node = new XmlObject(this[$namespaceId], "#text"); + this[$appendChild](node); + node[$content] = this[$content]; + this[$content] = ""; + } + this[$appendChild](child); + return true; + } + [$onText](str) { + this[$content] += str; + } + [$finalize]() { + if (this[$content] && this[_children].length > 0) { + const node = new XmlObject(this[$namespaceId], "#text"); + this[$appendChild](node); + node[$content] = this[$content]; + delete this[$content]; + } + } + [$toHTML]() { + if (this[$nodeName] === "#text") { + return HTMLResult.success({ + name: "#text", + value: this[$content] + }); + } + return HTMLResult.EMPTY; + } + [$getChildren](name = null) { + if (!name) { + return this[_children]; + } + return this[_children].filter(c => c[$nodeName] === name); + } + [$getAttributes]() { + return this[_attributes]; + } + [$getChildrenByClass](name) { + const value = this[_attributes].get(name); + if (value !== undefined) { + return value; + } + return this[$getChildren](name); + } + *[$getChildrenByNameIt](name, allTransparent) { + const value = this[_attributes].get(name); + if (value) { + yield value; + } + for (const child of this[_children]) { + if (child[$nodeName] === name) { + yield child; + } + if (allTransparent) { + yield* child[$getChildrenByNameIt](name, allTransparent); + } + } + } + *[$getAttributeIt](name, skipConsumed) { + const value = this[_attributes].get(name); + if (value && (!skipConsumed || !value[$consumed])) { + yield value; + } + for (const child of this[_children]) { + yield* child[$getAttributeIt](name, skipConsumed); + } + } + *[$getRealChildrenByNameIt](name, allTransparent, skipConsumed) { + for (const child of this[_children]) { + if (child[$nodeName] === name && (!skipConsumed || !child[$consumed])) { + yield child; + } + if (allTransparent) { + yield* child[$getRealChildrenByNameIt](name, allTransparent, skipConsumed); + } + } + } + [$isDataValue]() { + if (this[_dataValue] === null) { + return this[_children].length === 0 || this[_children][0][$namespaceId] === NamespaceIds.xhtml.id; + } + return this[_dataValue]; + } + [$getDataValue]() { + if (this[_dataValue] === null) { + if (this[_children].length === 0) { + return this[$content].trim(); + } + if (this[_children][0][$namespaceId] === NamespaceIds.xhtml.id) { + return this[_children][0][$text]().trim(); + } + return null; + } + return this[$content].trim(); + } + [$setValue](value) { + value = value.value || ""; + this[$content] = value.toString(); + } + [$dump](hasNS = false) { + const dumped = Object.create(null); + if (hasNS) { + dumped.$ns = this[$namespaceId]; + } + if (this[$content]) { + dumped.$content = this[$content]; + } + dumped.$name = this[$nodeName]; + dumped.children = []; + for (const child of this[_children]) { + dumped.children.push(child[$dump](hasNS)); + } + dumped.attributes = Object.create(null); + for (const [name, value] of this[_attributes]) { + dumped.attributes[name] = value[$content]; + } + return dumped; + } +} +class ContentObject extends XFAObject { + constructor(nsId, name) { + super(nsId, name); + this[$content] = ""; + } + [$onText](text) { + this[$content] += text; + } + [$finalize]() {} +} +class OptionObject extends ContentObject { + constructor(nsId, name, options) { + super(nsId, name); + this[_options] = options; + } + [$finalize]() { + this[$content] = getKeyword({ + data: this[$content], + defaultValue: this[_options][0], + validate: k => this[_options].includes(k) + }); + } + [$clean](builder) { + super[$clean](builder); + delete this[_options]; + } +} +class StringObject extends ContentObject { + [$finalize]() { + this[$content] = this[$content].trim(); + } +} +class IntegerObject extends ContentObject { + constructor(nsId, name, defaultValue, validator) { + super(nsId, name); + this[_defaultValue] = defaultValue; + this[_validator] = validator; + } + [$finalize]() { + this[$content] = getInteger({ + data: this[$content], + defaultValue: this[_defaultValue], + validate: this[_validator] + }); + } + [$clean](builder) { + super[$clean](builder); + delete this[_defaultValue]; + delete this[_validator]; + } +} +class Option01 extends IntegerObject { + constructor(nsId, name) { + super(nsId, name, 0, n => n === 1); + } +} +class Option10 extends IntegerObject { + constructor(nsId, name) { + super(nsId, name, 1, n => n === 0); + } +} + +;// ./src/core/xfa/html_utils.js + + + + + + +function measureToString(m) { + if (typeof m === "string") { + return "0px"; + } + return Number.isInteger(m) ? `${m}px` : `${m.toFixed(2)}px`; +} +const converters = { + anchorType(node, style) { + const parent = node[$getSubformParent](); + if (!parent || parent.layout && parent.layout !== "position") { + return; + } + if (!("transform" in style)) { + style.transform = ""; + } + switch (node.anchorType) { + case "bottomCenter": + style.transform += "translate(-50%, -100%)"; + break; + case "bottomLeft": + style.transform += "translate(0,-100%)"; + break; + case "bottomRight": + style.transform += "translate(-100%,-100%)"; + break; + case "middleCenter": + style.transform += "translate(-50%,-50%)"; + break; + case "middleLeft": + style.transform += "translate(0,-50%)"; + break; + case "middleRight": + style.transform += "translate(-100%,-50%)"; + break; + case "topCenter": + style.transform += "translate(-50%,0)"; + break; + case "topRight": + style.transform += "translate(-100%,0)"; + break; + } + }, + dimensions(node, style) { + const parent = node[$getSubformParent](); + let width = node.w; + const height = node.h; + if (parent.layout?.includes("row")) { + const extra = parent[$extra]; + const colSpan = node.colSpan; + let w; + if (colSpan === -1) { + w = extra.columnWidths.slice(extra.currentColumn).reduce((a, x) => a + x, 0); + extra.currentColumn = 0; + } else { + w = extra.columnWidths.slice(extra.currentColumn, extra.currentColumn + colSpan).reduce((a, x) => a + x, 0); + extra.currentColumn = (extra.currentColumn + node.colSpan) % extra.columnWidths.length; + } + if (!isNaN(w)) { + width = node.w = w; + } + } + style.width = width !== "" ? measureToString(width) : "auto"; + style.height = height !== "" ? measureToString(height) : "auto"; + }, + position(node, style) { + const parent = node[$getSubformParent](); + if (parent?.layout && parent.layout !== "position") { + return; + } + style.position = "absolute"; + style.left = measureToString(node.x); + style.top = measureToString(node.y); + }, + rotate(node, style) { + if (node.rotate) { + if (!("transform" in style)) { + style.transform = ""; + } + style.transform += `rotate(-${node.rotate}deg)`; + style.transformOrigin = "top left"; + } + }, + presence(node, style) { + switch (node.presence) { + case "invisible": + style.visibility = "hidden"; + break; + case "hidden": + case "inactive": + style.display = "none"; + break; + } + }, + hAlign(node, style) { + if (node[$nodeName] === "para") { + switch (node.hAlign) { + case "justifyAll": + style.textAlign = "justify-all"; + break; + case "radix": + style.textAlign = "left"; + break; + default: + style.textAlign = node.hAlign; + } + } else { + switch (node.hAlign) { + case "left": + style.alignSelf = "start"; + break; + case "center": + style.alignSelf = "center"; + break; + case "right": + style.alignSelf = "end"; + break; + } + } + }, + margin(node, style) { + if (node.margin) { + style.margin = node.margin[$toStyle]().margin; + } + } +}; +function setMinMaxDimensions(node, style) { + const parent = node[$getSubformParent](); + if (parent.layout === "position") { + if (node.minW > 0) { + style.minWidth = measureToString(node.minW); + } + if (node.maxW > 0) { + style.maxWidth = measureToString(node.maxW); + } + if (node.minH > 0) { + style.minHeight = measureToString(node.minH); + } + if (node.maxH > 0) { + style.maxHeight = measureToString(node.maxH); + } + } +} +function layoutText(text, xfaFont, margin, lineHeight, fontFinder, width) { + const measure = new TextMeasure(xfaFont, margin, lineHeight, fontFinder); + if (typeof text === "string") { + measure.addString(text); + } else { + text[$pushGlyphs](measure); + } + return measure.compute(width); +} +function layoutNode(node, availableSpace) { + let height = null; + let width = null; + let isBroken = false; + if ((!node.w || !node.h) && node.value) { + let marginH = 0; + let marginV = 0; + if (node.margin) { + marginH = node.margin.leftInset + node.margin.rightInset; + marginV = node.margin.topInset + node.margin.bottomInset; + } + let lineHeight = null; + let margin = null; + if (node.para) { + margin = Object.create(null); + lineHeight = node.para.lineHeight === "" ? null : node.para.lineHeight; + margin.top = node.para.spaceAbove === "" ? 0 : node.para.spaceAbove; + margin.bottom = node.para.spaceBelow === "" ? 0 : node.para.spaceBelow; + margin.left = node.para.marginLeft === "" ? 0 : node.para.marginLeft; + margin.right = node.para.marginRight === "" ? 0 : node.para.marginRight; + } + let font = node.font; + if (!font) { + const root = node[$getTemplateRoot](); + let parent = node[$getParent](); + while (parent && parent !== root) { + if (parent.font) { + font = parent.font; + break; + } + parent = parent[$getParent](); + } + } + const maxWidth = (node.w || availableSpace.width) - marginH; + const fontFinder = node[$globalData].fontFinder; + if (node.value.exData && node.value.exData[$content] && node.value.exData.contentType === "text/html") { + const res = layoutText(node.value.exData[$content], font, margin, lineHeight, fontFinder, maxWidth); + width = res.width; + height = res.height; + isBroken = res.isBroken; + } else { + const text = node.value[$text](); + if (text) { + const res = layoutText(text, font, margin, lineHeight, fontFinder, maxWidth); + width = res.width; + height = res.height; + isBroken = res.isBroken; + } + } + if (width !== null && !node.w) { + width += marginH; + } + if (height !== null && !node.h) { + height += marginV; + } + } + return { + w: width, + h: height, + isBroken + }; +} +function computeBbox(node, html, availableSpace) { + let bbox; + if (node.w !== "" && node.h !== "") { + bbox = [node.x, node.y, node.w, node.h]; + } else { + if (!availableSpace) { + return null; + } + let width = node.w; + if (width === "") { + if (node.maxW === 0) { + const parent = node[$getSubformParent](); + width = parent.layout === "position" && parent.w !== "" ? 0 : node.minW; + } else { + width = Math.min(node.maxW, availableSpace.width); + } + html.attributes.style.width = measureToString(width); + } + let height = node.h; + if (height === "") { + if (node.maxH === 0) { + const parent = node[$getSubformParent](); + height = parent.layout === "position" && parent.h !== "" ? 0 : node.minH; + } else { + height = Math.min(node.maxH, availableSpace.height); + } + html.attributes.style.height = measureToString(height); + } + bbox = [node.x, node.y, width, height]; + } + return bbox; +} +function fixDimensions(node) { + const parent = node[$getSubformParent](); + if (parent.layout?.includes("row")) { + const extra = parent[$extra]; + const colSpan = node.colSpan; + let width; + if (colSpan === -1) { + width = extra.columnWidths.slice(extra.currentColumn).reduce((a, w) => a + w, 0); + } else { + width = extra.columnWidths.slice(extra.currentColumn, extra.currentColumn + colSpan).reduce((a, w) => a + w, 0); + } + if (!isNaN(width)) { + node.w = width; + } + } + if (parent.layout && parent.layout !== "position") { + node.x = node.y = 0; + } + if (node.layout === "table") { + if (node.w === "" && Array.isArray(node.columnWidths)) { + node.w = node.columnWidths.reduce((a, x) => a + x, 0); + } + } +} +function layoutClass(node) { + switch (node.layout) { + case "position": + return "xfaPosition"; + case "lr-tb": + return "xfaLrTb"; + case "rl-row": + return "xfaRlRow"; + case "rl-tb": + return "xfaRlTb"; + case "row": + return "xfaRow"; + case "table": + return "xfaTable"; + case "tb": + return "xfaTb"; + default: + return "xfaPosition"; + } +} +function toStyle(node, ...names) { + const style = Object.create(null); + for (const name of names) { + const value = node[name]; + if (value === null) { + continue; + } + if (converters.hasOwnProperty(name)) { + converters[name](node, style); + continue; + } + if (value instanceof XFAObject) { + const newStyle = value[$toStyle](); + if (newStyle) { + Object.assign(style, newStyle); + } else { + warn(`(DEBUG) - XFA - style for ${name} not implemented yet`); + } + } + } + return style; +} +function createWrapper(node, html) { + const { + attributes + } = html; + const { + style + } = attributes; + const wrapper = { + name: "div", + attributes: { + class: ["xfaWrapper"], + style: Object.create(null) + }, + children: [] + }; + attributes.class.push("xfaWrapped"); + if (node.border) { + const { + widths, + insets + } = node.border[$extra]; + let width, height; + let top = insets[0]; + let left = insets[3]; + const insetsH = insets[0] + insets[2]; + const insetsW = insets[1] + insets[3]; + switch (node.border.hand) { + case "even": + top -= widths[0] / 2; + left -= widths[3] / 2; + width = `calc(100% + ${(widths[1] + widths[3]) / 2 - insetsW}px)`; + height = `calc(100% + ${(widths[0] + widths[2]) / 2 - insetsH}px)`; + break; + case "left": + top -= widths[0]; + left -= widths[3]; + width = `calc(100% + ${widths[1] + widths[3] - insetsW}px)`; + height = `calc(100% + ${widths[0] + widths[2] - insetsH}px)`; + break; + case "right": + width = insetsW ? `calc(100% - ${insetsW}px)` : "100%"; + height = insetsH ? `calc(100% - ${insetsH}px)` : "100%"; + break; + } + const classNames = ["xfaBorder"]; + if (isPrintOnly(node.border)) { + classNames.push("xfaPrintOnly"); + } + const border = { + name: "div", + attributes: { + class: classNames, + style: { + top: `${top}px`, + left: `${left}px`, + width, + height + } + }, + children: [] + }; + for (const key of ["border", "borderWidth", "borderColor", "borderRadius", "borderStyle"]) { + if (style[key] !== undefined) { + border.attributes.style[key] = style[key]; + delete style[key]; + } + } + wrapper.children.push(border, html); + } else { + wrapper.children.push(html); + } + for (const key of ["background", "backgroundClip", "top", "left", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "transform", "transformOrigin", "visibility"]) { + if (style[key] !== undefined) { + wrapper.attributes.style[key] = style[key]; + delete style[key]; + } + } + wrapper.attributes.style.position = style.position === "absolute" ? "absolute" : "relative"; + delete style.position; + if (style.alignSelf) { + wrapper.attributes.style.alignSelf = style.alignSelf; + delete style.alignSelf; + } + return wrapper; +} +function fixTextIndent(styles) { + const indent = getMeasurement(styles.textIndent, "0px"); + if (indent >= 0) { + return; + } + const align = styles.textAlign === "right" ? "right" : "left"; + const name = "padding" + (align === "left" ? "Left" : "Right"); + const padding = getMeasurement(styles[name], "0px"); + styles[name] = `${padding - indent}px`; +} +function setAccess(node, classNames) { + switch (node.access) { + case "nonInteractive": + classNames.push("xfaNonInteractive"); + break; + case "readOnly": + classNames.push("xfaReadOnly"); + break; + case "protected": + classNames.push("xfaDisabled"); + break; + } +} +function isPrintOnly(node) { + return node.relevant.length > 0 && !node.relevant[0].excluded && node.relevant[0].viewname === "print"; +} +function getCurrentPara(node) { + const stack = node[$getTemplateRoot]()[$extra].paraStack; + return stack.length ? stack.at(-1) : null; +} +function setPara(node, nodeStyle, value) { + if (value.attributes.class?.includes("xfaRich")) { + if (nodeStyle) { + if (node.h === "") { + nodeStyle.height = "auto"; + } + if (node.w === "") { + nodeStyle.width = "auto"; + } + } + const para = getCurrentPara(node); + if (para) { + const valueStyle = value.attributes.style; + valueStyle.display = "flex"; + valueStyle.flexDirection = "column"; + switch (para.vAlign) { + case "top": + valueStyle.justifyContent = "start"; + break; + case "bottom": + valueStyle.justifyContent = "end"; + break; + case "middle": + valueStyle.justifyContent = "center"; + break; + } + const paraStyle = para[$toStyle](); + for (const [key, val] of Object.entries(paraStyle)) { + if (!(key in valueStyle)) { + valueStyle[key] = val; + } + } + } + } +} +function setFontFamily(xfaFont, node, fontFinder, style) { + if (!fontFinder) { + delete style.fontFamily; + return; + } + const name = stripQuotes(xfaFont.typeface); + style.fontFamily = `"${name}"`; + const typeface = fontFinder.find(name); + if (typeface) { + const { + fontFamily + } = typeface.regular.cssFontInfo; + if (fontFamily !== name) { + style.fontFamily = `"${fontFamily}"`; + } + const para = getCurrentPara(node); + if (para && para.lineHeight !== "") { + return; + } + if (style.lineHeight) { + return; + } + const pdfFont = selectFont(xfaFont, typeface); + if (pdfFont) { + style.lineHeight = Math.max(1.2, pdfFont.lineHeight); + } + } +} +function fixURL(str) { + const absoluteUrl = createValidAbsoluteUrl(str, null, { + addDefaultProtocol: true, + tryConvertEncoding: true + }); + return absoluteUrl ? absoluteUrl.href : null; +} + +;// ./src/core/xfa/layout.js + + +function createLine(node, children) { + return { + name: "div", + attributes: { + class: [node.layout === "lr-tb" ? "xfaLr" : "xfaRl"] + }, + children + }; +} +function flushHTML(node) { + if (!node[$extra]) { + return null; + } + const attributes = node[$extra].attributes; + const html = { + name: "div", + attributes, + children: node[$extra].children + }; + if (node[$extra].failingNode) { + const htmlFromFailing = node[$extra].failingNode[$flushHTML](); + if (htmlFromFailing) { + if (node.layout.endsWith("-tb")) { + html.children.push(createLine(node, [htmlFromFailing])); + } else { + html.children.push(htmlFromFailing); + } + } + } + if (html.children.length === 0) { + return null; + } + return html; +} +function addHTML(node, html, bbox) { + const extra = node[$extra]; + const availableSpace = extra.availableSpace; + const [x, y, w, h] = bbox; + switch (node.layout) { + case "position": + { + extra.width = Math.max(extra.width, x + w); + extra.height = Math.max(extra.height, y + h); + extra.children.push(html); + break; + } + case "lr-tb": + case "rl-tb": + if (!extra.line || extra.attempt === 1) { + extra.line = createLine(node, []); + extra.children.push(extra.line); + extra.numberInLine = 0; + } + extra.numberInLine += 1; + extra.line.children.push(html); + if (extra.attempt === 0) { + extra.currentWidth += w; + extra.height = Math.max(extra.height, extra.prevHeight + h); + } else { + extra.currentWidth = w; + extra.prevHeight = extra.height; + extra.height += h; + extra.attempt = 0; + } + extra.width = Math.max(extra.width, extra.currentWidth); + break; + case "rl-row": + case "row": + { + extra.children.push(html); + extra.width += w; + extra.height = Math.max(extra.height, h); + const height = measureToString(extra.height); + for (const child of extra.children) { + child.attributes.style.height = height; + } + break; + } + case "table": + { + extra.width = Math.min(availableSpace.width, Math.max(extra.width, w)); + extra.height += h; + extra.children.push(html); + break; + } + case "tb": + { + extra.width = Math.min(availableSpace.width, Math.max(extra.width, w)); + extra.height += h; + extra.children.push(html); + break; + } + } +} +function getAvailableSpace(node) { + const availableSpace = node[$extra].availableSpace; + const marginV = node.margin ? node.margin.topInset + node.margin.bottomInset : 0; + const marginH = node.margin ? node.margin.leftInset + node.margin.rightInset : 0; + switch (node.layout) { + case "lr-tb": + case "rl-tb": + if (node[$extra].attempt === 0) { + return { + width: availableSpace.width - marginH - node[$extra].currentWidth, + height: availableSpace.height - marginV - node[$extra].prevHeight + }; + } + return { + width: availableSpace.width - marginH, + height: availableSpace.height - marginV - node[$extra].height + }; + case "rl-row": + case "row": + const width = node[$extra].columnWidths.slice(node[$extra].currentColumn).reduce((a, x) => a + x); + return { + width, + height: availableSpace.height - marginH + }; + case "table": + case "tb": + return { + width: availableSpace.width - marginH, + height: availableSpace.height - marginV - node[$extra].height + }; + case "position": + default: + return availableSpace; + } +} +function getTransformedBBox(node) { + let w = node.w === "" ? NaN : node.w; + let h = node.h === "" ? NaN : node.h; + let [centerX, centerY] = [0, 0]; + switch (node.anchorType || "") { + case "bottomCenter": + [centerX, centerY] = [w / 2, h]; + break; + case "bottomLeft": + [centerX, centerY] = [0, h]; + break; + case "bottomRight": + [centerX, centerY] = [w, h]; + break; + case "middleCenter": + [centerX, centerY] = [w / 2, h / 2]; + break; + case "middleLeft": + [centerX, centerY] = [0, h / 2]; + break; + case "middleRight": + [centerX, centerY] = [w, h / 2]; + break; + case "topCenter": + [centerX, centerY] = [w / 2, 0]; + break; + case "topRight": + [centerX, centerY] = [w, 0]; + break; + } + let x, y; + switch (node.rotate || 0) { + case 0: + [x, y] = [-centerX, -centerY]; + break; + case 90: + [x, y] = [-centerY, centerX]; + [w, h] = [h, -w]; + break; + case 180: + [x, y] = [centerX, centerY]; + [w, h] = [-w, -h]; + break; + case 270: + [x, y] = [centerY, -centerX]; + [w, h] = [-h, w]; + break; + } + return [node.x + x + Math.min(0, w), node.y + y + Math.min(0, h), Math.abs(w), Math.abs(h)]; +} +function checkDimensions(node, space) { + if (node[$getTemplateRoot]()[$extra].firstUnsplittable === null) { + return true; + } + if (node.w === 0 || node.h === 0) { + return true; + } + const ERROR = 2; + const parent = node[$getSubformParent](); + const attempt = parent[$extra]?.attempt || 0; + const [, y, w, h] = getTransformedBBox(node); + switch (parent.layout) { + case "lr-tb": + case "rl-tb": + if (attempt === 0) { + if (!node[$getTemplateRoot]()[$extra].noLayoutFailure) { + if (node.h !== "" && Math.round(h - space.height) > ERROR) { + return false; + } + if (node.w !== "") { + if (Math.round(w - space.width) <= ERROR) { + return true; + } + if (parent[$extra].numberInLine === 0) { + return space.height > ERROR; + } + return false; + } + return space.width > ERROR; + } + if (node.w !== "") { + return Math.round(w - space.width) <= ERROR; + } + return space.width > ERROR; + } + if (node[$getTemplateRoot]()[$extra].noLayoutFailure) { + return true; + } + if (node.h !== "" && Math.round(h - space.height) > ERROR) { + return false; + } + if (node.w === "" || Math.round(w - space.width) <= ERROR) { + return space.height > ERROR; + } + if (parent[$isThereMoreWidth]()) { + return false; + } + return space.height > ERROR; + case "table": + case "tb": + if (node[$getTemplateRoot]()[$extra].noLayoutFailure) { + return true; + } + if (node.h !== "" && !node[$isSplittable]()) { + return Math.round(h - space.height) <= ERROR; + } + if (node.w === "" || Math.round(w - space.width) <= ERROR) { + return space.height > ERROR; + } + if (parent[$isThereMoreWidth]()) { + return false; + } + return space.height > ERROR; + case "position": + if (node[$getTemplateRoot]()[$extra].noLayoutFailure) { + return true; + } + if (node.h === "" || Math.round(h + y - space.height) <= ERROR) { + return true; + } + const area = node[$getTemplateRoot]()[$extra].currentContentArea; + return h + y > area.h; + case "rl-row": + case "row": + if (node[$getTemplateRoot]()[$extra].noLayoutFailure) { + return true; + } + if (node.h !== "") { + return Math.round(h - space.height) <= ERROR; + } + return true; + default: + return true; + } +} + +;// ./src/core/xfa/template.js + + + + + + + + + + +const TEMPLATE_NS_ID = NamespaceIds.template.id; +const SVG_NS = "http://www.w3.org/2000/svg"; +const MAX_ATTEMPTS_FOR_LRTB_LAYOUT = 2; +const MAX_EMPTY_PAGES = 3; +const DEFAULT_TAB_INDEX = 5000; +const HEADING_PATTERN = /^H(\d+)$/; +const MIMES = new Set(["image/gif", "image/jpeg", "image/jpg", "image/pjpeg", "image/png", "image/apng", "image/x-png", "image/bmp", "image/x-ms-bmp", "image/tiff", "image/tif", "application/octet-stream"]); +const IMAGES_HEADERS = [[[0x42, 0x4d], "image/bmp"], [[0xff, 0xd8, 0xff], "image/jpeg"], [[0x49, 0x49, 0x2a, 0x00], "image/tiff"], [[0x4d, 0x4d, 0x00, 0x2a], "image/tiff"], [[0x47, 0x49, 0x46, 0x38, 0x39, 0x61], "image/gif"], [[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], "image/png"]]; +function getBorderDims(node) { + if (!node || !node.border) { + return { + w: 0, + h: 0 + }; + } + const borderExtra = node.border[$getExtra](); + if (!borderExtra) { + return { + w: 0, + h: 0 + }; + } + return { + w: borderExtra.widths[0] + borderExtra.widths[2] + borderExtra.insets[0] + borderExtra.insets[2], + h: borderExtra.widths[1] + borderExtra.widths[3] + borderExtra.insets[1] + borderExtra.insets[3] + }; +} +function hasMargin(node) { + return node.margin && (node.margin.topInset || node.margin.rightInset || node.margin.bottomInset || node.margin.leftInset); +} +function _setValue(templateNode, value) { + if (!templateNode.value) { + const nodeValue = new Value({}); + templateNode[$appendChild](nodeValue); + templateNode.value = nodeValue; + } + templateNode.value[$setValue](value); +} +function* getContainedChildren(node) { + for (const child of node[$getChildren]()) { + if (child instanceof SubformSet) { + yield* child[$getContainedChildren](); + continue; + } + yield child; + } +} +function isRequired(node) { + return node.validate?.nullTest === "error"; +} +function setTabIndex(node) { + while (node) { + if (!node.traversal) { + node[$tabIndex] = node[$getParent]()[$tabIndex]; + return; + } + if (node[$tabIndex]) { + return; + } + let next = null; + for (const child of node.traversal[$getChildren]()) { + if (child.operation === "next") { + next = child; + break; + } + } + if (!next || !next.ref) { + node[$tabIndex] = node[$getParent]()[$tabIndex]; + return; + } + const root = node[$getTemplateRoot](); + node[$tabIndex] = ++root[$tabIndex]; + const ref = root[$searchNode](next.ref, node); + if (!ref) { + return; + } + node = ref[0]; + } +} +function applyAssist(obj, attributes) { + const assist = obj.assist; + if (assist) { + const assistTitle = assist[$toHTML](); + if (assistTitle) { + attributes.title = assistTitle; + } + const role = assist.role; + const match = role.match(HEADING_PATTERN); + if (match) { + const ariaRole = "heading"; + const ariaLevel = match[1]; + attributes.role = ariaRole; + attributes["aria-level"] = ariaLevel; + } + } + if (obj.layout === "table") { + attributes.role = "table"; + } else if (obj.layout === "row") { + attributes.role = "row"; + } else { + const parent = obj[$getParent](); + if (parent.layout === "row") { + attributes.role = parent.assist?.role === "TH" ? "columnheader" : "cell"; + } + } +} +function ariaLabel(obj) { + if (!obj.assist) { + return null; + } + const assist = obj.assist; + if (assist.speak && assist.speak[$content] !== "") { + return assist.speak[$content]; + } + if (assist.toolTip) { + return assist.toolTip[$content]; + } + return null; +} +function valueToHtml(value) { + return HTMLResult.success({ + name: "div", + attributes: { + class: ["xfaRich"], + style: Object.create(null) + }, + children: [{ + name: "span", + attributes: { + style: Object.create(null) + }, + value + }] + }); +} +function setFirstUnsplittable(node) { + const root = node[$getTemplateRoot](); + if (root[$extra].firstUnsplittable === null) { + root[$extra].firstUnsplittable = node; + root[$extra].noLayoutFailure = true; + } +} +function unsetFirstUnsplittable(node) { + const root = node[$getTemplateRoot](); + if (root[$extra].firstUnsplittable === node) { + root[$extra].noLayoutFailure = false; + } +} +function handleBreak(node) { + if (node[$extra]) { + return false; + } + node[$extra] = Object.create(null); + if (node.targetType === "auto") { + return false; + } + const root = node[$getTemplateRoot](); + let target = null; + if (node.target) { + target = root[$searchNode](node.target, node[$getParent]()); + if (!target) { + return false; + } + target = target[0]; + } + const { + currentPageArea, + currentContentArea + } = root[$extra]; + if (node.targetType === "pageArea") { + if (!(target instanceof PageArea)) { + target = null; + } + if (node.startNew) { + node[$extra].target = target || currentPageArea; + return true; + } else if (target && target !== currentPageArea) { + node[$extra].target = target; + return true; + } + return false; + } + if (!(target instanceof ContentArea)) { + target = null; + } + const pageArea = target && target[$getParent](); + let index; + let nextPageArea = pageArea; + if (node.startNew) { + if (target) { + const contentAreas = pageArea.contentArea.children; + const indexForCurrent = contentAreas.indexOf(currentContentArea); + const indexForTarget = contentAreas.indexOf(target); + if (indexForCurrent !== -1 && indexForCurrent < indexForTarget) { + nextPageArea = null; + } + index = indexForTarget - 1; + } else { + index = currentPageArea.contentArea.children.indexOf(currentContentArea); + } + } else if (target && target !== currentContentArea) { + const contentAreas = pageArea.contentArea.children; + index = contentAreas.indexOf(target) - 1; + nextPageArea = pageArea === currentPageArea ? null : pageArea; + } else { + return false; + } + node[$extra].target = nextPageArea; + node[$extra].index = index; + return true; +} +function handleOverflow(node, extraNode, space) { + const root = node[$getTemplateRoot](); + const saved = root[$extra].noLayoutFailure; + const savedMethod = extraNode[$getSubformParent]; + extraNode[$getSubformParent] = () => node; + root[$extra].noLayoutFailure = true; + const res = extraNode[$toHTML](space); + node[$addHTML](res.html, res.bbox); + root[$extra].noLayoutFailure = saved; + extraNode[$getSubformParent] = savedMethod; +} +class AppearanceFilter extends StringObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "appearanceFilter"); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Arc extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "arc", true); + this.circular = getInteger({ + data: attributes.circular, + defaultValue: 0, + validate: x => x === 1 + }); + this.hand = getStringOption(attributes.hand, ["even", "left", "right"]); + this.id = attributes.id || ""; + this.startAngle = getFloat({ + data: attributes.startAngle, + defaultValue: 0, + validate: x => true + }); + this.sweepAngle = getFloat({ + data: attributes.sweepAngle, + defaultValue: 360, + validate: x => true + }); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.edge = null; + this.fill = null; + } + [$toHTML]() { + const edge = this.edge || new Edge({}); + const edgeStyle = edge[$toStyle](); + const style = Object.create(null); + if (this.fill?.presence === "visible") { + Object.assign(style, this.fill[$toStyle]()); + } else { + style.fill = "transparent"; + } + style.strokeWidth = measureToString(edge.presence === "visible" ? edge.thickness : 0); + style.stroke = edgeStyle.color; + let arc; + const attributes = { + xmlns: SVG_NS, + style: { + width: "100%", + height: "100%", + overflow: "visible" + } + }; + if (this.sweepAngle === 360) { + arc = { + name: "ellipse", + attributes: { + xmlns: SVG_NS, + cx: "50%", + cy: "50%", + rx: "50%", + ry: "50%", + style + } + }; + } else { + const startAngle = this.startAngle * Math.PI / 180; + const sweepAngle = this.sweepAngle * Math.PI / 180; + const largeArc = this.sweepAngle > 180 ? 1 : 0; + const [x1, y1, x2, y2] = [50 * (1 + Math.cos(startAngle)), 50 * (1 - Math.sin(startAngle)), 50 * (1 + Math.cos(startAngle + sweepAngle)), 50 * (1 - Math.sin(startAngle + sweepAngle))]; + arc = { + name: "path", + attributes: { + xmlns: SVG_NS, + d: `M ${x1} ${y1} A 50 50 0 ${largeArc} 0 ${x2} ${y2}`, + vectorEffect: "non-scaling-stroke", + style + } + }; + Object.assign(attributes, { + viewBox: "0 0 100 100", + preserveAspectRatio: "none" + }); + } + const svg = { + name: "svg", + children: [arc], + attributes + }; + const parent = this[$getParent]()[$getParent](); + if (hasMargin(parent)) { + return HTMLResult.success({ + name: "div", + attributes: { + style: { + display: "inline", + width: "100%", + height: "100%" + } + }, + children: [svg] + }); + } + svg.attributes.style.position = "absolute"; + return HTMLResult.success(svg); + } +} +class Area extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "area", true); + this.colSpan = getInteger({ + data: attributes.colSpan, + defaultValue: 1, + validate: n => n >= 1 || n === -1 + }); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.relevant = getRelevant(attributes.relevant); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.x = getMeasurement(attributes.x, "0pt"); + this.y = getMeasurement(attributes.y, "0pt"); + this.desc = null; + this.extras = null; + this.area = new XFAObjectArray(); + this.draw = new XFAObjectArray(); + this.exObject = new XFAObjectArray(); + this.exclGroup = new XFAObjectArray(); + this.field = new XFAObjectArray(); + this.subform = new XFAObjectArray(); + this.subformSet = new XFAObjectArray(); + } + *[$getContainedChildren]() { + yield* getContainedChildren(this); + } + [$isTransparent]() { + return true; + } + [$isBindable]() { + return true; + } + [$addHTML](html, bbox) { + const [x, y, w, h] = bbox; + this[$extra].width = Math.max(this[$extra].width, x + w); + this[$extra].height = Math.max(this[$extra].height, y + h); + this[$extra].children.push(html); + } + [$getAvailableSpace]() { + return this[$extra].availableSpace; + } + [$toHTML](availableSpace) { + const style = toStyle(this, "position"); + const attributes = { + style, + id: this[$uid], + class: ["xfaArea"] + }; + if (isPrintOnly(this)) { + attributes.class.push("xfaPrintOnly"); + } + if (this.name) { + attributes.xfaName = this.name; + } + const children = []; + this[$extra] = { + children, + width: 0, + height: 0, + availableSpace + }; + const result = this[$childrenToHTML]({ + filter: new Set(["area", "draw", "field", "exclGroup", "subform", "subformSet"]), + include: true + }); + if (!result.success) { + if (result.isBreak()) { + return result; + } + delete this[$extra]; + return HTMLResult.FAILURE; + } + style.width = measureToString(this[$extra].width); + style.height = measureToString(this[$extra].height); + const html = { + name: "div", + attributes, + children + }; + const bbox = [this.x, this.y, this[$extra].width, this[$extra].height]; + delete this[$extra]; + return HTMLResult.success(html, bbox); + } +} +class Assist extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "assist", true); + this.id = attributes.id || ""; + this.role = attributes.role || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.speak = null; + this.toolTip = null; + } + [$toHTML]() { + return this.toolTip?.[$content] || null; + } +} +class Barcode extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "barcode", true); + this.charEncoding = getKeyword({ + data: attributes.charEncoding ? attributes.charEncoding.toLowerCase() : "", + defaultValue: "", + validate: k => ["utf-8", "big-five", "fontspecific", "gbk", "gb-18030", "gb-2312", "ksc-5601", "none", "shift-jis", "ucs-2", "utf-16"].includes(k) || k.match(/iso-8859-\d{2}/) + }); + this.checksum = getStringOption(attributes.checksum, ["none", "1mod10", "1mod10_1mod11", "2mod10", "auto"]); + this.dataColumnCount = getInteger({ + data: attributes.dataColumnCount, + defaultValue: -1, + validate: x => x >= 0 + }); + this.dataLength = getInteger({ + data: attributes.dataLength, + defaultValue: -1, + validate: x => x >= 0 + }); + this.dataPrep = getStringOption(attributes.dataPrep, ["none", "flateCompress"]); + this.dataRowCount = getInteger({ + data: attributes.dataRowCount, + defaultValue: -1, + validate: x => x >= 0 + }); + this.endChar = attributes.endChar || ""; + this.errorCorrectionLevel = getInteger({ + data: attributes.errorCorrectionLevel, + defaultValue: -1, + validate: x => x >= 0 && x <= 8 + }); + this.id = attributes.id || ""; + this.moduleHeight = getMeasurement(attributes.moduleHeight, "5mm"); + this.moduleWidth = getMeasurement(attributes.moduleWidth, "0.25mm"); + this.printCheckDigit = getInteger({ + data: attributes.printCheckDigit, + defaultValue: 0, + validate: x => x === 1 + }); + this.rowColumnRatio = getRatio(attributes.rowColumnRatio); + this.startChar = attributes.startChar || ""; + this.textLocation = getStringOption(attributes.textLocation, ["below", "above", "aboveEmbedded", "belowEmbedded", "none"]); + this.truncate = getInteger({ + data: attributes.truncate, + defaultValue: 0, + validate: x => x === 1 + }); + this.type = getStringOption(attributes.type ? attributes.type.toLowerCase() : "", ["aztec", "codabar", "code2of5industrial", "code2of5interleaved", "code2of5matrix", "code2of5standard", "code3of9", "code3of9extended", "code11", "code49", "code93", "code128", "code128a", "code128b", "code128c", "code128sscc", "datamatrix", "ean8", "ean8add2", "ean8add5", "ean13", "ean13add2", "ean13add5", "ean13pwcd", "fim", "logmars", "maxicode", "msi", "pdf417", "pdf417macro", "plessey", "postauscust2", "postauscust3", "postausreplypaid", "postausstandard", "postukrm4scc", "postusdpbc", "postusimb", "postusstandard", "postus5zip", "qrcode", "rfid", "rss14", "rss14expanded", "rss14limited", "rss14stacked", "rss14stackedomni", "rss14truncated", "telepen", "ucc128", "ucc128random", "ucc128sscc", "upca", "upcaadd2", "upcaadd5", "upcapwcd", "upce", "upceadd2", "upceadd5", "upcean2", "upcean5", "upsmaxicode"]); + this.upsMode = getStringOption(attributes.upsMode, ["usCarrier", "internationalCarrier", "secureSymbol", "standardSymbol"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.wideNarrowRatio = getRatio(attributes.wideNarrowRatio); + this.encrypt = null; + this.extras = null; + } +} +class Bind extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "bind", true); + this.match = getStringOption(attributes.match, ["once", "dataRef", "global", "none"]); + this.ref = attributes.ref || ""; + this.picture = null; + } +} +class BindItems extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "bindItems"); + this.connection = attributes.connection || ""; + this.labelRef = attributes.labelRef || ""; + this.ref = attributes.ref || ""; + this.valueRef = attributes.valueRef || ""; + } +} +class Bookend extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "bookend"); + this.id = attributes.id || ""; + this.leader = attributes.leader || ""; + this.trailer = attributes.trailer || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class BooleanElement extends Option01 { + constructor(attributes) { + super(TEMPLATE_NS_ID, "boolean"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } + [$toHTML](availableSpace) { + return valueToHtml(this[$content] === 1 ? "1" : "0"); + } +} +class Border extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "border", true); + this.break = getStringOption(attributes.break, ["close", "open"]); + this.hand = getStringOption(attributes.hand, ["even", "left", "right"]); + this.id = attributes.id || ""; + this.presence = getStringOption(attributes.presence, ["visible", "hidden", "inactive", "invisible"]); + this.relevant = getRelevant(attributes.relevant); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.corner = new XFAObjectArray(4); + this.edge = new XFAObjectArray(4); + this.extras = null; + this.fill = null; + this.margin = null; + } + [$getExtra]() { + if (!this[$extra]) { + const edges = this.edge.children.slice(); + if (edges.length < 4) { + const defaultEdge = edges.at(-1) || new Edge({}); + for (let i = edges.length; i < 4; i++) { + edges.push(defaultEdge); + } + } + const widths = edges.map(edge => edge.thickness); + const insets = [0, 0, 0, 0]; + if (this.margin) { + insets[0] = this.margin.topInset; + insets[1] = this.margin.rightInset; + insets[2] = this.margin.bottomInset; + insets[3] = this.margin.leftInset; + } + this[$extra] = { + widths, + insets, + edges + }; + } + return this[$extra]; + } + [$toStyle]() { + const { + edges + } = this[$getExtra](); + const edgeStyles = edges.map(node => { + const style = node[$toStyle](); + style.color ||= "#000000"; + return style; + }); + const style = Object.create(null); + if (this.margin) { + Object.assign(style, this.margin[$toStyle]()); + } + if (this.fill?.presence === "visible") { + Object.assign(style, this.fill[$toStyle]()); + } + if (this.corner.children.some(node => node.radius !== 0)) { + const cornerStyles = this.corner.children.map(node => node[$toStyle]()); + if (cornerStyles.length === 2 || cornerStyles.length === 3) { + const last = cornerStyles.at(-1); + for (let i = cornerStyles.length; i < 4; i++) { + cornerStyles.push(last); + } + } + style.borderRadius = cornerStyles.map(s => s.radius).join(" "); + } + switch (this.presence) { + case "invisible": + case "hidden": + style.borderStyle = ""; + break; + case "inactive": + style.borderStyle = "none"; + break; + default: + style.borderStyle = edgeStyles.map(s => s.style).join(" "); + break; + } + style.borderWidth = edgeStyles.map(s => s.width).join(" "); + style.borderColor = edgeStyles.map(s => s.color).join(" "); + return style; + } +} +class Break extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "break", true); + this.after = getStringOption(attributes.after, ["auto", "contentArea", "pageArea", "pageEven", "pageOdd"]); + this.afterTarget = attributes.afterTarget || ""; + this.before = getStringOption(attributes.before, ["auto", "contentArea", "pageArea", "pageEven", "pageOdd"]); + this.beforeTarget = attributes.beforeTarget || ""; + this.bookendLeader = attributes.bookendLeader || ""; + this.bookendTrailer = attributes.bookendTrailer || ""; + this.id = attributes.id || ""; + this.overflowLeader = attributes.overflowLeader || ""; + this.overflowTarget = attributes.overflowTarget || ""; + this.overflowTrailer = attributes.overflowTrailer || ""; + this.startNew = getInteger({ + data: attributes.startNew, + defaultValue: 0, + validate: x => x === 1 + }); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + } +} +class BreakAfter extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "breakAfter", true); + this.id = attributes.id || ""; + this.leader = attributes.leader || ""; + this.startNew = getInteger({ + data: attributes.startNew, + defaultValue: 0, + validate: x => x === 1 + }); + this.target = attributes.target || ""; + this.targetType = getStringOption(attributes.targetType, ["auto", "contentArea", "pageArea"]); + this.trailer = attributes.trailer || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.script = null; + } +} +class BreakBefore extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "breakBefore", true); + this.id = attributes.id || ""; + this.leader = attributes.leader || ""; + this.startNew = getInteger({ + data: attributes.startNew, + defaultValue: 0, + validate: x => x === 1 + }); + this.target = attributes.target || ""; + this.targetType = getStringOption(attributes.targetType, ["auto", "contentArea", "pageArea"]); + this.trailer = attributes.trailer || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.script = null; + } + [$toHTML](availableSpace) { + this[$extra] = {}; + return HTMLResult.FAILURE; + } +} +class Button extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "button", true); + this.highlight = getStringOption(attributes.highlight, ["inverted", "none", "outline", "push"]); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + } + [$toHTML](availableSpace) { + const parent = this[$getParent](); + const grandpa = parent[$getParent](); + const htmlButton = { + name: "button", + attributes: { + id: this[$uid], + class: ["xfaButton"], + style: {} + }, + children: [] + }; + for (const event of grandpa.event.children) { + if (event.activity !== "click" || !event.script) { + continue; + } + const jsURL = recoverJsURL(event.script[$content]); + if (!jsURL) { + continue; + } + const href = fixURL(jsURL.url); + if (!href) { + continue; + } + htmlButton.children.push({ + name: "a", + attributes: { + id: "link" + this[$uid], + href, + newWindow: jsURL.newWindow, + class: ["xfaLink"], + style: {} + }, + children: [] + }); + } + return HTMLResult.success(htmlButton); + } +} +class Calculate extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "calculate", true); + this.id = attributes.id || ""; + this.override = getStringOption(attributes.override, ["disabled", "error", "ignore", "warning"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + this.message = null; + this.script = null; + } +} +class Caption extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "caption", true); + this.id = attributes.id || ""; + this.placement = getStringOption(attributes.placement, ["left", "bottom", "inline", "right", "top"]); + this.presence = getStringOption(attributes.presence, ["visible", "hidden", "inactive", "invisible"]); + this.reserve = Math.ceil(getMeasurement(attributes.reserve)); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + this.font = null; + this.margin = null; + this.para = null; + this.value = null; + } + [$setValue](value) { + _setValue(this, value); + } + [$getExtra](availableSpace) { + if (!this[$extra]) { + let { + width, + height + } = availableSpace; + switch (this.placement) { + case "left": + case "right": + case "inline": + width = this.reserve <= 0 ? width : this.reserve; + break; + case "top": + case "bottom": + height = this.reserve <= 0 ? height : this.reserve; + break; + } + this[$extra] = layoutNode(this, { + width, + height + }); + } + return this[$extra]; + } + [$toHTML](availableSpace) { + if (!this.value) { + return HTMLResult.EMPTY; + } + this[$pushPara](); + const value = this.value[$toHTML](availableSpace).html; + if (!value) { + this[$popPara](); + return HTMLResult.EMPTY; + } + const savedReserve = this.reserve; + if (this.reserve <= 0) { + const { + w, + h + } = this[$getExtra](availableSpace); + switch (this.placement) { + case "left": + case "right": + case "inline": + this.reserve = w; + break; + case "top": + case "bottom": + this.reserve = h; + break; + } + } + const children = []; + if (typeof value === "string") { + children.push({ + name: "#text", + value + }); + } else { + children.push(value); + } + const style = toStyle(this, "font", "margin", "visibility"); + switch (this.placement) { + case "left": + case "right": + if (this.reserve > 0) { + style.width = measureToString(this.reserve); + } + break; + case "top": + case "bottom": + if (this.reserve > 0) { + style.height = measureToString(this.reserve); + } + break; + } + setPara(this, null, value); + this[$popPara](); + this.reserve = savedReserve; + return HTMLResult.success({ + name: "div", + attributes: { + style, + class: ["xfaCaption"] + }, + children + }); + } +} +class Certificate extends StringObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "certificate"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Certificates extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "certificates", true); + this.credentialServerPolicy = getStringOption(attributes.credentialServerPolicy, ["optional", "required"]); + this.id = attributes.id || ""; + this.url = attributes.url || ""; + this.urlPolicy = attributes.urlPolicy || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.encryption = null; + this.issuers = null; + this.keyUsage = null; + this.oids = null; + this.signing = null; + this.subjectDNs = null; + } +} +class CheckButton extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "checkButton", true); + this.id = attributes.id || ""; + this.mark = getStringOption(attributes.mark, ["default", "check", "circle", "cross", "diamond", "square", "star"]); + this.shape = getStringOption(attributes.shape, ["square", "round"]); + this.size = getMeasurement(attributes.size, "10pt"); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.border = null; + this.extras = null; + this.margin = null; + } + [$toHTML](availableSpace) { + const style = toStyle("margin"); + const size = measureToString(this.size); + style.width = style.height = size; + let type; + let className; + let groupId; + const field = this[$getParent]()[$getParent](); + const items = field.items.children.length && field.items.children[0][$toHTML]().html || []; + const exportedValue = { + on: (items[0] !== undefined ? items[0] : "on").toString(), + off: (items[1] !== undefined ? items[1] : "off").toString() + }; + const value = field.value?.[$text]() || "off"; + const checked = value === exportedValue.on || undefined; + const container = field[$getSubformParent](); + const fieldId = field[$uid]; + let dataId; + if (container instanceof ExclGroup) { + groupId = container[$uid]; + type = "radio"; + className = "xfaRadio"; + dataId = container[$data]?.[$uid] || container[$uid]; + } else { + type = "checkbox"; + className = "xfaCheckbox"; + dataId = field[$data]?.[$uid] || field[$uid]; + } + const input = { + name: "input", + attributes: { + class: [className], + style, + fieldId, + dataId, + type, + checked, + xfaOn: exportedValue.on, + xfaOff: exportedValue.off, + "aria-label": ariaLabel(field), + "aria-required": false + } + }; + if (groupId) { + input.attributes.name = groupId; + } + if (isRequired(field)) { + input.attributes["aria-required"] = true; + input.attributes.required = true; + } + return HTMLResult.success({ + name: "label", + attributes: { + class: ["xfaLabel"] + }, + children: [input] + }); + } +} +class ChoiceList extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "choiceList", true); + this.commitOn = getStringOption(attributes.commitOn, ["select", "exit"]); + this.id = attributes.id || ""; + this.open = getStringOption(attributes.open, ["userControl", "always", "multiSelect", "onEntry"]); + this.textEntry = getInteger({ + data: attributes.textEntry, + defaultValue: 0, + validate: x => x === 1 + }); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.border = null; + this.extras = null; + this.margin = null; + } + [$toHTML](availableSpace) { + const style = toStyle(this, "border", "margin"); + const ui = this[$getParent](); + const field = ui[$getParent](); + const fontSize = field.font?.size || 10; + const optionStyle = { + fontSize: `calc(${fontSize}px * var(--scale-factor))` + }; + const children = []; + if (field.items.children.length > 0) { + const items = field.items; + let displayedIndex = 0; + let saveIndex = 0; + if (items.children.length === 2) { + displayedIndex = items.children[0].save; + saveIndex = 1 - displayedIndex; + } + const displayed = items.children[displayedIndex][$toHTML]().html; + const values = items.children[saveIndex][$toHTML]().html; + let selected = false; + const value = field.value?.[$text]() || ""; + for (let i = 0, ii = displayed.length; i < ii; i++) { + const option = { + name: "option", + attributes: { + value: values[i] || displayed[i], + style: optionStyle + }, + value: displayed[i] + }; + if (values[i] === value) { + option.attributes.selected = selected = true; + } + children.push(option); + } + if (!selected) { + children.splice(0, 0, { + name: "option", + attributes: { + hidden: true, + selected: true + }, + value: " " + }); + } + } + const selectAttributes = { + class: ["xfaSelect"], + fieldId: field[$uid], + dataId: field[$data]?.[$uid] || field[$uid], + style, + "aria-label": ariaLabel(field), + "aria-required": false + }; + if (isRequired(field)) { + selectAttributes["aria-required"] = true; + selectAttributes.required = true; + } + if (this.open === "multiSelect") { + selectAttributes.multiple = true; + } + return HTMLResult.success({ + name: "label", + attributes: { + class: ["xfaLabel"] + }, + children: [{ + name: "select", + children, + attributes: selectAttributes + }] + }); + } +} +class Color extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "color", true); + this.cSpace = getStringOption(attributes.cSpace, ["SRGB"]); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.value = attributes.value ? getColor(attributes.value) : ""; + this.extras = null; + } + [$hasSettableValue]() { + return false; + } + [$toStyle]() { + return this.value ? Util.makeHexColor(this.value.r, this.value.g, this.value.b) : null; + } +} +class Comb extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "comb"); + this.id = attributes.id || ""; + this.numberOfCells = getInteger({ + data: attributes.numberOfCells, + defaultValue: 0, + validate: x => x >= 0 + }); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Connect extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "connect", true); + this.connection = attributes.connection || ""; + this.id = attributes.id || ""; + this.ref = attributes.ref || ""; + this.usage = getStringOption(attributes.usage, ["exportAndImport", "exportOnly", "importOnly"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.picture = null; + } +} +class ContentArea extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "contentArea", true); + this.h = getMeasurement(attributes.h); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.relevant = getRelevant(attributes.relevant); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.w = getMeasurement(attributes.w); + this.x = getMeasurement(attributes.x, "0pt"); + this.y = getMeasurement(attributes.y, "0pt"); + this.desc = null; + this.extras = null; + } + [$toHTML](availableSpace) { + const left = measureToString(this.x); + const top = measureToString(this.y); + const style = { + left, + top, + width: measureToString(this.w), + height: measureToString(this.h) + }; + const classNames = ["xfaContentarea"]; + if (isPrintOnly(this)) { + classNames.push("xfaPrintOnly"); + } + return HTMLResult.success({ + name: "div", + children: [], + attributes: { + style, + class: classNames, + id: this[$uid] + } + }); + } +} +class Corner extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "corner", true); + this.id = attributes.id || ""; + this.inverted = getInteger({ + data: attributes.inverted, + defaultValue: 0, + validate: x => x === 1 + }); + this.join = getStringOption(attributes.join, ["square", "round"]); + this.presence = getStringOption(attributes.presence, ["visible", "hidden", "inactive", "invisible"]); + this.radius = getMeasurement(attributes.radius); + this.stroke = getStringOption(attributes.stroke, ["solid", "dashDot", "dashDotDot", "dashed", "dotted", "embossed", "etched", "lowered", "raised"]); + this.thickness = getMeasurement(attributes.thickness, "0.5pt"); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.color = null; + this.extras = null; + } + [$toStyle]() { + const style = toStyle(this, "visibility"); + style.radius = measureToString(this.join === "square" ? 0 : this.radius); + return style; + } +} +class DateElement extends ContentObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "date"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } + [$finalize]() { + const date = this[$content].trim(); + this[$content] = date ? new Date(date) : null; + } + [$toHTML](availableSpace) { + return valueToHtml(this[$content] ? this[$content].toString() : ""); + } +} +class DateTime extends ContentObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "dateTime"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } + [$finalize]() { + const date = this[$content].trim(); + this[$content] = date ? new Date(date) : null; + } + [$toHTML](availableSpace) { + return valueToHtml(this[$content] ? this[$content].toString() : ""); + } +} +class DateTimeEdit extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "dateTimeEdit", true); + this.hScrollPolicy = getStringOption(attributes.hScrollPolicy, ["auto", "off", "on"]); + this.id = attributes.id || ""; + this.picker = getStringOption(attributes.picker, ["host", "none"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.border = null; + this.comb = null; + this.extras = null; + this.margin = null; + } + [$toHTML](availableSpace) { + const style = toStyle(this, "border", "font", "margin"); + const field = this[$getParent]()[$getParent](); + const html = { + name: "input", + attributes: { + type: "text", + fieldId: field[$uid], + dataId: field[$data]?.[$uid] || field[$uid], + class: ["xfaTextfield"], + style, + "aria-label": ariaLabel(field), + "aria-required": false + } + }; + if (isRequired(field)) { + html.attributes["aria-required"] = true; + html.attributes.required = true; + } + return HTMLResult.success({ + name: "label", + attributes: { + class: ["xfaLabel"] + }, + children: [html] + }); + } +} +class Decimal extends ContentObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "decimal"); + this.fracDigits = getInteger({ + data: attributes.fracDigits, + defaultValue: 2, + validate: x => true + }); + this.id = attributes.id || ""; + this.leadDigits = getInteger({ + data: attributes.leadDigits, + defaultValue: -1, + validate: x => true + }); + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } + [$finalize]() { + const number = parseFloat(this[$content].trim()); + this[$content] = isNaN(number) ? null : number; + } + [$toHTML](availableSpace) { + return valueToHtml(this[$content] !== null ? this[$content].toString() : ""); + } +} +class DefaultUi extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "defaultUi", true); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + } +} +class Desc extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "desc", true); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.boolean = new XFAObjectArray(); + this.date = new XFAObjectArray(); + this.dateTime = new XFAObjectArray(); + this.decimal = new XFAObjectArray(); + this.exData = new XFAObjectArray(); + this.float = new XFAObjectArray(); + this.image = new XFAObjectArray(); + this.integer = new XFAObjectArray(); + this.text = new XFAObjectArray(); + this.time = new XFAObjectArray(); + } +} +class DigestMethod extends OptionObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "digestMethod", ["", "SHA1", "SHA256", "SHA512", "RIPEMD160"]); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class DigestMethods extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "digestMethods", true); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.digestMethod = new XFAObjectArray(); + } +} +class Draw extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "draw", true); + this.anchorType = getStringOption(attributes.anchorType, ["topLeft", "bottomCenter", "bottomLeft", "bottomRight", "middleCenter", "middleLeft", "middleRight", "topCenter", "topRight"]); + this.colSpan = getInteger({ + data: attributes.colSpan, + defaultValue: 1, + validate: n => n >= 1 || n === -1 + }); + this.h = attributes.h ? getMeasurement(attributes.h) : ""; + this.hAlign = getStringOption(attributes.hAlign, ["left", "center", "justify", "justifyAll", "radix", "right"]); + this.id = attributes.id || ""; + this.locale = attributes.locale || ""; + this.maxH = getMeasurement(attributes.maxH, "0pt"); + this.maxW = getMeasurement(attributes.maxW, "0pt"); + this.minH = getMeasurement(attributes.minH, "0pt"); + this.minW = getMeasurement(attributes.minW, "0pt"); + this.name = attributes.name || ""; + this.presence = getStringOption(attributes.presence, ["visible", "hidden", "inactive", "invisible"]); + this.relevant = getRelevant(attributes.relevant); + this.rotate = getInteger({ + data: attributes.rotate, + defaultValue: 0, + validate: x => x % 90 === 0 + }); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.w = attributes.w ? getMeasurement(attributes.w) : ""; + this.x = getMeasurement(attributes.x, "0pt"); + this.y = getMeasurement(attributes.y, "0pt"); + this.assist = null; + this.border = null; + this.caption = null; + this.desc = null; + this.extras = null; + this.font = null; + this.keep = null; + this.margin = null; + this.para = null; + this.traversal = null; + this.ui = null; + this.value = null; + this.setProperty = new XFAObjectArray(); + } + [$setValue](value) { + _setValue(this, value); + } + [$toHTML](availableSpace) { + setTabIndex(this); + if (this.presence === "hidden" || this.presence === "inactive") { + return HTMLResult.EMPTY; + } + fixDimensions(this); + this[$pushPara](); + const savedW = this.w; + const savedH = this.h; + const { + w, + h, + isBroken + } = layoutNode(this, availableSpace); + if (w && this.w === "") { + if (isBroken && this[$getSubformParent]()[$isThereMoreWidth]()) { + this[$popPara](); + return HTMLResult.FAILURE; + } + this.w = w; + } + if (h && this.h === "") { + this.h = h; + } + setFirstUnsplittable(this); + if (!checkDimensions(this, availableSpace)) { + this.w = savedW; + this.h = savedH; + this[$popPara](); + return HTMLResult.FAILURE; + } + unsetFirstUnsplittable(this); + const style = toStyle(this, "font", "hAlign", "dimensions", "position", "presence", "rotate", "anchorType", "border", "margin"); + setMinMaxDimensions(this, style); + if (style.margin) { + style.padding = style.margin; + delete style.margin; + } + const classNames = ["xfaDraw"]; + if (this.font) { + classNames.push("xfaFont"); + } + if (isPrintOnly(this)) { + classNames.push("xfaPrintOnly"); + } + const attributes = { + style, + id: this[$uid], + class: classNames + }; + if (this.name) { + attributes.xfaName = this.name; + } + const html = { + name: "div", + attributes, + children: [] + }; + applyAssist(this, attributes); + const bbox = computeBbox(this, html, availableSpace); + const value = this.value ? this.value[$toHTML](availableSpace).html : null; + if (value === null) { + this.w = savedW; + this.h = savedH; + this[$popPara](); + return HTMLResult.success(createWrapper(this, html), bbox); + } + html.children.push(value); + setPara(this, style, value); + this.w = savedW; + this.h = savedH; + this[$popPara](); + return HTMLResult.success(createWrapper(this, html), bbox); + } +} +class Edge extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "edge", true); + this.cap = getStringOption(attributes.cap, ["square", "butt", "round"]); + this.id = attributes.id || ""; + this.presence = getStringOption(attributes.presence, ["visible", "hidden", "inactive", "invisible"]); + this.stroke = getStringOption(attributes.stroke, ["solid", "dashDot", "dashDotDot", "dashed", "dotted", "embossed", "etched", "lowered", "raised"]); + this.thickness = getMeasurement(attributes.thickness, "0.5pt"); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.color = null; + this.extras = null; + } + [$toStyle]() { + const style = toStyle(this, "visibility"); + Object.assign(style, { + linecap: this.cap, + width: measureToString(this.thickness), + color: this.color ? this.color[$toStyle]() : "#000000", + style: "" + }); + if (this.presence !== "visible") { + style.style = "none"; + } else { + switch (this.stroke) { + case "solid": + style.style = "solid"; + break; + case "dashDot": + style.style = "dashed"; + break; + case "dashDotDot": + style.style = "dashed"; + break; + case "dashed": + style.style = "dashed"; + break; + case "dotted": + style.style = "dotted"; + break; + case "embossed": + style.style = "ridge"; + break; + case "etched": + style.style = "groove"; + break; + case "lowered": + style.style = "inset"; + break; + case "raised": + style.style = "outset"; + break; + } + } + return style; + } +} +class Encoding extends OptionObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "encoding", ["adbe.x509.rsa_sha1", "adbe.pkcs7.detached", "adbe.pkcs7.sha1"]); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Encodings extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "encodings", true); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.encoding = new XFAObjectArray(); + } +} +class Encrypt extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "encrypt", true); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.certificate = null; + } +} +class EncryptData extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "encryptData", true); + this.id = attributes.id || ""; + this.operation = getStringOption(attributes.operation, ["encrypt", "decrypt"]); + this.target = attributes.target || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.filter = null; + this.manifest = null; + } +} +class Encryption extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "encryption", true); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.certificate = new XFAObjectArray(); + } +} +class EncryptionMethod extends OptionObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "encryptionMethod", ["", "AES256-CBC", "TRIPLEDES-CBC", "AES128-CBC", "AES192-CBC"]); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class EncryptionMethods extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "encryptionMethods", true); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.encryptionMethod = new XFAObjectArray(); + } +} +class Event extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "event", true); + this.activity = getStringOption(attributes.activity, ["click", "change", "docClose", "docReady", "enter", "exit", "full", "indexChange", "initialize", "mouseDown", "mouseEnter", "mouseExit", "mouseUp", "postExecute", "postOpen", "postPrint", "postSave", "postSign", "postSubmit", "preExecute", "preOpen", "prePrint", "preSave", "preSign", "preSubmit", "ready", "validationState"]); + this.id = attributes.id || ""; + this.listen = getStringOption(attributes.listen, ["refOnly", "refAndDescendents"]); + this.name = attributes.name || ""; + this.ref = attributes.ref || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + this.encryptData = null; + this.execute = null; + this.script = null; + this.signData = null; + this.submit = null; + } +} +class ExData extends ContentObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "exData"); + this.contentType = attributes.contentType || ""; + this.href = attributes.href || ""; + this.id = attributes.id || ""; + this.maxLength = getInteger({ + data: attributes.maxLength, + defaultValue: -1, + validate: x => x >= -1 + }); + this.name = attributes.name || ""; + this.rid = attributes.rid || ""; + this.transferEncoding = getStringOption(attributes.transferEncoding, ["none", "base64", "package"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } + [$isCDATAXml]() { + return this.contentType === "text/html"; + } + [$onChild](child) { + if (this.contentType === "text/html" && child[$namespaceId] === NamespaceIds.xhtml.id) { + this[$content] = child; + return true; + } + if (this.contentType === "text/xml") { + this[$content] = child; + return true; + } + return false; + } + [$toHTML](availableSpace) { + if (this.contentType !== "text/html" || !this[$content]) { + return HTMLResult.EMPTY; + } + return this[$content][$toHTML](availableSpace); + } +} +class ExObject extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "exObject", true); + this.archive = attributes.archive || ""; + this.classId = attributes.classId || ""; + this.codeBase = attributes.codeBase || ""; + this.codeType = attributes.codeType || ""; + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + this.boolean = new XFAObjectArray(); + this.date = new XFAObjectArray(); + this.dateTime = new XFAObjectArray(); + this.decimal = new XFAObjectArray(); + this.exData = new XFAObjectArray(); + this.exObject = new XFAObjectArray(); + this.float = new XFAObjectArray(); + this.image = new XFAObjectArray(); + this.integer = new XFAObjectArray(); + this.text = new XFAObjectArray(); + this.time = new XFAObjectArray(); + } +} +class ExclGroup extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "exclGroup", true); + this.access = getStringOption(attributes.access, ["open", "nonInteractive", "protected", "readOnly"]); + this.accessKey = attributes.accessKey || ""; + this.anchorType = getStringOption(attributes.anchorType, ["topLeft", "bottomCenter", "bottomLeft", "bottomRight", "middleCenter", "middleLeft", "middleRight", "topCenter", "topRight"]); + this.colSpan = getInteger({ + data: attributes.colSpan, + defaultValue: 1, + validate: n => n >= 1 || n === -1 + }); + this.h = attributes.h ? getMeasurement(attributes.h) : ""; + this.hAlign = getStringOption(attributes.hAlign, ["left", "center", "justify", "justifyAll", "radix", "right"]); + this.id = attributes.id || ""; + this.layout = getStringOption(attributes.layout, ["position", "lr-tb", "rl-row", "rl-tb", "row", "table", "tb"]); + this.maxH = getMeasurement(attributes.maxH, "0pt"); + this.maxW = getMeasurement(attributes.maxW, "0pt"); + this.minH = getMeasurement(attributes.minH, "0pt"); + this.minW = getMeasurement(attributes.minW, "0pt"); + this.name = attributes.name || ""; + this.presence = getStringOption(attributes.presence, ["visible", "hidden", "inactive", "invisible"]); + this.relevant = getRelevant(attributes.relevant); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.w = attributes.w ? getMeasurement(attributes.w) : ""; + this.x = getMeasurement(attributes.x, "0pt"); + this.y = getMeasurement(attributes.y, "0pt"); + this.assist = null; + this.bind = null; + this.border = null; + this.calculate = null; + this.caption = null; + this.desc = null; + this.extras = null; + this.margin = null; + this.para = null; + this.traversal = null; + this.validate = null; + this.connect = new XFAObjectArray(); + this.event = new XFAObjectArray(); + this.field = new XFAObjectArray(); + this.setProperty = new XFAObjectArray(); + } + [$isBindable]() { + return true; + } + [$hasSettableValue]() { + return true; + } + [$setValue](value) { + for (const field of this.field.children) { + if (!field.value) { + const nodeValue = new Value({}); + field[$appendChild](nodeValue); + field.value = nodeValue; + } + field.value[$setValue](value); + } + } + [$isThereMoreWidth]() { + return this.layout.endsWith("-tb") && this[$extra].attempt === 0 && this[$extra].numberInLine > 0 || this[$getParent]()[$isThereMoreWidth](); + } + [$isSplittable]() { + const parent = this[$getSubformParent](); + if (!parent[$isSplittable]()) { + return false; + } + if (this[$extra]._isSplittable !== undefined) { + return this[$extra]._isSplittable; + } + if (this.layout === "position" || this.layout.includes("row")) { + this[$extra]._isSplittable = false; + return false; + } + if (parent.layout?.endsWith("-tb") && parent[$extra].numberInLine !== 0) { + return false; + } + this[$extra]._isSplittable = true; + return true; + } + [$flushHTML]() { + return flushHTML(this); + } + [$addHTML](html, bbox) { + addHTML(this, html, bbox); + } + [$getAvailableSpace]() { + return getAvailableSpace(this); + } + [$toHTML](availableSpace) { + setTabIndex(this); + if (this.presence === "hidden" || this.presence === "inactive" || this.h === 0 || this.w === 0) { + return HTMLResult.EMPTY; + } + fixDimensions(this); + const children = []; + const attributes = { + id: this[$uid], + class: [] + }; + setAccess(this, attributes.class); + if (!this[$extra]) { + this[$extra] = Object.create(null); + } + Object.assign(this[$extra], { + children, + attributes, + attempt: 0, + line: null, + numberInLine: 0, + availableSpace: { + width: Math.min(this.w || Infinity, availableSpace.width), + height: Math.min(this.h || Infinity, availableSpace.height) + }, + width: 0, + height: 0, + prevHeight: 0, + currentWidth: 0 + }); + const isSplittable = this[$isSplittable](); + if (!isSplittable) { + setFirstUnsplittable(this); + } + if (!checkDimensions(this, availableSpace)) { + return HTMLResult.FAILURE; + } + const filter = new Set(["field"]); + if (this.layout.includes("row")) { + const columnWidths = this[$getSubformParent]().columnWidths; + if (Array.isArray(columnWidths) && columnWidths.length > 0) { + this[$extra].columnWidths = columnWidths; + this[$extra].currentColumn = 0; + } + } + const style = toStyle(this, "anchorType", "dimensions", "position", "presence", "border", "margin", "hAlign"); + const classNames = ["xfaExclgroup"]; + const cl = layoutClass(this); + if (cl) { + classNames.push(cl); + } + if (isPrintOnly(this)) { + classNames.push("xfaPrintOnly"); + } + attributes.style = style; + attributes.class = classNames; + if (this.name) { + attributes.xfaName = this.name; + } + this[$pushPara](); + const isLrTb = this.layout === "lr-tb" || this.layout === "rl-tb"; + const maxRun = isLrTb ? MAX_ATTEMPTS_FOR_LRTB_LAYOUT : 1; + for (; this[$extra].attempt < maxRun; this[$extra].attempt++) { + if (isLrTb && this[$extra].attempt === MAX_ATTEMPTS_FOR_LRTB_LAYOUT - 1) { + this[$extra].numberInLine = 0; + } + const result = this[$childrenToHTML]({ + filter, + include: true + }); + if (result.success) { + break; + } + if (result.isBreak()) { + this[$popPara](); + return result; + } + if (isLrTb && this[$extra].attempt === 0 && this[$extra].numberInLine === 0 && !this[$getTemplateRoot]()[$extra].noLayoutFailure) { + this[$extra].attempt = maxRun; + break; + } + } + this[$popPara](); + if (!isSplittable) { + unsetFirstUnsplittable(this); + } + if (this[$extra].attempt === maxRun) { + if (!isSplittable) { + delete this[$extra]; + } + return HTMLResult.FAILURE; + } + let marginH = 0; + let marginV = 0; + if (this.margin) { + marginH = this.margin.leftInset + this.margin.rightInset; + marginV = this.margin.topInset + this.margin.bottomInset; + } + const width = Math.max(this[$extra].width + marginH, this.w || 0); + const height = Math.max(this[$extra].height + marginV, this.h || 0); + const bbox = [this.x, this.y, width, height]; + if (this.w === "") { + style.width = measureToString(width); + } + if (this.h === "") { + style.height = measureToString(height); + } + const html = { + name: "div", + attributes, + children + }; + applyAssist(this, attributes); + delete this[$extra]; + return HTMLResult.success(createWrapper(this, html), bbox); + } +} +class Execute extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "execute"); + this.connection = attributes.connection || ""; + this.executeType = getStringOption(attributes.executeType, ["import", "remerge"]); + this.id = attributes.id || ""; + this.runAt = getStringOption(attributes.runAt, ["client", "both", "server"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Extras extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "extras", true); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.boolean = new XFAObjectArray(); + this.date = new XFAObjectArray(); + this.dateTime = new XFAObjectArray(); + this.decimal = new XFAObjectArray(); + this.exData = new XFAObjectArray(); + this.extras = new XFAObjectArray(); + this.float = new XFAObjectArray(); + this.image = new XFAObjectArray(); + this.integer = new XFAObjectArray(); + this.text = new XFAObjectArray(); + this.time = new XFAObjectArray(); + } +} +class Field extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "field", true); + this.access = getStringOption(attributes.access, ["open", "nonInteractive", "protected", "readOnly"]); + this.accessKey = attributes.accessKey || ""; + this.anchorType = getStringOption(attributes.anchorType, ["topLeft", "bottomCenter", "bottomLeft", "bottomRight", "middleCenter", "middleLeft", "middleRight", "topCenter", "topRight"]); + this.colSpan = getInteger({ + data: attributes.colSpan, + defaultValue: 1, + validate: n => n >= 1 || n === -1 + }); + this.h = attributes.h ? getMeasurement(attributes.h) : ""; + this.hAlign = getStringOption(attributes.hAlign, ["left", "center", "justify", "justifyAll", "radix", "right"]); + this.id = attributes.id || ""; + this.locale = attributes.locale || ""; + this.maxH = getMeasurement(attributes.maxH, "0pt"); + this.maxW = getMeasurement(attributes.maxW, "0pt"); + this.minH = getMeasurement(attributes.minH, "0pt"); + this.minW = getMeasurement(attributes.minW, "0pt"); + this.name = attributes.name || ""; + this.presence = getStringOption(attributes.presence, ["visible", "hidden", "inactive", "invisible"]); + this.relevant = getRelevant(attributes.relevant); + this.rotate = getInteger({ + data: attributes.rotate, + defaultValue: 0, + validate: x => x % 90 === 0 + }); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.w = attributes.w ? getMeasurement(attributes.w) : ""; + this.x = getMeasurement(attributes.x, "0pt"); + this.y = getMeasurement(attributes.y, "0pt"); + this.assist = null; + this.bind = null; + this.border = null; + this.calculate = null; + this.caption = null; + this.desc = null; + this.extras = null; + this.font = null; + this.format = null; + this.items = new XFAObjectArray(2); + this.keep = null; + this.margin = null; + this.para = null; + this.traversal = null; + this.ui = null; + this.validate = null; + this.value = null; + this.bindItems = new XFAObjectArray(); + this.connect = new XFAObjectArray(); + this.event = new XFAObjectArray(); + this.setProperty = new XFAObjectArray(); + } + [$isBindable]() { + return true; + } + [$setValue](value) { + _setValue(this, value); + } + [$toHTML](availableSpace) { + setTabIndex(this); + if (!this.ui) { + this.ui = new Ui({}); + this.ui[$globalData] = this[$globalData]; + this[$appendChild](this.ui); + let node; + switch (this.items.children.length) { + case 0: + node = new TextEdit({}); + this.ui.textEdit = node; + break; + case 1: + node = new CheckButton({}); + this.ui.checkButton = node; + break; + case 2: + node = new ChoiceList({}); + this.ui.choiceList = node; + break; + } + this.ui[$appendChild](node); + } + if (!this.ui || this.presence === "hidden" || this.presence === "inactive" || this.h === 0 || this.w === 0) { + return HTMLResult.EMPTY; + } + if (this.caption) { + delete this.caption[$extra]; + } + this[$pushPara](); + const caption = this.caption ? this.caption[$toHTML](availableSpace).html : null; + const savedW = this.w; + const savedH = this.h; + let marginH = 0; + let marginV = 0; + if (this.margin) { + marginH = this.margin.leftInset + this.margin.rightInset; + marginV = this.margin.topInset + this.margin.bottomInset; + } + let borderDims = null; + if (this.w === "" || this.h === "") { + let width = null; + let height = null; + let uiW = 0; + let uiH = 0; + if (this.ui.checkButton) { + uiW = uiH = this.ui.checkButton.size; + } else { + const { + w, + h + } = layoutNode(this, availableSpace); + if (w !== null) { + uiW = w; + uiH = h; + } else { + uiH = fonts_getMetrics(this.font, true).lineNoGap; + } + } + borderDims = getBorderDims(this.ui[$getExtra]()); + uiW += borderDims.w; + uiH += borderDims.h; + if (this.caption) { + const { + w, + h, + isBroken + } = this.caption[$getExtra](availableSpace); + if (isBroken && this[$getSubformParent]()[$isThereMoreWidth]()) { + this[$popPara](); + return HTMLResult.FAILURE; + } + width = w; + height = h; + switch (this.caption.placement) { + case "left": + case "right": + case "inline": + width += uiW; + break; + case "top": + case "bottom": + height += uiH; + break; + } + } else { + width = uiW; + height = uiH; + } + if (width && this.w === "") { + width += marginH; + this.w = Math.min(this.maxW <= 0 ? Infinity : this.maxW, this.minW + 1 < width ? width : this.minW); + } + if (height && this.h === "") { + height += marginV; + this.h = Math.min(this.maxH <= 0 ? Infinity : this.maxH, this.minH + 1 < height ? height : this.minH); + } + } + this[$popPara](); + fixDimensions(this); + setFirstUnsplittable(this); + if (!checkDimensions(this, availableSpace)) { + this.w = savedW; + this.h = savedH; + this[$popPara](); + return HTMLResult.FAILURE; + } + unsetFirstUnsplittable(this); + const style = toStyle(this, "font", "dimensions", "position", "rotate", "anchorType", "presence", "margin", "hAlign"); + setMinMaxDimensions(this, style); + const classNames = ["xfaField"]; + if (this.font) { + classNames.push("xfaFont"); + } + if (isPrintOnly(this)) { + classNames.push("xfaPrintOnly"); + } + const attributes = { + style, + id: this[$uid], + class: classNames + }; + if (style.margin) { + style.padding = style.margin; + delete style.margin; + } + setAccess(this, classNames); + if (this.name) { + attributes.xfaName = this.name; + } + const children = []; + const html = { + name: "div", + attributes, + children + }; + applyAssist(this, attributes); + const borderStyle = this.border ? this.border[$toStyle]() : null; + const bbox = computeBbox(this, html, availableSpace); + const ui = this.ui[$toHTML]().html; + if (!ui) { + Object.assign(style, borderStyle); + return HTMLResult.success(createWrapper(this, html), bbox); + } + if (this[$tabIndex]) { + if (ui.children?.[0]) { + ui.children[0].attributes.tabindex = this[$tabIndex]; + } else { + ui.attributes.tabindex = this[$tabIndex]; + } + } + if (!ui.attributes.style) { + ui.attributes.style = Object.create(null); + } + let aElement = null; + if (this.ui.button) { + if (ui.children.length === 1) { + [aElement] = ui.children.splice(0, 1); + } + Object.assign(ui.attributes.style, borderStyle); + } else { + Object.assign(style, borderStyle); + } + children.push(ui); + if (this.value) { + if (this.ui.imageEdit) { + ui.children.push(this.value[$toHTML]().html); + } else if (!this.ui.button) { + let value = ""; + if (this.value.exData) { + value = this.value.exData[$text](); + } else if (this.value.text) { + value = this.value.text[$getExtra](); + } else { + const htmlValue = this.value[$toHTML]().html; + if (htmlValue !== null) { + value = htmlValue.children[0].value; + } + } + if (this.ui.textEdit && this.value.text?.maxChars) { + ui.children[0].attributes.maxLength = this.value.text.maxChars; + } + if (value) { + if (this.ui.numericEdit) { + value = parseFloat(value); + value = isNaN(value) ? "" : value.toString(); + } + if (ui.children[0].name === "textarea") { + ui.children[0].attributes.textContent = value; + } else { + ui.children[0].attributes.value = value; + } + } + } + } + if (!this.ui.imageEdit && ui.children?.[0] && this.h) { + borderDims = borderDims || getBorderDims(this.ui[$getExtra]()); + let captionHeight = 0; + if (this.caption && ["top", "bottom"].includes(this.caption.placement)) { + captionHeight = this.caption.reserve; + if (captionHeight <= 0) { + captionHeight = this.caption[$getExtra](availableSpace).h; + } + const inputHeight = this.h - captionHeight - marginV - borderDims.h; + ui.children[0].attributes.style.height = measureToString(inputHeight); + } else { + ui.children[0].attributes.style.height = "100%"; + } + } + if (aElement) { + ui.children.push(aElement); + } + if (!caption) { + if (ui.attributes.class) { + ui.attributes.class.push("xfaLeft"); + } + this.w = savedW; + this.h = savedH; + return HTMLResult.success(createWrapper(this, html), bbox); + } + if (this.ui.button) { + if (style.padding) { + delete style.padding; + } + if (caption.name === "div") { + caption.name = "span"; + } + ui.children.push(caption); + return HTMLResult.success(html, bbox); + } else if (this.ui.checkButton) { + caption.attributes.class[0] = "xfaCaptionForCheckButton"; + } + if (!ui.attributes.class) { + ui.attributes.class = []; + } + ui.children.splice(0, 0, caption); + switch (this.caption.placement) { + case "left": + ui.attributes.class.push("xfaLeft"); + break; + case "right": + ui.attributes.class.push("xfaRight"); + break; + case "top": + ui.attributes.class.push("xfaTop"); + break; + case "bottom": + ui.attributes.class.push("xfaBottom"); + break; + case "inline": + ui.attributes.class.push("xfaLeft"); + break; + } + this.w = savedW; + this.h = savedH; + return HTMLResult.success(createWrapper(this, html), bbox); + } +} +class Fill extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "fill", true); + this.id = attributes.id || ""; + this.presence = getStringOption(attributes.presence, ["visible", "hidden", "inactive", "invisible"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.color = null; + this.extras = null; + this.linear = null; + this.pattern = null; + this.radial = null; + this.solid = null; + this.stipple = null; + } + [$toStyle]() { + const parent = this[$getParent](); + const grandpa = parent[$getParent](); + const ggrandpa = grandpa[$getParent](); + const style = Object.create(null); + let propName = "color"; + let altPropName = propName; + if (parent instanceof Border) { + propName = "background-color"; + altPropName = "background"; + if (ggrandpa instanceof Ui) { + style.backgroundColor = "white"; + } + } + if (parent instanceof Rectangle || parent instanceof Arc) { + propName = altPropName = "fill"; + style.fill = "white"; + } + for (const name of Object.getOwnPropertyNames(this)) { + if (name === "extras" || name === "color") { + continue; + } + const obj = this[name]; + if (!(obj instanceof XFAObject)) { + continue; + } + const color = obj[$toStyle](this.color); + if (color) { + style[color.startsWith("#") ? propName : altPropName] = color; + } + return style; + } + if (this.color?.value) { + const color = this.color[$toStyle](); + style[color.startsWith("#") ? propName : altPropName] = color; + } + return style; + } +} +class Filter extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "filter", true); + this.addRevocationInfo = getStringOption(attributes.addRevocationInfo, ["", "required", "optional", "none"]); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.version = getInteger({ + data: this.version, + defaultValue: 5, + validate: x => x >= 1 && x <= 5 + }); + this.appearanceFilter = null; + this.certificates = null; + this.digestMethods = null; + this.encodings = null; + this.encryptionMethods = null; + this.handler = null; + this.lockDocument = null; + this.mdp = null; + this.reasons = null; + this.timeStamp = null; + } +} +class Float extends ContentObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "float"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } + [$finalize]() { + const number = parseFloat(this[$content].trim()); + this[$content] = isNaN(number) ? null : number; + } + [$toHTML](availableSpace) { + return valueToHtml(this[$content] !== null ? this[$content].toString() : ""); + } +} +class template_Font extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "font", true); + this.baselineShift = getMeasurement(attributes.baselineShift); + this.fontHorizontalScale = getFloat({ + data: attributes.fontHorizontalScale, + defaultValue: 100, + validate: x => x >= 0 + }); + this.fontVerticalScale = getFloat({ + data: attributes.fontVerticalScale, + defaultValue: 100, + validate: x => x >= 0 + }); + this.id = attributes.id || ""; + this.kerningMode = getStringOption(attributes.kerningMode, ["none", "pair"]); + this.letterSpacing = getMeasurement(attributes.letterSpacing, "0"); + this.lineThrough = getInteger({ + data: attributes.lineThrough, + defaultValue: 0, + validate: x => x === 1 || x === 2 + }); + this.lineThroughPeriod = getStringOption(attributes.lineThroughPeriod, ["all", "word"]); + this.overline = getInteger({ + data: attributes.overline, + defaultValue: 0, + validate: x => x === 1 || x === 2 + }); + this.overlinePeriod = getStringOption(attributes.overlinePeriod, ["all", "word"]); + this.posture = getStringOption(attributes.posture, ["normal", "italic"]); + this.size = getMeasurement(attributes.size, "10pt"); + this.typeface = attributes.typeface || "Courier"; + this.underline = getInteger({ + data: attributes.underline, + defaultValue: 0, + validate: x => x === 1 || x === 2 + }); + this.underlinePeriod = getStringOption(attributes.underlinePeriod, ["all", "word"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.weight = getStringOption(attributes.weight, ["normal", "bold"]); + this.extras = null; + this.fill = null; + } + [$clean](builder) { + super[$clean](builder); + this[$globalData].usedTypefaces.add(this.typeface); + } + [$toStyle]() { + const style = toStyle(this, "fill"); + const color = style.color; + if (color) { + if (color === "#000000") { + delete style.color; + } else if (!color.startsWith("#")) { + style.background = color; + style.backgroundClip = "text"; + style.color = "transparent"; + } + } + if (this.baselineShift) { + style.verticalAlign = measureToString(this.baselineShift); + } + style.fontKerning = this.kerningMode === "none" ? "none" : "normal"; + style.letterSpacing = measureToString(this.letterSpacing); + if (this.lineThrough !== 0) { + style.textDecoration = "line-through"; + if (this.lineThrough === 2) { + style.textDecorationStyle = "double"; + } + } + if (this.overline !== 0) { + style.textDecoration = "overline"; + if (this.overline === 2) { + style.textDecorationStyle = "double"; + } + } + style.fontStyle = this.posture; + style.fontSize = measureToString(0.99 * this.size); + setFontFamily(this, this, this[$globalData].fontFinder, style); + if (this.underline !== 0) { + style.textDecoration = "underline"; + if (this.underline === 2) { + style.textDecorationStyle = "double"; + } + } + style.fontWeight = this.weight; + return style; + } +} +class Format extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "format", true); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + this.picture = null; + } +} +class Handler extends StringObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "handler"); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Hyphenation extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "hyphenation"); + this.excludeAllCaps = getInteger({ + data: attributes.excludeAllCaps, + defaultValue: 0, + validate: x => x === 1 + }); + this.excludeInitialCap = getInteger({ + data: attributes.excludeInitialCap, + defaultValue: 0, + validate: x => x === 1 + }); + this.hyphenate = getInteger({ + data: attributes.hyphenate, + defaultValue: 0, + validate: x => x === 1 + }); + this.id = attributes.id || ""; + this.pushCharacterCount = getInteger({ + data: attributes.pushCharacterCount, + defaultValue: 3, + validate: x => x >= 0 + }); + this.remainCharacterCount = getInteger({ + data: attributes.remainCharacterCount, + defaultValue: 3, + validate: x => x >= 0 + }); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.wordCharacterCount = getInteger({ + data: attributes.wordCharacterCount, + defaultValue: 7, + validate: x => x >= 0 + }); + } +} +class Image extends StringObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "image"); + this.aspect = getStringOption(attributes.aspect, ["fit", "actual", "height", "none", "width"]); + this.contentType = attributes.contentType || ""; + this.href = attributes.href || ""; + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.transferEncoding = getStringOption(attributes.transferEncoding, ["base64", "none", "package"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } + [$toHTML]() { + if (this.contentType && !MIMES.has(this.contentType.toLowerCase())) { + return HTMLResult.EMPTY; + } + let buffer = this[$globalData].images && this[$globalData].images.get(this.href); + if (!buffer && (this.href || !this[$content])) { + return HTMLResult.EMPTY; + } + if (!buffer && this.transferEncoding === "base64") { + buffer = fromBase64Util(this[$content]); + } + if (!buffer) { + return HTMLResult.EMPTY; + } + if (!this.contentType) { + for (const [header, type] of IMAGES_HEADERS) { + if (buffer.length > header.length && header.every((x, i) => x === buffer[i])) { + this.contentType = type; + break; + } + } + if (!this.contentType) { + return HTMLResult.EMPTY; + } + } + const blob = new Blob([buffer], { + type: this.contentType + }); + let style; + switch (this.aspect) { + case "fit": + case "actual": + break; + case "height": + style = { + height: "100%", + objectFit: "fill" + }; + break; + case "none": + style = { + width: "100%", + height: "100%", + objectFit: "fill" + }; + break; + case "width": + style = { + width: "100%", + objectFit: "fill" + }; + break; + } + const parent = this[$getParent](); + return HTMLResult.success({ + name: "img", + attributes: { + class: ["xfaImage"], + style, + src: URL.createObjectURL(blob), + alt: parent ? ariaLabel(parent[$getParent]()) : null + } + }); + } +} +class ImageEdit extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "imageEdit", true); + this.data = getStringOption(attributes.data, ["link", "embed"]); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.border = null; + this.extras = null; + this.margin = null; + } + [$toHTML](availableSpace) { + if (this.data === "embed") { + return HTMLResult.success({ + name: "div", + children: [], + attributes: {} + }); + } + return HTMLResult.EMPTY; + } +} +class Integer extends ContentObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "integer"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } + [$finalize]() { + const number = parseInt(this[$content].trim(), 10); + this[$content] = isNaN(number) ? null : number; + } + [$toHTML](availableSpace) { + return valueToHtml(this[$content] !== null ? this[$content].toString() : ""); + } +} +class Issuers extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "issuers", true); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.certificate = new XFAObjectArray(); + } +} +class Items extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "items", true); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.presence = getStringOption(attributes.presence, ["visible", "hidden", "inactive", "invisible"]); + this.ref = attributes.ref || ""; + this.save = getInteger({ + data: attributes.save, + defaultValue: 0, + validate: x => x === 1 + }); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.boolean = new XFAObjectArray(); + this.date = new XFAObjectArray(); + this.dateTime = new XFAObjectArray(); + this.decimal = new XFAObjectArray(); + this.exData = new XFAObjectArray(); + this.float = new XFAObjectArray(); + this.image = new XFAObjectArray(); + this.integer = new XFAObjectArray(); + this.text = new XFAObjectArray(); + this.time = new XFAObjectArray(); + } + [$toHTML]() { + const output = []; + for (const child of this[$getChildren]()) { + output.push(child[$text]()); + } + return HTMLResult.success(output); + } +} +class Keep extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "keep", true); + this.id = attributes.id || ""; + const options = ["none", "contentArea", "pageArea"]; + this.intact = getStringOption(attributes.intact, options); + this.next = getStringOption(attributes.next, options); + this.previous = getStringOption(attributes.previous, options); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + } +} +class KeyUsage extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "keyUsage"); + const options = ["", "yes", "no"]; + this.crlSign = getStringOption(attributes.crlSign, options); + this.dataEncipherment = getStringOption(attributes.dataEncipherment, options); + this.decipherOnly = getStringOption(attributes.decipherOnly, options); + this.digitalSignature = getStringOption(attributes.digitalSignature, options); + this.encipherOnly = getStringOption(attributes.encipherOnly, options); + this.id = attributes.id || ""; + this.keyAgreement = getStringOption(attributes.keyAgreement, options); + this.keyCertSign = getStringOption(attributes.keyCertSign, options); + this.keyEncipherment = getStringOption(attributes.keyEncipherment, options); + this.nonRepudiation = getStringOption(attributes.nonRepudiation, options); + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Line extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "line", true); + this.hand = getStringOption(attributes.hand, ["even", "left", "right"]); + this.id = attributes.id || ""; + this.slope = getStringOption(attributes.slope, ["\\", "/"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.edge = null; + } + [$toHTML]() { + const parent = this[$getParent]()[$getParent](); + const edge = this.edge || new Edge({}); + const edgeStyle = edge[$toStyle](); + const style = Object.create(null); + const thickness = edge.presence === "visible" ? edge.thickness : 0; + style.strokeWidth = measureToString(thickness); + style.stroke = edgeStyle.color; + let x1, y1, x2, y2; + let width = "100%"; + let height = "100%"; + if (parent.w <= thickness) { + [x1, y1, x2, y2] = ["50%", 0, "50%", "100%"]; + width = style.strokeWidth; + } else if (parent.h <= thickness) { + [x1, y1, x2, y2] = [0, "50%", "100%", "50%"]; + height = style.strokeWidth; + } else if (this.slope === "\\") { + [x1, y1, x2, y2] = [0, 0, "100%", "100%"]; + } else { + [x1, y1, x2, y2] = [0, "100%", "100%", 0]; + } + const line = { + name: "line", + attributes: { + xmlns: SVG_NS, + x1, + y1, + x2, + y2, + style + } + }; + const svg = { + name: "svg", + children: [line], + attributes: { + xmlns: SVG_NS, + width, + height, + style: { + overflow: "visible" + } + } + }; + if (hasMargin(parent)) { + return HTMLResult.success({ + name: "div", + attributes: { + style: { + display: "inline", + width: "100%", + height: "100%" + } + }, + children: [svg] + }); + } + svg.attributes.style.position = "absolute"; + return HTMLResult.success(svg); + } +} +class Linear extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "linear", true); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["toRight", "toBottom", "toLeft", "toTop"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.color = null; + this.extras = null; + } + [$toStyle](startColor) { + startColor = startColor ? startColor[$toStyle]() : "#FFFFFF"; + const transf = this.type.replace(/([RBLT])/, " $1").toLowerCase(); + const endColor = this.color ? this.color[$toStyle]() : "#000000"; + return `linear-gradient(${transf}, ${startColor}, ${endColor})`; + } +} +class LockDocument extends ContentObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "lockDocument"); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } + [$finalize]() { + this[$content] = getStringOption(this[$content], ["auto", "0", "1"]); + } +} +class Manifest extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "manifest", true); + this.action = getStringOption(attributes.action, ["include", "all", "exclude"]); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + this.ref = new XFAObjectArray(); + } +} +class Margin extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "margin", true); + this.bottomInset = getMeasurement(attributes.bottomInset, "0"); + this.id = attributes.id || ""; + this.leftInset = getMeasurement(attributes.leftInset, "0"); + this.rightInset = getMeasurement(attributes.rightInset, "0"); + this.topInset = getMeasurement(attributes.topInset, "0"); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + } + [$toStyle]() { + return { + margin: measureToString(this.topInset) + " " + measureToString(this.rightInset) + " " + measureToString(this.bottomInset) + " " + measureToString(this.leftInset) + }; + } +} +class Mdp extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "mdp"); + this.id = attributes.id || ""; + this.permissions = getInteger({ + data: attributes.permissions, + defaultValue: 2, + validate: x => x === 1 || x === 3 + }); + this.signatureType = getStringOption(attributes.signatureType, ["filler", "author"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Medium extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "medium"); + this.id = attributes.id || ""; + this.imagingBBox = getBBox(attributes.imagingBBox); + this.long = getMeasurement(attributes.long); + this.orientation = getStringOption(attributes.orientation, ["portrait", "landscape"]); + this.short = getMeasurement(attributes.short); + this.stock = attributes.stock || ""; + this.trayIn = getStringOption(attributes.trayIn, ["auto", "delegate", "pageFront"]); + this.trayOut = getStringOption(attributes.trayOut, ["auto", "delegate"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Message extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "message", true); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.text = new XFAObjectArray(); + } +} +class NumericEdit extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "numericEdit", true); + this.hScrollPolicy = getStringOption(attributes.hScrollPolicy, ["auto", "off", "on"]); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.border = null; + this.comb = null; + this.extras = null; + this.margin = null; + } + [$toHTML](availableSpace) { + const style = toStyle(this, "border", "font", "margin"); + const field = this[$getParent]()[$getParent](); + const html = { + name: "input", + attributes: { + type: "text", + fieldId: field[$uid], + dataId: field[$data]?.[$uid] || field[$uid], + class: ["xfaTextfield"], + style, + "aria-label": ariaLabel(field), + "aria-required": false + } + }; + if (isRequired(field)) { + html.attributes["aria-required"] = true; + html.attributes.required = true; + } + return HTMLResult.success({ + name: "label", + attributes: { + class: ["xfaLabel"] + }, + children: [html] + }); + } +} +class Occur extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "occur", true); + this.id = attributes.id || ""; + this.initial = attributes.initial !== "" ? getInteger({ + data: attributes.initial, + defaultValue: "", + validate: x => true + }) : ""; + this.max = attributes.max !== "" ? getInteger({ + data: attributes.max, + defaultValue: 1, + validate: x => true + }) : ""; + this.min = attributes.min !== "" ? getInteger({ + data: attributes.min, + defaultValue: 1, + validate: x => true + }) : ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + } + [$clean]() { + const parent = this[$getParent](); + const originalMin = this.min; + if (this.min === "") { + this.min = parent instanceof PageArea || parent instanceof PageSet ? 0 : 1; + } + if (this.max === "") { + if (originalMin === "") { + this.max = parent instanceof PageArea || parent instanceof PageSet ? -1 : 1; + } else { + this.max = this.min; + } + } + if (this.max !== -1 && this.max < this.min) { + this.max = this.min; + } + if (this.initial === "") { + this.initial = parent instanceof Template ? 1 : this.min; + } + } +} +class Oid extends StringObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "oid"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Oids extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "oids", true); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.oid = new XFAObjectArray(); + } +} +class Overflow extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "overflow"); + this.id = attributes.id || ""; + this.leader = attributes.leader || ""; + this.target = attributes.target || ""; + this.trailer = attributes.trailer || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } + [$getExtra]() { + if (!this[$extra]) { + const parent = this[$getParent](); + const root = this[$getTemplateRoot](); + const target = root[$searchNode](this.target, parent); + const leader = root[$searchNode](this.leader, parent); + const trailer = root[$searchNode](this.trailer, parent); + this[$extra] = { + target: target?.[0] || null, + leader: leader?.[0] || null, + trailer: trailer?.[0] || null, + addLeader: false, + addTrailer: false + }; + } + return this[$extra]; + } +} +class PageArea extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "pageArea", true); + this.blankOrNotBlank = getStringOption(attributes.blankOrNotBlank, ["any", "blank", "notBlank"]); + this.id = attributes.id || ""; + this.initialNumber = getInteger({ + data: attributes.initialNumber, + defaultValue: 1, + validate: x => true + }); + this.name = attributes.name || ""; + this.numbered = getInteger({ + data: attributes.numbered, + defaultValue: 1, + validate: x => true + }); + this.oddOrEven = getStringOption(attributes.oddOrEven, ["any", "even", "odd"]); + this.pagePosition = getStringOption(attributes.pagePosition, ["any", "first", "last", "only", "rest"]); + this.relevant = getRelevant(attributes.relevant); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.desc = null; + this.extras = null; + this.medium = null; + this.occur = null; + this.area = new XFAObjectArray(); + this.contentArea = new XFAObjectArray(); + this.draw = new XFAObjectArray(); + this.exclGroup = new XFAObjectArray(); + this.field = new XFAObjectArray(); + this.subform = new XFAObjectArray(); + } + [$isUsable]() { + if (!this[$extra]) { + this[$extra] = { + numberOfUse: 0 + }; + return true; + } + return !this.occur || this.occur.max === -1 || this[$extra].numberOfUse < this.occur.max; + } + [$cleanPage]() { + delete this[$extra]; + } + [$getNextPage]() { + if (!this[$extra]) { + this[$extra] = { + numberOfUse: 0 + }; + } + const parent = this[$getParent](); + if (parent.relation === "orderedOccurrence") { + if (this[$isUsable]()) { + this[$extra].numberOfUse += 1; + return this; + } + } + return parent[$getNextPage](); + } + [$getAvailableSpace]() { + return this[$extra].space || { + width: 0, + height: 0 + }; + } + [$toHTML]() { + if (!this[$extra]) { + this[$extra] = { + numberOfUse: 1 + }; + } + const children = []; + this[$extra].children = children; + const style = Object.create(null); + if (this.medium && this.medium.short && this.medium.long) { + style.width = measureToString(this.medium.short); + style.height = measureToString(this.medium.long); + this[$extra].space = { + width: this.medium.short, + height: this.medium.long + }; + if (this.medium.orientation === "landscape") { + const x = style.width; + style.width = style.height; + style.height = x; + this[$extra].space = { + width: this.medium.long, + height: this.medium.short + }; + } + } else { + warn("XFA - No medium specified in pageArea: please file a bug."); + } + this[$childrenToHTML]({ + filter: new Set(["area", "draw", "field", "subform"]), + include: true + }); + this[$childrenToHTML]({ + filter: new Set(["contentArea"]), + include: true + }); + return HTMLResult.success({ + name: "div", + children, + attributes: { + class: ["xfaPage"], + id: this[$uid], + style, + xfaName: this.name + } + }); + } +} +class PageSet extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "pageSet", true); + this.duplexImposition = getStringOption(attributes.duplexImposition, ["longEdge", "shortEdge"]); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.relation = getStringOption(attributes.relation, ["orderedOccurrence", "duplexPaginated", "simplexPaginated"]); + this.relevant = getRelevant(attributes.relevant); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + this.occur = null; + this.pageArea = new XFAObjectArray(); + this.pageSet = new XFAObjectArray(); + } + [$cleanPage]() { + for (const page of this.pageArea.children) { + page[$cleanPage](); + } + for (const page of this.pageSet.children) { + page[$cleanPage](); + } + } + [$isUsable]() { + return !this.occur || this.occur.max === -1 || this[$extra].numberOfUse < this.occur.max; + } + [$getNextPage]() { + if (!this[$extra]) { + this[$extra] = { + numberOfUse: 1, + pageIndex: -1, + pageSetIndex: -1 + }; + } + if (this.relation === "orderedOccurrence") { + if (this[$extra].pageIndex + 1 < this.pageArea.children.length) { + this[$extra].pageIndex += 1; + const pageArea = this.pageArea.children[this[$extra].pageIndex]; + return pageArea[$getNextPage](); + } + if (this[$extra].pageSetIndex + 1 < this.pageSet.children.length) { + this[$extra].pageSetIndex += 1; + return this.pageSet.children[this[$extra].pageSetIndex][$getNextPage](); + } + if (this[$isUsable]()) { + this[$extra].numberOfUse += 1; + this[$extra].pageIndex = -1; + this[$extra].pageSetIndex = -1; + return this[$getNextPage](); + } + const parent = this[$getParent](); + if (parent instanceof PageSet) { + return parent[$getNextPage](); + } + this[$cleanPage](); + return this[$getNextPage](); + } + const pageNumber = this[$getTemplateRoot]()[$extra].pageNumber; + const parity = pageNumber % 2 === 0 ? "even" : "odd"; + const position = pageNumber === 0 ? "first" : "rest"; + let page = this.pageArea.children.find(p => p.oddOrEven === parity && p.pagePosition === position); + if (page) { + return page; + } + page = this.pageArea.children.find(p => p.oddOrEven === "any" && p.pagePosition === position); + if (page) { + return page; + } + page = this.pageArea.children.find(p => p.oddOrEven === "any" && p.pagePosition === "any"); + if (page) { + return page; + } + return this.pageArea.children[0]; + } +} +class Para extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "para", true); + this.hAlign = getStringOption(attributes.hAlign, ["left", "center", "justify", "justifyAll", "radix", "right"]); + this.id = attributes.id || ""; + this.lineHeight = attributes.lineHeight ? getMeasurement(attributes.lineHeight, "0pt") : ""; + this.marginLeft = attributes.marginLeft ? getMeasurement(attributes.marginLeft, "0pt") : ""; + this.marginRight = attributes.marginRight ? getMeasurement(attributes.marginRight, "0pt") : ""; + this.orphans = getInteger({ + data: attributes.orphans, + defaultValue: 0, + validate: x => x >= 0 + }); + this.preserve = attributes.preserve || ""; + this.radixOffset = attributes.radixOffset ? getMeasurement(attributes.radixOffset, "0pt") : ""; + this.spaceAbove = attributes.spaceAbove ? getMeasurement(attributes.spaceAbove, "0pt") : ""; + this.spaceBelow = attributes.spaceBelow ? getMeasurement(attributes.spaceBelow, "0pt") : ""; + this.tabDefault = attributes.tabDefault ? getMeasurement(this.tabDefault) : ""; + this.tabStops = (attributes.tabStops || "").trim().split(/\s+/).map((x, i) => i % 2 === 1 ? getMeasurement(x) : x); + this.textIndent = attributes.textIndent ? getMeasurement(attributes.textIndent, "0pt") : ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.vAlign = getStringOption(attributes.vAlign, ["top", "bottom", "middle"]); + this.widows = getInteger({ + data: attributes.widows, + defaultValue: 0, + validate: x => x >= 0 + }); + this.hyphenation = null; + } + [$toStyle]() { + const style = toStyle(this, "hAlign"); + if (this.marginLeft !== "") { + style.paddingLeft = measureToString(this.marginLeft); + } + if (this.marginRight !== "") { + style.paddingRight = measureToString(this.marginRight); + } + if (this.spaceAbove !== "") { + style.paddingTop = measureToString(this.spaceAbove); + } + if (this.spaceBelow !== "") { + style.paddingBottom = measureToString(this.spaceBelow); + } + if (this.textIndent !== "") { + style.textIndent = measureToString(this.textIndent); + fixTextIndent(style); + } + if (this.lineHeight > 0) { + style.lineHeight = measureToString(this.lineHeight); + } + if (this.tabDefault !== "") { + style.tabSize = measureToString(this.tabDefault); + } + if (this.tabStops.length > 0) {} + if (this.hyphenatation) { + Object.assign(style, this.hyphenatation[$toStyle]()); + } + return style; + } +} +class PasswordEdit extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "passwordEdit", true); + this.hScrollPolicy = getStringOption(attributes.hScrollPolicy, ["auto", "off", "on"]); + this.id = attributes.id || ""; + this.passwordChar = attributes.passwordChar || "*"; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.border = null; + this.extras = null; + this.margin = null; + } +} +class template_Pattern extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "pattern", true); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["crossHatch", "crossDiagonal", "diagonalLeft", "diagonalRight", "horizontal", "vertical"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.color = null; + this.extras = null; + } + [$toStyle](startColor) { + startColor = startColor ? startColor[$toStyle]() : "#FFFFFF"; + const endColor = this.color ? this.color[$toStyle]() : "#000000"; + const width = 5; + const cmd = "repeating-linear-gradient"; + const colors = `${startColor},${startColor} ${width}px,${endColor} ${width}px,${endColor} ${2 * width}px`; + switch (this.type) { + case "crossHatch": + return `${cmd}(to top,${colors}) ${cmd}(to right,${colors})`; + case "crossDiagonal": + return `${cmd}(45deg,${colors}) ${cmd}(-45deg,${colors})`; + case "diagonalLeft": + return `${cmd}(45deg,${colors})`; + case "diagonalRight": + return `${cmd}(-45deg,${colors})`; + case "horizontal": + return `${cmd}(to top,${colors})`; + case "vertical": + return `${cmd}(to right,${colors})`; + } + return ""; + } +} +class Picture extends StringObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "picture"); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Proto extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "proto", true); + this.appearanceFilter = new XFAObjectArray(); + this.arc = new XFAObjectArray(); + this.area = new XFAObjectArray(); + this.assist = new XFAObjectArray(); + this.barcode = new XFAObjectArray(); + this.bindItems = new XFAObjectArray(); + this.bookend = new XFAObjectArray(); + this.boolean = new XFAObjectArray(); + this.border = new XFAObjectArray(); + this.break = new XFAObjectArray(); + this.breakAfter = new XFAObjectArray(); + this.breakBefore = new XFAObjectArray(); + this.button = new XFAObjectArray(); + this.calculate = new XFAObjectArray(); + this.caption = new XFAObjectArray(); + this.certificate = new XFAObjectArray(); + this.certificates = new XFAObjectArray(); + this.checkButton = new XFAObjectArray(); + this.choiceList = new XFAObjectArray(); + this.color = new XFAObjectArray(); + this.comb = new XFAObjectArray(); + this.connect = new XFAObjectArray(); + this.contentArea = new XFAObjectArray(); + this.corner = new XFAObjectArray(); + this.date = new XFAObjectArray(); + this.dateTime = new XFAObjectArray(); + this.dateTimeEdit = new XFAObjectArray(); + this.decimal = new XFAObjectArray(); + this.defaultUi = new XFAObjectArray(); + this.desc = new XFAObjectArray(); + this.digestMethod = new XFAObjectArray(); + this.digestMethods = new XFAObjectArray(); + this.draw = new XFAObjectArray(); + this.edge = new XFAObjectArray(); + this.encoding = new XFAObjectArray(); + this.encodings = new XFAObjectArray(); + this.encrypt = new XFAObjectArray(); + this.encryptData = new XFAObjectArray(); + this.encryption = new XFAObjectArray(); + this.encryptionMethod = new XFAObjectArray(); + this.encryptionMethods = new XFAObjectArray(); + this.event = new XFAObjectArray(); + this.exData = new XFAObjectArray(); + this.exObject = new XFAObjectArray(); + this.exclGroup = new XFAObjectArray(); + this.execute = new XFAObjectArray(); + this.extras = new XFAObjectArray(); + this.field = new XFAObjectArray(); + this.fill = new XFAObjectArray(); + this.filter = new XFAObjectArray(); + this.float = new XFAObjectArray(); + this.font = new XFAObjectArray(); + this.format = new XFAObjectArray(); + this.handler = new XFAObjectArray(); + this.hyphenation = new XFAObjectArray(); + this.image = new XFAObjectArray(); + this.imageEdit = new XFAObjectArray(); + this.integer = new XFAObjectArray(); + this.issuers = new XFAObjectArray(); + this.items = new XFAObjectArray(); + this.keep = new XFAObjectArray(); + this.keyUsage = new XFAObjectArray(); + this.line = new XFAObjectArray(); + this.linear = new XFAObjectArray(); + this.lockDocument = new XFAObjectArray(); + this.manifest = new XFAObjectArray(); + this.margin = new XFAObjectArray(); + this.mdp = new XFAObjectArray(); + this.medium = new XFAObjectArray(); + this.message = new XFAObjectArray(); + this.numericEdit = new XFAObjectArray(); + this.occur = new XFAObjectArray(); + this.oid = new XFAObjectArray(); + this.oids = new XFAObjectArray(); + this.overflow = new XFAObjectArray(); + this.pageArea = new XFAObjectArray(); + this.pageSet = new XFAObjectArray(); + this.para = new XFAObjectArray(); + this.passwordEdit = new XFAObjectArray(); + this.pattern = new XFAObjectArray(); + this.picture = new XFAObjectArray(); + this.radial = new XFAObjectArray(); + this.reason = new XFAObjectArray(); + this.reasons = new XFAObjectArray(); + this.rectangle = new XFAObjectArray(); + this.ref = new XFAObjectArray(); + this.script = new XFAObjectArray(); + this.setProperty = new XFAObjectArray(); + this.signData = new XFAObjectArray(); + this.signature = new XFAObjectArray(); + this.signing = new XFAObjectArray(); + this.solid = new XFAObjectArray(); + this.speak = new XFAObjectArray(); + this.stipple = new XFAObjectArray(); + this.subform = new XFAObjectArray(); + this.subformSet = new XFAObjectArray(); + this.subjectDN = new XFAObjectArray(); + this.subjectDNs = new XFAObjectArray(); + this.submit = new XFAObjectArray(); + this.text = new XFAObjectArray(); + this.textEdit = new XFAObjectArray(); + this.time = new XFAObjectArray(); + this.timeStamp = new XFAObjectArray(); + this.toolTip = new XFAObjectArray(); + this.traversal = new XFAObjectArray(); + this.traverse = new XFAObjectArray(); + this.ui = new XFAObjectArray(); + this.validate = new XFAObjectArray(); + this.value = new XFAObjectArray(); + this.variables = new XFAObjectArray(); + } +} +class Radial extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "radial", true); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["toEdge", "toCenter"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.color = null; + this.extras = null; + } + [$toStyle](startColor) { + startColor = startColor ? startColor[$toStyle]() : "#FFFFFF"; + const endColor = this.color ? this.color[$toStyle]() : "#000000"; + const colors = this.type === "toEdge" ? `${startColor},${endColor}` : `${endColor},${startColor}`; + return `radial-gradient(circle at center, ${colors})`; + } +} +class Reason extends StringObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "reason"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Reasons extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "reasons", true); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.reason = new XFAObjectArray(); + } +} +class Rectangle extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "rectangle", true); + this.hand = getStringOption(attributes.hand, ["even", "left", "right"]); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.corner = new XFAObjectArray(4); + this.edge = new XFAObjectArray(4); + this.fill = null; + } + [$toHTML]() { + const edge = this.edge.children.length ? this.edge.children[0] : new Edge({}); + const edgeStyle = edge[$toStyle](); + const style = Object.create(null); + if (this.fill?.presence === "visible") { + Object.assign(style, this.fill[$toStyle]()); + } else { + style.fill = "transparent"; + } + style.strokeWidth = measureToString(edge.presence === "visible" ? edge.thickness : 0); + style.stroke = edgeStyle.color; + const corner = this.corner.children.length ? this.corner.children[0] : new Corner({}); + const cornerStyle = corner[$toStyle](); + const rect = { + name: "rect", + attributes: { + xmlns: SVG_NS, + width: "100%", + height: "100%", + x: 0, + y: 0, + rx: cornerStyle.radius, + ry: cornerStyle.radius, + style + } + }; + const svg = { + name: "svg", + children: [rect], + attributes: { + xmlns: SVG_NS, + style: { + overflow: "visible" + }, + width: "100%", + height: "100%" + } + }; + const parent = this[$getParent]()[$getParent](); + if (hasMargin(parent)) { + return HTMLResult.success({ + name: "div", + attributes: { + style: { + display: "inline", + width: "100%", + height: "100%" + } + }, + children: [svg] + }); + } + svg.attributes.style.position = "absolute"; + return HTMLResult.success(svg); + } +} +class RefElement extends StringObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "ref"); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Script extends StringObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "script"); + this.binding = attributes.binding || ""; + this.contentType = attributes.contentType || ""; + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.runAt = getStringOption(attributes.runAt, ["client", "both", "server"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class SetProperty extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "setProperty"); + this.connection = attributes.connection || ""; + this.ref = attributes.ref || ""; + this.target = attributes.target || ""; + } +} +class SignData extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "signData", true); + this.id = attributes.id || ""; + this.operation = getStringOption(attributes.operation, ["sign", "clear", "verify"]); + this.ref = attributes.ref || ""; + this.target = attributes.target || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.filter = null; + this.manifest = null; + } +} +class Signature extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "signature", true); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["PDF1.3", "PDF1.6"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.border = null; + this.extras = null; + this.filter = null; + this.manifest = null; + this.margin = null; + } +} +class Signing extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "signing", true); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.certificate = new XFAObjectArray(); + } +} +class Solid extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "solid", true); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + } + [$toStyle](startColor) { + return startColor ? startColor[$toStyle]() : "#FFFFFF"; + } +} +class Speak extends StringObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "speak"); + this.disable = getInteger({ + data: attributes.disable, + defaultValue: 0, + validate: x => x === 1 + }); + this.id = attributes.id || ""; + this.priority = getStringOption(attributes.priority, ["custom", "caption", "name", "toolTip"]); + this.rid = attributes.rid || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Stipple extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "stipple", true); + this.id = attributes.id || ""; + this.rate = getInteger({ + data: attributes.rate, + defaultValue: 50, + validate: x => x >= 0 && x <= 100 + }); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.color = null; + this.extras = null; + } + [$toStyle](bgColor) { + const alpha = this.rate / 100; + return Util.makeHexColor(Math.round(bgColor.value.r * (1 - alpha) + this.value.r * alpha), Math.round(bgColor.value.g * (1 - alpha) + this.value.g * alpha), Math.round(bgColor.value.b * (1 - alpha) + this.value.b * alpha)); + } +} +class Subform extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "subform", true); + this.access = getStringOption(attributes.access, ["open", "nonInteractive", "protected", "readOnly"]); + this.allowMacro = getInteger({ + data: attributes.allowMacro, + defaultValue: 0, + validate: x => x === 1 + }); + this.anchorType = getStringOption(attributes.anchorType, ["topLeft", "bottomCenter", "bottomLeft", "bottomRight", "middleCenter", "middleLeft", "middleRight", "topCenter", "topRight"]); + this.colSpan = getInteger({ + data: attributes.colSpan, + defaultValue: 1, + validate: n => n >= 1 || n === -1 + }); + this.columnWidths = (attributes.columnWidths || "").trim().split(/\s+/).map(x => x === "-1" ? -1 : getMeasurement(x)); + this.h = attributes.h ? getMeasurement(attributes.h) : ""; + this.hAlign = getStringOption(attributes.hAlign, ["left", "center", "justify", "justifyAll", "radix", "right"]); + this.id = attributes.id || ""; + this.layout = getStringOption(attributes.layout, ["position", "lr-tb", "rl-row", "rl-tb", "row", "table", "tb"]); + this.locale = attributes.locale || ""; + this.maxH = getMeasurement(attributes.maxH, "0pt"); + this.maxW = getMeasurement(attributes.maxW, "0pt"); + this.mergeMode = getStringOption(attributes.mergeMode, ["consumeData", "matchTemplate"]); + this.minH = getMeasurement(attributes.minH, "0pt"); + this.minW = getMeasurement(attributes.minW, "0pt"); + this.name = attributes.name || ""; + this.presence = getStringOption(attributes.presence, ["visible", "hidden", "inactive", "invisible"]); + this.relevant = getRelevant(attributes.relevant); + this.restoreState = getStringOption(attributes.restoreState, ["manual", "auto"]); + this.scope = getStringOption(attributes.scope, ["name", "none"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.w = attributes.w ? getMeasurement(attributes.w) : ""; + this.x = getMeasurement(attributes.x, "0pt"); + this.y = getMeasurement(attributes.y, "0pt"); + this.assist = null; + this.bind = null; + this.bookend = null; + this.border = null; + this.break = null; + this.calculate = null; + this.desc = null; + this.extras = null; + this.keep = null; + this.margin = null; + this.occur = null; + this.overflow = null; + this.pageSet = null; + this.para = null; + this.traversal = null; + this.validate = null; + this.variables = null; + this.area = new XFAObjectArray(); + this.breakAfter = new XFAObjectArray(); + this.breakBefore = new XFAObjectArray(); + this.connect = new XFAObjectArray(); + this.draw = new XFAObjectArray(); + this.event = new XFAObjectArray(); + this.exObject = new XFAObjectArray(); + this.exclGroup = new XFAObjectArray(); + this.field = new XFAObjectArray(); + this.proto = new XFAObjectArray(); + this.setProperty = new XFAObjectArray(); + this.subform = new XFAObjectArray(); + this.subformSet = new XFAObjectArray(); + } + [$getSubformParent]() { + const parent = this[$getParent](); + if (parent instanceof SubformSet) { + return parent[$getSubformParent](); + } + return parent; + } + [$isBindable]() { + return true; + } + [$isThereMoreWidth]() { + return this.layout.endsWith("-tb") && this[$extra].attempt === 0 && this[$extra].numberInLine > 0 || this[$getParent]()[$isThereMoreWidth](); + } + *[$getContainedChildren]() { + yield* getContainedChildren(this); + } + [$flushHTML]() { + return flushHTML(this); + } + [$addHTML](html, bbox) { + addHTML(this, html, bbox); + } + [$getAvailableSpace]() { + return getAvailableSpace(this); + } + [$isSplittable]() { + const parent = this[$getSubformParent](); + if (!parent[$isSplittable]()) { + return false; + } + if (this[$extra]._isSplittable !== undefined) { + return this[$extra]._isSplittable; + } + if (this.layout === "position" || this.layout.includes("row")) { + this[$extra]._isSplittable = false; + return false; + } + if (this.keep && this.keep.intact !== "none") { + this[$extra]._isSplittable = false; + return false; + } + if (parent.layout?.endsWith("-tb") && parent[$extra].numberInLine !== 0) { + return false; + } + this[$extra]._isSplittable = true; + return true; + } + [$toHTML](availableSpace) { + setTabIndex(this); + if (this.break) { + if (this.break.after !== "auto" || this.break.afterTarget !== "") { + const node = new BreakAfter({ + targetType: this.break.after, + target: this.break.afterTarget, + startNew: this.break.startNew.toString() + }); + node[$globalData] = this[$globalData]; + this[$appendChild](node); + this.breakAfter.push(node); + } + if (this.break.before !== "auto" || this.break.beforeTarget !== "") { + const node = new BreakBefore({ + targetType: this.break.before, + target: this.break.beforeTarget, + startNew: this.break.startNew.toString() + }); + node[$globalData] = this[$globalData]; + this[$appendChild](node); + this.breakBefore.push(node); + } + if (this.break.overflowTarget !== "") { + const node = new Overflow({ + target: this.break.overflowTarget, + leader: this.break.overflowLeader, + trailer: this.break.overflowTrailer + }); + node[$globalData] = this[$globalData]; + this[$appendChild](node); + this.overflow.push(node); + } + this[$removeChild](this.break); + this.break = null; + } + if (this.presence === "hidden" || this.presence === "inactive") { + return HTMLResult.EMPTY; + } + if (this.breakBefore.children.length > 1 || this.breakAfter.children.length > 1) { + warn("XFA - Several breakBefore or breakAfter in subforms: please file a bug."); + } + if (this.breakBefore.children.length >= 1) { + const breakBefore = this.breakBefore.children[0]; + if (handleBreak(breakBefore)) { + return HTMLResult.breakNode(breakBefore); + } + } + if (this[$extra]?.afterBreakAfter) { + return HTMLResult.EMPTY; + } + fixDimensions(this); + const children = []; + const attributes = { + id: this[$uid], + class: [] + }; + setAccess(this, attributes.class); + if (!this[$extra]) { + this[$extra] = Object.create(null); + } + Object.assign(this[$extra], { + children, + line: null, + attributes, + attempt: 0, + numberInLine: 0, + availableSpace: { + width: Math.min(this.w || Infinity, availableSpace.width), + height: Math.min(this.h || Infinity, availableSpace.height) + }, + width: 0, + height: 0, + prevHeight: 0, + currentWidth: 0 + }); + const root = this[$getTemplateRoot](); + const savedNoLayoutFailure = root[$extra].noLayoutFailure; + const isSplittable = this[$isSplittable](); + if (!isSplittable) { + setFirstUnsplittable(this); + } + if (!checkDimensions(this, availableSpace)) { + return HTMLResult.FAILURE; + } + const filter = new Set(["area", "draw", "exclGroup", "field", "subform", "subformSet"]); + if (this.layout.includes("row")) { + const columnWidths = this[$getSubformParent]().columnWidths; + if (Array.isArray(columnWidths) && columnWidths.length > 0) { + this[$extra].columnWidths = columnWidths; + this[$extra].currentColumn = 0; + } + } + const style = toStyle(this, "anchorType", "dimensions", "position", "presence", "border", "margin", "hAlign"); + const classNames = ["xfaSubform"]; + const cl = layoutClass(this); + if (cl) { + classNames.push(cl); + } + attributes.style = style; + attributes.class = classNames; + if (this.name) { + attributes.xfaName = this.name; + } + if (this.overflow) { + const overflowExtra = this.overflow[$getExtra](); + if (overflowExtra.addLeader) { + overflowExtra.addLeader = false; + handleOverflow(this, overflowExtra.leader, availableSpace); + } + } + this[$pushPara](); + const isLrTb = this.layout === "lr-tb" || this.layout === "rl-tb"; + const maxRun = isLrTb ? MAX_ATTEMPTS_FOR_LRTB_LAYOUT : 1; + for (; this[$extra].attempt < maxRun; this[$extra].attempt++) { + if (isLrTb && this[$extra].attempt === MAX_ATTEMPTS_FOR_LRTB_LAYOUT - 1) { + this[$extra].numberInLine = 0; + } + const result = this[$childrenToHTML]({ + filter, + include: true + }); + if (result.success) { + break; + } + if (result.isBreak()) { + this[$popPara](); + return result; + } + if (isLrTb && this[$extra].attempt === 0 && this[$extra].numberInLine === 0 && !root[$extra].noLayoutFailure) { + this[$extra].attempt = maxRun; + break; + } + } + this[$popPara](); + if (!isSplittable) { + unsetFirstUnsplittable(this); + } + root[$extra].noLayoutFailure = savedNoLayoutFailure; + if (this[$extra].attempt === maxRun) { + if (this.overflow) { + this[$getTemplateRoot]()[$extra].overflowNode = this.overflow; + } + if (!isSplittable) { + delete this[$extra]; + } + return HTMLResult.FAILURE; + } + if (this.overflow) { + const overflowExtra = this.overflow[$getExtra](); + if (overflowExtra.addTrailer) { + overflowExtra.addTrailer = false; + handleOverflow(this, overflowExtra.trailer, availableSpace); + } + } + let marginH = 0; + let marginV = 0; + if (this.margin) { + marginH = this.margin.leftInset + this.margin.rightInset; + marginV = this.margin.topInset + this.margin.bottomInset; + } + const width = Math.max(this[$extra].width + marginH, this.w || 0); + const height = Math.max(this[$extra].height + marginV, this.h || 0); + const bbox = [this.x, this.y, width, height]; + if (this.w === "") { + style.width = measureToString(width); + } + if (this.h === "") { + style.height = measureToString(height); + } + if ((style.width === "0px" || style.height === "0px") && children.length === 0) { + return HTMLResult.EMPTY; + } + const html = { + name: "div", + attributes, + children + }; + applyAssist(this, attributes); + const result = HTMLResult.success(createWrapper(this, html), bbox); + if (this.breakAfter.children.length >= 1) { + const breakAfter = this.breakAfter.children[0]; + if (handleBreak(breakAfter)) { + this[$extra].afterBreakAfter = result; + return HTMLResult.breakNode(breakAfter); + } + } + delete this[$extra]; + return result; + } +} +class SubformSet extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "subformSet", true); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.relation = getStringOption(attributes.relation, ["ordered", "choice", "unordered"]); + this.relevant = getRelevant(attributes.relevant); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.bookend = null; + this.break = null; + this.desc = null; + this.extras = null; + this.occur = null; + this.overflow = null; + this.breakAfter = new XFAObjectArray(); + this.breakBefore = new XFAObjectArray(); + this.subform = new XFAObjectArray(); + this.subformSet = new XFAObjectArray(); + } + *[$getContainedChildren]() { + yield* getContainedChildren(this); + } + [$getSubformParent]() { + let parent = this[$getParent](); + while (!(parent instanceof Subform)) { + parent = parent[$getParent](); + } + return parent; + } + [$isBindable]() { + return true; + } +} +class SubjectDN extends ContentObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "subjectDN"); + this.delimiter = attributes.delimiter || ","; + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } + [$finalize]() { + this[$content] = new Map(this[$content].split(this.delimiter).map(kv => { + kv = kv.split("=", 2); + kv[0] = kv[0].trim(); + return kv; + })); + } +} +class SubjectDNs extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "subjectDNs", true); + this.id = attributes.id || ""; + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.subjectDN = new XFAObjectArray(); + } +} +class Submit extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "submit", true); + this.embedPDF = getInteger({ + data: attributes.embedPDF, + defaultValue: 0, + validate: x => x === 1 + }); + this.format = getStringOption(attributes.format, ["xdp", "formdata", "pdf", "urlencoded", "xfd", "xml"]); + this.id = attributes.id || ""; + this.target = attributes.target || ""; + this.textEncoding = getKeyword({ + data: attributes.textEncoding ? attributes.textEncoding.toLowerCase() : "", + defaultValue: "", + validate: k => ["utf-8", "big-five", "fontspecific", "gbk", "gb-18030", "gb-2312", "ksc-5601", "none", "shift-jis", "ucs-2", "utf-16"].includes(k) || k.match(/iso-8859-\d{2}/) + }); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.xdpContent = attributes.xdpContent || ""; + this.encrypt = null; + this.encryptData = new XFAObjectArray(); + this.signData = new XFAObjectArray(); + } +} +class Template extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "template", true); + this.baseProfile = getStringOption(attributes.baseProfile, ["full", "interactiveForms"]); + this.extras = null; + this.subform = new XFAObjectArray(); + } + [$finalize]() { + if (this.subform.children.length === 0) { + warn("XFA - No subforms in template node."); + } + if (this.subform.children.length >= 2) { + warn("XFA - Several subforms in template node: please file a bug."); + } + this[$tabIndex] = DEFAULT_TAB_INDEX; + } + [$isSplittable]() { + return true; + } + [$searchNode](expr, container) { + if (expr.startsWith("#")) { + return [this[$ids].get(expr.slice(1))]; + } + return searchNode(this, container, expr, true, true); + } + *[$toPages]() { + if (!this.subform.children.length) { + return HTMLResult.success({ + name: "div", + children: [] + }); + } + this[$extra] = { + overflowNode: null, + firstUnsplittable: null, + currentContentArea: null, + currentPageArea: null, + noLayoutFailure: false, + pageNumber: 1, + pagePosition: "first", + oddOrEven: "odd", + blankOrNotBlank: "nonBlank", + paraStack: [] + }; + const root = this.subform.children[0]; + root.pageSet[$cleanPage](); + const pageAreas = root.pageSet.pageArea.children; + const mainHtml = { + name: "div", + children: [] + }; + let pageArea = null; + let breakBefore = null; + let breakBeforeTarget = null; + if (root.breakBefore.children.length >= 1) { + breakBefore = root.breakBefore.children[0]; + breakBeforeTarget = breakBefore.target; + } else if (root.subform.children.length >= 1 && root.subform.children[0].breakBefore.children.length >= 1) { + breakBefore = root.subform.children[0].breakBefore.children[0]; + breakBeforeTarget = breakBefore.target; + } else if (root.break?.beforeTarget) { + breakBefore = root.break; + breakBeforeTarget = breakBefore.beforeTarget; + } else if (root.subform.children.length >= 1 && root.subform.children[0].break?.beforeTarget) { + breakBefore = root.subform.children[0].break; + breakBeforeTarget = breakBefore.beforeTarget; + } + if (breakBefore) { + const target = this[$searchNode](breakBeforeTarget, breakBefore[$getParent]()); + if (target instanceof PageArea) { + pageArea = target; + breakBefore[$extra] = {}; + } + } + if (!pageArea) { + pageArea = pageAreas[0]; + } + pageArea[$extra] = { + numberOfUse: 1 + }; + const pageAreaParent = pageArea[$getParent](); + pageAreaParent[$extra] = { + numberOfUse: 1, + pageIndex: pageAreaParent.pageArea.children.indexOf(pageArea), + pageSetIndex: 0 + }; + let targetPageArea; + let leader = null; + let trailer = null; + let hasSomething = true; + let hasSomethingCounter = 0; + let startIndex = 0; + while (true) { + if (!hasSomething) { + mainHtml.children.pop(); + if (++hasSomethingCounter === MAX_EMPTY_PAGES) { + warn("XFA - Something goes wrong: please file a bug."); + return mainHtml; + } + } else { + hasSomethingCounter = 0; + } + targetPageArea = null; + this[$extra].currentPageArea = pageArea; + const page = pageArea[$toHTML]().html; + mainHtml.children.push(page); + if (leader) { + this[$extra].noLayoutFailure = true; + page.children.push(leader[$toHTML](pageArea[$extra].space).html); + leader = null; + } + if (trailer) { + this[$extra].noLayoutFailure = true; + page.children.push(trailer[$toHTML](pageArea[$extra].space).html); + trailer = null; + } + const contentAreas = pageArea.contentArea.children; + const htmlContentAreas = page.children.filter(node => node.attributes.class.includes("xfaContentarea")); + hasSomething = false; + this[$extra].firstUnsplittable = null; + this[$extra].noLayoutFailure = false; + const flush = index => { + const html = root[$flushHTML](); + if (html) { + hasSomething ||= html.children?.length > 0; + htmlContentAreas[index].children.push(html); + } + }; + for (let i = startIndex, ii = contentAreas.length; i < ii; i++) { + const contentArea = this[$extra].currentContentArea = contentAreas[i]; + const space = { + width: contentArea.w, + height: contentArea.h + }; + startIndex = 0; + if (leader) { + htmlContentAreas[i].children.push(leader[$toHTML](space).html); + leader = null; + } + if (trailer) { + htmlContentAreas[i].children.push(trailer[$toHTML](space).html); + trailer = null; + } + const html = root[$toHTML](space); + if (html.success) { + if (html.html) { + hasSomething ||= html.html.children?.length > 0; + htmlContentAreas[i].children.push(html.html); + } else if (!hasSomething && mainHtml.children.length > 1) { + mainHtml.children.pop(); + } + return mainHtml; + } + if (html.isBreak()) { + const node = html.breakNode; + flush(i); + if (node.targetType === "auto") { + continue; + } + if (node.leader) { + leader = this[$searchNode](node.leader, node[$getParent]()); + leader = leader ? leader[0] : null; + } + if (node.trailer) { + trailer = this[$searchNode](node.trailer, node[$getParent]()); + trailer = trailer ? trailer[0] : null; + } + if (node.targetType === "pageArea") { + targetPageArea = node[$extra].target; + i = Infinity; + } else if (!node[$extra].target) { + i = node[$extra].index; + } else { + targetPageArea = node[$extra].target; + startIndex = node[$extra].index + 1; + i = Infinity; + } + continue; + } + if (this[$extra].overflowNode) { + const node = this[$extra].overflowNode; + this[$extra].overflowNode = null; + const overflowExtra = node[$getExtra](); + const target = overflowExtra.target; + overflowExtra.addLeader = overflowExtra.leader !== null; + overflowExtra.addTrailer = overflowExtra.trailer !== null; + flush(i); + const currentIndex = i; + i = Infinity; + if (target instanceof PageArea) { + targetPageArea = target; + } else if (target instanceof ContentArea) { + const index = contentAreas.indexOf(target); + if (index !== -1) { + if (index > currentIndex) { + i = index - 1; + } else { + startIndex = index; + } + } else { + targetPageArea = target[$getParent](); + startIndex = targetPageArea.contentArea.children.indexOf(target); + } + } + continue; + } + flush(i); + } + this[$extra].pageNumber += 1; + if (targetPageArea) { + if (targetPageArea[$isUsable]()) { + targetPageArea[$extra].numberOfUse += 1; + } else { + targetPageArea = null; + } + } + pageArea = targetPageArea || pageArea[$getNextPage](); + yield null; + } + } +} +class Text extends ContentObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "text"); + this.id = attributes.id || ""; + this.maxChars = getInteger({ + data: attributes.maxChars, + defaultValue: 0, + validate: x => x >= 0 + }); + this.name = attributes.name || ""; + this.rid = attributes.rid || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } + [$acceptWhitespace]() { + return true; + } + [$onChild](child) { + if (child[$namespaceId] === NamespaceIds.xhtml.id) { + this[$content] = child; + return true; + } + warn(`XFA - Invalid content in Text: ${child[$nodeName]}.`); + return false; + } + [$onText](str) { + if (this[$content] instanceof XFAObject) { + return; + } + super[$onText](str); + } + [$finalize]() { + if (typeof this[$content] === "string") { + this[$content] = this[$content].replaceAll("\r\n", "\n"); + } + } + [$getExtra]() { + if (typeof this[$content] === "string") { + return this[$content].split(/[\u2029\u2028\n]/).reduce((acc, line) => { + if (line) { + acc.push(line); + } + return acc; + }, []).join("\n"); + } + return this[$content][$text](); + } + [$toHTML](availableSpace) { + if (typeof this[$content] === "string") { + const html = valueToHtml(this[$content]).html; + if (this[$content].includes("\u2029")) { + html.name = "div"; + html.children = []; + this[$content].split("\u2029").map(para => para.split(/[\u2028\n]/).reduce((acc, line) => { + acc.push({ + name: "span", + value: line + }, { + name: "br" + }); + return acc; + }, [])).forEach(lines => { + html.children.push({ + name: "p", + children: lines + }); + }); + } else if (/[\u2028\n]/.test(this[$content])) { + html.name = "div"; + html.children = []; + this[$content].split(/[\u2028\n]/).forEach(line => { + html.children.push({ + name: "span", + value: line + }, { + name: "br" + }); + }); + } + return HTMLResult.success(html); + } + return this[$content][$toHTML](availableSpace); + } +} +class TextEdit extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "textEdit", true); + this.allowRichText = getInteger({ + data: attributes.allowRichText, + defaultValue: 0, + validate: x => x === 1 + }); + this.hScrollPolicy = getStringOption(attributes.hScrollPolicy, ["auto", "off", "on"]); + this.id = attributes.id || ""; + this.multiLine = getInteger({ + data: attributes.multiLine, + defaultValue: "", + validate: x => x === 0 || x === 1 + }); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.vScrollPolicy = getStringOption(attributes.vScrollPolicy, ["auto", "off", "on"]); + this.border = null; + this.comb = null; + this.extras = null; + this.margin = null; + } + [$toHTML](availableSpace) { + const style = toStyle(this, "border", "font", "margin"); + let html; + const field = this[$getParent]()[$getParent](); + if (this.multiLine === "") { + this.multiLine = field instanceof Draw ? 1 : 0; + } + if (this.multiLine === 1) { + html = { + name: "textarea", + attributes: { + dataId: field[$data]?.[$uid] || field[$uid], + fieldId: field[$uid], + class: ["xfaTextfield"], + style, + "aria-label": ariaLabel(field), + "aria-required": false + } + }; + } else { + html = { + name: "input", + attributes: { + type: "text", + dataId: field[$data]?.[$uid] || field[$uid], + fieldId: field[$uid], + class: ["xfaTextfield"], + style, + "aria-label": ariaLabel(field), + "aria-required": false + } + }; + } + if (isRequired(field)) { + html.attributes["aria-required"] = true; + html.attributes.required = true; + } + return HTMLResult.success({ + name: "label", + attributes: { + class: ["xfaLabel"] + }, + children: [html] + }); + } +} +class Time extends StringObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "time"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } + [$finalize]() { + const date = this[$content].trim(); + this[$content] = date ? new Date(date) : null; + } + [$toHTML](availableSpace) { + return valueToHtml(this[$content] ? this[$content].toString() : ""); + } +} +class TimeStamp extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "timeStamp"); + this.id = attributes.id || ""; + this.server = attributes.server || ""; + this.type = getStringOption(attributes.type, ["optional", "required"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class ToolTip extends StringObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "toolTip"); + this.id = attributes.id || ""; + this.rid = attributes.rid || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Traversal extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "traversal", true); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + this.traverse = new XFAObjectArray(); + } +} +class Traverse extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "traverse", true); + this.id = attributes.id || ""; + this.operation = getStringOption(attributes.operation, ["next", "back", "down", "first", "left", "right", "up"]); + this.ref = attributes.ref || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + this.script = null; + } + get name() { + return this.operation; + } + [$isTransparent]() { + return false; + } +} +class Ui extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "ui", true); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + this.picture = null; + this.barcode = null; + this.button = null; + this.checkButton = null; + this.choiceList = null; + this.dateTimeEdit = null; + this.defaultUi = null; + this.imageEdit = null; + this.numericEdit = null; + this.passwordEdit = null; + this.signature = null; + this.textEdit = null; + } + [$getExtra]() { + if (this[$extra] === undefined) { + for (const name of Object.getOwnPropertyNames(this)) { + if (name === "extras" || name === "picture") { + continue; + } + const obj = this[name]; + if (!(obj instanceof XFAObject)) { + continue; + } + this[$extra] = obj; + return obj; + } + this[$extra] = null; + } + return this[$extra]; + } + [$toHTML](availableSpace) { + const obj = this[$getExtra](); + if (obj) { + return obj[$toHTML](availableSpace); + } + return HTMLResult.EMPTY; + } +} +class Validate extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "validate", true); + this.formatTest = getStringOption(attributes.formatTest, ["warning", "disabled", "error"]); + this.id = attributes.id || ""; + this.nullTest = getStringOption(attributes.nullTest, ["disabled", "error", "warning"]); + this.scriptTest = getStringOption(attributes.scriptTest, ["error", "disabled", "warning"]); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.extras = null; + this.message = null; + this.picture = null; + this.script = null; + } +} +class Value extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "value", true); + this.id = attributes.id || ""; + this.override = getInteger({ + data: attributes.override, + defaultValue: 0, + validate: x => x === 1 + }); + this.relevant = getRelevant(attributes.relevant); + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.arc = null; + this.boolean = null; + this.date = null; + this.dateTime = null; + this.decimal = null; + this.exData = null; + this.float = null; + this.image = null; + this.integer = null; + this.line = null; + this.rectangle = null; + this.text = null; + this.time = null; + } + [$setValue](value) { + const parent = this[$getParent](); + if (parent instanceof Field) { + if (parent.ui?.imageEdit) { + if (!this.image) { + this.image = new Image({}); + this[$appendChild](this.image); + } + this.image[$content] = value[$content]; + return; + } + } + const valueName = value[$nodeName]; + if (this[valueName] !== null) { + this[valueName][$content] = value[$content]; + return; + } + for (const name of Object.getOwnPropertyNames(this)) { + const obj = this[name]; + if (obj instanceof XFAObject) { + this[name] = null; + this[$removeChild](obj); + } + } + this[value[$nodeName]] = value; + this[$appendChild](value); + } + [$text]() { + if (this.exData) { + if (typeof this.exData[$content] === "string") { + return this.exData[$content].trim(); + } + return this.exData[$content][$text]().trim(); + } + for (const name of Object.getOwnPropertyNames(this)) { + if (name === "image") { + continue; + } + const obj = this[name]; + if (obj instanceof XFAObject) { + return (obj[$content] || "").toString().trim(); + } + } + return null; + } + [$toHTML](availableSpace) { + for (const name of Object.getOwnPropertyNames(this)) { + const obj = this[name]; + if (!(obj instanceof XFAObject)) { + continue; + } + return obj[$toHTML](availableSpace); + } + return HTMLResult.EMPTY; + } +} +class Variables extends XFAObject { + constructor(attributes) { + super(TEMPLATE_NS_ID, "variables", true); + this.id = attributes.id || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + this.boolean = new XFAObjectArray(); + this.date = new XFAObjectArray(); + this.dateTime = new XFAObjectArray(); + this.decimal = new XFAObjectArray(); + this.exData = new XFAObjectArray(); + this.float = new XFAObjectArray(); + this.image = new XFAObjectArray(); + this.integer = new XFAObjectArray(); + this.manifest = new XFAObjectArray(); + this.script = new XFAObjectArray(); + this.text = new XFAObjectArray(); + this.time = new XFAObjectArray(); + } + [$isTransparent]() { + return true; + } +} +class TemplateNamespace { + static [$buildXFAObject](name, attributes) { + if (TemplateNamespace.hasOwnProperty(name)) { + const node = TemplateNamespace[name](attributes); + node[$setSetAttributes](attributes); + return node; + } + return undefined; + } + static appearanceFilter(attrs) { + return new AppearanceFilter(attrs); + } + static arc(attrs) { + return new Arc(attrs); + } + static area(attrs) { + return new Area(attrs); + } + static assist(attrs) { + return new Assist(attrs); + } + static barcode(attrs) { + return new Barcode(attrs); + } + static bind(attrs) { + return new Bind(attrs); + } + static bindItems(attrs) { + return new BindItems(attrs); + } + static bookend(attrs) { + return new Bookend(attrs); + } + static boolean(attrs) { + return new BooleanElement(attrs); + } + static border(attrs) { + return new Border(attrs); + } + static break(attrs) { + return new Break(attrs); + } + static breakAfter(attrs) { + return new BreakAfter(attrs); + } + static breakBefore(attrs) { + return new BreakBefore(attrs); + } + static button(attrs) { + return new Button(attrs); + } + static calculate(attrs) { + return new Calculate(attrs); + } + static caption(attrs) { + return new Caption(attrs); + } + static certificate(attrs) { + return new Certificate(attrs); + } + static certificates(attrs) { + return new Certificates(attrs); + } + static checkButton(attrs) { + return new CheckButton(attrs); + } + static choiceList(attrs) { + return new ChoiceList(attrs); + } + static color(attrs) { + return new Color(attrs); + } + static comb(attrs) { + return new Comb(attrs); + } + static connect(attrs) { + return new Connect(attrs); + } + static contentArea(attrs) { + return new ContentArea(attrs); + } + static corner(attrs) { + return new Corner(attrs); + } + static date(attrs) { + return new DateElement(attrs); + } + static dateTime(attrs) { + return new DateTime(attrs); + } + static dateTimeEdit(attrs) { + return new DateTimeEdit(attrs); + } + static decimal(attrs) { + return new Decimal(attrs); + } + static defaultUi(attrs) { + return new DefaultUi(attrs); + } + static desc(attrs) { + return new Desc(attrs); + } + static digestMethod(attrs) { + return new DigestMethod(attrs); + } + static digestMethods(attrs) { + return new DigestMethods(attrs); + } + static draw(attrs) { + return new Draw(attrs); + } + static edge(attrs) { + return new Edge(attrs); + } + static encoding(attrs) { + return new Encoding(attrs); + } + static encodings(attrs) { + return new Encodings(attrs); + } + static encrypt(attrs) { + return new Encrypt(attrs); + } + static encryptData(attrs) { + return new EncryptData(attrs); + } + static encryption(attrs) { + return new Encryption(attrs); + } + static encryptionMethod(attrs) { + return new EncryptionMethod(attrs); + } + static encryptionMethods(attrs) { + return new EncryptionMethods(attrs); + } + static event(attrs) { + return new Event(attrs); + } + static exData(attrs) { + return new ExData(attrs); + } + static exObject(attrs) { + return new ExObject(attrs); + } + static exclGroup(attrs) { + return new ExclGroup(attrs); + } + static execute(attrs) { + return new Execute(attrs); + } + static extras(attrs) { + return new Extras(attrs); + } + static field(attrs) { + return new Field(attrs); + } + static fill(attrs) { + return new Fill(attrs); + } + static filter(attrs) { + return new Filter(attrs); + } + static float(attrs) { + return new Float(attrs); + } + static font(attrs) { + return new template_Font(attrs); + } + static format(attrs) { + return new Format(attrs); + } + static handler(attrs) { + return new Handler(attrs); + } + static hyphenation(attrs) { + return new Hyphenation(attrs); + } + static image(attrs) { + return new Image(attrs); + } + static imageEdit(attrs) { + return new ImageEdit(attrs); + } + static integer(attrs) { + return new Integer(attrs); + } + static issuers(attrs) { + return new Issuers(attrs); + } + static items(attrs) { + return new Items(attrs); + } + static keep(attrs) { + return new Keep(attrs); + } + static keyUsage(attrs) { + return new KeyUsage(attrs); + } + static line(attrs) { + return new Line(attrs); + } + static linear(attrs) { + return new Linear(attrs); + } + static lockDocument(attrs) { + return new LockDocument(attrs); + } + static manifest(attrs) { + return new Manifest(attrs); + } + static margin(attrs) { + return new Margin(attrs); + } + static mdp(attrs) { + return new Mdp(attrs); + } + static medium(attrs) { + return new Medium(attrs); + } + static message(attrs) { + return new Message(attrs); + } + static numericEdit(attrs) { + return new NumericEdit(attrs); + } + static occur(attrs) { + return new Occur(attrs); + } + static oid(attrs) { + return new Oid(attrs); + } + static oids(attrs) { + return new Oids(attrs); + } + static overflow(attrs) { + return new Overflow(attrs); + } + static pageArea(attrs) { + return new PageArea(attrs); + } + static pageSet(attrs) { + return new PageSet(attrs); + } + static para(attrs) { + return new Para(attrs); + } + static passwordEdit(attrs) { + return new PasswordEdit(attrs); + } + static pattern(attrs) { + return new template_Pattern(attrs); + } + static picture(attrs) { + return new Picture(attrs); + } + static proto(attrs) { + return new Proto(attrs); + } + static radial(attrs) { + return new Radial(attrs); + } + static reason(attrs) { + return new Reason(attrs); + } + static reasons(attrs) { + return new Reasons(attrs); + } + static rectangle(attrs) { + return new Rectangle(attrs); + } + static ref(attrs) { + return new RefElement(attrs); + } + static script(attrs) { + return new Script(attrs); + } + static setProperty(attrs) { + return new SetProperty(attrs); + } + static signData(attrs) { + return new SignData(attrs); + } + static signature(attrs) { + return new Signature(attrs); + } + static signing(attrs) { + return new Signing(attrs); + } + static solid(attrs) { + return new Solid(attrs); + } + static speak(attrs) { + return new Speak(attrs); + } + static stipple(attrs) { + return new Stipple(attrs); + } + static subform(attrs) { + return new Subform(attrs); + } + static subformSet(attrs) { + return new SubformSet(attrs); + } + static subjectDN(attrs) { + return new SubjectDN(attrs); + } + static subjectDNs(attrs) { + return new SubjectDNs(attrs); + } + static submit(attrs) { + return new Submit(attrs); + } + static template(attrs) { + return new Template(attrs); + } + static text(attrs) { + return new Text(attrs); + } + static textEdit(attrs) { + return new TextEdit(attrs); + } + static time(attrs) { + return new Time(attrs); + } + static timeStamp(attrs) { + return new TimeStamp(attrs); + } + static toolTip(attrs) { + return new ToolTip(attrs); + } + static traversal(attrs) { + return new Traversal(attrs); + } + static traverse(attrs) { + return new Traverse(attrs); + } + static ui(attrs) { + return new Ui(attrs); + } + static validate(attrs) { + return new Validate(attrs); + } + static value(attrs) { + return new Value(attrs); + } + static variables(attrs) { + return new Variables(attrs); + } +} + +;// ./src/core/xfa/bind.js + + + + + + +const bind_NS_DATASETS = NamespaceIds.datasets.id; +function createText(content) { + const node = new Text({}); + node[$content] = content; + return node; +} +class Binder { + constructor(root) { + this.root = root; + this.datasets = root.datasets; + this.data = root.datasets?.data || new XmlObject(NamespaceIds.datasets.id, "data"); + this.emptyMerge = this.data[$getChildren]().length === 0; + this.root.form = this.form = root.template[$clone](); + } + _isConsumeData() { + return !this.emptyMerge && this._mergeMode; + } + _isMatchTemplate() { + return !this._isConsumeData(); + } + bind() { + this._bindElement(this.form, this.data); + return this.form; + } + getData() { + return this.data; + } + _bindValue(formNode, data, picture) { + formNode[$data] = data; + if (formNode[$hasSettableValue]()) { + if (data[$isDataValue]()) { + const value = data[$getDataValue](); + formNode[$setValue](createText(value)); + } else if (formNode instanceof Field && formNode.ui?.choiceList?.open === "multiSelect") { + const value = data[$getChildren]().map(child => child[$content].trim()).join("\n"); + formNode[$setValue](createText(value)); + } else if (this._isConsumeData()) { + warn(`XFA - Nodes haven't the same type.`); + } + } else if (!data[$isDataValue]() || this._isMatchTemplate()) { + this._bindElement(formNode, data); + } else { + warn(`XFA - Nodes haven't the same type.`); + } + } + _findDataByNameToConsume(name, isValue, dataNode, global) { + if (!name) { + return null; + } + let generator, match; + for (let i = 0; i < 3; i++) { + generator = dataNode[$getRealChildrenByNameIt](name, false, true); + while (true) { + match = generator.next().value; + if (!match) { + break; + } + if (isValue === match[$isDataValue]()) { + return match; + } + } + if (dataNode[$namespaceId] === NamespaceIds.datasets.id && dataNode[$nodeName] === "data") { + break; + } + dataNode = dataNode[$getParent](); + } + if (!global) { + return null; + } + generator = this.data[$getRealChildrenByNameIt](name, true, false); + match = generator.next().value; + if (match) { + return match; + } + generator = this.data[$getAttributeIt](name, true); + match = generator.next().value; + if (match?.[$isDataValue]()) { + return match; + } + return null; + } + _setProperties(formNode, dataNode) { + if (!formNode.hasOwnProperty("setProperty")) { + return; + } + for (const { + ref, + target, + connection + } of formNode.setProperty.children) { + if (connection) { + continue; + } + if (!ref) { + continue; + } + const nodes = searchNode(this.root, dataNode, ref, false, false); + if (!nodes) { + warn(`XFA - Invalid reference: ${ref}.`); + continue; + } + const [node] = nodes; + if (!node[$isDescendent](this.data)) { + warn(`XFA - Invalid node: must be a data node.`); + continue; + } + const targetNodes = searchNode(this.root, formNode, target, false, false); + if (!targetNodes) { + warn(`XFA - Invalid target: ${target}.`); + continue; + } + const [targetNode] = targetNodes; + if (!targetNode[$isDescendent](formNode)) { + warn(`XFA - Invalid target: must be a property or subproperty.`); + continue; + } + const targetParent = targetNode[$getParent](); + if (targetNode instanceof SetProperty || targetParent instanceof SetProperty) { + warn(`XFA - Invalid target: cannot be a setProperty or one of its properties.`); + continue; + } + if (targetNode instanceof BindItems || targetParent instanceof BindItems) { + warn(`XFA - Invalid target: cannot be a bindItems or one of its properties.`); + continue; + } + const content = node[$text](); + const name = targetNode[$nodeName]; + if (targetNode instanceof XFAAttribute) { + const attrs = Object.create(null); + attrs[name] = content; + const obj = Reflect.construct(Object.getPrototypeOf(targetParent).constructor, [attrs]); + targetParent[name] = obj[name]; + continue; + } + if (!targetNode.hasOwnProperty($content)) { + warn(`XFA - Invalid node to use in setProperty`); + continue; + } + targetNode[$data] = node; + targetNode[$content] = content; + targetNode[$finalize](); + } + } + _bindItems(formNode, dataNode) { + if (!formNode.hasOwnProperty("items") || !formNode.hasOwnProperty("bindItems") || formNode.bindItems.isEmpty()) { + return; + } + for (const item of formNode.items.children) { + formNode[$removeChild](item); + } + formNode.items.clear(); + const labels = new Items({}); + const values = new Items({}); + formNode[$appendChild](labels); + formNode.items.push(labels); + formNode[$appendChild](values); + formNode.items.push(values); + for (const { + ref, + labelRef, + valueRef, + connection + } of formNode.bindItems.children) { + if (connection) { + continue; + } + if (!ref) { + continue; + } + const nodes = searchNode(this.root, dataNode, ref, false, false); + if (!nodes) { + warn(`XFA - Invalid reference: ${ref}.`); + continue; + } + for (const node of nodes) { + if (!node[$isDescendent](this.datasets)) { + warn(`XFA - Invalid ref (${ref}): must be a datasets child.`); + continue; + } + const labelNodes = searchNode(this.root, node, labelRef, true, false); + if (!labelNodes) { + warn(`XFA - Invalid label: ${labelRef}.`); + continue; + } + const [labelNode] = labelNodes; + if (!labelNode[$isDescendent](this.datasets)) { + warn(`XFA - Invalid label: must be a datasets child.`); + continue; + } + const valueNodes = searchNode(this.root, node, valueRef, true, false); + if (!valueNodes) { + warn(`XFA - Invalid value: ${valueRef}.`); + continue; + } + const [valueNode] = valueNodes; + if (!valueNode[$isDescendent](this.datasets)) { + warn(`XFA - Invalid value: must be a datasets child.`); + continue; + } + const label = createText(labelNode[$text]()); + const value = createText(valueNode[$text]()); + labels[$appendChild](label); + labels.text.push(label); + values[$appendChild](value); + values.text.push(value); + } + } + } + _bindOccurrences(formNode, matches, picture) { + let baseClone; + if (matches.length > 1) { + baseClone = formNode[$clone](); + baseClone[$removeChild](baseClone.occur); + baseClone.occur = null; + } + this._bindValue(formNode, matches[0], picture); + this._setProperties(formNode, matches[0]); + this._bindItems(formNode, matches[0]); + if (matches.length === 1) { + return; + } + const parent = formNode[$getParent](); + const name = formNode[$nodeName]; + const pos = parent[$indexOf](formNode); + for (let i = 1, ii = matches.length; i < ii; i++) { + const match = matches[i]; + const clone = baseClone[$clone](); + parent[name].push(clone); + parent[$insertAt](pos + i, clone); + this._bindValue(clone, match, picture); + this._setProperties(clone, match); + this._bindItems(clone, match); + } + } + _createOccurrences(formNode) { + if (!this.emptyMerge) { + return; + } + const { + occur + } = formNode; + if (!occur || occur.initial <= 1) { + return; + } + const parent = formNode[$getParent](); + const name = formNode[$nodeName]; + if (!(parent[name] instanceof XFAObjectArray)) { + return; + } + let currentNumber; + if (formNode.name) { + currentNumber = parent[name].children.filter(e => e.name === formNode.name).length; + } else { + currentNumber = parent[name].children.length; + } + const pos = parent[$indexOf](formNode) + 1; + const ii = occur.initial - currentNumber; + if (ii) { + const nodeClone = formNode[$clone](); + nodeClone[$removeChild](nodeClone.occur); + nodeClone.occur = null; + parent[name].push(nodeClone); + parent[$insertAt](pos, nodeClone); + for (let i = 1; i < ii; i++) { + const clone = nodeClone[$clone](); + parent[name].push(clone); + parent[$insertAt](pos + i, clone); + } + } + } + _getOccurInfo(formNode) { + const { + name, + occur + } = formNode; + if (!occur || !name) { + return [1, 1]; + } + const max = occur.max === -1 ? Infinity : occur.max; + return [occur.min, max]; + } + _setAndBind(formNode, dataNode) { + this._setProperties(formNode, dataNode); + this._bindItems(formNode, dataNode); + this._bindElement(formNode, dataNode); + } + _bindElement(formNode, dataNode) { + const uselessNodes = []; + this._createOccurrences(formNode); + for (const child of formNode[$getChildren]()) { + if (child[$data]) { + continue; + } + if (this._mergeMode === undefined && child[$nodeName] === "subform") { + this._mergeMode = child.mergeMode === "consumeData"; + const dataChildren = dataNode[$getChildren](); + if (dataChildren.length > 0) { + this._bindOccurrences(child, [dataChildren[0]], null); + } else if (this.emptyMerge) { + const nsId = dataNode[$namespaceId] === bind_NS_DATASETS ? -1 : dataNode[$namespaceId]; + const dataChild = child[$data] = new XmlObject(nsId, child.name || "root"); + dataNode[$appendChild](dataChild); + this._bindElement(child, dataChild); + } + continue; + } + if (!child[$isBindable]()) { + continue; + } + let global = false; + let picture = null; + let ref = null; + let match = null; + if (child.bind) { + switch (child.bind.match) { + case "none": + this._setAndBind(child, dataNode); + continue; + case "global": + global = true; + break; + case "dataRef": + if (!child.bind.ref) { + warn(`XFA - ref is empty in node ${child[$nodeName]}.`); + this._setAndBind(child, dataNode); + continue; + } + ref = child.bind.ref; + break; + default: + break; + } + if (child.bind.picture) { + picture = child.bind.picture[$content]; + } + } + const [min, max] = this._getOccurInfo(child); + if (ref) { + match = searchNode(this.root, dataNode, ref, true, false); + if (match === null) { + match = createDataNode(this.data, dataNode, ref); + if (!match) { + continue; + } + if (this._isConsumeData()) { + match[$consumed] = true; + } + this._setAndBind(child, match); + continue; + } else { + if (this._isConsumeData()) { + match = match.filter(node => !node[$consumed]); + } + if (match.length > max) { + match = match.slice(0, max); + } else if (match.length === 0) { + match = null; + } + if (match && this._isConsumeData()) { + match.forEach(node => { + node[$consumed] = true; + }); + } + } + } else { + if (!child.name) { + this._setAndBind(child, dataNode); + continue; + } + if (this._isConsumeData()) { + const matches = []; + while (matches.length < max) { + const found = this._findDataByNameToConsume(child.name, child[$hasSettableValue](), dataNode, global); + if (!found) { + break; + } + found[$consumed] = true; + matches.push(found); + } + match = matches.length > 0 ? matches : null; + } else { + match = dataNode[$getRealChildrenByNameIt](child.name, false, this.emptyMerge).next().value; + if (!match) { + if (min === 0) { + uselessNodes.push(child); + continue; + } + const nsId = dataNode[$namespaceId] === bind_NS_DATASETS ? -1 : dataNode[$namespaceId]; + match = child[$data] = new XmlObject(nsId, child.name); + if (this.emptyMerge) { + match[$consumed] = true; + } + dataNode[$appendChild](match); + this._setAndBind(child, match); + continue; + } + if (this.emptyMerge) { + match[$consumed] = true; + } + match = [match]; + } + } + if (match) { + this._bindOccurrences(child, match, picture); + } else if (min > 0) { + this._setAndBind(child, dataNode); + } else { + uselessNodes.push(child); + } + } + uselessNodes.forEach(node => node[$getParent]()[$removeChild](node)); + } +} + +;// ./src/core/xfa/data.js + +class DataHandler { + constructor(root, data) { + this.data = data; + this.dataset = root.datasets || null; + } + serialize(storage) { + const stack = [[-1, this.data[$getChildren]()]]; + while (stack.length > 0) { + const last = stack.at(-1); + const [i, children] = last; + if (i + 1 === children.length) { + stack.pop(); + continue; + } + const child = children[++last[0]]; + const storageEntry = storage.get(child[$uid]); + if (storageEntry) { + child[$setValue](storageEntry); + } else { + const attributes = child[$getAttributes](); + for (const value of attributes.values()) { + const entry = storage.get(value[$uid]); + if (entry) { + value[$setValue](entry); + break; + } + } + } + const nodes = child[$getChildren](); + if (nodes.length > 0) { + stack.push([-1, nodes]); + } + } + const buf = [``]; + if (this.dataset) { + for (const child of this.dataset[$getChildren]()) { + if (child[$nodeName] !== "data") { + child[$toString](buf); + } + } + } + this.data[$toString](buf); + buf.push(""); + return buf.join(""); + } +} + +;// ./src/core/xfa/config.js + + + + + +const CONFIG_NS_ID = NamespaceIds.config.id; +class Acrobat extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "acrobat", true); + this.acrobat7 = null; + this.autoSave = null; + this.common = null; + this.validate = null; + this.validateApprovalSignatures = null; + this.submitUrl = new XFAObjectArray(); + } +} +class Acrobat7 extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "acrobat7", true); + this.dynamicRender = null; + } +} +class ADBE_JSConsole extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "ADBE_JSConsole", ["delegate", "Enable", "Disable"]); + } +} +class ADBE_JSDebugger extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "ADBE_JSDebugger", ["delegate", "Enable", "Disable"]); + } +} +class AddSilentPrint extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "addSilentPrint"); + } +} +class AddViewerPreferences extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "addViewerPreferences"); + } +} +class AdjustData extends Option10 { + constructor(attributes) { + super(CONFIG_NS_ID, "adjustData"); + } +} +class AdobeExtensionLevel extends IntegerObject { + constructor(attributes) { + super(CONFIG_NS_ID, "adobeExtensionLevel", 0, n => n >= 1 && n <= 8); + } +} +class Agent extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "agent", true); + this.name = attributes.name ? attributes.name.trim() : ""; + this.common = new XFAObjectArray(); + } +} +class AlwaysEmbed extends ContentObject { + constructor(attributes) { + super(CONFIG_NS_ID, "alwaysEmbed"); + } +} +class Amd extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "amd"); + } +} +class config_Area extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "area"); + this.level = getInteger({ + data: attributes.level, + defaultValue: 0, + validate: n => n >= 1 && n <= 3 + }); + this.name = getStringOption(attributes.name, ["", "barcode", "coreinit", "deviceDriver", "font", "general", "layout", "merge", "script", "signature", "sourceSet", "templateCache"]); + } +} +class Attributes extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "attributes", ["preserve", "delegate", "ignore"]); + } +} +class AutoSave extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "autoSave", ["disabled", "enabled"]); + } +} +class Base extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "base"); + } +} +class BatchOutput extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "batchOutput"); + this.format = getStringOption(attributes.format, ["none", "concat", "zip", "zipCompress"]); + } +} +class BehaviorOverride extends ContentObject { + constructor(attributes) { + super(CONFIG_NS_ID, "behaviorOverride"); + } + [$finalize]() { + this[$content] = new Map(this[$content].trim().split(/\s+/).filter(x => x.includes(":")).map(x => x.split(":", 2))); + } +} +class Cache extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "cache", true); + this.templateCache = null; + } +} +class Change extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "change"); + } +} +class Common extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "common", true); + this.data = null; + this.locale = null; + this.localeSet = null; + this.messaging = null; + this.suppressBanner = null; + this.template = null; + this.validationMessaging = null; + this.versionControl = null; + this.log = new XFAObjectArray(); + } +} +class Compress extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "compress"); + this.scope = getStringOption(attributes.scope, ["imageOnly", "document"]); + } +} +class CompressLogicalStructure extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "compressLogicalStructure"); + } +} +class CompressObjectStream extends Option10 { + constructor(attributes) { + super(CONFIG_NS_ID, "compressObjectStream"); + } +} +class Compression extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "compression", true); + this.compressLogicalStructure = null; + this.compressObjectStream = null; + this.level = null; + this.type = null; + } +} +class Config extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "config", true); + this.acrobat = null; + this.present = null; + this.trace = null; + this.agent = new XFAObjectArray(); + } +} +class Conformance extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "conformance", ["A", "B"]); + } +} +class ContentCopy extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "contentCopy"); + } +} +class Copies extends IntegerObject { + constructor(attributes) { + super(CONFIG_NS_ID, "copies", 1, n => n >= 1); + } +} +class Creator extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "creator"); + } +} +class CurrentPage extends IntegerObject { + constructor(attributes) { + super(CONFIG_NS_ID, "currentPage", 0, n => n >= 0); + } +} +class Data extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "data", true); + this.adjustData = null; + this.attributes = null; + this.incrementalLoad = null; + this.outputXSL = null; + this.range = null; + this.record = null; + this.startNode = null; + this.uri = null; + this.window = null; + this.xsl = null; + this.excludeNS = new XFAObjectArray(); + this.transform = new XFAObjectArray(); + } +} +class Debug extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "debug", true); + this.uri = null; + } +} +class DefaultTypeface extends ContentObject { + constructor(attributes) { + super(CONFIG_NS_ID, "defaultTypeface"); + this.writingScript = getStringOption(attributes.writingScript, ["*", "Arabic", "Cyrillic", "EastEuropeanRoman", "Greek", "Hebrew", "Japanese", "Korean", "Roman", "SimplifiedChinese", "Thai", "TraditionalChinese", "Vietnamese"]); + } +} +class Destination extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "destination", ["pdf", "pcl", "ps", "webClient", "zpl"]); + } +} +class DocumentAssembly extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "documentAssembly"); + } +} +class Driver extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "driver", true); + this.name = attributes.name ? attributes.name.trim() : ""; + this.fontInfo = null; + this.xdc = null; + } +} +class DuplexOption extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "duplexOption", ["simplex", "duplexFlipLongEdge", "duplexFlipShortEdge"]); + } +} +class DynamicRender extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "dynamicRender", ["forbidden", "required"]); + } +} +class Embed extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "embed"); + } +} +class config_Encrypt extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "encrypt"); + } +} +class config_Encryption extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "encryption", true); + this.encrypt = null; + this.encryptionLevel = null; + this.permissions = null; + } +} +class EncryptionLevel extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "encryptionLevel", ["40bit", "128bit"]); + } +} +class Enforce extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "enforce"); + } +} +class Equate extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "equate"); + this.force = getInteger({ + data: attributes.force, + defaultValue: 1, + validate: n => n === 0 + }); + this.from = attributes.from || ""; + this.to = attributes.to || ""; + } +} +class EquateRange extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "equateRange"); + this.from = attributes.from || ""; + this.to = attributes.to || ""; + this._unicodeRange = attributes.unicodeRange || ""; + } + get unicodeRange() { + const ranges = []; + const unicodeRegex = /U\+([0-9a-fA-F]+)/; + const unicodeRange = this._unicodeRange; + for (let range of unicodeRange.split(",").map(x => x.trim()).filter(x => !!x)) { + range = range.split("-", 2).map(x => { + const found = x.match(unicodeRegex); + if (!found) { + return 0; + } + return parseInt(found[1], 16); + }); + if (range.length === 1) { + range.push(range[0]); + } + ranges.push(range); + } + return shadow(this, "unicodeRange", ranges); + } +} +class Exclude extends ContentObject { + constructor(attributes) { + super(CONFIG_NS_ID, "exclude"); + } + [$finalize]() { + this[$content] = this[$content].trim().split(/\s+/).filter(x => x && ["calculate", "close", "enter", "exit", "initialize", "ready", "validate"].includes(x)); + } +} +class ExcludeNS extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "excludeNS"); + } +} +class FlipLabel extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "flipLabel", ["usePrinterSetting", "on", "off"]); + } +} +class config_FontInfo extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "fontInfo", true); + this.embed = null; + this.map = null; + this.subsetBelow = null; + this.alwaysEmbed = new XFAObjectArray(); + this.defaultTypeface = new XFAObjectArray(); + this.neverEmbed = new XFAObjectArray(); + } +} +class FormFieldFilling extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "formFieldFilling"); + } +} +class GroupParent extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "groupParent"); + } +} +class IfEmpty extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "ifEmpty", ["dataValue", "dataGroup", "ignore", "remove"]); + } +} +class IncludeXDPContent extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "includeXDPContent"); + } +} +class IncrementalLoad extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "incrementalLoad", ["none", "forwardOnly"]); + } +} +class IncrementalMerge extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "incrementalMerge"); + } +} +class Interactive extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "interactive"); + } +} +class Jog extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "jog", ["usePrinterSetting", "none", "pageSet"]); + } +} +class LabelPrinter extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "labelPrinter", true); + this.name = getStringOption(attributes.name, ["zpl", "dpl", "ipl", "tcpl"]); + this.batchOutput = null; + this.flipLabel = null; + this.fontInfo = null; + this.xdc = null; + } +} +class Layout extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "layout", ["paginate", "panel"]); + } +} +class Level extends IntegerObject { + constructor(attributes) { + super(CONFIG_NS_ID, "level", 0, n => n > 0); + } +} +class Linearized extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "linearized"); + } +} +class Locale extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "locale"); + } +} +class LocaleSet extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "localeSet"); + } +} +class Log extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "log", true); + this.mode = null; + this.threshold = null; + this.to = null; + this.uri = null; + } +} +class MapElement extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "map", true); + this.equate = new XFAObjectArray(); + this.equateRange = new XFAObjectArray(); + } +} +class MediumInfo extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "mediumInfo", true); + this.map = null; + } +} +class config_Message extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "message", true); + this.msgId = null; + this.severity = null; + } +} +class Messaging extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "messaging", true); + this.message = new XFAObjectArray(); + } +} +class Mode extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "mode", ["append", "overwrite"]); + } +} +class ModifyAnnots extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "modifyAnnots"); + } +} +class MsgId extends IntegerObject { + constructor(attributes) { + super(CONFIG_NS_ID, "msgId", 1, n => n >= 1); + } +} +class NameAttr extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "nameAttr"); + } +} +class NeverEmbed extends ContentObject { + constructor(attributes) { + super(CONFIG_NS_ID, "neverEmbed"); + } +} +class NumberOfCopies extends IntegerObject { + constructor(attributes) { + super(CONFIG_NS_ID, "numberOfCopies", null, n => n >= 2 && n <= 5); + } +} +class OpenAction extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "openAction", true); + this.destination = null; + } +} +class Output extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "output", true); + this.to = null; + this.type = null; + this.uri = null; + } +} +class OutputBin extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "outputBin"); + } +} +class OutputXSL extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "outputXSL", true); + this.uri = null; + } +} +class Overprint extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "overprint", ["none", "both", "draw", "field"]); + } +} +class Packets extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "packets"); + } + [$finalize]() { + if (this[$content] === "*") { + return; + } + this[$content] = this[$content].trim().split(/\s+/).filter(x => ["config", "datasets", "template", "xfdf", "xslt"].includes(x)); + } +} +class PageOffset extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "pageOffset"); + this.x = getInteger({ + data: attributes.x, + defaultValue: "useXDCSetting", + validate: n => true + }); + this.y = getInteger({ + data: attributes.y, + defaultValue: "useXDCSetting", + validate: n => true + }); + } +} +class PageRange extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "pageRange"); + } + [$finalize]() { + const numbers = this[$content].trim().split(/\s+/).map(x => parseInt(x, 10)); + const ranges = []; + for (let i = 0, ii = numbers.length; i < ii; i += 2) { + ranges.push(numbers.slice(i, i + 2)); + } + this[$content] = ranges; + } +} +class Pagination extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "pagination", ["simplex", "duplexShortEdge", "duplexLongEdge"]); + } +} +class PaginationOverride extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "paginationOverride", ["none", "forceDuplex", "forceDuplexLongEdge", "forceDuplexShortEdge", "forceSimplex"]); + } +} +class Part extends IntegerObject { + constructor(attributes) { + super(CONFIG_NS_ID, "part", 1, n => false); + } +} +class Pcl extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "pcl", true); + this.name = attributes.name || ""; + this.batchOutput = null; + this.fontInfo = null; + this.jog = null; + this.mediumInfo = null; + this.outputBin = null; + this.pageOffset = null; + this.staple = null; + this.xdc = null; + } +} +class Pdf extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "pdf", true); + this.name = attributes.name || ""; + this.adobeExtensionLevel = null; + this.batchOutput = null; + this.compression = null; + this.creator = null; + this.encryption = null; + this.fontInfo = null; + this.interactive = null; + this.linearized = null; + this.openAction = null; + this.pdfa = null; + this.producer = null; + this.renderPolicy = null; + this.scriptModel = null; + this.silentPrint = null; + this.submitFormat = null; + this.tagged = null; + this.version = null; + this.viewerPreferences = null; + this.xdc = null; + } +} +class Pdfa extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "pdfa", true); + this.amd = null; + this.conformance = null; + this.includeXDPContent = null; + this.part = null; + } +} +class Permissions extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "permissions", true); + this.accessibleContent = null; + this.change = null; + this.contentCopy = null; + this.documentAssembly = null; + this.formFieldFilling = null; + this.modifyAnnots = null; + this.plaintextMetadata = null; + this.print = null; + this.printHighQuality = null; + } +} +class PickTrayByPDFSize extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "pickTrayByPDFSize"); + } +} +class config_Picture extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "picture"); + } +} +class PlaintextMetadata extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "plaintextMetadata"); + } +} +class Presence extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "presence", ["preserve", "dissolve", "dissolveStructure", "ignore", "remove"]); + } +} +class Present extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "present", true); + this.behaviorOverride = null; + this.cache = null; + this.common = null; + this.copies = null; + this.destination = null; + this.incrementalMerge = null; + this.layout = null; + this.output = null; + this.overprint = null; + this.pagination = null; + this.paginationOverride = null; + this.script = null; + this.validate = null; + this.xdp = null; + this.driver = new XFAObjectArray(); + this.labelPrinter = new XFAObjectArray(); + this.pcl = new XFAObjectArray(); + this.pdf = new XFAObjectArray(); + this.ps = new XFAObjectArray(); + this.submitUrl = new XFAObjectArray(); + this.webClient = new XFAObjectArray(); + this.zpl = new XFAObjectArray(); + } +} +class Print extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "print"); + } +} +class PrintHighQuality extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "printHighQuality"); + } +} +class PrintScaling extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "printScaling", ["appdefault", "noScaling"]); + } +} +class PrinterName extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "printerName"); + } +} +class Producer extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "producer"); + } +} +class Ps extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "ps", true); + this.name = attributes.name || ""; + this.batchOutput = null; + this.fontInfo = null; + this.jog = null; + this.mediumInfo = null; + this.outputBin = null; + this.staple = null; + this.xdc = null; + } +} +class Range extends ContentObject { + constructor(attributes) { + super(CONFIG_NS_ID, "range"); + } + [$finalize]() { + this[$content] = this[$content].trim().split(/\s*,\s*/, 2).map(range => range.split("-").map(x => parseInt(x.trim(), 10))).filter(range => range.every(x => !isNaN(x))).map(range => { + if (range.length === 1) { + range.push(range[0]); + } + return range; + }); + } +} +class Record extends ContentObject { + constructor(attributes) { + super(CONFIG_NS_ID, "record"); + } + [$finalize]() { + this[$content] = this[$content].trim(); + const n = parseInt(this[$content], 10); + if (!isNaN(n) && n >= 0) { + this[$content] = n; + } + } +} +class Relevant extends ContentObject { + constructor(attributes) { + super(CONFIG_NS_ID, "relevant"); + } + [$finalize]() { + this[$content] = this[$content].trim().split(/\s+/); + } +} +class Rename extends ContentObject { + constructor(attributes) { + super(CONFIG_NS_ID, "rename"); + } + [$finalize]() { + this[$content] = this[$content].trim(); + if (this[$content].toLowerCase().startsWith("xml") || new RegExp("[\\p{L}_][\\p{L}\\d._\\p{M}-]*", "u").test(this[$content])) { + warn("XFA - Rename: invalid XFA name"); + } + } +} +class RenderPolicy extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "renderPolicy", ["server", "client"]); + } +} +class RunScripts extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "runScripts", ["both", "client", "none", "server"]); + } +} +class config_Script extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "script", true); + this.currentPage = null; + this.exclude = null; + this.runScripts = null; + } +} +class ScriptModel extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "scriptModel", ["XFA", "none"]); + } +} +class Severity extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "severity", ["ignore", "error", "information", "trace", "warning"]); + } +} +class SilentPrint extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "silentPrint", true); + this.addSilentPrint = null; + this.printerName = null; + } +} +class Staple extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "staple"); + this.mode = getStringOption(attributes.mode, ["usePrinterSetting", "on", "off"]); + } +} +class StartNode extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "startNode"); + } +} +class StartPage extends IntegerObject { + constructor(attributes) { + super(CONFIG_NS_ID, "startPage", 0, n => true); + } +} +class SubmitFormat extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "submitFormat", ["html", "delegate", "fdf", "xml", "pdf"]); + } +} +class SubmitUrl extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "submitUrl"); + } +} +class SubsetBelow extends IntegerObject { + constructor(attributes) { + super(CONFIG_NS_ID, "subsetBelow", 100, n => n >= 0 && n <= 100); + } +} +class SuppressBanner extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "suppressBanner"); + } +} +class Tagged extends Option01 { + constructor(attributes) { + super(CONFIG_NS_ID, "tagged"); + } +} +class config_Template extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "template", true); + this.base = null; + this.relevant = null; + this.startPage = null; + this.uri = null; + this.xsl = null; + } +} +class Threshold extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "threshold", ["trace", "error", "information", "warning"]); + } +} +class To extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "to", ["null", "memory", "stderr", "stdout", "system", "uri"]); + } +} +class TemplateCache extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "templateCache"); + this.maxEntries = getInteger({ + data: attributes.maxEntries, + defaultValue: 5, + validate: n => n >= 0 + }); + } +} +class Trace extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "trace", true); + this.area = new XFAObjectArray(); + } +} +class Transform extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "transform", true); + this.groupParent = null; + this.ifEmpty = null; + this.nameAttr = null; + this.picture = null; + this.presence = null; + this.rename = null; + this.whitespace = null; + } +} +class Type extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "type", ["none", "ascii85", "asciiHex", "ccittfax", "flate", "lzw", "runLength", "native", "xdp", "mergedXDP"]); + } +} +class Uri extends StringObject { + constructor(attributes) { + super(CONFIG_NS_ID, "uri"); + } +} +class config_Validate extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "validate", ["preSubmit", "prePrint", "preExecute", "preSave"]); + } +} +class ValidateApprovalSignatures extends ContentObject { + constructor(attributes) { + super(CONFIG_NS_ID, "validateApprovalSignatures"); + } + [$finalize]() { + this[$content] = this[$content].trim().split(/\s+/).filter(x => ["docReady", "postSign"].includes(x)); + } +} +class ValidationMessaging extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "validationMessaging", ["allMessagesIndividually", "allMessagesTogether", "firstMessageOnly", "noMessages"]); + } +} +class Version extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "version", ["1.7", "1.6", "1.5", "1.4", "1.3", "1.2"]); + } +} +class VersionControl extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "VersionControl"); + this.outputBelow = getStringOption(attributes.outputBelow, ["warn", "error", "update"]); + this.sourceAbove = getStringOption(attributes.sourceAbove, ["warn", "error"]); + this.sourceBelow = getStringOption(attributes.sourceBelow, ["update", "maintain"]); + } +} +class ViewerPreferences extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "viewerPreferences", true); + this.ADBE_JSConsole = null; + this.ADBE_JSDebugger = null; + this.addViewerPreferences = null; + this.duplexOption = null; + this.enforce = null; + this.numberOfCopies = null; + this.pageRange = null; + this.pickTrayByPDFSize = null; + this.printScaling = null; + } +} +class WebClient extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "webClient", true); + this.name = attributes.name ? attributes.name.trim() : ""; + this.fontInfo = null; + this.xdc = null; + } +} +class Whitespace extends OptionObject { + constructor(attributes) { + super(CONFIG_NS_ID, "whitespace", ["preserve", "ltrim", "normalize", "rtrim", "trim"]); + } +} +class Window extends ContentObject { + constructor(attributes) { + super(CONFIG_NS_ID, "window"); + } + [$finalize]() { + const pair = this[$content].trim().split(/\s*,\s*/, 2).map(x => parseInt(x, 10)); + if (pair.some(x => isNaN(x))) { + this[$content] = [0, 0]; + return; + } + if (pair.length === 1) { + pair.push(pair[0]); + } + this[$content] = pair; + } +} +class Xdc extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "xdc", true); + this.uri = new XFAObjectArray(); + this.xsl = new XFAObjectArray(); + } +} +class Xdp extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "xdp", true); + this.packets = null; + } +} +class Xsl extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "xsl", true); + this.debug = null; + this.uri = null; + } +} +class Zpl extends XFAObject { + constructor(attributes) { + super(CONFIG_NS_ID, "zpl", true); + this.name = attributes.name ? attributes.name.trim() : ""; + this.batchOutput = null; + this.flipLabel = null; + this.fontInfo = null; + this.xdc = null; + } +} +class ConfigNamespace { + static [$buildXFAObject](name, attributes) { + if (ConfigNamespace.hasOwnProperty(name)) { + return ConfigNamespace[name](attributes); + } + return undefined; + } + static acrobat(attrs) { + return new Acrobat(attrs); + } + static acrobat7(attrs) { + return new Acrobat7(attrs); + } + static ADBE_JSConsole(attrs) { + return new ADBE_JSConsole(attrs); + } + static ADBE_JSDebugger(attrs) { + return new ADBE_JSDebugger(attrs); + } + static addSilentPrint(attrs) { + return new AddSilentPrint(attrs); + } + static addViewerPreferences(attrs) { + return new AddViewerPreferences(attrs); + } + static adjustData(attrs) { + return new AdjustData(attrs); + } + static adobeExtensionLevel(attrs) { + return new AdobeExtensionLevel(attrs); + } + static agent(attrs) { + return new Agent(attrs); + } + static alwaysEmbed(attrs) { + return new AlwaysEmbed(attrs); + } + static amd(attrs) { + return new Amd(attrs); + } + static area(attrs) { + return new config_Area(attrs); + } + static attributes(attrs) { + return new Attributes(attrs); + } + static autoSave(attrs) { + return new AutoSave(attrs); + } + static base(attrs) { + return new Base(attrs); + } + static batchOutput(attrs) { + return new BatchOutput(attrs); + } + static behaviorOverride(attrs) { + return new BehaviorOverride(attrs); + } + static cache(attrs) { + return new Cache(attrs); + } + static change(attrs) { + return new Change(attrs); + } + static common(attrs) { + return new Common(attrs); + } + static compress(attrs) { + return new Compress(attrs); + } + static compressLogicalStructure(attrs) { + return new CompressLogicalStructure(attrs); + } + static compressObjectStream(attrs) { + return new CompressObjectStream(attrs); + } + static compression(attrs) { + return new Compression(attrs); + } + static config(attrs) { + return new Config(attrs); + } + static conformance(attrs) { + return new Conformance(attrs); + } + static contentCopy(attrs) { + return new ContentCopy(attrs); + } + static copies(attrs) { + return new Copies(attrs); + } + static creator(attrs) { + return new Creator(attrs); + } + static currentPage(attrs) { + return new CurrentPage(attrs); + } + static data(attrs) { + return new Data(attrs); + } + static debug(attrs) { + return new Debug(attrs); + } + static defaultTypeface(attrs) { + return new DefaultTypeface(attrs); + } + static destination(attrs) { + return new Destination(attrs); + } + static documentAssembly(attrs) { + return new DocumentAssembly(attrs); + } + static driver(attrs) { + return new Driver(attrs); + } + static duplexOption(attrs) { + return new DuplexOption(attrs); + } + static dynamicRender(attrs) { + return new DynamicRender(attrs); + } + static embed(attrs) { + return new Embed(attrs); + } + static encrypt(attrs) { + return new config_Encrypt(attrs); + } + static encryption(attrs) { + return new config_Encryption(attrs); + } + static encryptionLevel(attrs) { + return new EncryptionLevel(attrs); + } + static enforce(attrs) { + return new Enforce(attrs); + } + static equate(attrs) { + return new Equate(attrs); + } + static equateRange(attrs) { + return new EquateRange(attrs); + } + static exclude(attrs) { + return new Exclude(attrs); + } + static excludeNS(attrs) { + return new ExcludeNS(attrs); + } + static flipLabel(attrs) { + return new FlipLabel(attrs); + } + static fontInfo(attrs) { + return new config_FontInfo(attrs); + } + static formFieldFilling(attrs) { + return new FormFieldFilling(attrs); + } + static groupParent(attrs) { + return new GroupParent(attrs); + } + static ifEmpty(attrs) { + return new IfEmpty(attrs); + } + static includeXDPContent(attrs) { + return new IncludeXDPContent(attrs); + } + static incrementalLoad(attrs) { + return new IncrementalLoad(attrs); + } + static incrementalMerge(attrs) { + return new IncrementalMerge(attrs); + } + static interactive(attrs) { + return new Interactive(attrs); + } + static jog(attrs) { + return new Jog(attrs); + } + static labelPrinter(attrs) { + return new LabelPrinter(attrs); + } + static layout(attrs) { + return new Layout(attrs); + } + static level(attrs) { + return new Level(attrs); + } + static linearized(attrs) { + return new Linearized(attrs); + } + static locale(attrs) { + return new Locale(attrs); + } + static localeSet(attrs) { + return new LocaleSet(attrs); + } + static log(attrs) { + return new Log(attrs); + } + static map(attrs) { + return new MapElement(attrs); + } + static mediumInfo(attrs) { + return new MediumInfo(attrs); + } + static message(attrs) { + return new config_Message(attrs); + } + static messaging(attrs) { + return new Messaging(attrs); + } + static mode(attrs) { + return new Mode(attrs); + } + static modifyAnnots(attrs) { + return new ModifyAnnots(attrs); + } + static msgId(attrs) { + return new MsgId(attrs); + } + static nameAttr(attrs) { + return new NameAttr(attrs); + } + static neverEmbed(attrs) { + return new NeverEmbed(attrs); + } + static numberOfCopies(attrs) { + return new NumberOfCopies(attrs); + } + static openAction(attrs) { + return new OpenAction(attrs); + } + static output(attrs) { + return new Output(attrs); + } + static outputBin(attrs) { + return new OutputBin(attrs); + } + static outputXSL(attrs) { + return new OutputXSL(attrs); + } + static overprint(attrs) { + return new Overprint(attrs); + } + static packets(attrs) { + return new Packets(attrs); + } + static pageOffset(attrs) { + return new PageOffset(attrs); + } + static pageRange(attrs) { + return new PageRange(attrs); + } + static pagination(attrs) { + return new Pagination(attrs); + } + static paginationOverride(attrs) { + return new PaginationOverride(attrs); + } + static part(attrs) { + return new Part(attrs); + } + static pcl(attrs) { + return new Pcl(attrs); + } + static pdf(attrs) { + return new Pdf(attrs); + } + static pdfa(attrs) { + return new Pdfa(attrs); + } + static permissions(attrs) { + return new Permissions(attrs); + } + static pickTrayByPDFSize(attrs) { + return new PickTrayByPDFSize(attrs); + } + static picture(attrs) { + return new config_Picture(attrs); + } + static plaintextMetadata(attrs) { + return new PlaintextMetadata(attrs); + } + static presence(attrs) { + return new Presence(attrs); + } + static present(attrs) { + return new Present(attrs); + } + static print(attrs) { + return new Print(attrs); + } + static printHighQuality(attrs) { + return new PrintHighQuality(attrs); + } + static printScaling(attrs) { + return new PrintScaling(attrs); + } + static printerName(attrs) { + return new PrinterName(attrs); + } + static producer(attrs) { + return new Producer(attrs); + } + static ps(attrs) { + return new Ps(attrs); + } + static range(attrs) { + return new Range(attrs); + } + static record(attrs) { + return new Record(attrs); + } + static relevant(attrs) { + return new Relevant(attrs); + } + static rename(attrs) { + return new Rename(attrs); + } + static renderPolicy(attrs) { + return new RenderPolicy(attrs); + } + static runScripts(attrs) { + return new RunScripts(attrs); + } + static script(attrs) { + return new config_Script(attrs); + } + static scriptModel(attrs) { + return new ScriptModel(attrs); + } + static severity(attrs) { + return new Severity(attrs); + } + static silentPrint(attrs) { + return new SilentPrint(attrs); + } + static staple(attrs) { + return new Staple(attrs); + } + static startNode(attrs) { + return new StartNode(attrs); + } + static startPage(attrs) { + return new StartPage(attrs); + } + static submitFormat(attrs) { + return new SubmitFormat(attrs); + } + static submitUrl(attrs) { + return new SubmitUrl(attrs); + } + static subsetBelow(attrs) { + return new SubsetBelow(attrs); + } + static suppressBanner(attrs) { + return new SuppressBanner(attrs); + } + static tagged(attrs) { + return new Tagged(attrs); + } + static template(attrs) { + return new config_Template(attrs); + } + static templateCache(attrs) { + return new TemplateCache(attrs); + } + static threshold(attrs) { + return new Threshold(attrs); + } + static to(attrs) { + return new To(attrs); + } + static trace(attrs) { + return new Trace(attrs); + } + static transform(attrs) { + return new Transform(attrs); + } + static type(attrs) { + return new Type(attrs); + } + static uri(attrs) { + return new Uri(attrs); + } + static validate(attrs) { + return new config_Validate(attrs); + } + static validateApprovalSignatures(attrs) { + return new ValidateApprovalSignatures(attrs); + } + static validationMessaging(attrs) { + return new ValidationMessaging(attrs); + } + static version(attrs) { + return new Version(attrs); + } + static versionControl(attrs) { + return new VersionControl(attrs); + } + static viewerPreferences(attrs) { + return new ViewerPreferences(attrs); + } + static webClient(attrs) { + return new WebClient(attrs); + } + static whitespace(attrs) { + return new Whitespace(attrs); + } + static window(attrs) { + return new Window(attrs); + } + static xdc(attrs) { + return new Xdc(attrs); + } + static xdp(attrs) { + return new Xdp(attrs); + } + static xsl(attrs) { + return new Xsl(attrs); + } + static zpl(attrs) { + return new Zpl(attrs); + } +} + +;// ./src/core/xfa/connection_set.js + + +const CONNECTION_SET_NS_ID = NamespaceIds.connectionSet.id; +class ConnectionSet extends XFAObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "connectionSet", true); + this.wsdlConnection = new XFAObjectArray(); + this.xmlConnection = new XFAObjectArray(); + this.xsdConnection = new XFAObjectArray(); + } +} +class EffectiveInputPolicy extends XFAObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "effectiveInputPolicy"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class EffectiveOutputPolicy extends XFAObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "effectiveOutputPolicy"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class Operation extends StringObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "operation"); + this.id = attributes.id || ""; + this.input = attributes.input || ""; + this.name = attributes.name || ""; + this.output = attributes.output || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class RootElement extends StringObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "rootElement"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class SoapAction extends StringObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "soapAction"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class SoapAddress extends StringObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "soapAddress"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class connection_set_Uri extends StringObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "uri"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class WsdlAddress extends StringObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "wsdlAddress"); + this.id = attributes.id || ""; + this.name = attributes.name || ""; + this.use = attributes.use || ""; + this.usehref = attributes.usehref || ""; + } +} +class WsdlConnection extends XFAObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "wsdlConnection", true); + this.dataDescription = attributes.dataDescription || ""; + this.name = attributes.name || ""; + this.effectiveInputPolicy = null; + this.effectiveOutputPolicy = null; + this.operation = null; + this.soapAction = null; + this.soapAddress = null; + this.wsdlAddress = null; + } +} +class XmlConnection extends XFAObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "xmlConnection", true); + this.dataDescription = attributes.dataDescription || ""; + this.name = attributes.name || ""; + this.uri = null; + } +} +class XsdConnection extends XFAObject { + constructor(attributes) { + super(CONNECTION_SET_NS_ID, "xsdConnection", true); + this.dataDescription = attributes.dataDescription || ""; + this.name = attributes.name || ""; + this.rootElement = null; + this.uri = null; + } +} +class ConnectionSetNamespace { + static [$buildXFAObject](name, attributes) { + if (ConnectionSetNamespace.hasOwnProperty(name)) { + return ConnectionSetNamespace[name](attributes); + } + return undefined; + } + static connectionSet(attrs) { + return new ConnectionSet(attrs); + } + static effectiveInputPolicy(attrs) { + return new EffectiveInputPolicy(attrs); + } + static effectiveOutputPolicy(attrs) { + return new EffectiveOutputPolicy(attrs); + } + static operation(attrs) { + return new Operation(attrs); + } + static rootElement(attrs) { + return new RootElement(attrs); + } + static soapAction(attrs) { + return new SoapAction(attrs); + } + static soapAddress(attrs) { + return new SoapAddress(attrs); + } + static uri(attrs) { + return new connection_set_Uri(attrs); + } + static wsdlAddress(attrs) { + return new WsdlAddress(attrs); + } + static wsdlConnection(attrs) { + return new WsdlConnection(attrs); + } + static xmlConnection(attrs) { + return new XmlConnection(attrs); + } + static xsdConnection(attrs) { + return new XsdConnection(attrs); + } +} + +;// ./src/core/xfa/datasets.js + + + +const DATASETS_NS_ID = NamespaceIds.datasets.id; +class datasets_Data extends XmlObject { + constructor(attributes) { + super(DATASETS_NS_ID, "data", attributes); + } + [$isNsAgnostic]() { + return true; + } +} +class Datasets extends XFAObject { + constructor(attributes) { + super(DATASETS_NS_ID, "datasets", true); + this.data = null; + this.Signature = null; + } + [$onChild](child) { + const name = child[$nodeName]; + if (name === "data" && child[$namespaceId] === DATASETS_NS_ID || name === "Signature" && child[$namespaceId] === NamespaceIds.signature.id) { + this[name] = child; + } + this[$appendChild](child); + } +} +class DatasetsNamespace { + static [$buildXFAObject](name, attributes) { + if (DatasetsNamespace.hasOwnProperty(name)) { + return DatasetsNamespace[name](attributes); + } + return undefined; + } + static datasets(attributes) { + return new Datasets(attributes); + } + static data(attributes) { + return new datasets_Data(attributes); + } +} + +;// ./src/core/xfa/locale_set.js + + + +const LOCALE_SET_NS_ID = NamespaceIds.localeSet.id; +class CalendarSymbols extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "calendarSymbols", true); + this.name = "gregorian"; + this.dayNames = new XFAObjectArray(2); + this.eraNames = null; + this.meridiemNames = null; + this.monthNames = new XFAObjectArray(2); + } +} +class CurrencySymbol extends StringObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "currencySymbol"); + this.name = getStringOption(attributes.name, ["symbol", "isoname", "decimal"]); + } +} +class CurrencySymbols extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "currencySymbols", true); + this.currencySymbol = new XFAObjectArray(3); + } +} +class DatePattern extends StringObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "datePattern"); + this.name = getStringOption(attributes.name, ["full", "long", "med", "short"]); + } +} +class DatePatterns extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "datePatterns", true); + this.datePattern = new XFAObjectArray(4); + } +} +class DateTimeSymbols extends ContentObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "dateTimeSymbols"); + } +} +class Day extends StringObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "day"); + } +} +class DayNames extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "dayNames", true); + this.abbr = getInteger({ + data: attributes.abbr, + defaultValue: 0, + validate: x => x === 1 + }); + this.day = new XFAObjectArray(7); + } +} +class Era extends StringObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "era"); + } +} +class EraNames extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "eraNames", true); + this.era = new XFAObjectArray(2); + } +} +class locale_set_Locale extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "locale", true); + this.desc = attributes.desc || ""; + this.name = "isoname"; + this.calendarSymbols = null; + this.currencySymbols = null; + this.datePatterns = null; + this.dateTimeSymbols = null; + this.numberPatterns = null; + this.numberSymbols = null; + this.timePatterns = null; + this.typeFaces = null; + } +} +class locale_set_LocaleSet extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "localeSet", true); + this.locale = new XFAObjectArray(); + } +} +class Meridiem extends StringObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "meridiem"); + } +} +class MeridiemNames extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "meridiemNames", true); + this.meridiem = new XFAObjectArray(2); + } +} +class Month extends StringObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "month"); + } +} +class MonthNames extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "monthNames", true); + this.abbr = getInteger({ + data: attributes.abbr, + defaultValue: 0, + validate: x => x === 1 + }); + this.month = new XFAObjectArray(12); + } +} +class NumberPattern extends StringObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "numberPattern"); + this.name = getStringOption(attributes.name, ["full", "long", "med", "short"]); + } +} +class NumberPatterns extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "numberPatterns", true); + this.numberPattern = new XFAObjectArray(4); + } +} +class NumberSymbol extends StringObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "numberSymbol"); + this.name = getStringOption(attributes.name, ["decimal", "grouping", "percent", "minus", "zero"]); + } +} +class NumberSymbols extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "numberSymbols", true); + this.numberSymbol = new XFAObjectArray(5); + } +} +class TimePattern extends StringObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "timePattern"); + this.name = getStringOption(attributes.name, ["full", "long", "med", "short"]); + } +} +class TimePatterns extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "timePatterns", true); + this.timePattern = new XFAObjectArray(4); + } +} +class TypeFace extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "typeFace", true); + this.name = attributes.name | ""; + } +} +class TypeFaces extends XFAObject { + constructor(attributes) { + super(LOCALE_SET_NS_ID, "typeFaces", true); + this.typeFace = new XFAObjectArray(); + } +} +class LocaleSetNamespace { + static [$buildXFAObject](name, attributes) { + if (LocaleSetNamespace.hasOwnProperty(name)) { + return LocaleSetNamespace[name](attributes); + } + return undefined; + } + static calendarSymbols(attrs) { + return new CalendarSymbols(attrs); + } + static currencySymbol(attrs) { + return new CurrencySymbol(attrs); + } + static currencySymbols(attrs) { + return new CurrencySymbols(attrs); + } + static datePattern(attrs) { + return new DatePattern(attrs); + } + static datePatterns(attrs) { + return new DatePatterns(attrs); + } + static dateTimeSymbols(attrs) { + return new DateTimeSymbols(attrs); + } + static day(attrs) { + return new Day(attrs); + } + static dayNames(attrs) { + return new DayNames(attrs); + } + static era(attrs) { + return new Era(attrs); + } + static eraNames(attrs) { + return new EraNames(attrs); + } + static locale(attrs) { + return new locale_set_Locale(attrs); + } + static localeSet(attrs) { + return new locale_set_LocaleSet(attrs); + } + static meridiem(attrs) { + return new Meridiem(attrs); + } + static meridiemNames(attrs) { + return new MeridiemNames(attrs); + } + static month(attrs) { + return new Month(attrs); + } + static monthNames(attrs) { + return new MonthNames(attrs); + } + static numberPattern(attrs) { + return new NumberPattern(attrs); + } + static numberPatterns(attrs) { + return new NumberPatterns(attrs); + } + static numberSymbol(attrs) { + return new NumberSymbol(attrs); + } + static numberSymbols(attrs) { + return new NumberSymbols(attrs); + } + static timePattern(attrs) { + return new TimePattern(attrs); + } + static timePatterns(attrs) { + return new TimePatterns(attrs); + } + static typeFace(attrs) { + return new TypeFace(attrs); + } + static typeFaces(attrs) { + return new TypeFaces(attrs); + } +} + +;// ./src/core/xfa/signature.js + + +const SIGNATURE_NS_ID = NamespaceIds.signature.id; +class signature_Signature extends XFAObject { + constructor(attributes) { + super(SIGNATURE_NS_ID, "signature", true); + } +} +class SignatureNamespace { + static [$buildXFAObject](name, attributes) { + if (SignatureNamespace.hasOwnProperty(name)) { + return SignatureNamespace[name](attributes); + } + return undefined; + } + static signature(attributes) { + return new signature_Signature(attributes); + } +} + +;// ./src/core/xfa/stylesheet.js + + +const STYLESHEET_NS_ID = NamespaceIds.stylesheet.id; +class Stylesheet extends XFAObject { + constructor(attributes) { + super(STYLESHEET_NS_ID, "stylesheet", true); + } +} +class StylesheetNamespace { + static [$buildXFAObject](name, attributes) { + if (StylesheetNamespace.hasOwnProperty(name)) { + return StylesheetNamespace[name](attributes); + } + return undefined; + } + static stylesheet(attributes) { + return new Stylesheet(attributes); + } +} + +;// ./src/core/xfa/xdp.js + + + +const XDP_NS_ID = NamespaceIds.xdp.id; +class xdp_Xdp extends XFAObject { + constructor(attributes) { + super(XDP_NS_ID, "xdp", true); + this.uuid = attributes.uuid || ""; + this.timeStamp = attributes.timeStamp || ""; + this.config = null; + this.connectionSet = null; + this.datasets = null; + this.localeSet = null; + this.stylesheet = new XFAObjectArray(); + this.template = null; + } + [$onChildCheck](child) { + const ns = NamespaceIds[child[$nodeName]]; + return ns && child[$namespaceId] === ns.id; + } +} +class XdpNamespace { + static [$buildXFAObject](name, attributes) { + if (XdpNamespace.hasOwnProperty(name)) { + return XdpNamespace[name](attributes); + } + return undefined; + } + static xdp(attributes) { + return new xdp_Xdp(attributes); + } +} + +;// ./src/core/xfa/xhtml.js + + + + + +const XHTML_NS_ID = NamespaceIds.xhtml.id; +const $richText = Symbol(); +const VALID_STYLES = new Set(["color", "font", "font-family", "font-size", "font-stretch", "font-style", "font-weight", "margin", "margin-bottom", "margin-left", "margin-right", "margin-top", "letter-spacing", "line-height", "orphans", "page-break-after", "page-break-before", "page-break-inside", "tab-interval", "tab-stop", "text-align", "text-decoration", "text-indent", "vertical-align", "widows", "kerning-mode", "xfa-font-horizontal-scale", "xfa-font-vertical-scale", "xfa-spacerun", "xfa-tab-stops"]); +const StyleMapping = new Map([["page-break-after", "breakAfter"], ["page-break-before", "breakBefore"], ["page-break-inside", "breakInside"], ["kerning-mode", value => value === "none" ? "none" : "normal"], ["xfa-font-horizontal-scale", value => `scaleX(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`], ["xfa-font-vertical-scale", value => `scaleY(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`], ["xfa-spacerun", ""], ["xfa-tab-stops", ""], ["font-size", (value, original) => { + value = original.fontSize = Math.abs(getMeasurement(value)); + return measureToString(0.99 * value); +}], ["letter-spacing", value => measureToString(getMeasurement(value))], ["line-height", value => measureToString(getMeasurement(value))], ["margin", value => measureToString(getMeasurement(value))], ["margin-bottom", value => measureToString(getMeasurement(value))], ["margin-left", value => measureToString(getMeasurement(value))], ["margin-right", value => measureToString(getMeasurement(value))], ["margin-top", value => measureToString(getMeasurement(value))], ["text-indent", value => measureToString(getMeasurement(value))], ["font-family", value => value], ["vertical-align", value => measureToString(getMeasurement(value))]]); +const spacesRegExp = /\s+/g; +const crlfRegExp = /[\r\n]+/g; +const crlfForRichTextRegExp = /\r\n?/g; +function mapStyle(styleStr, node, richText) { + const style = Object.create(null); + if (!styleStr) { + return style; + } + const original = Object.create(null); + for (const [key, value] of styleStr.split(";").map(s => s.split(":", 2))) { + const mapping = StyleMapping.get(key); + if (mapping === "") { + continue; + } + let newValue = value; + if (mapping) { + newValue = typeof mapping === "string" ? mapping : mapping(value, original); + } + if (key.endsWith("scale")) { + style.transform = style.transform ? `${style[key]} ${newValue}` : newValue; + } else { + style[key.replaceAll(/-([a-zA-Z])/g, (_, x) => x.toUpperCase())] = newValue; + } + } + if (style.fontFamily) { + setFontFamily({ + typeface: style.fontFamily, + weight: style.fontWeight || "normal", + posture: style.fontStyle || "normal", + size: original.fontSize || 0 + }, node, node[$globalData].fontFinder, style); + } + if (richText && style.verticalAlign && style.verticalAlign !== "0px" && style.fontSize) { + const SUB_SUPER_SCRIPT_FACTOR = 0.583; + const VERTICAL_FACTOR = 0.333; + const fontSize = getMeasurement(style.fontSize); + style.fontSize = measureToString(fontSize * SUB_SUPER_SCRIPT_FACTOR); + style.verticalAlign = measureToString(Math.sign(getMeasurement(style.verticalAlign)) * fontSize * VERTICAL_FACTOR); + } + if (richText && style.fontSize) { + style.fontSize = `calc(${style.fontSize} * var(--scale-factor))`; + } + fixTextIndent(style); + return style; +} +function checkStyle(node) { + if (!node.style) { + return ""; + } + return node.style.trim().split(/\s*;\s*/).filter(s => !!s).map(s => s.split(/\s*:\s*/, 2)).filter(([key, value]) => { + if (key === "font-family") { + node[$globalData].usedTypefaces.add(value); + } + return VALID_STYLES.has(key); + }).map(kv => kv.join(":")).join(";"); +} +const NoWhites = new Set(["body", "html"]); +class XhtmlObject extends XmlObject { + constructor(attributes, name) { + super(XHTML_NS_ID, name); + this[$richText] = false; + this.style = attributes.style || ""; + } + [$clean](builder) { + super[$clean](builder); + this.style = checkStyle(this); + } + [$acceptWhitespace]() { + return !NoWhites.has(this[$nodeName]); + } + [$onText](str, richText = false) { + if (!richText) { + str = str.replaceAll(crlfRegExp, ""); + if (!this.style.includes("xfa-spacerun:yes")) { + str = str.replaceAll(spacesRegExp, " "); + } + } else { + this[$richText] = true; + } + if (str) { + this[$content] += str; + } + } + [$pushGlyphs](measure, mustPop = true) { + const xfaFont = Object.create(null); + const margin = { + top: NaN, + bottom: NaN, + left: NaN, + right: NaN + }; + let lineHeight = null; + for (const [key, value] of this.style.split(";").map(s => s.split(":", 2))) { + switch (key) { + case "font-family": + xfaFont.typeface = stripQuotes(value); + break; + case "font-size": + xfaFont.size = getMeasurement(value); + break; + case "font-weight": + xfaFont.weight = value; + break; + case "font-style": + xfaFont.posture = value; + break; + case "letter-spacing": + xfaFont.letterSpacing = getMeasurement(value); + break; + case "margin": + const values = value.split(/ \t/).map(x => getMeasurement(x)); + switch (values.length) { + case 1: + margin.top = margin.bottom = margin.left = margin.right = values[0]; + break; + case 2: + margin.top = margin.bottom = values[0]; + margin.left = margin.right = values[1]; + break; + case 3: + margin.top = values[0]; + margin.bottom = values[2]; + margin.left = margin.right = values[1]; + break; + case 4: + margin.top = values[0]; + margin.left = values[1]; + margin.bottom = values[2]; + margin.right = values[3]; + break; + } + break; + case "margin-top": + margin.top = getMeasurement(value); + break; + case "margin-bottom": + margin.bottom = getMeasurement(value); + break; + case "margin-left": + margin.left = getMeasurement(value); + break; + case "margin-right": + margin.right = getMeasurement(value); + break; + case "line-height": + lineHeight = getMeasurement(value); + break; + } + } + measure.pushData(xfaFont, margin, lineHeight); + if (this[$content]) { + measure.addString(this[$content]); + } else { + for (const child of this[$getChildren]()) { + if (child[$nodeName] === "#text") { + measure.addString(child[$content]); + continue; + } + child[$pushGlyphs](measure); + } + } + if (mustPop) { + measure.popFont(); + } + } + [$toHTML](availableSpace) { + const children = []; + this[$extra] = { + children + }; + this[$childrenToHTML]({}); + if (children.length === 0 && !this[$content]) { + return HTMLResult.EMPTY; + } + let value; + if (this[$richText]) { + value = this[$content] ? this[$content].replaceAll(crlfForRichTextRegExp, "\n") : undefined; + } else { + value = this[$content] || undefined; + } + return HTMLResult.success({ + name: this[$nodeName], + attributes: { + href: this.href, + style: mapStyle(this.style, this, this[$richText]) + }, + children, + value + }); + } +} +class A extends XhtmlObject { + constructor(attributes) { + super(attributes, "a"); + this.href = fixURL(attributes.href) || ""; + } +} +class B extends XhtmlObject { + constructor(attributes) { + super(attributes, "b"); + } + [$pushGlyphs](measure) { + measure.pushFont({ + weight: "bold" + }); + super[$pushGlyphs](measure); + measure.popFont(); + } +} +class Body extends XhtmlObject { + constructor(attributes) { + super(attributes, "body"); + } + [$toHTML](availableSpace) { + const res = super[$toHTML](availableSpace); + const { + html + } = res; + if (!html) { + return HTMLResult.EMPTY; + } + html.name = "div"; + html.attributes.class = ["xfaRich"]; + return res; + } +} +class Br extends XhtmlObject { + constructor(attributes) { + super(attributes, "br"); + } + [$text]() { + return "\n"; + } + [$pushGlyphs](measure) { + measure.addString("\n"); + } + [$toHTML](availableSpace) { + return HTMLResult.success({ + name: "br" + }); + } +} +class Html extends XhtmlObject { + constructor(attributes) { + super(attributes, "html"); + } + [$toHTML](availableSpace) { + const children = []; + this[$extra] = { + children + }; + this[$childrenToHTML]({}); + if (children.length === 0) { + return HTMLResult.success({ + name: "div", + attributes: { + class: ["xfaRich"], + style: {} + }, + value: this[$content] || "" + }); + } + if (children.length === 1) { + const child = children[0]; + if (child.attributes?.class.includes("xfaRich")) { + return HTMLResult.success(child); + } + } + return HTMLResult.success({ + name: "div", + attributes: { + class: ["xfaRich"], + style: {} + }, + children + }); + } +} +class I extends XhtmlObject { + constructor(attributes) { + super(attributes, "i"); + } + [$pushGlyphs](measure) { + measure.pushFont({ + posture: "italic" + }); + super[$pushGlyphs](measure); + measure.popFont(); + } +} +class Li extends XhtmlObject { + constructor(attributes) { + super(attributes, "li"); + } +} +class Ol extends XhtmlObject { + constructor(attributes) { + super(attributes, "ol"); + } +} +class P extends XhtmlObject { + constructor(attributes) { + super(attributes, "p"); + } + [$pushGlyphs](measure) { + super[$pushGlyphs](measure, false); + measure.addString("\n"); + measure.addPara(); + measure.popFont(); + } + [$text]() { + const siblings = this[$getParent]()[$getChildren](); + if (siblings.at(-1) === this) { + return super[$text](); + } + return super[$text]() + "\n"; + } +} +class Span extends XhtmlObject { + constructor(attributes) { + super(attributes, "span"); + } +} +class Sub extends XhtmlObject { + constructor(attributes) { + super(attributes, "sub"); + } +} +class Sup extends XhtmlObject { + constructor(attributes) { + super(attributes, "sup"); + } +} +class Ul extends XhtmlObject { + constructor(attributes) { + super(attributes, "ul"); + } +} +class XhtmlNamespace { + static [$buildXFAObject](name, attributes) { + if (XhtmlNamespace.hasOwnProperty(name)) { + return XhtmlNamespace[name](attributes); + } + return undefined; + } + static a(attributes) { + return new A(attributes); + } + static b(attributes) { + return new B(attributes); + } + static body(attributes) { + return new Body(attributes); + } + static br(attributes) { + return new Br(attributes); + } + static html(attributes) { + return new Html(attributes); + } + static i(attributes) { + return new I(attributes); + } + static li(attributes) { + return new Li(attributes); + } + static ol(attributes) { + return new Ol(attributes); + } + static p(attributes) { + return new P(attributes); + } + static span(attributes) { + return new Span(attributes); + } + static sub(attributes) { + return new Sub(attributes); + } + static sup(attributes) { + return new Sup(attributes); + } + static ul(attributes) { + return new Ul(attributes); + } +} + +;// ./src/core/xfa/setup.js + + + + + + + + + +const NamespaceSetUp = { + config: ConfigNamespace, + connection: ConnectionSetNamespace, + datasets: DatasetsNamespace, + localeSet: LocaleSetNamespace, + signature: SignatureNamespace, + stylesheet: StylesheetNamespace, + template: TemplateNamespace, + xdp: XdpNamespace, + xhtml: XhtmlNamespace +}; + +;// ./src/core/xfa/unknown.js + + +class UnknownNamespace { + constructor(nsId) { + this.namespaceId = nsId; + } + [$buildXFAObject](name, attributes) { + return new XmlObject(this.namespaceId, name, attributes); + } +} + +;// ./src/core/xfa/builder.js + + + + + + + +class Root extends XFAObject { + constructor(ids) { + super(-1, "root", Object.create(null)); + this.element = null; + this[$ids] = ids; + } + [$onChild](child) { + this.element = child; + return true; + } + [$finalize]() { + super[$finalize](); + if (this.element.template instanceof Template) { + this[$ids].set($root, this.element); + this.element.template[$resolvePrototypes](this[$ids]); + this.element.template[$ids] = this[$ids]; + } + } +} +class Empty extends XFAObject { + constructor() { + super(-1, "", Object.create(null)); + } + [$onChild](_) { + return false; + } +} +class Builder { + constructor(rootNameSpace = null) { + this._namespaceStack = []; + this._nsAgnosticLevel = 0; + this._namespacePrefixes = new Map(); + this._namespaces = new Map(); + this._nextNsId = Math.max(...Object.values(NamespaceIds).map(({ + id + }) => id)); + this._currentNamespace = rootNameSpace || new UnknownNamespace(++this._nextNsId); + } + buildRoot(ids) { + return new Root(ids); + } + build({ + nsPrefix, + name, + attributes, + namespace, + prefixes + }) { + const hasNamespaceDef = namespace !== null; + if (hasNamespaceDef) { + this._namespaceStack.push(this._currentNamespace); + this._currentNamespace = this._searchNamespace(namespace); + } + if (prefixes) { + this._addNamespacePrefix(prefixes); + } + if (attributes.hasOwnProperty($nsAttributes)) { + const dataTemplate = NamespaceSetUp.datasets; + const nsAttrs = attributes[$nsAttributes]; + let xfaAttrs = null; + for (const [ns, attrs] of Object.entries(nsAttrs)) { + const nsToUse = this._getNamespaceToUse(ns); + if (nsToUse === dataTemplate) { + xfaAttrs = { + xfa: attrs + }; + break; + } + } + if (xfaAttrs) { + attributes[$nsAttributes] = xfaAttrs; + } else { + delete attributes[$nsAttributes]; + } + } + const namespaceToUse = this._getNamespaceToUse(nsPrefix); + const node = namespaceToUse?.[$buildXFAObject](name, attributes) || new Empty(); + if (node[$isNsAgnostic]()) { + this._nsAgnosticLevel++; + } + if (hasNamespaceDef || prefixes || node[$isNsAgnostic]()) { + node[$cleanup] = { + hasNamespace: hasNamespaceDef, + prefixes, + nsAgnostic: node[$isNsAgnostic]() + }; + } + return node; + } + isNsAgnostic() { + return this._nsAgnosticLevel > 0; + } + _searchNamespace(nsName) { + let ns = this._namespaces.get(nsName); + if (ns) { + return ns; + } + for (const [name, { + check + }] of Object.entries(NamespaceIds)) { + if (check(nsName)) { + ns = NamespaceSetUp[name]; + if (ns) { + this._namespaces.set(nsName, ns); + return ns; + } + break; + } + } + ns = new UnknownNamespace(++this._nextNsId); + this._namespaces.set(nsName, ns); + return ns; + } + _addNamespacePrefix(prefixes) { + for (const { + prefix, + value + } of prefixes) { + const namespace = this._searchNamespace(value); + let prefixStack = this._namespacePrefixes.get(prefix); + if (!prefixStack) { + prefixStack = []; + this._namespacePrefixes.set(prefix, prefixStack); + } + prefixStack.push(namespace); + } + } + _getNamespaceToUse(prefix) { + if (!prefix) { + return this._currentNamespace; + } + const prefixStack = this._namespacePrefixes.get(prefix); + if (prefixStack?.length > 0) { + return prefixStack.at(-1); + } + warn(`Unknown namespace prefix: ${prefix}.`); + return null; + } + clean(data) { + const { + hasNamespace, + prefixes, + nsAgnostic + } = data; + if (hasNamespace) { + this._currentNamespace = this._namespaceStack.pop(); + } + if (prefixes) { + prefixes.forEach(({ + prefix + }) => { + this._namespacePrefixes.get(prefix).pop(); + }); + } + if (nsAgnostic) { + this._nsAgnosticLevel--; + } + } +} + +;// ./src/core/xfa/parser.js + + + + +class XFAParser extends XMLParserBase { + constructor(rootNameSpace = null, richText = false) { + super(); + this._builder = new Builder(rootNameSpace); + this._stack = []; + this._globalData = { + usedTypefaces: new Set() + }; + this._ids = new Map(); + this._current = this._builder.buildRoot(this._ids); + this._errorCode = XMLParserErrorCode.NoError; + this._whiteRegex = /^\s+$/; + this._nbsps = /\xa0+/g; + this._richText = richText; + } + parse(data) { + this.parseXml(data); + if (this._errorCode !== XMLParserErrorCode.NoError) { + return undefined; + } + this._current[$finalize](); + return this._current.element; + } + onText(text) { + text = text.replace(this._nbsps, match => match.slice(1) + " "); + if (this._richText || this._current[$acceptWhitespace]()) { + this._current[$onText](text, this._richText); + return; + } + if (this._whiteRegex.test(text)) { + return; + } + this._current[$onText](text.trim()); + } + onCdata(text) { + this._current[$onText](text); + } + _mkAttributes(attributes, tagName) { + let namespace = null; + let prefixes = null; + const attributeObj = Object.create({}); + for (const { + name, + value + } of attributes) { + if (name === "xmlns") { + if (!namespace) { + namespace = value; + } else { + warn(`XFA - multiple namespace definition in <${tagName}>`); + } + } else if (name.startsWith("xmlns:")) { + const prefix = name.substring("xmlns:".length); + if (!prefixes) { + prefixes = []; + } + prefixes.push({ + prefix, + value + }); + } else { + const i = name.indexOf(":"); + if (i === -1) { + attributeObj[name] = value; + } else { + let nsAttrs = attributeObj[$nsAttributes]; + if (!nsAttrs) { + nsAttrs = attributeObj[$nsAttributes] = Object.create(null); + } + const [ns, attrName] = [name.slice(0, i), name.slice(i + 1)]; + const attrs = nsAttrs[ns] ||= Object.create(null); + attrs[attrName] = value; + } + } + } + return [namespace, prefixes, attributeObj]; + } + _getNameAndPrefix(name, nsAgnostic) { + const i = name.indexOf(":"); + if (i === -1) { + return [name, null]; + } + return [name.substring(i + 1), nsAgnostic ? "" : name.substring(0, i)]; + } + onBeginElement(tagName, attributes, isEmpty) { + const [namespace, prefixes, attributesObj] = this._mkAttributes(attributes, tagName); + const [name, nsPrefix] = this._getNameAndPrefix(tagName, this._builder.isNsAgnostic()); + const node = this._builder.build({ + nsPrefix, + name, + attributes: attributesObj, + namespace, + prefixes + }); + node[$globalData] = this._globalData; + if (isEmpty) { + node[$finalize](); + if (this._current[$onChild](node)) { + node[$setId](this._ids); + } + node[$clean](this._builder); + return; + } + this._stack.push(this._current); + this._current = node; + } + onEndElement(name) { + const node = this._current; + if (node[$isCDATAXml]() && typeof node[$content] === "string") { + const parser = new XFAParser(); + parser._globalData = this._globalData; + const root = parser.parse(node[$content]); + node[$content] = null; + node[$onChild](root); + } + node[$finalize](); + this._current = this._stack.pop(); + if (this._current[$onChild](node)) { + node[$setId](this._ids); + } + node[$clean](this._builder); + } + onError(code) { + this._errorCode = code; + } +} + +;// ./src/core/xfa/factory.js + + + + + + + + +class XFAFactory { + constructor(data) { + try { + this.root = new XFAParser().parse(XFAFactory._createDocument(data)); + const binder = new Binder(this.root); + this.form = binder.bind(); + this.dataHandler = new DataHandler(this.root, binder.getData()); + this.form[$globalData].template = this.form; + } catch (e) { + warn(`XFA - an error occurred during parsing and binding: ${e}`); + } + } + isValid() { + return this.root && this.form; + } + _createPagesHelper() { + const iterator = this.form[$toPages](); + return new Promise((resolve, reject) => { + const nextIteration = () => { + try { + const value = iterator.next(); + if (value.done) { + resolve(value.value); + } else { + setTimeout(nextIteration, 0); + } + } catch (e) { + reject(e); + } + }; + setTimeout(nextIteration, 0); + }); + } + async _createPages() { + try { + this.pages = await this._createPagesHelper(); + this.dims = this.pages.children.map(c => { + const { + width, + height + } = c.attributes.style; + return [0, 0, parseInt(width), parseInt(height)]; + }); + } catch (e) { + warn(`XFA - an error occurred during layout: ${e}`); + } + } + getBoundingBox(pageIndex) { + return this.dims[pageIndex]; + } + async getNumPages() { + if (!this.pages) { + await this._createPages(); + } + return this.dims.length; + } + setImages(images) { + this.form[$globalData].images = images; + } + setFonts(fonts) { + this.form[$globalData].fontFinder = new FontFinder(fonts); + const missingFonts = []; + for (let typeface of this.form[$globalData].usedTypefaces) { + typeface = stripQuotes(typeface); + const font = this.form[$globalData].fontFinder.find(typeface); + if (!font) { + missingFonts.push(typeface); + } + } + if (missingFonts.length > 0) { + return missingFonts; + } + return null; + } + appendFonts(fonts, reallyMissingFonts) { + this.form[$globalData].fontFinder.add(fonts, reallyMissingFonts); + } + async getPages() { + if (!this.pages) { + await this._createPages(); + } + const pages = this.pages; + this.pages = null; + return pages; + } + serializeData(storage) { + return this.dataHandler.serialize(storage); + } + static _createDocument(data) { + if (!data["/xdp:xdp"]) { + return data["xdp:xdp"]; + } + return Object.values(data).join(""); + } + static getRichTextAsHtml(rc) { + if (!rc || typeof rc !== "string") { + return null; + } + try { + let root = new XFAParser(XhtmlNamespace, true).parse(rc); + if (!["body", "xhtml"].includes(root[$nodeName])) { + const newRoot = XhtmlNamespace.body({}); + newRoot[$appendChild](root); + root = newRoot; + } + const result = root[$toHTML](); + if (!result.success) { + return null; + } + const { + html + } = result; + const { + attributes + } = html; + if (attributes) { + if (attributes.class) { + attributes.class = attributes.class.filter(attr => !attr.startsWith("xfa")); + } + attributes.dir = "auto"; + } + return { + html, + str: root[$text]() + }; + } catch (e) { + warn(`XFA - an error occurred during parsing of rich text: ${e}`); + } + return null; + } +} + +;// ./src/core/annotation.js + + + + + + + + + + + + + + +class AnnotationFactory { + static createGlobals(pdfManager) { + return Promise.all([pdfManager.ensureCatalog("acroForm"), pdfManager.ensureDoc("xfaDatasets"), pdfManager.ensureCatalog("structTreeRoot"), pdfManager.ensureCatalog("baseUrl"), pdfManager.ensureCatalog("attachments")]).then(([acroForm, xfaDatasets, structTreeRoot, baseUrl, attachments]) => { + return { + pdfManager, + acroForm: acroForm instanceof Dict ? acroForm : Dict.empty, + xfaDatasets, + structTreeRoot, + baseUrl, + attachments + }; + }, reason => { + warn(`createGlobals: "${reason}".`); + return null; + }); + } + static async create(xref, ref, annotationGlobals, idFactory, collectFields, orphanFields, pageRef) { + const pageIndex = collectFields ? await this._getPageIndex(xref, ref, annotationGlobals.pdfManager) : null; + return annotationGlobals.pdfManager.ensure(this, "_create", [xref, ref, annotationGlobals, idFactory, collectFields, orphanFields, pageIndex, pageRef]); + } + static _create(xref, ref, annotationGlobals, idFactory, collectFields = false, orphanFields = null, pageIndex = null, pageRef = null) { + const dict = xref.fetchIfRef(ref); + if (!(dict instanceof Dict)) { + return undefined; + } + const { + acroForm, + pdfManager + } = annotationGlobals; + const id = ref instanceof Ref ? ref.toString() : `annot_${idFactory.createObjId()}`; + let subtype = dict.get("Subtype"); + subtype = subtype instanceof Name ? subtype.name : null; + const parameters = { + xref, + ref, + dict, + subtype, + id, + annotationGlobals, + collectFields, + orphanFields, + needAppearances: !collectFields && acroForm.get("NeedAppearances") === true, + pageIndex, + evaluatorOptions: pdfManager.evaluatorOptions, + pageRef + }; + switch (subtype) { + case "Link": + return new LinkAnnotation(parameters); + case "Text": + return new TextAnnotation(parameters); + case "Widget": + let fieldType = getInheritableProperty({ + dict, + key: "FT" + }); + fieldType = fieldType instanceof Name ? fieldType.name : null; + switch (fieldType) { + case "Tx": + return new TextWidgetAnnotation(parameters); + case "Btn": + return new ButtonWidgetAnnotation(parameters); + case "Ch": + return new ChoiceWidgetAnnotation(parameters); + case "Sig": + return new SignatureWidgetAnnotation(parameters); + } + warn(`Unimplemented widget field type "${fieldType}", ` + "falling back to base field type."); + return new WidgetAnnotation(parameters); + case "Popup": + return new PopupAnnotation(parameters); + case "FreeText": + return new FreeTextAnnotation(parameters); + case "Line": + return new LineAnnotation(parameters); + case "Square": + return new SquareAnnotation(parameters); + case "Circle": + return new CircleAnnotation(parameters); + case "PolyLine": + return new PolylineAnnotation(parameters); + case "Polygon": + return new PolygonAnnotation(parameters); + case "Caret": + return new CaretAnnotation(parameters); + case "Ink": + return new InkAnnotation(parameters); + case "Highlight": + return new HighlightAnnotation(parameters); + case "Underline": + return new UnderlineAnnotation(parameters); + case "Squiggly": + return new SquigglyAnnotation(parameters); + case "StrikeOut": + return new StrikeOutAnnotation(parameters); + case "Stamp": + return new StampAnnotation(parameters); + case "FileAttachment": + return new FileAttachmentAnnotation(parameters); + default: + if (!collectFields) { + if (!subtype) { + warn("Annotation is missing the required /Subtype."); + } else { + warn(`Unimplemented annotation type "${subtype}", ` + "falling back to base annotation."); + } + } + return new Annotation(parameters); + } + } + static async _getPageIndex(xref, ref, pdfManager) { + try { + const annotDict = await xref.fetchIfRefAsync(ref); + if (!(annotDict instanceof Dict)) { + return -1; + } + const pageRef = annotDict.getRaw("P"); + if (pageRef instanceof Ref) { + try { + const pageIndex = await pdfManager.ensureCatalog("getPageIndex", [pageRef]); + return pageIndex; + } catch (ex) { + info(`_getPageIndex -- not a valid page reference: "${ex}".`); + } + } + if (annotDict.has("Kids")) { + return -1; + } + const numPages = await pdfManager.ensureDoc("numPages"); + for (let pageIndex = 0; pageIndex < numPages; pageIndex++) { + const page = await pdfManager.getPage(pageIndex); + const annotations = await pdfManager.ensure(page, "annotations"); + for (const annotRef of annotations) { + if (annotRef instanceof Ref && isRefsEqual(annotRef, ref)) { + return pageIndex; + } + } + } + } catch (ex) { + warn(`_getPageIndex: "${ex}".`); + } + return -1; + } + static generateImages(annotations, xref, isOffscreenCanvasSupported) { + if (!isOffscreenCanvasSupported) { + warn("generateImages: OffscreenCanvas is not supported, cannot save or print some annotations with images."); + return null; + } + let imagePromises; + for (const { + bitmapId, + bitmap + } of annotations) { + if (!bitmap) { + continue; + } + imagePromises ||= new Map(); + imagePromises.set(bitmapId, StampAnnotation.createImage(bitmap, xref)); + } + return imagePromises; + } + static async saveNewAnnotations(evaluator, task, annotations, imagePromises, changes) { + const xref = evaluator.xref; + let baseFontRef; + const promises = []; + const { + isOffscreenCanvasSupported + } = evaluator.options; + for (const annotation of annotations) { + if (annotation.deleted) { + continue; + } + switch (annotation.annotationType) { + case AnnotationEditorType.FREETEXT: + if (!baseFontRef) { + const baseFont = new Dict(xref); + baseFont.set("BaseFont", Name.get("Helvetica")); + baseFont.set("Type", Name.get("Font")); + baseFont.set("Subtype", Name.get("Type1")); + baseFont.set("Encoding", Name.get("WinAnsiEncoding")); + baseFontRef = xref.getNewTemporaryRef(); + changes.put(baseFontRef, { + data: baseFont + }); + } + promises.push(FreeTextAnnotation.createNewAnnotation(xref, annotation, changes, { + evaluator, + task, + baseFontRef + })); + break; + case AnnotationEditorType.HIGHLIGHT: + if (annotation.quadPoints) { + promises.push(HighlightAnnotation.createNewAnnotation(xref, annotation, changes)); + } else { + promises.push(InkAnnotation.createNewAnnotation(xref, annotation, changes)); + } + break; + case AnnotationEditorType.INK: + promises.push(InkAnnotation.createNewAnnotation(xref, annotation, changes)); + break; + case AnnotationEditorType.STAMP: + const image = isOffscreenCanvasSupported ? await imagePromises?.get(annotation.bitmapId) : null; + if (image?.imageStream) { + const { + imageStream, + smaskStream + } = image; + if (smaskStream) { + const smaskRef = xref.getNewTemporaryRef(); + changes.put(smaskRef, { + data: smaskStream + }); + imageStream.dict.set("SMask", smaskRef); + } + const imageRef = image.imageRef = xref.getNewTemporaryRef(); + changes.put(imageRef, { + data: imageStream + }); + image.imageStream = image.smaskStream = null; + } + promises.push(StampAnnotation.createNewAnnotation(xref, annotation, changes, { + image + })); + break; + } + } + return { + annotations: await Promise.all(promises) + }; + } + static async printNewAnnotations(annotationGlobals, evaluator, task, annotations, imagePromises) { + if (!annotations) { + return null; + } + const { + options, + xref + } = evaluator; + const promises = []; + for (const annotation of annotations) { + if (annotation.deleted) { + continue; + } + switch (annotation.annotationType) { + case AnnotationEditorType.FREETEXT: + promises.push(FreeTextAnnotation.createNewPrintAnnotation(annotationGlobals, xref, annotation, { + evaluator, + task, + evaluatorOptions: options + })); + break; + case AnnotationEditorType.HIGHLIGHT: + if (annotation.quadPoints) { + promises.push(HighlightAnnotation.createNewPrintAnnotation(annotationGlobals, xref, annotation, { + evaluatorOptions: options + })); + } else { + promises.push(InkAnnotation.createNewPrintAnnotation(annotationGlobals, xref, annotation, { + evaluatorOptions: options + })); + } + break; + case AnnotationEditorType.INK: + promises.push(InkAnnotation.createNewPrintAnnotation(annotationGlobals, xref, annotation, { + evaluatorOptions: options + })); + break; + case AnnotationEditorType.STAMP: + const image = options.isOffscreenCanvasSupported ? await imagePromises?.get(annotation.bitmapId) : null; + if (image?.imageStream) { + const { + imageStream, + smaskStream + } = image; + if (smaskStream) { + imageStream.dict.set("SMask", smaskStream); + } + image.imageRef = new JpegStream(imageStream, imageStream.length); + image.imageStream = image.smaskStream = null; + } + promises.push(StampAnnotation.createNewPrintAnnotation(annotationGlobals, xref, annotation, { + image, + evaluatorOptions: options + })); + break; + } + } + return Promise.all(promises); + } +} +function getRgbColor(color, defaultColor = new Uint8ClampedArray(3)) { + if (!Array.isArray(color)) { + return defaultColor; + } + const rgbColor = defaultColor || new Uint8ClampedArray(3); + switch (color.length) { + case 0: + return null; + case 1: + ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0); + return rgbColor; + case 3: + ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0); + return rgbColor; + case 4: + ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0); + return rgbColor; + default: + return defaultColor; + } +} +function getPdfColorArray(color) { + return Array.from(color, c => c / 255); +} +function getQuadPoints(dict, rect) { + const quadPoints = dict.getArray("QuadPoints"); + if (!isNumberArray(quadPoints, null) || quadPoints.length === 0 || quadPoints.length % 8 > 0) { + return null; + } + const newQuadPoints = new Float32Array(quadPoints.length); + for (let i = 0, ii = quadPoints.length; i < ii; i += 8) { + const [x1, y1, x2, y2, x3, y3, x4, y4] = quadPoints.slice(i, i + 8); + const minX = Math.min(x1, x2, x3, x4); + const maxX = Math.max(x1, x2, x3, x4); + const minY = Math.min(y1, y2, y3, y4); + const maxY = Math.max(y1, y2, y3, y4); + if (rect !== null && (minX < rect[0] || maxX > rect[2] || minY < rect[1] || maxY > rect[3])) { + return null; + } + newQuadPoints.set([minX, maxY, maxX, maxY, minX, minY, maxX, minY], i); + } + return newQuadPoints; +} +function getTransformMatrix(rect, bbox, matrix) { + const [minX, minY, maxX, maxY] = Util.getAxialAlignedBoundingBox(bbox, matrix); + if (minX === maxX || minY === maxY) { + return [1, 0, 0, 1, rect[0], rect[1]]; + } + const xRatio = (rect[2] - rect[0]) / (maxX - minX); + const yRatio = (rect[3] - rect[1]) / (maxY - minY); + return [xRatio, 0, 0, yRatio, rect[0] - minX * xRatio, rect[1] - minY * yRatio]; +} +class Annotation { + constructor(params) { + const { + dict, + xref, + annotationGlobals, + ref, + orphanFields + } = params; + const parentRef = orphanFields?.get(ref); + if (parentRef) { + dict.set("Parent", parentRef); + } + this.setTitle(dict.get("T")); + this.setContents(dict.get("Contents")); + this.setModificationDate(dict.get("M")); + this.setFlags(dict.get("F")); + this.setRectangle(dict.getArray("Rect")); + this.setColor(dict.getArray("C")); + this.setBorderStyle(dict); + this.setAppearance(dict); + this.setOptionalContent(dict); + const MK = dict.get("MK"); + this.setBorderAndBackgroundColors(MK); + this.setRotation(MK, dict); + this.ref = params.ref instanceof Ref ? params.ref : null; + this._streams = []; + if (this.appearance) { + this._streams.push(this.appearance); + } + const isLocked = !!(this.flags & AnnotationFlag.LOCKED); + const isContentLocked = !!(this.flags & AnnotationFlag.LOCKEDCONTENTS); + this.data = { + annotationFlags: this.flags, + borderStyle: this.borderStyle, + color: this.color, + backgroundColor: this.backgroundColor, + borderColor: this.borderColor, + rotation: this.rotation, + contentsObj: this._contents, + hasAppearance: !!this.appearance, + id: params.id, + modificationDate: this.modificationDate, + rect: this.rectangle, + subtype: params.subtype, + hasOwnCanvas: false, + noRotate: !!(this.flags & AnnotationFlag.NOROTATE), + noHTML: isLocked && isContentLocked, + isEditable: false, + structParent: -1 + }; + if (annotationGlobals.structTreeRoot) { + let structParent = dict.get("StructParent"); + this.data.structParent = structParent = Number.isInteger(structParent) && structParent >= 0 ? structParent : -1; + annotationGlobals.structTreeRoot.addAnnotationIdToPage(params.pageRef, structParent); + } + if (params.collectFields) { + const kids = dict.get("Kids"); + if (Array.isArray(kids)) { + const kidIds = []; + for (const kid of kids) { + if (kid instanceof Ref) { + kidIds.push(kid.toString()); + } + } + if (kidIds.length !== 0) { + this.data.kidIds = kidIds; + } + } + this.data.actions = collectActions(xref, dict, AnnotationActionEventType); + this.data.fieldName = this._constructFieldName(dict); + this.data.pageIndex = params.pageIndex; + } + const it = dict.get("IT"); + if (it instanceof Name) { + this.data.it = it.name; + } + this._isOffscreenCanvasSupported = params.evaluatorOptions.isOffscreenCanvasSupported; + this._fallbackFontDict = null; + this._needAppearances = false; + } + _hasFlag(flags, flag) { + return !!(flags & flag); + } + _buildFlags(noView, noPrint) { + let { + flags + } = this; + if (noView === undefined) { + if (noPrint === undefined) { + return undefined; + } + if (noPrint) { + return flags & ~AnnotationFlag.PRINT; + } + return flags & ~AnnotationFlag.HIDDEN | AnnotationFlag.PRINT; + } + if (noView) { + flags |= AnnotationFlag.PRINT; + if (noPrint) { + return flags & ~AnnotationFlag.NOVIEW | AnnotationFlag.HIDDEN; + } + return flags & ~AnnotationFlag.HIDDEN | AnnotationFlag.NOVIEW; + } + flags &= ~(AnnotationFlag.HIDDEN | AnnotationFlag.NOVIEW); + if (noPrint) { + return flags & ~AnnotationFlag.PRINT; + } + return flags | AnnotationFlag.PRINT; + } + _isViewable(flags) { + return !this._hasFlag(flags, AnnotationFlag.INVISIBLE) && !this._hasFlag(flags, AnnotationFlag.NOVIEW); + } + _isPrintable(flags) { + return this._hasFlag(flags, AnnotationFlag.PRINT) && !this._hasFlag(flags, AnnotationFlag.HIDDEN) && !this._hasFlag(flags, AnnotationFlag.INVISIBLE); + } + mustBeViewed(annotationStorage, _renderForms) { + const noView = annotationStorage?.get(this.data.id)?.noView; + if (noView !== undefined) { + return !noView; + } + return this.viewable && !this._hasFlag(this.flags, AnnotationFlag.HIDDEN); + } + mustBePrinted(annotationStorage) { + const noPrint = annotationStorage?.get(this.data.id)?.noPrint; + if (noPrint !== undefined) { + return !noPrint; + } + return this.printable; + } + mustBeViewedWhenEditing(isEditing, modifiedIds = null) { + return isEditing ? !this.data.isEditable : !modifiedIds?.has(this.data.id); + } + get viewable() { + if (this.data.quadPoints === null) { + return false; + } + if (this.flags === 0) { + return true; + } + return this._isViewable(this.flags); + } + get printable() { + if (this.data.quadPoints === null) { + return false; + } + if (this.flags === 0) { + return false; + } + return this._isPrintable(this.flags); + } + _parseStringHelper(data) { + const str = typeof data === "string" ? stringToPDFString(data) : ""; + const dir = str && bidi(str).dir === "rtl" ? "rtl" : "ltr"; + return { + str, + dir + }; + } + setDefaultAppearance(params) { + const { + dict, + annotationGlobals + } = params; + const defaultAppearance = getInheritableProperty({ + dict, + key: "DA" + }) || annotationGlobals.acroForm.get("DA"); + this._defaultAppearance = typeof defaultAppearance === "string" ? defaultAppearance : ""; + this.data.defaultAppearanceData = parseDefaultAppearance(this._defaultAppearance); + } + setTitle(title) { + this._title = this._parseStringHelper(title); + } + setContents(contents) { + this._contents = this._parseStringHelper(contents); + } + setModificationDate(modificationDate) { + this.modificationDate = typeof modificationDate === "string" ? modificationDate : null; + } + setFlags(flags) { + this.flags = Number.isInteger(flags) && flags > 0 ? flags : 0; + if (this.flags & AnnotationFlag.INVISIBLE && this.constructor.name !== "Annotation") { + this.flags ^= AnnotationFlag.INVISIBLE; + } + } + hasFlag(flag) { + return this._hasFlag(this.flags, flag); + } + setRectangle(rectangle) { + this.rectangle = lookupNormalRect(rectangle, [0, 0, 0, 0]); + } + setColor(color) { + this.color = getRgbColor(color); + } + setLineEndings(lineEndings) { + this.lineEndings = ["None", "None"]; + if (Array.isArray(lineEndings) && lineEndings.length === 2) { + for (let i = 0; i < 2; i++) { + const obj = lineEndings[i]; + if (obj instanceof Name) { + switch (obj.name) { + case "None": + continue; + case "Square": + case "Circle": + case "Diamond": + case "OpenArrow": + case "ClosedArrow": + case "Butt": + case "ROpenArrow": + case "RClosedArrow": + case "Slash": + this.lineEndings[i] = obj.name; + continue; + } + } + warn(`Ignoring invalid lineEnding: ${obj}`); + } + } + } + setRotation(mk, dict) { + this.rotation = 0; + let angle = mk instanceof Dict ? mk.get("R") || 0 : dict.get("Rotate") || 0; + if (Number.isInteger(angle) && angle !== 0) { + angle %= 360; + if (angle < 0) { + angle += 360; + } + if (angle % 90 === 0) { + this.rotation = angle; + } + } + } + setBorderAndBackgroundColors(mk) { + if (mk instanceof Dict) { + this.borderColor = getRgbColor(mk.getArray("BC"), null); + this.backgroundColor = getRgbColor(mk.getArray("BG"), null); + } else { + this.borderColor = this.backgroundColor = null; + } + } + setBorderStyle(borderStyle) { + this.borderStyle = new AnnotationBorderStyle(); + if (!(borderStyle instanceof Dict)) { + return; + } + if (borderStyle.has("BS")) { + const dict = borderStyle.get("BS"); + if (dict instanceof Dict) { + const dictType = dict.get("Type"); + if (!dictType || isName(dictType, "Border")) { + this.borderStyle.setWidth(dict.get("W"), this.rectangle); + this.borderStyle.setStyle(dict.get("S")); + this.borderStyle.setDashArray(dict.getArray("D")); + } + } + } else if (borderStyle.has("Border")) { + const array = borderStyle.getArray("Border"); + if (Array.isArray(array) && array.length >= 3) { + this.borderStyle.setHorizontalCornerRadius(array[0]); + this.borderStyle.setVerticalCornerRadius(array[1]); + this.borderStyle.setWidth(array[2], this.rectangle); + if (array.length === 4) { + this.borderStyle.setDashArray(array[3], true); + } + } + } else { + this.borderStyle.setWidth(0); + } + } + setAppearance(dict) { + this.appearance = null; + const appearanceStates = dict.get("AP"); + if (!(appearanceStates instanceof Dict)) { + return; + } + const normalAppearanceState = appearanceStates.get("N"); + if (normalAppearanceState instanceof BaseStream) { + this.appearance = normalAppearanceState; + return; + } + if (!(normalAppearanceState instanceof Dict)) { + return; + } + const as = dict.get("AS"); + if (!(as instanceof Name) || !normalAppearanceState.has(as.name)) { + return; + } + const appearance = normalAppearanceState.get(as.name); + if (appearance instanceof BaseStream) { + this.appearance = appearance; + } + } + setOptionalContent(dict) { + this.oc = null; + const oc = dict.get("OC"); + if (oc instanceof Name) { + warn("setOptionalContent: Support for /Name-entry is not implemented."); + } else if (oc instanceof Dict) { + this.oc = oc; + } + } + loadResources(keys, appearance) { + return appearance.dict.getAsync("Resources").then(resources => { + if (!resources) { + return undefined; + } + const objectLoader = new ObjectLoader(resources, keys, resources.xref); + return objectLoader.load().then(function () { + return resources; + }); + }); + } + async getOperatorList(evaluator, task, intent, annotationStorage) { + const { + hasOwnCanvas, + id, + rect + } = this.data; + let appearance = this.appearance; + const isUsingOwnCanvas = !!(hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY); + if (isUsingOwnCanvas && (rect[0] === rect[2] || rect[1] === rect[3])) { + this.data.hasOwnCanvas = false; + return { + opList: new OperatorList(), + separateForm: false, + separateCanvas: false + }; + } + if (!appearance) { + if (!isUsingOwnCanvas) { + return { + opList: new OperatorList(), + separateForm: false, + separateCanvas: false + }; + } + appearance = new StringStream(""); + appearance.dict = new Dict(); + } + const appearanceDict = appearance.dict; + const resources = await this.loadResources(["ExtGState", "ColorSpace", "Pattern", "Shading", "XObject", "Font"], appearance); + const bbox = lookupRect(appearanceDict.getArray("BBox"), [0, 0, 1, 1]); + const matrix = lookupMatrix(appearanceDict.getArray("Matrix"), IDENTITY_MATRIX); + const transform = getTransformMatrix(rect, bbox, matrix); + const opList = new OperatorList(); + let optionalContent; + if (this.oc) { + optionalContent = await evaluator.parseMarkedContentProps(this.oc, null); + } + if (optionalContent !== undefined) { + opList.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]); + } + opList.addOp(OPS.beginAnnotation, [id, rect, transform, matrix, isUsingOwnCanvas]); + await evaluator.getOperatorList({ + stream: appearance, + task, + resources, + operatorList: opList, + fallbackFontDict: this._fallbackFontDict + }); + opList.addOp(OPS.endAnnotation, []); + if (optionalContent !== undefined) { + opList.addOp(OPS.endMarkedContent, []); + } + this.reset(); + return { + opList, + separateForm: false, + separateCanvas: isUsingOwnCanvas + }; + } + async save(evaluator, task, annotationStorage, changes) { + return null; + } + get hasTextContent() { + return false; + } + async extractTextContent(evaluator, task, viewBox) { + if (!this.appearance) { + return; + } + const resources = await this.loadResources(["ExtGState", "Font", "Properties", "XObject"], this.appearance); + const text = []; + const buffer = []; + let firstPosition = null; + const sink = { + desiredSize: Math.Infinity, + ready: true, + enqueue(chunk, size) { + for (const item of chunk.items) { + if (item.str === undefined) { + continue; + } + firstPosition ||= item.transform.slice(-2); + buffer.push(item.str); + if (item.hasEOL) { + text.push(buffer.join("").trimEnd()); + buffer.length = 0; + } + } + } + }; + await evaluator.getTextContent({ + stream: this.appearance, + task, + resources, + includeMarkedContent: true, + keepWhiteSpace: true, + sink, + viewBox + }); + this.reset(); + if (buffer.length) { + text.push(buffer.join("").trimEnd()); + } + if (text.length > 1 || text[0]) { + const appearanceDict = this.appearance.dict; + const bbox = lookupRect(appearanceDict.getArray("BBox"), null); + const matrix = lookupMatrix(appearanceDict.getArray("Matrix"), null); + this.data.textPosition = this._transformPoint(firstPosition, bbox, matrix); + this.data.textContent = text; + } + } + _transformPoint(coords, bbox, matrix) { + const { + rect + } = this.data; + bbox ||= [0, 0, 1, 1]; + matrix ||= [1, 0, 0, 1, 0, 0]; + const transform = getTransformMatrix(rect, bbox, matrix); + transform[4] -= rect[0]; + transform[5] -= rect[1]; + coords = Util.applyTransform(coords, transform); + return Util.applyTransform(coords, matrix); + } + getFieldObject() { + if (this.data.kidIds) { + return { + id: this.data.id, + actions: this.data.actions, + name: this.data.fieldName, + strokeColor: this.data.borderColor, + fillColor: this.data.backgroundColor, + type: "", + kidIds: this.data.kidIds, + page: this.data.pageIndex, + rotation: this.rotation + }; + } + return null; + } + reset() { + for (const stream of this._streams) { + stream.reset(); + } + } + _constructFieldName(dict) { + if (!dict.has("T") && !dict.has("Parent")) { + warn("Unknown field name, falling back to empty field name."); + return ""; + } + if (!dict.has("Parent")) { + return stringToPDFString(dict.get("T")); + } + const fieldName = []; + if (dict.has("T")) { + fieldName.unshift(stringToPDFString(dict.get("T"))); + } + let loopDict = dict; + const visited = new RefSet(); + if (dict.objId) { + visited.put(dict.objId); + } + while (loopDict.has("Parent")) { + loopDict = loopDict.get("Parent"); + if (!(loopDict instanceof Dict) || loopDict.objId && visited.has(loopDict.objId)) { + break; + } + if (loopDict.objId) { + visited.put(loopDict.objId); + } + if (loopDict.has("T")) { + fieldName.unshift(stringToPDFString(loopDict.get("T"))); + } + } + return fieldName.join("."); + } +} +class AnnotationBorderStyle { + constructor() { + this.width = 1; + this.rawWidth = 1; + this.style = AnnotationBorderStyleType.SOLID; + this.dashArray = [3]; + this.horizontalCornerRadius = 0; + this.verticalCornerRadius = 0; + } + setWidth(width, rect = [0, 0, 0, 0]) { + if (width instanceof Name) { + this.width = 0; + return; + } + if (typeof width === "number") { + if (width > 0) { + this.rawWidth = width; + const maxWidth = (rect[2] - rect[0]) / 2; + const maxHeight = (rect[3] - rect[1]) / 2; + if (maxWidth > 0 && maxHeight > 0 && (width > maxWidth || width > maxHeight)) { + warn(`AnnotationBorderStyle.setWidth - ignoring width: ${width}`); + width = 1; + } + } + this.width = width; + } + } + setStyle(style) { + if (!(style instanceof Name)) { + return; + } + switch (style.name) { + case "S": + this.style = AnnotationBorderStyleType.SOLID; + break; + case "D": + this.style = AnnotationBorderStyleType.DASHED; + break; + case "B": + this.style = AnnotationBorderStyleType.BEVELED; + break; + case "I": + this.style = AnnotationBorderStyleType.INSET; + break; + case "U": + this.style = AnnotationBorderStyleType.UNDERLINE; + break; + default: + break; + } + } + setDashArray(dashArray, forceStyle = false) { + if (Array.isArray(dashArray)) { + let isValid = true; + let allZeros = true; + for (const element of dashArray) { + const validNumber = +element >= 0; + if (!validNumber) { + isValid = false; + break; + } else if (element > 0) { + allZeros = false; + } + } + if (dashArray.length === 0 || isValid && !allZeros) { + this.dashArray = dashArray; + if (forceStyle) { + this.setStyle(Name.get("D")); + } + } else { + this.width = 0; + } + } else if (dashArray) { + this.width = 0; + } + } + setHorizontalCornerRadius(radius) { + if (Number.isInteger(radius)) { + this.horizontalCornerRadius = radius; + } + } + setVerticalCornerRadius(radius) { + if (Number.isInteger(radius)) { + this.verticalCornerRadius = radius; + } + } +} +class MarkupAnnotation extends Annotation { + constructor(params) { + super(params); + const { + dict + } = params; + if (dict.has("IRT")) { + const rawIRT = dict.getRaw("IRT"); + this.data.inReplyTo = rawIRT instanceof Ref ? rawIRT.toString() : null; + const rt = dict.get("RT"); + this.data.replyType = rt instanceof Name ? rt.name : AnnotationReplyType.REPLY; + } + let popupRef = null; + if (this.data.replyType === AnnotationReplyType.GROUP) { + const parent = dict.get("IRT"); + this.setTitle(parent.get("T")); + this.data.titleObj = this._title; + this.setContents(parent.get("Contents")); + this.data.contentsObj = this._contents; + if (!parent.has("CreationDate")) { + this.data.creationDate = null; + } else { + this.setCreationDate(parent.get("CreationDate")); + this.data.creationDate = this.creationDate; + } + if (!parent.has("M")) { + this.data.modificationDate = null; + } else { + this.setModificationDate(parent.get("M")); + this.data.modificationDate = this.modificationDate; + } + popupRef = parent.getRaw("Popup"); + if (!parent.has("C")) { + this.data.color = null; + } else { + this.setColor(parent.getArray("C")); + this.data.color = this.color; + } + } else { + this.data.titleObj = this._title; + this.setCreationDate(dict.get("CreationDate")); + this.data.creationDate = this.creationDate; + popupRef = dict.getRaw("Popup"); + if (!dict.has("C")) { + this.data.color = null; + } + } + this.data.popupRef = popupRef instanceof Ref ? popupRef.toString() : null; + if (dict.has("RC")) { + this.data.richText = XFAFactory.getRichTextAsHtml(dict.get("RC")); + } + } + setCreationDate(creationDate) { + this.creationDate = typeof creationDate === "string" ? creationDate : null; + } + _setDefaultAppearance({ + xref, + extra, + strokeColor, + fillColor, + blendMode, + strokeAlpha, + fillAlpha, + pointsCallback + }) { + let minX = Number.MAX_VALUE; + let minY = Number.MAX_VALUE; + let maxX = Number.MIN_VALUE; + let maxY = Number.MIN_VALUE; + const buffer = ["q"]; + if (extra) { + buffer.push(extra); + } + if (strokeColor) { + buffer.push(`${strokeColor[0]} ${strokeColor[1]} ${strokeColor[2]} RG`); + } + if (fillColor) { + buffer.push(`${fillColor[0]} ${fillColor[1]} ${fillColor[2]} rg`); + } + const pointsArray = this.data.quadPoints || Float32Array.from([this.rectangle[0], this.rectangle[3], this.rectangle[2], this.rectangle[3], this.rectangle[0], this.rectangle[1], this.rectangle[2], this.rectangle[1]]); + for (let i = 0, ii = pointsArray.length; i < ii; i += 8) { + const [mX, MX, mY, MY] = pointsCallback(buffer, pointsArray.subarray(i, i + 8)); + minX = Math.min(minX, mX); + maxX = Math.max(maxX, MX); + minY = Math.min(minY, mY); + maxY = Math.max(maxY, MY); + } + buffer.push("Q"); + const formDict = new Dict(xref); + const appearanceStreamDict = new Dict(xref); + appearanceStreamDict.set("Subtype", Name.get("Form")); + const appearanceStream = new StringStream(buffer.join(" ")); + appearanceStream.dict = appearanceStreamDict; + formDict.set("Fm0", appearanceStream); + const gsDict = new Dict(xref); + if (blendMode) { + gsDict.set("BM", Name.get(blendMode)); + } + if (typeof strokeAlpha === "number") { + gsDict.set("CA", strokeAlpha); + } + if (typeof fillAlpha === "number") { + gsDict.set("ca", fillAlpha); + } + const stateDict = new Dict(xref); + stateDict.set("GS0", gsDict); + const resources = new Dict(xref); + resources.set("ExtGState", stateDict); + resources.set("XObject", formDict); + const appearanceDict = new Dict(xref); + appearanceDict.set("Resources", resources); + const bbox = this.data.rect = [minX, minY, maxX, maxY]; + appearanceDict.set("BBox", bbox); + this.appearance = new StringStream("/GS0 gs /Fm0 Do"); + this.appearance.dict = appearanceDict; + this._streams.push(this.appearance, appearanceStream); + } + static async createNewAnnotation(xref, annotation, changes, params) { + const annotationRef = annotation.ref ||= xref.getNewTemporaryRef(); + const ap = await this.createNewAppearanceStream(annotation, xref, params); + let annotationDict; + if (ap) { + const apRef = xref.getNewTemporaryRef(); + annotationDict = this.createNewDict(annotation, xref, { + apRef + }); + changes.put(apRef, { + data: ap + }); + } else { + annotationDict = this.createNewDict(annotation, xref, {}); + } + if (Number.isInteger(annotation.parentTreeId)) { + annotationDict.set("StructParent", annotation.parentTreeId); + } + changes.put(annotationRef, { + data: annotationDict + }); + return { + ref: annotationRef + }; + } + static async createNewPrintAnnotation(annotationGlobals, xref, annotation, params) { + const ap = await this.createNewAppearanceStream(annotation, xref, params); + const annotationDict = this.createNewDict(annotation, xref, ap ? { + ap + } : {}); + const newAnnotation = new this.prototype.constructor({ + dict: annotationDict, + xref, + annotationGlobals, + evaluatorOptions: params.evaluatorOptions + }); + if (annotation.ref) { + newAnnotation.ref = newAnnotation.refToReplace = annotation.ref; + } + return newAnnotation; + } +} +class WidgetAnnotation extends Annotation { + constructor(params) { + super(params); + const { + dict, + xref, + annotationGlobals + } = params; + const data = this.data; + this._needAppearances = params.needAppearances; + data.annotationType = AnnotationType.WIDGET; + if (data.fieldName === undefined) { + data.fieldName = this._constructFieldName(dict); + } + if (data.actions === undefined) { + data.actions = collectActions(xref, dict, AnnotationActionEventType); + } + let fieldValue = getInheritableProperty({ + dict, + key: "V", + getArray: true + }); + data.fieldValue = this._decodeFormValue(fieldValue); + const defaultFieldValue = getInheritableProperty({ + dict, + key: "DV", + getArray: true + }); + data.defaultFieldValue = this._decodeFormValue(defaultFieldValue); + if (fieldValue === undefined && annotationGlobals.xfaDatasets) { + const path = this._title.str; + if (path) { + this._hasValueFromXFA = true; + data.fieldValue = fieldValue = annotationGlobals.xfaDatasets.getValue(path); + } + } + if (fieldValue === undefined && data.defaultFieldValue !== null) { + data.fieldValue = data.defaultFieldValue; + } + data.alternativeText = stringToPDFString(dict.get("TU") || ""); + this.setDefaultAppearance(params); + data.hasAppearance ||= this._needAppearances && data.fieldValue !== undefined && data.fieldValue !== null; + const fieldType = getInheritableProperty({ + dict, + key: "FT" + }); + data.fieldType = fieldType instanceof Name ? fieldType.name : null; + const localResources = getInheritableProperty({ + dict, + key: "DR" + }); + const acroFormResources = annotationGlobals.acroForm.get("DR"); + const appearanceResources = this.appearance?.dict.get("Resources"); + this._fieldResources = { + localResources, + acroFormResources, + appearanceResources, + mergedResources: Dict.merge({ + xref, + dictArray: [localResources, appearanceResources, acroFormResources], + mergeSubDicts: true + }) + }; + data.fieldFlags = getInheritableProperty({ + dict, + key: "Ff" + }); + if (!Number.isInteger(data.fieldFlags) || data.fieldFlags < 0) { + data.fieldFlags = 0; + } + data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY); + data.required = this.hasFieldFlag(AnnotationFieldFlag.REQUIRED); + data.hidden = this._hasFlag(data.annotationFlags, AnnotationFlag.HIDDEN) || this._hasFlag(data.annotationFlags, AnnotationFlag.NOVIEW); + } + _decodeFormValue(formValue) { + if (Array.isArray(formValue)) { + return formValue.filter(item => typeof item === "string").map(item => stringToPDFString(item)); + } else if (formValue instanceof Name) { + return stringToPDFString(formValue.name); + } else if (typeof formValue === "string") { + return stringToPDFString(formValue); + } + return null; + } + hasFieldFlag(flag) { + return !!(this.data.fieldFlags & flag); + } + _isViewable(flags) { + return true; + } + mustBeViewed(annotationStorage, renderForms) { + if (renderForms) { + return this.viewable; + } + return super.mustBeViewed(annotationStorage, renderForms) && !this._hasFlag(this.flags, AnnotationFlag.NOVIEW); + } + getRotationMatrix(annotationStorage) { + let rotation = annotationStorage?.get(this.data.id)?.rotation; + if (rotation === undefined) { + rotation = this.rotation; + } + if (rotation === 0) { + return IDENTITY_MATRIX; + } + const width = this.data.rect[2] - this.data.rect[0]; + const height = this.data.rect[3] - this.data.rect[1]; + return getRotationMatrix(rotation, width, height); + } + getBorderAndBackgroundAppearances(annotationStorage) { + let rotation = annotationStorage?.get(this.data.id)?.rotation; + if (rotation === undefined) { + rotation = this.rotation; + } + if (!this.backgroundColor && !this.borderColor) { + return ""; + } + const width = this.data.rect[2] - this.data.rect[0]; + const height = this.data.rect[3] - this.data.rect[1]; + const rect = rotation === 0 || rotation === 180 ? `0 0 ${width} ${height} re` : `0 0 ${height} ${width} re`; + let str = ""; + if (this.backgroundColor) { + str = `${getPdfColor(this.backgroundColor, true)} ${rect} f `; + } + if (this.borderColor) { + const borderWidth = this.borderStyle.width || 1; + str += `${borderWidth} w ${getPdfColor(this.borderColor, false)} ${rect} S `; + } + return str; + } + async getOperatorList(evaluator, task, intent, annotationStorage) { + if (intent & RenderingIntentFlag.ANNOTATIONS_FORMS && !(this instanceof SignatureWidgetAnnotation) && !this.data.noHTML && !this.data.hasOwnCanvas) { + return { + opList: new OperatorList(), + separateForm: true, + separateCanvas: false + }; + } + if (!this._hasText) { + return super.getOperatorList(evaluator, task, intent, annotationStorage); + } + const content = await this._getAppearance(evaluator, task, intent, annotationStorage); + if (this.appearance && content === null) { + return super.getOperatorList(evaluator, task, intent, annotationStorage); + } + const opList = new OperatorList(); + if (!this._defaultAppearance || content === null) { + return { + opList, + separateForm: false, + separateCanvas: false + }; + } + const isUsingOwnCanvas = !!(this.data.hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY); + const matrix = [1, 0, 0, 1, 0, 0]; + const bbox = [0, 0, this.data.rect[2] - this.data.rect[0], this.data.rect[3] - this.data.rect[1]]; + const transform = getTransformMatrix(this.data.rect, bbox, matrix); + let optionalContent; + if (this.oc) { + optionalContent = await evaluator.parseMarkedContentProps(this.oc, null); + } + if (optionalContent !== undefined) { + opList.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]); + } + opList.addOp(OPS.beginAnnotation, [this.data.id, this.data.rect, transform, this.getRotationMatrix(annotationStorage), isUsingOwnCanvas]); + const stream = new StringStream(content); + await evaluator.getOperatorList({ + stream, + task, + resources: this._fieldResources.mergedResources, + operatorList: opList + }); + opList.addOp(OPS.endAnnotation, []); + if (optionalContent !== undefined) { + opList.addOp(OPS.endMarkedContent, []); + } + return { + opList, + separateForm: false, + separateCanvas: isUsingOwnCanvas + }; + } + _getMKDict(rotation) { + const mk = new Dict(null); + if (rotation) { + mk.set("R", rotation); + } + if (this.borderColor) { + mk.set("BC", getPdfColorArray(this.borderColor)); + } + if (this.backgroundColor) { + mk.set("BG", getPdfColorArray(this.backgroundColor)); + } + return mk.size > 0 ? mk : null; + } + amendSavedDict(annotationStorage, dict) {} + setValue(dict, value, xref, changes) { + const { + dict: parentDict, + ref: parentRef + } = getParentToUpdate(dict, this.ref, xref); + if (!parentDict) { + dict.set("V", value); + } else if (!changes.has(parentRef)) { + const newParentDict = parentDict.clone(); + newParentDict.set("V", value); + changes.put(parentRef, { + data: newParentDict + }); + return newParentDict; + } + return null; + } + async save(evaluator, task, annotationStorage, changes) { + const storageEntry = annotationStorage?.get(this.data.id); + const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint); + let value = storageEntry?.value, + rotation = storageEntry?.rotation; + if (value === this.data.fieldValue || value === undefined) { + if (!this._hasValueFromXFA && rotation === undefined && flags === undefined) { + return; + } + value ||= this.data.fieldValue; + } + if (rotation === undefined && !this._hasValueFromXFA && Array.isArray(value) && Array.isArray(this.data.fieldValue) && isArrayEqual(value, this.data.fieldValue) && flags === undefined) { + return; + } + if (rotation === undefined) { + rotation = this.rotation; + } + let appearance = null; + if (!this._needAppearances) { + appearance = await this._getAppearance(evaluator, task, RenderingIntentFlag.SAVE, annotationStorage); + if (appearance === null && flags === undefined) { + return; + } + } else {} + let needAppearances = false; + if (appearance?.needAppearances) { + needAppearances = true; + appearance = null; + } + const { + xref + } = evaluator; + const originalDict = xref.fetchIfRef(this.ref); + if (!(originalDict instanceof Dict)) { + return; + } + const dict = new Dict(xref); + for (const key of originalDict.getKeys()) { + if (key !== "AP") { + dict.set(key, originalDict.getRaw(key)); + } + } + if (flags !== undefined) { + dict.set("F", flags); + if (appearance === null && !needAppearances) { + const ap = originalDict.getRaw("AP"); + if (ap) { + dict.set("AP", ap); + } + } + } + const xfa = { + path: this.data.fieldName, + value + }; + const newParentDict = this.setValue(dict, Array.isArray(value) ? value.map(stringToAsciiOrUTF16BE) : stringToAsciiOrUTF16BE(value), xref, changes); + this.amendSavedDict(annotationStorage, newParentDict || dict); + const maybeMK = this._getMKDict(rotation); + if (maybeMK) { + dict.set("MK", maybeMK); + } + changes.put(this.ref, { + data: dict, + xfa, + needAppearances + }); + if (appearance !== null) { + const newRef = xref.getNewTemporaryRef(); + const AP = new Dict(xref); + dict.set("AP", AP); + AP.set("N", newRef); + const resources = this._getSaveFieldResources(xref); + const appearanceStream = new StringStream(appearance); + const appearanceDict = appearanceStream.dict = new Dict(xref); + appearanceDict.set("Subtype", Name.get("Form")); + appearanceDict.set("Resources", resources); + appearanceDict.set("BBox", [0, 0, this.data.rect[2] - this.data.rect[0], this.data.rect[3] - this.data.rect[1]]); + const rotationMatrix = this.getRotationMatrix(annotationStorage); + if (rotationMatrix !== IDENTITY_MATRIX) { + appearanceDict.set("Matrix", rotationMatrix); + } + changes.put(newRef, { + data: appearanceStream, + xfa: null, + needAppearances: false + }); + } + dict.set("M", `D:${getModificationDate()}`); + } + async _getAppearance(evaluator, task, intent, annotationStorage) { + const isPassword = this.hasFieldFlag(AnnotationFieldFlag.PASSWORD); + if (isPassword) { + return null; + } + const storageEntry = annotationStorage?.get(this.data.id); + let value, rotation; + if (storageEntry) { + value = storageEntry.formattedValue || storageEntry.value; + rotation = storageEntry.rotation; + } + if (rotation === undefined && value === undefined && !this._needAppearances) { + if (!this._hasValueFromXFA || this.appearance) { + return null; + } + } + const colors = this.getBorderAndBackgroundAppearances(annotationStorage); + if (value === undefined) { + value = this.data.fieldValue; + if (!value) { + return `/Tx BMC q ${colors}Q EMC`; + } + } + if (Array.isArray(value) && value.length === 1) { + value = value[0]; + } + assert(typeof value === "string", "Expected `value` to be a string."); + value = value.trimEnd(); + if (this.data.combo) { + const option = this.data.options.find(({ + exportValue + }) => value === exportValue); + value = option?.displayValue || value; + } + if (value === "") { + return `/Tx BMC q ${colors}Q EMC`; + } + if (rotation === undefined) { + rotation = this.rotation; + } + let lineCount = -1; + let lines; + if (this.data.multiLine) { + lines = value.split(/\r\n?|\n/).map(line => line.normalize("NFC")); + lineCount = lines.length; + } else { + lines = [value.replace(/\r\n?|\n/, "").normalize("NFC")]; + } + const defaultPadding = 1; + const defaultHPadding = 2; + let totalHeight = this.data.rect[3] - this.data.rect[1]; + let totalWidth = this.data.rect[2] - this.data.rect[0]; + if (rotation === 90 || rotation === 270) { + [totalWidth, totalHeight] = [totalHeight, totalWidth]; + } + if (!this._defaultAppearance) { + this.data.defaultAppearanceData = parseDefaultAppearance(this._defaultAppearance = "/Helvetica 0 Tf 0 g"); + } + let font = await WidgetAnnotation._getFontData(evaluator, task, this.data.defaultAppearanceData, this._fieldResources.mergedResources); + let defaultAppearance, fontSize, lineHeight; + const encodedLines = []; + let encodingError = false; + for (const line of lines) { + const encodedString = font.encodeString(line); + if (encodedString.length > 1) { + encodingError = true; + } + encodedLines.push(encodedString.join("")); + } + if (encodingError && intent & RenderingIntentFlag.SAVE) { + return { + needAppearances: true + }; + } + if (encodingError && this._isOffscreenCanvasSupported) { + const fontFamily = this.data.comb ? "monospace" : "sans-serif"; + const fakeUnicodeFont = new FakeUnicodeFont(evaluator.xref, fontFamily); + const resources = fakeUnicodeFont.createFontResources(lines.join("")); + const newFont = resources.getRaw("Font"); + if (this._fieldResources.mergedResources.has("Font")) { + const oldFont = this._fieldResources.mergedResources.get("Font"); + for (const key of newFont.getKeys()) { + oldFont.set(key, newFont.getRaw(key)); + } + } else { + this._fieldResources.mergedResources.set("Font", newFont); + } + const fontName = fakeUnicodeFont.fontName.name; + font = await WidgetAnnotation._getFontData(evaluator, task, { + fontName, + fontSize: 0 + }, resources); + for (let i = 0, ii = encodedLines.length; i < ii; i++) { + encodedLines[i] = stringToUTF16String(lines[i]); + } + const savedDefaultAppearance = Object.assign(Object.create(null), this.data.defaultAppearanceData); + this.data.defaultAppearanceData.fontSize = 0; + this.data.defaultAppearanceData.fontName = fontName; + [defaultAppearance, fontSize, lineHeight] = this._computeFontSize(totalHeight - 2 * defaultPadding, totalWidth - 2 * defaultHPadding, value, font, lineCount); + this.data.defaultAppearanceData = savedDefaultAppearance; + } else { + if (!this._isOffscreenCanvasSupported) { + warn("_getAppearance: OffscreenCanvas is not supported, annotation may not render correctly."); + } + [defaultAppearance, fontSize, lineHeight] = this._computeFontSize(totalHeight - 2 * defaultPadding, totalWidth - 2 * defaultHPadding, value, font, lineCount); + } + let descent = font.descent; + if (isNaN(descent)) { + descent = BASELINE_FACTOR * lineHeight; + } else { + descent = Math.max(BASELINE_FACTOR * lineHeight, Math.abs(descent) * fontSize); + } + const defaultVPadding = Math.min(Math.floor((totalHeight - fontSize) / 2), defaultPadding); + const alignment = this.data.textAlignment; + if (this.data.multiLine) { + return this._getMultilineAppearance(defaultAppearance, encodedLines, font, fontSize, totalWidth, totalHeight, alignment, defaultHPadding, defaultVPadding, descent, lineHeight, annotationStorage); + } + if (this.data.comb) { + return this._getCombAppearance(defaultAppearance, font, encodedLines[0], fontSize, totalWidth, totalHeight, defaultHPadding, defaultVPadding, descent, lineHeight, annotationStorage); + } + const bottomPadding = defaultVPadding + descent; + if (alignment === 0 || alignment > 2) { + return `/Tx BMC q ${colors}BT ` + defaultAppearance + ` 1 0 0 1 ${numberToString(defaultHPadding)} ${numberToString(bottomPadding)} Tm (${escapeString(encodedLines[0])}) Tj` + " ET Q EMC"; + } + const prevInfo = { + shift: 0 + }; + const renderedText = this._renderText(encodedLines[0], font, fontSize, totalWidth, alignment, prevInfo, defaultHPadding, bottomPadding); + return `/Tx BMC q ${colors}BT ` + defaultAppearance + ` 1 0 0 1 0 0 Tm ${renderedText}` + " ET Q EMC"; + } + static async _getFontData(evaluator, task, appearanceData, resources) { + const operatorList = new OperatorList(); + const initialState = { + font: null, + clone() { + return this; + } + }; + const { + fontName, + fontSize + } = appearanceData; + await evaluator.handleSetFont(resources, [fontName && Name.get(fontName), fontSize], null, operatorList, task, initialState, null); + return initialState.font; + } + _getTextWidth(text, font) { + return font.charsToGlyphs(text).reduce((width, glyph) => width + glyph.width, 0) / 1000; + } + _computeFontSize(height, width, text, font, lineCount) { + let { + fontSize + } = this.data.defaultAppearanceData; + let lineHeight = (fontSize || 12) * LINE_FACTOR, + numberOfLines = Math.round(height / lineHeight); + if (!fontSize) { + const roundWithTwoDigits = x => Math.floor(x * 100) / 100; + if (lineCount === -1) { + const textWidth = this._getTextWidth(text, font); + fontSize = roundWithTwoDigits(Math.min(height / LINE_FACTOR, width / textWidth)); + numberOfLines = 1; + } else { + const lines = text.split(/\r\n?|\n/); + const cachedLines = []; + for (const line of lines) { + const encoded = font.encodeString(line).join(""); + const glyphs = font.charsToGlyphs(encoded); + const positions = font.getCharPositions(encoded); + cachedLines.push({ + line: encoded, + glyphs, + positions + }); + } + const isTooBig = fsize => { + let totalHeight = 0; + for (const cache of cachedLines) { + const chunks = this._splitLine(null, font, fsize, width, cache); + totalHeight += chunks.length * fsize; + if (totalHeight > height) { + return true; + } + } + return false; + }; + numberOfLines = Math.max(numberOfLines, lineCount); + while (true) { + lineHeight = height / numberOfLines; + fontSize = roundWithTwoDigits(lineHeight / LINE_FACTOR); + if (isTooBig(fontSize)) { + numberOfLines++; + continue; + } + break; + } + } + const { + fontName, + fontColor + } = this.data.defaultAppearanceData; + this._defaultAppearance = createDefaultAppearance({ + fontSize, + fontName, + fontColor + }); + } + return [this._defaultAppearance, fontSize, height / numberOfLines]; + } + _renderText(text, font, fontSize, totalWidth, alignment, prevInfo, hPadding, vPadding) { + let shift; + if (alignment === 1) { + const width = this._getTextWidth(text, font) * fontSize; + shift = (totalWidth - width) / 2; + } else if (alignment === 2) { + const width = this._getTextWidth(text, font) * fontSize; + shift = totalWidth - width - hPadding; + } else { + shift = hPadding; + } + const shiftStr = numberToString(shift - prevInfo.shift); + prevInfo.shift = shift; + vPadding = numberToString(vPadding); + return `${shiftStr} ${vPadding} Td (${escapeString(text)}) Tj`; + } + _getSaveFieldResources(xref) { + const { + localResources, + appearanceResources, + acroFormResources + } = this._fieldResources; + const fontName = this.data.defaultAppearanceData?.fontName; + if (!fontName) { + return localResources || Dict.empty; + } + for (const resources of [localResources, appearanceResources]) { + if (resources instanceof Dict) { + const localFont = resources.get("Font"); + if (localFont instanceof Dict && localFont.has(fontName)) { + return resources; + } + } + } + if (acroFormResources instanceof Dict) { + const acroFormFont = acroFormResources.get("Font"); + if (acroFormFont instanceof Dict && acroFormFont.has(fontName)) { + const subFontDict = new Dict(xref); + subFontDict.set(fontName, acroFormFont.getRaw(fontName)); + const subResourcesDict = new Dict(xref); + subResourcesDict.set("Font", subFontDict); + return Dict.merge({ + xref, + dictArray: [subResourcesDict, localResources], + mergeSubDicts: true + }); + } + } + return localResources || Dict.empty; + } + getFieldObject() { + return null; + } +} +class TextWidgetAnnotation extends WidgetAnnotation { + constructor(params) { + super(params); + const { + dict + } = params; + if (dict.has("PMD")) { + this.flags |= AnnotationFlag.HIDDEN; + this.data.hidden = true; + warn("Barcodes are not supported"); + } + this.data.hasOwnCanvas = this.data.readOnly && !this.data.noHTML; + this._hasText = true; + if (typeof this.data.fieldValue !== "string") { + this.data.fieldValue = ""; + } + let alignment = getInheritableProperty({ + dict, + key: "Q" + }); + if (!Number.isInteger(alignment) || alignment < 0 || alignment > 2) { + alignment = null; + } + this.data.textAlignment = alignment; + let maximumLength = getInheritableProperty({ + dict, + key: "MaxLen" + }); + if (!Number.isInteger(maximumLength) || maximumLength < 0) { + maximumLength = 0; + } + this.data.maxLen = maximumLength; + this.data.multiLine = this.hasFieldFlag(AnnotationFieldFlag.MULTILINE); + this.data.comb = this.hasFieldFlag(AnnotationFieldFlag.COMB) && !this.hasFieldFlag(AnnotationFieldFlag.MULTILINE) && !this.hasFieldFlag(AnnotationFieldFlag.PASSWORD) && !this.hasFieldFlag(AnnotationFieldFlag.FILESELECT) && this.data.maxLen !== 0; + this.data.doNotScroll = this.hasFieldFlag(AnnotationFieldFlag.DONOTSCROLL); + } + get hasTextContent() { + return !!this.appearance && !this._needAppearances; + } + _getCombAppearance(defaultAppearance, font, text, fontSize, width, height, hPadding, vPadding, descent, lineHeight, annotationStorage) { + const combWidth = width / this.data.maxLen; + const colors = this.getBorderAndBackgroundAppearances(annotationStorage); + const buf = []; + const positions = font.getCharPositions(text); + for (const [start, end] of positions) { + buf.push(`(${escapeString(text.substring(start, end))}) Tj`); + } + const renderedComb = buf.join(` ${numberToString(combWidth)} 0 Td `); + return `/Tx BMC q ${colors}BT ` + defaultAppearance + ` 1 0 0 1 ${numberToString(hPadding)} ${numberToString(vPadding + descent)} Tm ${renderedComb}` + " ET Q EMC"; + } + _getMultilineAppearance(defaultAppearance, lines, font, fontSize, width, height, alignment, hPadding, vPadding, descent, lineHeight, annotationStorage) { + const buf = []; + const totalWidth = width - 2 * hPadding; + const prevInfo = { + shift: 0 + }; + for (let i = 0, ii = lines.length; i < ii; i++) { + const line = lines[i]; + const chunks = this._splitLine(line, font, fontSize, totalWidth); + for (let j = 0, jj = chunks.length; j < jj; j++) { + const chunk = chunks[j]; + const vShift = i === 0 && j === 0 ? -vPadding - (lineHeight - descent) : -lineHeight; + buf.push(this._renderText(chunk, font, fontSize, width, alignment, prevInfo, hPadding, vShift)); + } + } + const colors = this.getBorderAndBackgroundAppearances(annotationStorage); + const renderedText = buf.join("\n"); + return `/Tx BMC q ${colors}BT ` + defaultAppearance + ` 1 0 0 1 0 ${numberToString(height)} Tm ${renderedText}` + " ET Q EMC"; + } + _splitLine(line, font, fontSize, width, cache = {}) { + line = cache.line || line; + const glyphs = cache.glyphs || font.charsToGlyphs(line); + if (glyphs.length <= 1) { + return [line]; + } + const positions = cache.positions || font.getCharPositions(line); + const scale = fontSize / 1000; + const chunks = []; + let lastSpacePosInStringStart = -1, + lastSpacePosInStringEnd = -1, + lastSpacePos = -1, + startChunk = 0, + currentWidth = 0; + for (let i = 0, ii = glyphs.length; i < ii; i++) { + const [start, end] = positions[i]; + const glyph = glyphs[i]; + const glyphWidth = glyph.width * scale; + if (glyph.unicode === " ") { + if (currentWidth + glyphWidth > width) { + chunks.push(line.substring(startChunk, start)); + startChunk = start; + currentWidth = glyphWidth; + lastSpacePosInStringStart = -1; + lastSpacePos = -1; + } else { + currentWidth += glyphWidth; + lastSpacePosInStringStart = start; + lastSpacePosInStringEnd = end; + lastSpacePos = i; + } + } else if (currentWidth + glyphWidth > width) { + if (lastSpacePosInStringStart !== -1) { + chunks.push(line.substring(startChunk, lastSpacePosInStringEnd)); + startChunk = lastSpacePosInStringEnd; + i = lastSpacePos + 1; + lastSpacePosInStringStart = -1; + currentWidth = 0; + } else { + chunks.push(line.substring(startChunk, start)); + startChunk = start; + currentWidth = glyphWidth; + } + } else { + currentWidth += glyphWidth; + } + } + if (startChunk < line.length) { + chunks.push(line.substring(startChunk, line.length)); + } + return chunks; + } + async extractTextContent(evaluator, task, viewBox) { + await super.extractTextContent(evaluator, task, viewBox); + const text = this.data.textContent; + if (!text) { + return; + } + const allText = text.join("\n"); + if (allText === this.data.fieldValue) { + return; + } + const regex = allText.replaceAll(/([.*+?^${}()|[\]\\])|(\s+)/g, (_m, p1) => p1 ? `\\${p1}` : "\\s+"); + if (new RegExp(`^\\s*${regex}\\s*$`).test(this.data.fieldValue)) { + this.data.textContent = this.data.fieldValue.split("\n"); + } + } + getFieldObject() { + return { + id: this.data.id, + value: this.data.fieldValue, + defaultValue: this.data.defaultFieldValue || "", + multiline: this.data.multiLine, + password: this.hasFieldFlag(AnnotationFieldFlag.PASSWORD), + charLimit: this.data.maxLen, + comb: this.data.comb, + editable: !this.data.readOnly, + hidden: this.data.hidden, + name: this.data.fieldName, + rect: this.data.rect, + actions: this.data.actions, + page: this.data.pageIndex, + strokeColor: this.data.borderColor, + fillColor: this.data.backgroundColor, + rotation: this.rotation, + type: "text" + }; + } +} +class ButtonWidgetAnnotation extends WidgetAnnotation { + constructor(params) { + super(params); + this.checkedAppearance = null; + this.uncheckedAppearance = null; + this.data.checkBox = !this.hasFieldFlag(AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON); + this.data.radioButton = this.hasFieldFlag(AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON); + this.data.pushButton = this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON); + this.data.isTooltipOnly = false; + if (this.data.checkBox) { + this._processCheckBox(params); + } else if (this.data.radioButton) { + this._processRadioButton(params); + } else if (this.data.pushButton) { + this.data.hasOwnCanvas = true; + this.data.noHTML = false; + this._processPushButton(params); + } else { + warn("Invalid field flags for button widget annotation"); + } + } + async getOperatorList(evaluator, task, intent, annotationStorage) { + if (this.data.pushButton) { + return super.getOperatorList(evaluator, task, intent, false, annotationStorage); + } + let value = null; + let rotation = null; + if (annotationStorage) { + const storageEntry = annotationStorage.get(this.data.id); + value = storageEntry ? storageEntry.value : null; + rotation = storageEntry ? storageEntry.rotation : null; + } + if (value === null && this.appearance) { + return super.getOperatorList(evaluator, task, intent, annotationStorage); + } + if (value === null || value === undefined) { + value = this.data.checkBox ? this.data.fieldValue === this.data.exportValue : this.data.fieldValue === this.data.buttonValue; + } + const appearance = value ? this.checkedAppearance : this.uncheckedAppearance; + if (appearance) { + const savedAppearance = this.appearance; + const savedMatrix = lookupMatrix(appearance.dict.getArray("Matrix"), IDENTITY_MATRIX); + if (rotation) { + appearance.dict.set("Matrix", this.getRotationMatrix(annotationStorage)); + } + this.appearance = appearance; + const operatorList = super.getOperatorList(evaluator, task, intent, annotationStorage); + this.appearance = savedAppearance; + appearance.dict.set("Matrix", savedMatrix); + return operatorList; + } + return { + opList: new OperatorList(), + separateForm: false, + separateCanvas: false + }; + } + async save(evaluator, task, annotationStorage, changes) { + if (this.data.checkBox) { + this._saveCheckbox(evaluator, task, annotationStorage, changes); + return; + } + if (this.data.radioButton) { + this._saveRadioButton(evaluator, task, annotationStorage, changes); + } + } + async _saveCheckbox(evaluator, task, annotationStorage, changes) { + if (!annotationStorage) { + return; + } + const storageEntry = annotationStorage.get(this.data.id); + const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint); + let rotation = storageEntry?.rotation, + value = storageEntry?.value; + if (rotation === undefined && flags === undefined) { + if (value === undefined) { + return; + } + const defaultValue = this.data.fieldValue === this.data.exportValue; + if (defaultValue === value) { + return; + } + } + let dict = evaluator.xref.fetchIfRef(this.ref); + if (!(dict instanceof Dict)) { + return; + } + dict = dict.clone(); + if (rotation === undefined) { + rotation = this.rotation; + } + if (value === undefined) { + value = this.data.fieldValue === this.data.exportValue; + } + const xfa = { + path: this.data.fieldName, + value: value ? this.data.exportValue : "" + }; + const name = Name.get(value ? this.data.exportValue : "Off"); + this.setValue(dict, name, evaluator.xref, changes); + dict.set("AS", name); + dict.set("M", `D:${getModificationDate()}`); + if (flags !== undefined) { + dict.set("F", flags); + } + const maybeMK = this._getMKDict(rotation); + if (maybeMK) { + dict.set("MK", maybeMK); + } + changes.put(this.ref, { + data: dict, + xfa, + needAppearances: false + }); + } + async _saveRadioButton(evaluator, task, annotationStorage, changes) { + if (!annotationStorage) { + return; + } + const storageEntry = annotationStorage.get(this.data.id); + const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint); + let rotation = storageEntry?.rotation, + value = storageEntry?.value; + if (rotation === undefined && flags === undefined) { + if (value === undefined) { + return; + } + const defaultValue = this.data.fieldValue === this.data.buttonValue; + if (defaultValue === value) { + return; + } + } + let dict = evaluator.xref.fetchIfRef(this.ref); + if (!(dict instanceof Dict)) { + return; + } + dict = dict.clone(); + if (value === undefined) { + value = this.data.fieldValue === this.data.buttonValue; + } + if (rotation === undefined) { + rotation = this.rotation; + } + const xfa = { + path: this.data.fieldName, + value: value ? this.data.buttonValue : "" + }; + const name = Name.get(value ? this.data.buttonValue : "Off"); + if (value) { + this.setValue(dict, name, evaluator.xref, changes); + } + dict.set("AS", name); + dict.set("M", `D:${getModificationDate()}`); + if (flags !== undefined) { + dict.set("F", flags); + } + const maybeMK = this._getMKDict(rotation); + if (maybeMK) { + dict.set("MK", maybeMK); + } + changes.put(this.ref, { + data: dict, + xfa, + needAppearances: false + }); + } + _getDefaultCheckedAppearance(params, type) { + const width = this.data.rect[2] - this.data.rect[0]; + const height = this.data.rect[3] - this.data.rect[1]; + const bbox = [0, 0, width, height]; + const FONT_RATIO = 0.8; + const fontSize = Math.min(width, height) * FONT_RATIO; + let metrics, char; + if (type === "check") { + metrics = { + width: 0.755 * fontSize, + height: 0.705 * fontSize + }; + char = "\x33"; + } else if (type === "disc") { + metrics = { + width: 0.791 * fontSize, + height: 0.705 * fontSize + }; + char = "\x6C"; + } else { + unreachable(`_getDefaultCheckedAppearance - unsupported type: ${type}`); + } + const xShift = numberToString((width - metrics.width) / 2); + const yShift = numberToString((height - metrics.height) / 2); + const appearance = `q BT /PdfJsZaDb ${fontSize} Tf 0 g ${xShift} ${yShift} Td (${char}) Tj ET Q`; + const appearanceStreamDict = new Dict(params.xref); + appearanceStreamDict.set("FormType", 1); + appearanceStreamDict.set("Subtype", Name.get("Form")); + appearanceStreamDict.set("Type", Name.get("XObject")); + appearanceStreamDict.set("BBox", bbox); + appearanceStreamDict.set("Matrix", [1, 0, 0, 1, 0, 0]); + appearanceStreamDict.set("Length", appearance.length); + const resources = new Dict(params.xref); + const font = new Dict(params.xref); + font.set("PdfJsZaDb", this.fallbackFontDict); + resources.set("Font", font); + appearanceStreamDict.set("Resources", resources); + this.checkedAppearance = new StringStream(appearance); + this.checkedAppearance.dict = appearanceStreamDict; + this._streams.push(this.checkedAppearance); + } + _processCheckBox(params) { + const customAppearance = params.dict.get("AP"); + if (!(customAppearance instanceof Dict)) { + return; + } + const normalAppearance = customAppearance.get("N"); + if (!(normalAppearance instanceof Dict)) { + return; + } + const asValue = this._decodeFormValue(params.dict.get("AS")); + if (typeof asValue === "string") { + this.data.fieldValue = asValue; + } + const yes = this.data.fieldValue !== null && this.data.fieldValue !== "Off" ? this.data.fieldValue : "Yes"; + const exportValues = normalAppearance.getKeys(); + if (exportValues.length === 0) { + exportValues.push("Off", yes); + } else if (exportValues.length === 1) { + if (exportValues[0] === "Off") { + exportValues.push(yes); + } else { + exportValues.unshift("Off"); + } + } else if (exportValues.includes(yes)) { + exportValues.length = 0; + exportValues.push("Off", yes); + } else { + const otherYes = exportValues.find(v => v !== "Off"); + exportValues.length = 0; + exportValues.push("Off", otherYes); + } + if (!exportValues.includes(this.data.fieldValue)) { + this.data.fieldValue = "Off"; + } + this.data.exportValue = exportValues[1]; + const checkedAppearance = normalAppearance.get(this.data.exportValue); + this.checkedAppearance = checkedAppearance instanceof BaseStream ? checkedAppearance : null; + const uncheckedAppearance = normalAppearance.get("Off"); + this.uncheckedAppearance = uncheckedAppearance instanceof BaseStream ? uncheckedAppearance : null; + if (this.checkedAppearance) { + this._streams.push(this.checkedAppearance); + } else { + this._getDefaultCheckedAppearance(params, "check"); + } + if (this.uncheckedAppearance) { + this._streams.push(this.uncheckedAppearance); + } + this._fallbackFontDict = this.fallbackFontDict; + if (this.data.defaultFieldValue === null) { + this.data.defaultFieldValue = "Off"; + } + } + _processRadioButton(params) { + this.data.buttonValue = null; + const fieldParent = params.dict.get("Parent"); + if (fieldParent instanceof Dict) { + this.parent = params.dict.getRaw("Parent"); + const fieldParentValue = fieldParent.get("V"); + if (fieldParentValue instanceof Name) { + this.data.fieldValue = this._decodeFormValue(fieldParentValue); + } + } + const appearanceStates = params.dict.get("AP"); + if (!(appearanceStates instanceof Dict)) { + return; + } + const normalAppearance = appearanceStates.get("N"); + if (!(normalAppearance instanceof Dict)) { + return; + } + for (const key of normalAppearance.getKeys()) { + if (key !== "Off") { + this.data.buttonValue = this._decodeFormValue(key); + break; + } + } + const checkedAppearance = normalAppearance.get(this.data.buttonValue); + this.checkedAppearance = checkedAppearance instanceof BaseStream ? checkedAppearance : null; + const uncheckedAppearance = normalAppearance.get("Off"); + this.uncheckedAppearance = uncheckedAppearance instanceof BaseStream ? uncheckedAppearance : null; + if (this.checkedAppearance) { + this._streams.push(this.checkedAppearance); + } else { + this._getDefaultCheckedAppearance(params, "disc"); + } + if (this.uncheckedAppearance) { + this._streams.push(this.uncheckedAppearance); + } + this._fallbackFontDict = this.fallbackFontDict; + if (this.data.defaultFieldValue === null) { + this.data.defaultFieldValue = "Off"; + } + } + _processPushButton(params) { + const { + dict, + annotationGlobals + } = params; + if (!dict.has("A") && !dict.has("AA") && !this.data.alternativeText) { + warn("Push buttons without action dictionaries are not supported"); + return; + } + this.data.isTooltipOnly = !dict.has("A") && !dict.has("AA"); + Catalog.parseDestDictionary({ + destDict: dict, + resultObj: this.data, + docBaseUrl: annotationGlobals.baseUrl, + docAttachments: annotationGlobals.attachments + }); + } + getFieldObject() { + let type = "button"; + let exportValues; + if (this.data.checkBox) { + type = "checkbox"; + exportValues = this.data.exportValue; + } else if (this.data.radioButton) { + type = "radiobutton"; + exportValues = this.data.buttonValue; + } + return { + id: this.data.id, + value: this.data.fieldValue || "Off", + defaultValue: this.data.defaultFieldValue, + exportValues, + editable: !this.data.readOnly, + name: this.data.fieldName, + rect: this.data.rect, + hidden: this.data.hidden, + actions: this.data.actions, + page: this.data.pageIndex, + strokeColor: this.data.borderColor, + fillColor: this.data.backgroundColor, + rotation: this.rotation, + type + }; + } + get fallbackFontDict() { + const dict = new Dict(); + dict.set("BaseFont", Name.get("ZapfDingbats")); + dict.set("Type", Name.get("FallbackType")); + dict.set("Subtype", Name.get("FallbackType")); + dict.set("Encoding", Name.get("ZapfDingbatsEncoding")); + return shadow(this, "fallbackFontDict", dict); + } +} +class ChoiceWidgetAnnotation extends WidgetAnnotation { + constructor(params) { + super(params); + const { + dict, + xref + } = params; + this.indices = dict.getArray("I"); + this.hasIndices = Array.isArray(this.indices) && this.indices.length > 0; + this.data.options = []; + const options = getInheritableProperty({ + dict, + key: "Opt" + }); + if (Array.isArray(options)) { + for (let i = 0, ii = options.length; i < ii; i++) { + const option = xref.fetchIfRef(options[i]); + const isOptionArray = Array.isArray(option); + this.data.options[i] = { + exportValue: this._decodeFormValue(isOptionArray ? xref.fetchIfRef(option[0]) : option), + displayValue: this._decodeFormValue(isOptionArray ? xref.fetchIfRef(option[1]) : option) + }; + } + } + if (!this.hasIndices) { + if (typeof this.data.fieldValue === "string") { + this.data.fieldValue = [this.data.fieldValue]; + } else if (!this.data.fieldValue) { + this.data.fieldValue = []; + } + } else { + this.data.fieldValue = []; + const ii = this.data.options.length; + for (const i of this.indices) { + if (Number.isInteger(i) && i >= 0 && i < ii) { + this.data.fieldValue.push(this.data.options[i].exportValue); + } + } + } + if (this.data.options.length === 0 && this.data.fieldValue.length > 0) { + this.data.options = this.data.fieldValue.map(value => ({ + exportValue: value, + displayValue: value + })); + } + this.data.combo = this.hasFieldFlag(AnnotationFieldFlag.COMBO); + this.data.multiSelect = this.hasFieldFlag(AnnotationFieldFlag.MULTISELECT); + this._hasText = true; + } + getFieldObject() { + const type = this.data.combo ? "combobox" : "listbox"; + const value = this.data.fieldValue.length > 0 ? this.data.fieldValue[0] : null; + return { + id: this.data.id, + value, + defaultValue: this.data.defaultFieldValue, + editable: !this.data.readOnly, + name: this.data.fieldName, + rect: this.data.rect, + numItems: this.data.fieldValue.length, + multipleSelection: this.data.multiSelect, + hidden: this.data.hidden, + actions: this.data.actions, + items: this.data.options, + page: this.data.pageIndex, + strokeColor: this.data.borderColor, + fillColor: this.data.backgroundColor, + rotation: this.rotation, + type + }; + } + amendSavedDict(annotationStorage, dict) { + if (!this.hasIndices) { + return; + } + let values = annotationStorage?.get(this.data.id)?.value; + if (!Array.isArray(values)) { + values = [values]; + } + const indices = []; + const { + options + } = this.data; + for (let i = 0, j = 0, ii = options.length; i < ii; i++) { + if (options[i].exportValue === values[j]) { + indices.push(i); + j += 1; + } + } + dict.set("I", indices); + } + async _getAppearance(evaluator, task, intent, annotationStorage) { + if (this.data.combo) { + return super._getAppearance(evaluator, task, intent, annotationStorage); + } + let exportedValue, rotation; + const storageEntry = annotationStorage?.get(this.data.id); + if (storageEntry) { + rotation = storageEntry.rotation; + exportedValue = storageEntry.value; + } + if (rotation === undefined && exportedValue === undefined && !this._needAppearances) { + return null; + } + if (exportedValue === undefined) { + exportedValue = this.data.fieldValue; + } else if (!Array.isArray(exportedValue)) { + exportedValue = [exportedValue]; + } + const defaultPadding = 1; + const defaultHPadding = 2; + let totalHeight = this.data.rect[3] - this.data.rect[1]; + let totalWidth = this.data.rect[2] - this.data.rect[0]; + if (rotation === 90 || rotation === 270) { + [totalWidth, totalHeight] = [totalHeight, totalWidth]; + } + const lineCount = this.data.options.length; + const valueIndices = []; + for (let i = 0; i < lineCount; i++) { + const { + exportValue + } = this.data.options[i]; + if (exportedValue.includes(exportValue)) { + valueIndices.push(i); + } + } + if (!this._defaultAppearance) { + this.data.defaultAppearanceData = parseDefaultAppearance(this._defaultAppearance = "/Helvetica 0 Tf 0 g"); + } + const font = await WidgetAnnotation._getFontData(evaluator, task, this.data.defaultAppearanceData, this._fieldResources.mergedResources); + let defaultAppearance; + let { + fontSize + } = this.data.defaultAppearanceData; + if (!fontSize) { + const lineHeight = (totalHeight - defaultPadding) / lineCount; + let lineWidth = -1; + let value; + for (const { + displayValue + } of this.data.options) { + const width = this._getTextWidth(displayValue, font); + if (width > lineWidth) { + lineWidth = width; + value = displayValue; + } + } + [defaultAppearance, fontSize] = this._computeFontSize(lineHeight, totalWidth - 2 * defaultHPadding, value, font, -1); + } else { + defaultAppearance = this._defaultAppearance; + } + const lineHeight = fontSize * LINE_FACTOR; + const vPadding = (lineHeight - fontSize) / 2; + const numberOfVisibleLines = Math.floor(totalHeight / lineHeight); + let firstIndex = 0; + if (valueIndices.length > 0) { + const minIndex = Math.min(...valueIndices); + const maxIndex = Math.max(...valueIndices); + firstIndex = Math.max(0, maxIndex - numberOfVisibleLines + 1); + if (firstIndex > minIndex) { + firstIndex = minIndex; + } + } + const end = Math.min(firstIndex + numberOfVisibleLines + 1, lineCount); + const buf = ["/Tx BMC q", `1 1 ${totalWidth} ${totalHeight} re W n`]; + if (valueIndices.length) { + buf.push("0.600006 0.756866 0.854904 rg"); + for (const index of valueIndices) { + if (firstIndex <= index && index < end) { + buf.push(`1 ${totalHeight - (index - firstIndex + 1) * lineHeight} ${totalWidth} ${lineHeight} re f`); + } + } + } + buf.push("BT", defaultAppearance, `1 0 0 1 0 ${totalHeight} Tm`); + const prevInfo = { + shift: 0 + }; + for (let i = firstIndex; i < end; i++) { + const { + displayValue + } = this.data.options[i]; + const vpadding = i === firstIndex ? vPadding : 0; + buf.push(this._renderText(displayValue, font, fontSize, totalWidth, 0, prevInfo, defaultHPadding, -lineHeight + vpadding)); + } + buf.push("ET Q EMC"); + return buf.join("\n"); + } +} +class SignatureWidgetAnnotation extends WidgetAnnotation { + constructor(params) { + super(params); + this.data.fieldValue = null; + this.data.hasOwnCanvas = this.data.noRotate; + this.data.noHTML = !this.data.hasOwnCanvas; + } + getFieldObject() { + return { + id: this.data.id, + value: null, + page: this.data.pageIndex, + type: "signature" + }; + } +} +class TextAnnotation extends MarkupAnnotation { + constructor(params) { + const DEFAULT_ICON_SIZE = 22; + super(params); + this.data.noRotate = true; + this.data.hasOwnCanvas = this.data.noRotate; + this.data.noHTML = false; + const { + dict + } = params; + this.data.annotationType = AnnotationType.TEXT; + if (this.data.hasAppearance) { + this.data.name = "NoIcon"; + } else { + this.data.rect[1] = this.data.rect[3] - DEFAULT_ICON_SIZE; + this.data.rect[2] = this.data.rect[0] + DEFAULT_ICON_SIZE; + this.data.name = dict.has("Name") ? dict.get("Name").name : "Note"; + } + if (dict.has("State")) { + this.data.state = dict.get("State") || null; + this.data.stateModel = dict.get("StateModel") || null; + } else { + this.data.state = null; + this.data.stateModel = null; + } + } +} +class LinkAnnotation extends Annotation { + constructor(params) { + super(params); + const { + dict, + annotationGlobals + } = params; + this.data.annotationType = AnnotationType.LINK; + this.data.noHTML = false; + const quadPoints = getQuadPoints(dict, this.rectangle); + if (quadPoints) { + this.data.quadPoints = quadPoints; + } + this.data.borderColor ||= this.data.color; + Catalog.parseDestDictionary({ + destDict: dict, + resultObj: this.data, + docBaseUrl: annotationGlobals.baseUrl, + docAttachments: annotationGlobals.attachments + }); + } +} +class PopupAnnotation extends Annotation { + constructor(params) { + super(params); + const { + dict + } = params; + this.data.annotationType = AnnotationType.POPUP; + this.data.noHTML = false; + if (this.data.rect[0] === this.data.rect[2] || this.data.rect[1] === this.data.rect[3]) { + this.data.rect = null; + } + let parentItem = dict.get("Parent"); + if (!parentItem) { + warn("Popup annotation has a missing or invalid parent annotation."); + return; + } + this.data.parentRect = lookupNormalRect(parentItem.getArray("Rect"), null); + const rt = parentItem.get("RT"); + if (isName(rt, AnnotationReplyType.GROUP)) { + parentItem = parentItem.get("IRT"); + } + if (!parentItem.has("M")) { + this.data.modificationDate = null; + } else { + this.setModificationDate(parentItem.get("M")); + this.data.modificationDate = this.modificationDate; + } + if (!parentItem.has("C")) { + this.data.color = null; + } else { + this.setColor(parentItem.getArray("C")); + this.data.color = this.color; + } + if (!this.viewable) { + const parentFlags = parentItem.get("F"); + if (this._isViewable(parentFlags)) { + this.setFlags(parentFlags); + } + } + this.setTitle(parentItem.get("T")); + this.data.titleObj = this._title; + this.setContents(parentItem.get("Contents")); + this.data.contentsObj = this._contents; + if (parentItem.has("RC")) { + this.data.richText = XFAFactory.getRichTextAsHtml(parentItem.get("RC")); + } + this.data.open = !!dict.get("Open"); + } +} +class FreeTextAnnotation extends MarkupAnnotation { + constructor(params) { + super(params); + this.data.hasOwnCanvas = this.data.noRotate; + this.data.isEditable = !this.data.noHTML; + this.data.noHTML = false; + const { + evaluatorOptions, + xref + } = params; + this.data.annotationType = AnnotationType.FREETEXT; + this.setDefaultAppearance(params); + this._hasAppearance = !!this.appearance; + if (this._hasAppearance) { + const { + fontColor, + fontSize + } = parseAppearanceStream(this.appearance, evaluatorOptions, xref); + this.data.defaultAppearanceData.fontColor = fontColor; + this.data.defaultAppearanceData.fontSize = fontSize || 10; + } else { + this.data.defaultAppearanceData.fontSize ||= 10; + const { + fontColor, + fontSize + } = this.data.defaultAppearanceData; + if (this._contents.str) { + this.data.textContent = this._contents.str.split(/\r\n?|\n/).map(line => line.trimEnd()); + const { + coords, + bbox, + matrix + } = FakeUnicodeFont.getFirstPositionInfo(this.rectangle, this.rotation, fontSize); + this.data.textPosition = this._transformPoint(coords, bbox, matrix); + } + if (this._isOffscreenCanvasSupported) { + const strokeAlpha = params.dict.get("CA"); + const fakeUnicodeFont = new FakeUnicodeFont(xref, "sans-serif"); + this.appearance = fakeUnicodeFont.createAppearance(this._contents.str, this.rectangle, this.rotation, fontSize, fontColor, strokeAlpha); + this._streams.push(this.appearance); + } else { + warn("FreeTextAnnotation: OffscreenCanvas is not supported, annotation may not render correctly."); + } + } + } + get hasTextContent() { + return this._hasAppearance; + } + static createNewDict(annotation, xref, { + apRef, + ap + }) { + const { + color, + fontSize, + oldAnnotation, + rect, + rotation, + user, + value + } = annotation; + const freetext = oldAnnotation || new Dict(xref); + freetext.set("Type", Name.get("Annot")); + freetext.set("Subtype", Name.get("FreeText")); + if (oldAnnotation) { + freetext.set("M", `D:${getModificationDate()}`); + freetext.delete("RC"); + } else { + freetext.set("CreationDate", `D:${getModificationDate()}`); + } + freetext.set("Rect", rect); + const da = `/Helv ${fontSize} Tf ${getPdfColor(color, true)}`; + freetext.set("DA", da); + freetext.set("Contents", stringToAsciiOrUTF16BE(value)); + freetext.set("F", 4); + freetext.set("Border", [0, 0, 0]); + freetext.set("Rotate", rotation); + if (user) { + freetext.set("T", stringToAsciiOrUTF16BE(user)); + } + if (apRef || ap) { + const n = new Dict(xref); + freetext.set("AP", n); + if (apRef) { + n.set("N", apRef); + } else { + n.set("N", ap); + } + } + return freetext; + } + static async createNewAppearanceStream(annotation, xref, params) { + const { + baseFontRef, + evaluator, + task + } = params; + const { + color, + fontSize, + rect, + rotation, + value + } = annotation; + const resources = new Dict(xref); + const font = new Dict(xref); + if (baseFontRef) { + font.set("Helv", baseFontRef); + } else { + const baseFont = new Dict(xref); + baseFont.set("BaseFont", Name.get("Helvetica")); + baseFont.set("Type", Name.get("Font")); + baseFont.set("Subtype", Name.get("Type1")); + baseFont.set("Encoding", Name.get("WinAnsiEncoding")); + font.set("Helv", baseFont); + } + resources.set("Font", font); + const helv = await WidgetAnnotation._getFontData(evaluator, task, { + fontName: "Helv", + fontSize + }, resources); + const [x1, y1, x2, y2] = rect; + let w = x2 - x1; + let h = y2 - y1; + if (rotation % 180 !== 0) { + [w, h] = [h, w]; + } + const lines = value.split("\n"); + const scale = fontSize / 1000; + let totalWidth = -Infinity; + const encodedLines = []; + for (let line of lines) { + const encoded = helv.encodeString(line); + if (encoded.length > 1) { + return null; + } + line = encoded.join(""); + encodedLines.push(line); + let lineWidth = 0; + const glyphs = helv.charsToGlyphs(line); + for (const glyph of glyphs) { + lineWidth += glyph.width * scale; + } + totalWidth = Math.max(totalWidth, lineWidth); + } + let hscale = 1; + if (totalWidth > w) { + hscale = w / totalWidth; + } + let vscale = 1; + const lineHeight = LINE_FACTOR * fontSize; + const lineAscent = (LINE_FACTOR - LINE_DESCENT_FACTOR) * fontSize; + const totalHeight = lineHeight * lines.length; + if (totalHeight > h) { + vscale = h / totalHeight; + } + const fscale = Math.min(hscale, vscale); + const newFontSize = fontSize * fscale; + let firstPoint, clipBox, matrix; + switch (rotation) { + case 0: + matrix = [1, 0, 0, 1]; + clipBox = [rect[0], rect[1], w, h]; + firstPoint = [rect[0], rect[3] - lineAscent]; + break; + case 90: + matrix = [0, 1, -1, 0]; + clipBox = [rect[1], -rect[2], w, h]; + firstPoint = [rect[1], -rect[0] - lineAscent]; + break; + case 180: + matrix = [-1, 0, 0, -1]; + clipBox = [-rect[2], -rect[3], w, h]; + firstPoint = [-rect[2], -rect[1] - lineAscent]; + break; + case 270: + matrix = [0, -1, 1, 0]; + clipBox = [-rect[3], rect[0], w, h]; + firstPoint = [-rect[3], rect[2] - lineAscent]; + break; + } + const buffer = ["q", `${matrix.join(" ")} 0 0 cm`, `${clipBox.join(" ")} re W n`, `BT`, `${getPdfColor(color, true)}`, `0 Tc /Helv ${numberToString(newFontSize)} Tf`]; + buffer.push(`${firstPoint.join(" ")} Td (${escapeString(encodedLines[0])}) Tj`); + const vShift = numberToString(lineHeight); + for (let i = 1, ii = encodedLines.length; i < ii; i++) { + const line = encodedLines[i]; + buffer.push(`0 -${vShift} Td (${escapeString(line)}) Tj`); + } + buffer.push("ET", "Q"); + const appearance = buffer.join("\n"); + const appearanceStreamDict = new Dict(xref); + appearanceStreamDict.set("FormType", 1); + appearanceStreamDict.set("Subtype", Name.get("Form")); + appearanceStreamDict.set("Type", Name.get("XObject")); + appearanceStreamDict.set("BBox", rect); + appearanceStreamDict.set("Resources", resources); + appearanceStreamDict.set("Matrix", [1, 0, 0, 1, -rect[0], -rect[1]]); + const ap = new StringStream(appearance); + ap.dict = appearanceStreamDict; + return ap; + } +} +class LineAnnotation extends MarkupAnnotation { + constructor(params) { + super(params); + const { + dict, + xref + } = params; + this.data.annotationType = AnnotationType.LINE; + this.data.hasOwnCanvas = this.data.noRotate; + this.data.noHTML = false; + const lineCoordinates = lookupRect(dict.getArray("L"), [0, 0, 0, 0]); + this.data.lineCoordinates = Util.normalizeRect(lineCoordinates); + this.setLineEndings(dict.getArray("LE")); + this.data.lineEndings = this.lineEndings; + if (!this.appearance) { + const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0]; + const strokeAlpha = dict.get("CA"); + const interiorColor = getRgbColor(dict.getArray("IC"), null); + const fillColor = interiorColor ? getPdfColorArray(interiorColor) : null; + const fillAlpha = fillColor ? strokeAlpha : null; + const borderWidth = this.borderStyle.width || 1, + borderAdjust = 2 * borderWidth; + const bbox = [this.data.lineCoordinates[0] - borderAdjust, this.data.lineCoordinates[1] - borderAdjust, this.data.lineCoordinates[2] + borderAdjust, this.data.lineCoordinates[3] + borderAdjust]; + if (!Util.intersect(this.rectangle, bbox)) { + this.rectangle = bbox; + } + this._setDefaultAppearance({ + xref, + extra: `${borderWidth} w`, + strokeColor, + fillColor, + strokeAlpha, + fillAlpha, + pointsCallback: (buffer, points) => { + buffer.push(`${lineCoordinates[0]} ${lineCoordinates[1]} m`, `${lineCoordinates[2]} ${lineCoordinates[3]} l`, "S"); + return [points[0] - borderWidth, points[2] + borderWidth, points[7] - borderWidth, points[3] + borderWidth]; + } + }); + } + } +} +class SquareAnnotation extends MarkupAnnotation { + constructor(params) { + super(params); + const { + dict, + xref + } = params; + this.data.annotationType = AnnotationType.SQUARE; + this.data.hasOwnCanvas = this.data.noRotate; + this.data.noHTML = false; + if (!this.appearance) { + const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0]; + const strokeAlpha = dict.get("CA"); + const interiorColor = getRgbColor(dict.getArray("IC"), null); + const fillColor = interiorColor ? getPdfColorArray(interiorColor) : null; + const fillAlpha = fillColor ? strokeAlpha : null; + if (this.borderStyle.width === 0 && !fillColor) { + return; + } + this._setDefaultAppearance({ + xref, + extra: `${this.borderStyle.width} w`, + strokeColor, + fillColor, + strokeAlpha, + fillAlpha, + pointsCallback: (buffer, points) => { + const x = points[4] + this.borderStyle.width / 2; + const y = points[5] + this.borderStyle.width / 2; + const width = points[6] - points[4] - this.borderStyle.width; + const height = points[3] - points[7] - this.borderStyle.width; + buffer.push(`${x} ${y} ${width} ${height} re`); + if (fillColor) { + buffer.push("B"); + } else { + buffer.push("S"); + } + return [points[0], points[2], points[7], points[3]]; + } + }); + } + } +} +class CircleAnnotation extends MarkupAnnotation { + constructor(params) { + super(params); + const { + dict, + xref + } = params; + this.data.annotationType = AnnotationType.CIRCLE; + if (!this.appearance) { + const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0]; + const strokeAlpha = dict.get("CA"); + const interiorColor = getRgbColor(dict.getArray("IC"), null); + const fillColor = interiorColor ? getPdfColorArray(interiorColor) : null; + const fillAlpha = fillColor ? strokeAlpha : null; + if (this.borderStyle.width === 0 && !fillColor) { + return; + } + const controlPointsDistance = 4 / 3 * Math.tan(Math.PI / (2 * 4)); + this._setDefaultAppearance({ + xref, + extra: `${this.borderStyle.width} w`, + strokeColor, + fillColor, + strokeAlpha, + fillAlpha, + pointsCallback: (buffer, points) => { + const x0 = points[0] + this.borderStyle.width / 2; + const y0 = points[1] - this.borderStyle.width / 2; + const x1 = points[6] - this.borderStyle.width / 2; + const y1 = points[7] + this.borderStyle.width / 2; + const xMid = x0 + (x1 - x0) / 2; + const yMid = y0 + (y1 - y0) / 2; + const xOffset = (x1 - x0) / 2 * controlPointsDistance; + const yOffset = (y1 - y0) / 2 * controlPointsDistance; + buffer.push(`${xMid} ${y1} m`, `${xMid + xOffset} ${y1} ${x1} ${yMid + yOffset} ${x1} ${yMid} c`, `${x1} ${yMid - yOffset} ${xMid + xOffset} ${y0} ${xMid} ${y0} c`, `${xMid - xOffset} ${y0} ${x0} ${yMid - yOffset} ${x0} ${yMid} c`, `${x0} ${yMid + yOffset} ${xMid - xOffset} ${y1} ${xMid} ${y1} c`, "h"); + if (fillColor) { + buffer.push("B"); + } else { + buffer.push("S"); + } + return [points[0], points[2], points[7], points[3]]; + } + }); + } + } +} +class PolylineAnnotation extends MarkupAnnotation { + constructor(params) { + super(params); + const { + dict, + xref + } = params; + this.data.annotationType = AnnotationType.POLYLINE; + this.data.hasOwnCanvas = this.data.noRotate; + this.data.noHTML = false; + this.data.vertices = null; + if (!(this instanceof PolygonAnnotation)) { + this.setLineEndings(dict.getArray("LE")); + this.data.lineEndings = this.lineEndings; + } + const rawVertices = dict.getArray("Vertices"); + if (!isNumberArray(rawVertices, null)) { + return; + } + const vertices = this.data.vertices = Float32Array.from(rawVertices); + if (!this.appearance) { + const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0]; + const strokeAlpha = dict.get("CA"); + const borderWidth = this.borderStyle.width || 1, + borderAdjust = 2 * borderWidth; + const bbox = [Infinity, Infinity, -Infinity, -Infinity]; + for (let i = 0, ii = vertices.length; i < ii; i += 2) { + bbox[0] = Math.min(bbox[0], vertices[i] - borderAdjust); + bbox[1] = Math.min(bbox[1], vertices[i + 1] - borderAdjust); + bbox[2] = Math.max(bbox[2], vertices[i] + borderAdjust); + bbox[3] = Math.max(bbox[3], vertices[i + 1] + borderAdjust); + } + if (!Util.intersect(this.rectangle, bbox)) { + this.rectangle = bbox; + } + this._setDefaultAppearance({ + xref, + extra: `${borderWidth} w`, + strokeColor, + strokeAlpha, + pointsCallback: (buffer, points) => { + for (let i = 0, ii = vertices.length; i < ii; i += 2) { + buffer.push(`${vertices[i]} ${vertices[i + 1]} ${i === 0 ? "m" : "l"}`); + } + buffer.push("S"); + return [points[0], points[2], points[7], points[3]]; + } + }); + } + } +} +class PolygonAnnotation extends PolylineAnnotation { + constructor(params) { + super(params); + this.data.annotationType = AnnotationType.POLYGON; + } +} +class CaretAnnotation extends MarkupAnnotation { + constructor(params) { + super(params); + this.data.annotationType = AnnotationType.CARET; + } +} +class InkAnnotation extends MarkupAnnotation { + constructor(params) { + super(params); + this.data.hasOwnCanvas = this.data.noRotate; + this.data.noHTML = false; + const { + dict, + xref + } = params; + this.data.annotationType = AnnotationType.INK; + this.data.inkLists = []; + this.data.isEditable = !this.data.noHTML; + this.data.noHTML = false; + this.data.opacity = dict.get("CA") || 1; + const rawInkLists = dict.getArray("InkList"); + if (!Array.isArray(rawInkLists)) { + return; + } + for (let i = 0, ii = rawInkLists.length; i < ii; ++i) { + if (!Array.isArray(rawInkLists[i])) { + continue; + } + const inkList = new Float32Array(rawInkLists[i].length); + this.data.inkLists.push(inkList); + for (let j = 0, jj = rawInkLists[i].length; j < jj; j += 2) { + const x = xref.fetchIfRef(rawInkLists[i][j]), + y = xref.fetchIfRef(rawInkLists[i][j + 1]); + if (typeof x === "number" && typeof y === "number") { + inkList[j] = x; + inkList[j + 1] = y; + } + } + } + if (!this.appearance) { + const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0]; + const strokeAlpha = dict.get("CA"); + const borderWidth = this.borderStyle.width || 1, + borderAdjust = 2 * borderWidth; + const bbox = [Infinity, Infinity, -Infinity, -Infinity]; + for (const inkList of this.data.inkLists) { + for (let i = 0, ii = inkList.length; i < ii; i += 2) { + bbox[0] = Math.min(bbox[0], inkList[i] - borderAdjust); + bbox[1] = Math.min(bbox[1], inkList[i + 1] - borderAdjust); + bbox[2] = Math.max(bbox[2], inkList[i] + borderAdjust); + bbox[3] = Math.max(bbox[3], inkList[i + 1] + borderAdjust); + } + } + if (!Util.intersect(this.rectangle, bbox)) { + this.rectangle = bbox; + } + this._setDefaultAppearance({ + xref, + extra: `${borderWidth} w`, + strokeColor, + strokeAlpha, + pointsCallback: (buffer, points) => { + for (const inkList of this.data.inkLists) { + for (let i = 0, ii = inkList.length; i < ii; i += 2) { + buffer.push(`${inkList[i]} ${inkList[i + 1]} ${i === 0 ? "m" : "l"}`); + } + buffer.push("S"); + } + return [points[0], points[2], points[7], points[3]]; + } + }); + } + } + static createNewDict(annotation, xref, { + apRef, + ap + }) { + const { + oldAnnotation, + color, + opacity, + paths, + outlines, + rect, + rotation, + thickness, + user + } = annotation; + const ink = oldAnnotation || new Dict(xref); + ink.set("Type", Name.get("Annot")); + ink.set("Subtype", Name.get("Ink")); + ink.set(oldAnnotation ? "M" : "CreationDate", `D:${getModificationDate()}`); + ink.set("Rect", rect); + ink.set("InkList", outlines?.points || paths.points); + ink.set("F", 4); + ink.set("Rotate", rotation); + if (user) { + ink.set("T", stringToAsciiOrUTF16BE(user)); + } + if (outlines) { + ink.set("IT", Name.get("InkHighlight")); + } + const bs = new Dict(xref); + ink.set("BS", bs); + bs.set("W", thickness); + ink.set("C", Array.from(color, c => c / 255)); + ink.set("CA", opacity); + const n = new Dict(xref); + ink.set("AP", n); + if (apRef) { + n.set("N", apRef); + } else { + n.set("N", ap); + } + return ink; + } + static async createNewAppearanceStream(annotation, xref, params) { + if (annotation.outlines) { + return this.createNewAppearanceStreamForHighlight(annotation, xref, params); + } + const { + color, + rect, + paths, + thickness, + opacity + } = annotation; + const appearanceBuffer = [`${thickness} w 1 J 1 j`, `${getPdfColor(color, false)}`]; + if (opacity !== 1) { + appearanceBuffer.push("/R0 gs"); + } + for (const outline of paths.lines) { + appearanceBuffer.push(`${numberToString(outline[4])} ${numberToString(outline[5])} m`); + for (let i = 6, ii = outline.length; i < ii; i += 6) { + if (isNaN(outline[i])) { + appearanceBuffer.push(`${numberToString(outline[i + 4])} ${numberToString(outline[i + 5])} l`); + } else { + const [c1x, c1y, c2x, c2y, x, y] = outline.slice(i, i + 6); + appearanceBuffer.push([c1x, c1y, c2x, c2y, x, y].map(numberToString).join(" ") + " c"); + } + } + if (outline.length === 6) { + appearanceBuffer.push(`${numberToString(outline[4])} ${numberToString(outline[5])} l`); + } + } + appearanceBuffer.push("S"); + const appearance = appearanceBuffer.join("\n"); + const appearanceStreamDict = new Dict(xref); + appearanceStreamDict.set("FormType", 1); + appearanceStreamDict.set("Subtype", Name.get("Form")); + appearanceStreamDict.set("Type", Name.get("XObject")); + appearanceStreamDict.set("BBox", rect); + appearanceStreamDict.set("Length", appearance.length); + if (opacity !== 1) { + const resources = new Dict(xref); + const extGState = new Dict(xref); + const r0 = new Dict(xref); + r0.set("CA", opacity); + r0.set("Type", Name.get("ExtGState")); + extGState.set("R0", r0); + resources.set("ExtGState", extGState); + appearanceStreamDict.set("Resources", resources); + } + const ap = new StringStream(appearance); + ap.dict = appearanceStreamDict; + return ap; + } + static async createNewAppearanceStreamForHighlight(annotation, xref, params) { + const { + color, + rect, + outlines: { + outline + }, + opacity + } = annotation; + const appearanceBuffer = [`${getPdfColor(color, true)}`, "/R0 gs"]; + appearanceBuffer.push(`${numberToString(outline[4])} ${numberToString(outline[5])} m`); + for (let i = 6, ii = outline.length; i < ii; i += 6) { + if (isNaN(outline[i])) { + appearanceBuffer.push(`${numberToString(outline[i + 4])} ${numberToString(outline[i + 5])} l`); + } else { + const [c1x, c1y, c2x, c2y, x, y] = outline.slice(i, i + 6); + appearanceBuffer.push([c1x, c1y, c2x, c2y, x, y].map(numberToString).join(" ") + " c"); + } + } + appearanceBuffer.push("h f"); + const appearance = appearanceBuffer.join("\n"); + const appearanceStreamDict = new Dict(xref); + appearanceStreamDict.set("FormType", 1); + appearanceStreamDict.set("Subtype", Name.get("Form")); + appearanceStreamDict.set("Type", Name.get("XObject")); + appearanceStreamDict.set("BBox", rect); + appearanceStreamDict.set("Length", appearance.length); + const resources = new Dict(xref); + const extGState = new Dict(xref); + resources.set("ExtGState", extGState); + appearanceStreamDict.set("Resources", resources); + const r0 = new Dict(xref); + extGState.set("R0", r0); + r0.set("BM", Name.get("Multiply")); + if (opacity !== 1) { + r0.set("ca", opacity); + r0.set("Type", Name.get("ExtGState")); + } + const ap = new StringStream(appearance); + ap.dict = appearanceStreamDict; + return ap; + } +} +class HighlightAnnotation extends MarkupAnnotation { + constructor(params) { + super(params); + const { + dict, + xref + } = params; + this.data.annotationType = AnnotationType.HIGHLIGHT; + this.data.isEditable = !this.data.noHTML; + this.data.noHTML = false; + this.data.opacity = dict.get("CA") || 1; + const quadPoints = this.data.quadPoints = getQuadPoints(dict, null); + if (quadPoints) { + const resources = this.appearance?.dict.get("Resources"); + if (!this.appearance || !resources?.has("ExtGState")) { + if (this.appearance) { + warn("HighlightAnnotation - ignoring built-in appearance stream."); + } + const fillColor = this.color ? getPdfColorArray(this.color) : [1, 1, 0]; + const fillAlpha = dict.get("CA"); + this._setDefaultAppearance({ + xref, + fillColor, + blendMode: "Multiply", + fillAlpha, + pointsCallback: (buffer, points) => { + buffer.push(`${points[0]} ${points[1]} m`, `${points[2]} ${points[3]} l`, `${points[6]} ${points[7]} l`, `${points[4]} ${points[5]} l`, "f"); + return [points[0], points[2], points[7], points[3]]; + } + }); + } + } else { + this.data.popupRef = null; + } + } + static createNewDict(annotation, xref, { + apRef, + ap + }) { + const { + color, + oldAnnotation, + opacity, + rect, + rotation, + user, + quadPoints + } = annotation; + const highlight = oldAnnotation || new Dict(xref); + highlight.set("Type", Name.get("Annot")); + highlight.set("Subtype", Name.get("Highlight")); + highlight.set(oldAnnotation ? "M" : "CreationDate", `D:${getModificationDate()}`); + highlight.set("CreationDate", `D:${getModificationDate()}`); + highlight.set("Rect", rect); + highlight.set("F", 4); + highlight.set("Border", [0, 0, 0]); + highlight.set("Rotate", rotation); + highlight.set("QuadPoints", quadPoints); + highlight.set("C", Array.from(color, c => c / 255)); + highlight.set("CA", opacity); + if (user) { + highlight.set("T", stringToAsciiOrUTF16BE(user)); + } + if (apRef || ap) { + const n = new Dict(xref); + highlight.set("AP", n); + n.set("N", apRef || ap); + } + return highlight; + } + static async createNewAppearanceStream(annotation, xref, params) { + const { + color, + rect, + outlines, + opacity + } = annotation; + const appearanceBuffer = [`${getPdfColor(color, true)}`, "/R0 gs"]; + const buffer = []; + for (const outline of outlines) { + buffer.length = 0; + buffer.push(`${numberToString(outline[0])} ${numberToString(outline[1])} m`); + for (let i = 2, ii = outline.length; i < ii; i += 2) { + buffer.push(`${numberToString(outline[i])} ${numberToString(outline[i + 1])} l`); + } + buffer.push("h"); + appearanceBuffer.push(buffer.join("\n")); + } + appearanceBuffer.push("f*"); + const appearance = appearanceBuffer.join("\n"); + const appearanceStreamDict = new Dict(xref); + appearanceStreamDict.set("FormType", 1); + appearanceStreamDict.set("Subtype", Name.get("Form")); + appearanceStreamDict.set("Type", Name.get("XObject")); + appearanceStreamDict.set("BBox", rect); + appearanceStreamDict.set("Length", appearance.length); + const resources = new Dict(xref); + const extGState = new Dict(xref); + resources.set("ExtGState", extGState); + appearanceStreamDict.set("Resources", resources); + const r0 = new Dict(xref); + extGState.set("R0", r0); + r0.set("BM", Name.get("Multiply")); + if (opacity !== 1) { + r0.set("ca", opacity); + r0.set("Type", Name.get("ExtGState")); + } + const ap = new StringStream(appearance); + ap.dict = appearanceStreamDict; + return ap; + } +} +class UnderlineAnnotation extends MarkupAnnotation { + constructor(params) { + super(params); + const { + dict, + xref + } = params; + this.data.annotationType = AnnotationType.UNDERLINE; + const quadPoints = this.data.quadPoints = getQuadPoints(dict, null); + if (quadPoints) { + if (!this.appearance) { + const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0]; + const strokeAlpha = dict.get("CA"); + this._setDefaultAppearance({ + xref, + extra: "[] 0 d 0.571 w", + strokeColor, + strokeAlpha, + pointsCallback: (buffer, points) => { + buffer.push(`${points[4]} ${points[5] + 1.3} m`, `${points[6]} ${points[7] + 1.3} l`, "S"); + return [points[0], points[2], points[7], points[3]]; + } + }); + } + } else { + this.data.popupRef = null; + } + } +} +class SquigglyAnnotation extends MarkupAnnotation { + constructor(params) { + super(params); + const { + dict, + xref + } = params; + this.data.annotationType = AnnotationType.SQUIGGLY; + const quadPoints = this.data.quadPoints = getQuadPoints(dict, null); + if (quadPoints) { + if (!this.appearance) { + const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0]; + const strokeAlpha = dict.get("CA"); + this._setDefaultAppearance({ + xref, + extra: "[] 0 d 1 w", + strokeColor, + strokeAlpha, + pointsCallback: (buffer, points) => { + const dy = (points[1] - points[5]) / 6; + let shift = dy; + let x = points[4]; + const y = points[5]; + const xEnd = points[6]; + buffer.push(`${x} ${y + shift} m`); + do { + x += 2; + shift = shift === 0 ? dy : 0; + buffer.push(`${x} ${y + shift} l`); + } while (x < xEnd); + buffer.push("S"); + return [points[4], xEnd, y - 2 * dy, y + 2 * dy]; + } + }); + } + } else { + this.data.popupRef = null; + } + } +} +class StrikeOutAnnotation extends MarkupAnnotation { + constructor(params) { + super(params); + const { + dict, + xref + } = params; + this.data.annotationType = AnnotationType.STRIKEOUT; + const quadPoints = this.data.quadPoints = getQuadPoints(dict, null); + if (quadPoints) { + if (!this.appearance) { + const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0]; + const strokeAlpha = dict.get("CA"); + this._setDefaultAppearance({ + xref, + extra: "[] 0 d 1 w", + strokeColor, + strokeAlpha, + pointsCallback: (buffer, points) => { + buffer.push(`${(points[0] + points[4]) / 2} ` + `${(points[1] + points[5]) / 2} m`, `${(points[2] + points[6]) / 2} ` + `${(points[3] + points[7]) / 2} l`, "S"); + return [points[0], points[2], points[7], points[3]]; + } + }); + } + } else { + this.data.popupRef = null; + } + } +} +class StampAnnotation extends MarkupAnnotation { + #savedHasOwnCanvas; + constructor(params) { + super(params); + this.data.annotationType = AnnotationType.STAMP; + this.#savedHasOwnCanvas = this.data.hasOwnCanvas = this.data.noRotate; + this.data.isEditable = !this.data.noHTML; + this.data.noHTML = false; + } + mustBeViewedWhenEditing(isEditing, modifiedIds = null) { + if (isEditing) { + if (!this.data.isEditable) { + return false; + } + this.#savedHasOwnCanvas = this.data.hasOwnCanvas; + this.data.hasOwnCanvas = true; + return true; + } + this.data.hasOwnCanvas = this.#savedHasOwnCanvas; + return !modifiedIds?.has(this.data.id); + } + static async createImage(bitmap, xref) { + const { + width, + height + } = bitmap; + const canvas = new OffscreenCanvas(width, height); + const ctx = canvas.getContext("2d", { + alpha: true + }); + ctx.drawImage(bitmap, 0, 0); + const data = ctx.getImageData(0, 0, width, height).data; + const buf32 = new Uint32Array(data.buffer); + const hasAlpha = buf32.some(FeatureTest.isLittleEndian ? x => x >>> 24 !== 0xff : x => (x & 0xff) !== 0xff); + if (hasAlpha) { + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, width, height); + ctx.drawImage(bitmap, 0, 0); + } + const jpegBufferPromise = canvas.convertToBlob({ + type: "image/jpeg", + quality: 1 + }).then(blob => blob.arrayBuffer()); + const xobjectName = Name.get("XObject"); + const imageName = Name.get("Image"); + const image = new Dict(xref); + image.set("Type", xobjectName); + image.set("Subtype", imageName); + image.set("BitsPerComponent", 8); + image.set("ColorSpace", Name.get("DeviceRGB")); + image.set("Filter", Name.get("DCTDecode")); + image.set("BBox", [0, 0, width, height]); + image.set("Width", width); + image.set("Height", height); + let smaskStream = null; + if (hasAlpha) { + const alphaBuffer = new Uint8Array(buf32.length); + if (FeatureTest.isLittleEndian) { + for (let i = 0, ii = buf32.length; i < ii; i++) { + alphaBuffer[i] = buf32[i] >>> 24; + } + } else { + for (let i = 0, ii = buf32.length; i < ii; i++) { + alphaBuffer[i] = buf32[i] & 0xff; + } + } + const smask = new Dict(xref); + smask.set("Type", xobjectName); + smask.set("Subtype", imageName); + smask.set("BitsPerComponent", 8); + smask.set("ColorSpace", Name.get("DeviceGray")); + smask.set("Width", width); + smask.set("Height", height); + smaskStream = new Stream(alphaBuffer, 0, 0, smask); + } + const imageStream = new Stream(await jpegBufferPromise, 0, 0, image); + return { + imageStream, + smaskStream, + width, + height + }; + } + static createNewDict(annotation, xref, { + apRef, + ap + }) { + const { + oldAnnotation, + rect, + rotation, + user + } = annotation; + const stamp = oldAnnotation || new Dict(xref); + stamp.set("Type", Name.get("Annot")); + stamp.set("Subtype", Name.get("Stamp")); + stamp.set(oldAnnotation ? "M" : "CreationDate", `D:${getModificationDate()}`); + stamp.set("Rect", rect); + stamp.set("F", 4); + stamp.set("Border", [0, 0, 0]); + stamp.set("Rotate", rotation); + if (user) { + stamp.set("T", stringToAsciiOrUTF16BE(user)); + } + if (apRef || ap) { + const n = new Dict(xref); + stamp.set("AP", n); + if (apRef) { + n.set("N", apRef); + } else { + n.set("N", ap); + } + } + return stamp; + } + static async createNewAppearanceStream(annotation, xref, params) { + if (annotation.oldAnnotation) { + return null; + } + const { + rotation + } = annotation; + const { + imageRef, + width, + height + } = params.image; + const resources = new Dict(xref); + const xobject = new Dict(xref); + resources.set("XObject", xobject); + xobject.set("Im0", imageRef); + const appearance = `q ${width} 0 0 ${height} 0 0 cm /Im0 Do Q`; + const appearanceStreamDict = new Dict(xref); + appearanceStreamDict.set("FormType", 1); + appearanceStreamDict.set("Subtype", Name.get("Form")); + appearanceStreamDict.set("Type", Name.get("XObject")); + appearanceStreamDict.set("BBox", [0, 0, width, height]); + appearanceStreamDict.set("Resources", resources); + if (rotation) { + const matrix = getRotationMatrix(rotation, width, height); + appearanceStreamDict.set("Matrix", matrix); + } + const ap = new StringStream(appearance); + ap.dict = appearanceStreamDict; + return ap; + } +} +class FileAttachmentAnnotation extends MarkupAnnotation { + constructor(params) { + super(params); + const { + dict, + xref + } = params; + const file = new FileSpec(dict.get("FS"), xref); + this.data.annotationType = AnnotationType.FILEATTACHMENT; + this.data.hasOwnCanvas = this.data.noRotate; + this.data.noHTML = false; + this.data.file = file.serializable; + const name = dict.get("Name"); + this.data.name = name instanceof Name ? stringToPDFString(name.name) : "PushPin"; + const fillAlpha = dict.get("ca"); + this.data.fillAlpha = typeof fillAlpha === "number" && fillAlpha >= 0 && fillAlpha <= 1 ? fillAlpha : null; + } +} + +;// ./src/core/decrypt_stream.js + +const chunkSize = 512; +class DecryptStream extends DecodeStream { + constructor(str, maybeLength, decrypt) { + super(maybeLength); + this.str = str; + this.dict = str.dict; + this.decrypt = decrypt; + this.nextChunk = null; + this.initialized = false; + } + readBlock() { + let chunk; + if (this.initialized) { + chunk = this.nextChunk; + } else { + chunk = this.str.getBytes(chunkSize); + this.initialized = true; + } + if (!chunk?.length) { + this.eof = true; + return; + } + this.nextChunk = this.str.getBytes(chunkSize); + const hasMoreData = this.nextChunk?.length > 0; + const decrypt = this.decrypt; + chunk = decrypt(chunk, !hasMoreData); + const bufferLength = this.bufferLength, + newLength = bufferLength + chunk.length, + buffer = this.ensureBuffer(newLength); + buffer.set(chunk, bufferLength); + this.bufferLength = newLength; + } +} + +;// ./src/core/crypto.js + + + +class ARCFourCipher { + constructor(key) { + this.a = 0; + this.b = 0; + const s = new Uint8Array(256); + const keyLength = key.length; + for (let i = 0; i < 256; ++i) { + s[i] = i; + } + for (let i = 0, j = 0; i < 256; ++i) { + const tmp = s[i]; + j = j + tmp + key[i % keyLength] & 0xff; + s[i] = s[j]; + s[j] = tmp; + } + this.s = s; + } + encryptBlock(data) { + let a = this.a, + b = this.b; + const s = this.s; + const n = data.length; + const output = new Uint8Array(n); + for (let i = 0; i < n; ++i) { + a = a + 1 & 0xff; + const tmp = s[a]; + b = b + tmp & 0xff; + const tmp2 = s[b]; + s[a] = tmp2; + s[b] = tmp; + output[i] = data[i] ^ s[tmp + tmp2 & 0xff]; + } + this.a = a; + this.b = b; + return output; + } + decryptBlock(data) { + return this.encryptBlock(data); + } + encrypt(data) { + return this.encryptBlock(data); + } +} +const calculateMD5 = function calculateMD5Closure() { + const r = new Uint8Array([7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]); + const k = new Int32Array([-680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426, -1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162, 1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632, 643717713, -373897302, -701558691, 38016083, -660478335, -405537848, 568446438, -1019803690, -187363961, 1163531501, -1444681467, -51403784, 1735328473, -1926607734, -378558, -2022574463, 1839030562, -35309556, -1530992060, 1272893353, -155497632, -1094730640, 681279174, -358537222, -722521979, 76029189, -640364487, -421815835, 530742520, -995338651, -198630844, 1126891415, -1416354905, -57434055, 1700485571, -1894986606, -1051523, -2054922799, 1873313359, -30611744, -1560198380, 1309151649, -145523070, -1120210379, 718787259, -343485551]); + function hash(data, offset, length) { + let h0 = 1732584193, + h1 = -271733879, + h2 = -1732584194, + h3 = 271733878; + const paddedLength = length + 72 & ~63; + const padded = new Uint8Array(paddedLength); + let i, j; + for (i = 0; i < length; ++i) { + padded[i] = data[offset++]; + } + padded[i++] = 0x80; + const n = paddedLength - 8; + while (i < n) { + padded[i++] = 0; + } + padded[i++] = length << 3 & 0xff; + padded[i++] = length >> 5 & 0xff; + padded[i++] = length >> 13 & 0xff; + padded[i++] = length >> 21 & 0xff; + padded[i++] = length >>> 29 & 0xff; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + const w = new Int32Array(16); + for (i = 0; i < paddedLength;) { + for (j = 0; j < 16; ++j, i += 4) { + w[j] = padded[i] | padded[i + 1] << 8 | padded[i + 2] << 16 | padded[i + 3] << 24; + } + let a = h0, + b = h1, + c = h2, + d = h3, + f, + g; + for (j = 0; j < 64; ++j) { + if (j < 16) { + f = b & c | ~b & d; + g = j; + } else if (j < 32) { + f = d & b | ~d & c; + g = 5 * j + 1 & 15; + } else if (j < 48) { + f = b ^ c ^ d; + g = 3 * j + 5 & 15; + } else { + f = c ^ (b | ~d); + g = 7 * j & 15; + } + const tmp = d, + rotateArg = a + f + k[j] + w[g] | 0, + rotate = r[j]; + d = c; + c = b; + b = b + (rotateArg << rotate | rotateArg >>> 32 - rotate) | 0; + a = tmp; + } + h0 = h0 + a | 0; + h1 = h1 + b | 0; + h2 = h2 + c | 0; + h3 = h3 + d | 0; + } + return new Uint8Array([h0 & 0xFF, h0 >> 8 & 0xFF, h0 >> 16 & 0xFF, h0 >>> 24 & 0xFF, h1 & 0xFF, h1 >> 8 & 0xFF, h1 >> 16 & 0xFF, h1 >>> 24 & 0xFF, h2 & 0xFF, h2 >> 8 & 0xFF, h2 >> 16 & 0xFF, h2 >>> 24 & 0xFF, h3 & 0xFF, h3 >> 8 & 0xFF, h3 >> 16 & 0xFF, h3 >>> 24 & 0xFF]); + } + return hash; +}(); +class Word64 { + constructor(highInteger, lowInteger) { + this.high = highInteger | 0; + this.low = lowInteger | 0; + } + and(word) { + this.high &= word.high; + this.low &= word.low; + } + xor(word) { + this.high ^= word.high; + this.low ^= word.low; + } + or(word) { + this.high |= word.high; + this.low |= word.low; + } + shiftRight(places) { + if (places >= 32) { + this.low = this.high >>> places - 32 | 0; + this.high = 0; + } else { + this.low = this.low >>> places | this.high << 32 - places; + this.high = this.high >>> places | 0; + } + } + shiftLeft(places) { + if (places >= 32) { + this.high = this.low << places - 32; + this.low = 0; + } else { + this.high = this.high << places | this.low >>> 32 - places; + this.low <<= places; + } + } + rotateRight(places) { + let low, high; + if (places & 32) { + high = this.low; + low = this.high; + } else { + low = this.low; + high = this.high; + } + places &= 31; + this.low = low >>> places | high << 32 - places; + this.high = high >>> places | low << 32 - places; + } + not() { + this.high = ~this.high; + this.low = ~this.low; + } + add(word) { + const lowAdd = (this.low >>> 0) + (word.low >>> 0); + let highAdd = (this.high >>> 0) + (word.high >>> 0); + if (lowAdd > 0xffffffff) { + highAdd += 1; + } + this.low = lowAdd | 0; + this.high = highAdd | 0; + } + copyTo(bytes, offset) { + bytes[offset] = this.high >>> 24 & 0xff; + bytes[offset + 1] = this.high >> 16 & 0xff; + bytes[offset + 2] = this.high >> 8 & 0xff; + bytes[offset + 3] = this.high & 0xff; + bytes[offset + 4] = this.low >>> 24 & 0xff; + bytes[offset + 5] = this.low >> 16 & 0xff; + bytes[offset + 6] = this.low >> 8 & 0xff; + bytes[offset + 7] = this.low & 0xff; + } + assign(word) { + this.high = word.high; + this.low = word.low; + } +} +const calculateSHA256 = function calculateSHA256Closure() { + function rotr(x, n) { + return x >>> n | x << 32 - n; + } + function ch(x, y, z) { + return x & y ^ ~x & z; + } + function maj(x, y, z) { + return x & y ^ x & z ^ y & z; + } + function sigma(x) { + return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22); + } + function sigmaPrime(x) { + return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25); + } + function littleSigma(x) { + return rotr(x, 7) ^ rotr(x, 18) ^ x >>> 3; + } + function littleSigmaPrime(x) { + return rotr(x, 17) ^ rotr(x, 19) ^ x >>> 10; + } + const k = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]; + function hash(data, offset, length) { + let h0 = 0x6a09e667, + h1 = 0xbb67ae85, + h2 = 0x3c6ef372, + h3 = 0xa54ff53a, + h4 = 0x510e527f, + h5 = 0x9b05688c, + h6 = 0x1f83d9ab, + h7 = 0x5be0cd19; + const paddedLength = Math.ceil((length + 9) / 64) * 64; + const padded = new Uint8Array(paddedLength); + let i, j; + for (i = 0; i < length; ++i) { + padded[i] = data[offset++]; + } + padded[i++] = 0x80; + const n = paddedLength - 8; + while (i < n) { + padded[i++] = 0; + } + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = length >>> 29 & 0xff; + padded[i++] = length >> 21 & 0xff; + padded[i++] = length >> 13 & 0xff; + padded[i++] = length >> 5 & 0xff; + padded[i++] = length << 3 & 0xff; + const w = new Uint32Array(64); + for (i = 0; i < paddedLength;) { + for (j = 0; j < 16; ++j) { + w[j] = padded[i] << 24 | padded[i + 1] << 16 | padded[i + 2] << 8 | padded[i + 3]; + i += 4; + } + for (j = 16; j < 64; ++j) { + w[j] = littleSigmaPrime(w[j - 2]) + w[j - 7] + littleSigma(w[j - 15]) + w[j - 16] | 0; + } + let a = h0, + b = h1, + c = h2, + d = h3, + e = h4, + f = h5, + g = h6, + h = h7, + t1, + t2; + for (j = 0; j < 64; ++j) { + t1 = h + sigmaPrime(e) + ch(e, f, g) + k[j] + w[j]; + t2 = sigma(a) + maj(a, b, c); + h = g; + g = f; + f = e; + e = d + t1 | 0; + d = c; + c = b; + b = a; + a = t1 + t2 | 0; + } + h0 = h0 + a | 0; + h1 = h1 + b | 0; + h2 = h2 + c | 0; + h3 = h3 + d | 0; + h4 = h4 + e | 0; + h5 = h5 + f | 0; + h6 = h6 + g | 0; + h7 = h7 + h | 0; + } + return new Uint8Array([h0 >> 24 & 0xFF, h0 >> 16 & 0xFF, h0 >> 8 & 0xFF, h0 & 0xFF, h1 >> 24 & 0xFF, h1 >> 16 & 0xFF, h1 >> 8 & 0xFF, h1 & 0xFF, h2 >> 24 & 0xFF, h2 >> 16 & 0xFF, h2 >> 8 & 0xFF, h2 & 0xFF, h3 >> 24 & 0xFF, h3 >> 16 & 0xFF, h3 >> 8 & 0xFF, h3 & 0xFF, h4 >> 24 & 0xFF, h4 >> 16 & 0xFF, h4 >> 8 & 0xFF, h4 & 0xFF, h5 >> 24 & 0xFF, h5 >> 16 & 0xFF, h5 >> 8 & 0xFF, h5 & 0xFF, h6 >> 24 & 0xFF, h6 >> 16 & 0xFF, h6 >> 8 & 0xFF, h6 & 0xFF, h7 >> 24 & 0xFF, h7 >> 16 & 0xFF, h7 >> 8 & 0xFF, h7 & 0xFF]); + } + return hash; +}(); +const calculateSHA512 = function calculateSHA512Closure() { + function ch(result, x, y, z, tmp) { + result.assign(x); + result.and(y); + tmp.assign(x); + tmp.not(); + tmp.and(z); + result.xor(tmp); + } + function maj(result, x, y, z, tmp) { + result.assign(x); + result.and(y); + tmp.assign(x); + tmp.and(z); + result.xor(tmp); + tmp.assign(y); + tmp.and(z); + result.xor(tmp); + } + function sigma(result, x, tmp) { + result.assign(x); + result.rotateRight(28); + tmp.assign(x); + tmp.rotateRight(34); + result.xor(tmp); + tmp.assign(x); + tmp.rotateRight(39); + result.xor(tmp); + } + function sigmaPrime(result, x, tmp) { + result.assign(x); + result.rotateRight(14); + tmp.assign(x); + tmp.rotateRight(18); + result.xor(tmp); + tmp.assign(x); + tmp.rotateRight(41); + result.xor(tmp); + } + function littleSigma(result, x, tmp) { + result.assign(x); + result.rotateRight(1); + tmp.assign(x); + tmp.rotateRight(8); + result.xor(tmp); + tmp.assign(x); + tmp.shiftRight(7); + result.xor(tmp); + } + function littleSigmaPrime(result, x, tmp) { + result.assign(x); + result.rotateRight(19); + tmp.assign(x); + tmp.rotateRight(61); + result.xor(tmp); + tmp.assign(x); + tmp.shiftRight(6); + result.xor(tmp); + } + const k = [new Word64(0x428a2f98, 0xd728ae22), new Word64(0x71374491, 0x23ef65cd), new Word64(0xb5c0fbcf, 0xec4d3b2f), new Word64(0xe9b5dba5, 0x8189dbbc), new Word64(0x3956c25b, 0xf348b538), new Word64(0x59f111f1, 0xb605d019), new Word64(0x923f82a4, 0xaf194f9b), new Word64(0xab1c5ed5, 0xda6d8118), new Word64(0xd807aa98, 0xa3030242), new Word64(0x12835b01, 0x45706fbe), new Word64(0x243185be, 0x4ee4b28c), new Word64(0x550c7dc3, 0xd5ffb4e2), new Word64(0x72be5d74, 0xf27b896f), new Word64(0x80deb1fe, 0x3b1696b1), new Word64(0x9bdc06a7, 0x25c71235), new Word64(0xc19bf174, 0xcf692694), new Word64(0xe49b69c1, 0x9ef14ad2), new Word64(0xefbe4786, 0x384f25e3), new Word64(0x0fc19dc6, 0x8b8cd5b5), new Word64(0x240ca1cc, 0x77ac9c65), new Word64(0x2de92c6f, 0x592b0275), new Word64(0x4a7484aa, 0x6ea6e483), new Word64(0x5cb0a9dc, 0xbd41fbd4), new Word64(0x76f988da, 0x831153b5), new Word64(0x983e5152, 0xee66dfab), new Word64(0xa831c66d, 0x2db43210), new Word64(0xb00327c8, 0x98fb213f), new Word64(0xbf597fc7, 0xbeef0ee4), new Word64(0xc6e00bf3, 0x3da88fc2), new Word64(0xd5a79147, 0x930aa725), new Word64(0x06ca6351, 0xe003826f), new Word64(0x14292967, 0x0a0e6e70), new Word64(0x27b70a85, 0x46d22ffc), new Word64(0x2e1b2138, 0x5c26c926), new Word64(0x4d2c6dfc, 0x5ac42aed), new Word64(0x53380d13, 0x9d95b3df), new Word64(0x650a7354, 0x8baf63de), new Word64(0x766a0abb, 0x3c77b2a8), new Word64(0x81c2c92e, 0x47edaee6), new Word64(0x92722c85, 0x1482353b), new Word64(0xa2bfe8a1, 0x4cf10364), new Word64(0xa81a664b, 0xbc423001), new Word64(0xc24b8b70, 0xd0f89791), new Word64(0xc76c51a3, 0x0654be30), new Word64(0xd192e819, 0xd6ef5218), new Word64(0xd6990624, 0x5565a910), new Word64(0xf40e3585, 0x5771202a), new Word64(0x106aa070, 0x32bbd1b8), new Word64(0x19a4c116, 0xb8d2d0c8), new Word64(0x1e376c08, 0x5141ab53), new Word64(0x2748774c, 0xdf8eeb99), new Word64(0x34b0bcb5, 0xe19b48a8), new Word64(0x391c0cb3, 0xc5c95a63), new Word64(0x4ed8aa4a, 0xe3418acb), new Word64(0x5b9cca4f, 0x7763e373), new Word64(0x682e6ff3, 0xd6b2b8a3), new Word64(0x748f82ee, 0x5defb2fc), new Word64(0x78a5636f, 0x43172f60), new Word64(0x84c87814, 0xa1f0ab72), new Word64(0x8cc70208, 0x1a6439ec), new Word64(0x90befffa, 0x23631e28), new Word64(0xa4506ceb, 0xde82bde9), new Word64(0xbef9a3f7, 0xb2c67915), new Word64(0xc67178f2, 0xe372532b), new Word64(0xca273ece, 0xea26619c), new Word64(0xd186b8c7, 0x21c0c207), new Word64(0xeada7dd6, 0xcde0eb1e), new Word64(0xf57d4f7f, 0xee6ed178), new Word64(0x06f067aa, 0x72176fba), new Word64(0x0a637dc5, 0xa2c898a6), new Word64(0x113f9804, 0xbef90dae), new Word64(0x1b710b35, 0x131c471b), new Word64(0x28db77f5, 0x23047d84), new Word64(0x32caab7b, 0x40c72493), new Word64(0x3c9ebe0a, 0x15c9bebc), new Word64(0x431d67c4, 0x9c100d4c), new Word64(0x4cc5d4be, 0xcb3e42b6), new Word64(0x597f299c, 0xfc657e2a), new Word64(0x5fcb6fab, 0x3ad6faec), new Word64(0x6c44198c, 0x4a475817)]; + function hash(data, offset, length, mode384 = false) { + let h0, h1, h2, h3, h4, h5, h6, h7; + if (!mode384) { + h0 = new Word64(0x6a09e667, 0xf3bcc908); + h1 = new Word64(0xbb67ae85, 0x84caa73b); + h2 = new Word64(0x3c6ef372, 0xfe94f82b); + h3 = new Word64(0xa54ff53a, 0x5f1d36f1); + h4 = new Word64(0x510e527f, 0xade682d1); + h5 = new Word64(0x9b05688c, 0x2b3e6c1f); + h6 = new Word64(0x1f83d9ab, 0xfb41bd6b); + h7 = new Word64(0x5be0cd19, 0x137e2179); + } else { + h0 = new Word64(0xcbbb9d5d, 0xc1059ed8); + h1 = new Word64(0x629a292a, 0x367cd507); + h2 = new Word64(0x9159015a, 0x3070dd17); + h3 = new Word64(0x152fecd8, 0xf70e5939); + h4 = new Word64(0x67332667, 0xffc00b31); + h5 = new Word64(0x8eb44a87, 0x68581511); + h6 = new Word64(0xdb0c2e0d, 0x64f98fa7); + h7 = new Word64(0x47b5481d, 0xbefa4fa4); + } + const paddedLength = Math.ceil((length + 17) / 128) * 128; + const padded = new Uint8Array(paddedLength); + let i, j; + for (i = 0; i < length; ++i) { + padded[i] = data[offset++]; + } + padded[i++] = 0x80; + const n = paddedLength - 16; + while (i < n) { + padded[i++] = 0; + } + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = length >>> 29 & 0xff; + padded[i++] = length >> 21 & 0xff; + padded[i++] = length >> 13 & 0xff; + padded[i++] = length >> 5 & 0xff; + padded[i++] = length << 3 & 0xff; + const w = new Array(80); + for (i = 0; i < 80; i++) { + w[i] = new Word64(0, 0); + } + let a = new Word64(0, 0), + b = new Word64(0, 0), + c = new Word64(0, 0); + let d = new Word64(0, 0), + e = new Word64(0, 0), + f = new Word64(0, 0); + let g = new Word64(0, 0), + h = new Word64(0, 0); + const t1 = new Word64(0, 0), + t2 = new Word64(0, 0); + const tmp1 = new Word64(0, 0), + tmp2 = new Word64(0, 0); + let tmp3; + for (i = 0; i < paddedLength;) { + for (j = 0; j < 16; ++j) { + w[j].high = padded[i] << 24 | padded[i + 1] << 16 | padded[i + 2] << 8 | padded[i + 3]; + w[j].low = padded[i + 4] << 24 | padded[i + 5] << 16 | padded[i + 6] << 8 | padded[i + 7]; + i += 8; + } + for (j = 16; j < 80; ++j) { + tmp3 = w[j]; + littleSigmaPrime(tmp3, w[j - 2], tmp2); + tmp3.add(w[j - 7]); + littleSigma(tmp1, w[j - 15], tmp2); + tmp3.add(tmp1); + tmp3.add(w[j - 16]); + } + a.assign(h0); + b.assign(h1); + c.assign(h2); + d.assign(h3); + e.assign(h4); + f.assign(h5); + g.assign(h6); + h.assign(h7); + for (j = 0; j < 80; ++j) { + t1.assign(h); + sigmaPrime(tmp1, e, tmp2); + t1.add(tmp1); + ch(tmp1, e, f, g, tmp2); + t1.add(tmp1); + t1.add(k[j]); + t1.add(w[j]); + sigma(t2, a, tmp2); + maj(tmp1, a, b, c, tmp2); + t2.add(tmp1); + tmp3 = h; + h = g; + g = f; + f = e; + d.add(t1); + e = d; + d = c; + c = b; + b = a; + tmp3.assign(t1); + tmp3.add(t2); + a = tmp3; + } + h0.add(a); + h1.add(b); + h2.add(c); + h3.add(d); + h4.add(e); + h5.add(f); + h6.add(g); + h7.add(h); + } + let result; + if (!mode384) { + result = new Uint8Array(64); + h0.copyTo(result, 0); + h1.copyTo(result, 8); + h2.copyTo(result, 16); + h3.copyTo(result, 24); + h4.copyTo(result, 32); + h5.copyTo(result, 40); + h6.copyTo(result, 48); + h7.copyTo(result, 56); + } else { + result = new Uint8Array(48); + h0.copyTo(result, 0); + h1.copyTo(result, 8); + h2.copyTo(result, 16); + h3.copyTo(result, 24); + h4.copyTo(result, 32); + h5.copyTo(result, 40); + } + return result; + } + return hash; +}(); +function calculateSHA384(data, offset, length) { + return calculateSHA512(data, offset, length, true); +} +class NullCipher { + decryptBlock(data) { + return data; + } + encrypt(data) { + return data; + } +} +class AESBaseCipher { + constructor() { + this._s = new Uint8Array([0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16]); + this._inv_s = new Uint8Array([0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d]); + this._mix = new Uint32Array([0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]); + this._mixCol = new Uint8Array(256); + for (let i = 0; i < 256; i++) { + this._mixCol[i] = i < 128 ? i << 1 : i << 1 ^ 0x1b; + } + this.buffer = new Uint8Array(16); + this.bufferPosition = 0; + } + _expandKey(cipherKey) { + unreachable("Cannot call `_expandKey` on the base class"); + } + _decrypt(input, key) { + let t, u, v; + const state = new Uint8Array(16); + state.set(input); + for (let j = 0, k = this._keySize; j < 16; ++j, ++k) { + state[j] ^= key[k]; + } + for (let i = this._cyclesOfRepetition - 1; i >= 1; --i) { + t = state[13]; + state[13] = state[9]; + state[9] = state[5]; + state[5] = state[1]; + state[1] = t; + t = state[14]; + u = state[10]; + state[14] = state[6]; + state[10] = state[2]; + state[6] = t; + state[2] = u; + t = state[15]; + u = state[11]; + v = state[7]; + state[15] = state[3]; + state[11] = t; + state[7] = u; + state[3] = v; + for (let j = 0; j < 16; ++j) { + state[j] = this._inv_s[state[j]]; + } + for (let j = 0, k = i * 16; j < 16; ++j, ++k) { + state[j] ^= key[k]; + } + for (let j = 0; j < 16; j += 4) { + const s0 = this._mix[state[j]]; + const s1 = this._mix[state[j + 1]]; + const s2 = this._mix[state[j + 2]]; + const s3 = this._mix[state[j + 3]]; + t = s0 ^ s1 >>> 8 ^ s1 << 24 ^ s2 >>> 16 ^ s2 << 16 ^ s3 >>> 24 ^ s3 << 8; + state[j] = t >>> 24 & 0xff; + state[j + 1] = t >> 16 & 0xff; + state[j + 2] = t >> 8 & 0xff; + state[j + 3] = t & 0xff; + } + } + t = state[13]; + state[13] = state[9]; + state[9] = state[5]; + state[5] = state[1]; + state[1] = t; + t = state[14]; + u = state[10]; + state[14] = state[6]; + state[10] = state[2]; + state[6] = t; + state[2] = u; + t = state[15]; + u = state[11]; + v = state[7]; + state[15] = state[3]; + state[11] = t; + state[7] = u; + state[3] = v; + for (let j = 0; j < 16; ++j) { + state[j] = this._inv_s[state[j]]; + state[j] ^= key[j]; + } + return state; + } + _encrypt(input, key) { + const s = this._s; + let t, u, v; + const state = new Uint8Array(16); + state.set(input); + for (let j = 0; j < 16; ++j) { + state[j] ^= key[j]; + } + for (let i = 1; i < this._cyclesOfRepetition; i++) { + for (let j = 0; j < 16; ++j) { + state[j] = s[state[j]]; + } + v = state[1]; + state[1] = state[5]; + state[5] = state[9]; + state[9] = state[13]; + state[13] = v; + v = state[2]; + u = state[6]; + state[2] = state[10]; + state[6] = state[14]; + state[10] = v; + state[14] = u; + v = state[3]; + u = state[7]; + t = state[11]; + state[3] = state[15]; + state[7] = v; + state[11] = u; + state[15] = t; + for (let j = 0; j < 16; j += 4) { + const s0 = state[j + 0]; + const s1 = state[j + 1]; + const s2 = state[j + 2]; + const s3 = state[j + 3]; + t = s0 ^ s1 ^ s2 ^ s3; + state[j + 0] ^= t ^ this._mixCol[s0 ^ s1]; + state[j + 1] ^= t ^ this._mixCol[s1 ^ s2]; + state[j + 2] ^= t ^ this._mixCol[s2 ^ s3]; + state[j + 3] ^= t ^ this._mixCol[s3 ^ s0]; + } + for (let j = 0, k = i * 16; j < 16; ++j, ++k) { + state[j] ^= key[k]; + } + } + for (let j = 0; j < 16; ++j) { + state[j] = s[state[j]]; + } + v = state[1]; + state[1] = state[5]; + state[5] = state[9]; + state[9] = state[13]; + state[13] = v; + v = state[2]; + u = state[6]; + state[2] = state[10]; + state[6] = state[14]; + state[10] = v; + state[14] = u; + v = state[3]; + u = state[7]; + t = state[11]; + state[3] = state[15]; + state[7] = v; + state[11] = u; + state[15] = t; + for (let j = 0, k = this._keySize; j < 16; ++j, ++k) { + state[j] ^= key[k]; + } + return state; + } + _decryptBlock2(data, finalize) { + const sourceLength = data.length; + let buffer = this.buffer, + bufferLength = this.bufferPosition; + const result = []; + let iv = this.iv; + for (let i = 0; i < sourceLength; ++i) { + buffer[bufferLength] = data[i]; + ++bufferLength; + if (bufferLength < 16) { + continue; + } + const plain = this._decrypt(buffer, this._key); + for (let j = 0; j < 16; ++j) { + plain[j] ^= iv[j]; + } + iv = buffer; + result.push(plain); + buffer = new Uint8Array(16); + bufferLength = 0; + } + this.buffer = buffer; + this.bufferLength = bufferLength; + this.iv = iv; + if (result.length === 0) { + return new Uint8Array(0); + } + let outputLength = 16 * result.length; + if (finalize) { + const lastBlock = result.at(-1); + let psLen = lastBlock[15]; + if (psLen <= 16) { + for (let i = 15, ii = 16 - psLen; i >= ii; --i) { + if (lastBlock[i] !== psLen) { + psLen = 0; + break; + } + } + outputLength -= psLen; + result[result.length - 1] = lastBlock.subarray(0, 16 - psLen); + } + } + const output = new Uint8Array(outputLength); + for (let i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) { + output.set(result[i], j); + } + return output; + } + decryptBlock(data, finalize, iv = null) { + const sourceLength = data.length; + const buffer = this.buffer; + let bufferLength = this.bufferPosition; + if (iv) { + this.iv = iv; + } else { + for (let i = 0; bufferLength < 16 && i < sourceLength; ++i, ++bufferLength) { + buffer[bufferLength] = data[i]; + } + if (bufferLength < 16) { + this.bufferLength = bufferLength; + return new Uint8Array(0); + } + this.iv = buffer; + data = data.subarray(16); + } + this.buffer = new Uint8Array(16); + this.bufferLength = 0; + this.decryptBlock = this._decryptBlock2; + return this.decryptBlock(data, finalize); + } + encrypt(data, iv) { + const sourceLength = data.length; + let buffer = this.buffer, + bufferLength = this.bufferPosition; + const result = []; + if (!iv) { + iv = new Uint8Array(16); + } + for (let i = 0; i < sourceLength; ++i) { + buffer[bufferLength] = data[i]; + ++bufferLength; + if (bufferLength < 16) { + continue; + } + for (let j = 0; j < 16; ++j) { + buffer[j] ^= iv[j]; + } + const cipher = this._encrypt(buffer, this._key); + iv = cipher; + result.push(cipher); + buffer = new Uint8Array(16); + bufferLength = 0; + } + this.buffer = buffer; + this.bufferLength = bufferLength; + this.iv = iv; + if (result.length === 0) { + return new Uint8Array(0); + } + const outputLength = 16 * result.length; + const output = new Uint8Array(outputLength); + for (let i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) { + output.set(result[i], j); + } + return output; + } +} +class AES128Cipher extends AESBaseCipher { + constructor(key) { + super(); + this._cyclesOfRepetition = 10; + this._keySize = 160; + this._rcon = new Uint8Array([0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d]); + this._key = this._expandKey(key); + } + _expandKey(cipherKey) { + const b = 176; + const s = this._s; + const rcon = this._rcon; + const result = new Uint8Array(b); + result.set(cipherKey); + for (let j = 16, i = 1; j < b; ++i) { + let t1 = result[j - 3]; + let t2 = result[j - 2]; + let t3 = result[j - 1]; + let t4 = result[j - 4]; + t1 = s[t1]; + t2 = s[t2]; + t3 = s[t3]; + t4 = s[t4]; + t1 ^= rcon[i]; + for (let n = 0; n < 4; ++n) { + result[j] = t1 ^= result[j - 16]; + j++; + result[j] = t2 ^= result[j - 16]; + j++; + result[j] = t3 ^= result[j - 16]; + j++; + result[j] = t4 ^= result[j - 16]; + j++; + } + } + return result; + } +} +class AES256Cipher extends AESBaseCipher { + constructor(key) { + super(); + this._cyclesOfRepetition = 14; + this._keySize = 224; + this._key = this._expandKey(key); + } + _expandKey(cipherKey) { + const b = 240; + const s = this._s; + const result = new Uint8Array(b); + result.set(cipherKey); + let r = 1; + let t1, t2, t3, t4; + for (let j = 32, i = 1; j < b; ++i) { + if (j % 32 === 16) { + t1 = s[t1]; + t2 = s[t2]; + t3 = s[t3]; + t4 = s[t4]; + } else if (j % 32 === 0) { + t1 = result[j - 3]; + t2 = result[j - 2]; + t3 = result[j - 1]; + t4 = result[j - 4]; + t1 = s[t1]; + t2 = s[t2]; + t3 = s[t3]; + t4 = s[t4]; + t1 ^= r; + if ((r <<= 1) >= 256) { + r = (r ^ 0x1b) & 0xff; + } + } + for (let n = 0; n < 4; ++n) { + result[j] = t1 ^= result[j - 32]; + j++; + result[j] = t2 ^= result[j - 32]; + j++; + result[j] = t3 ^= result[j - 32]; + j++; + result[j] = t4 ^= result[j - 32]; + j++; + } + } + return result; + } +} +class PDF17 { + checkOwnerPassword(password, ownerValidationSalt, userBytes, ownerPassword) { + const hashData = new Uint8Array(password.length + 56); + hashData.set(password, 0); + hashData.set(ownerValidationSalt, password.length); + hashData.set(userBytes, password.length + ownerValidationSalt.length); + const result = calculateSHA256(hashData, 0, hashData.length); + return isArrayEqual(result, ownerPassword); + } + checkUserPassword(password, userValidationSalt, userPassword) { + const hashData = new Uint8Array(password.length + 8); + hashData.set(password, 0); + hashData.set(userValidationSalt, password.length); + const result = calculateSHA256(hashData, 0, hashData.length); + return isArrayEqual(result, userPassword); + } + getOwnerKey(password, ownerKeySalt, userBytes, ownerEncryption) { + const hashData = new Uint8Array(password.length + 56); + hashData.set(password, 0); + hashData.set(ownerKeySalt, password.length); + hashData.set(userBytes, password.length + ownerKeySalt.length); + const key = calculateSHA256(hashData, 0, hashData.length); + const cipher = new AES256Cipher(key); + return cipher.decryptBlock(ownerEncryption, false, new Uint8Array(16)); + } + getUserKey(password, userKeySalt, userEncryption) { + const hashData = new Uint8Array(password.length + 8); + hashData.set(password, 0); + hashData.set(userKeySalt, password.length); + const key = calculateSHA256(hashData, 0, hashData.length); + const cipher = new AES256Cipher(key); + return cipher.decryptBlock(userEncryption, false, new Uint8Array(16)); + } +} +class PDF20 { + _hash(password, input, userBytes) { + let k = calculateSHA256(input, 0, input.length).subarray(0, 32); + let e = [0]; + let i = 0; + while (i < 64 || e.at(-1) > i - 32) { + const combinedLength = password.length + k.length + userBytes.length, + combinedArray = new Uint8Array(combinedLength); + let writeOffset = 0; + combinedArray.set(password, writeOffset); + writeOffset += password.length; + combinedArray.set(k, writeOffset); + writeOffset += k.length; + combinedArray.set(userBytes, writeOffset); + const k1 = new Uint8Array(combinedLength * 64); + for (let j = 0, pos = 0; j < 64; j++, pos += combinedLength) { + k1.set(combinedArray, pos); + } + const cipher = new AES128Cipher(k.subarray(0, 16)); + e = cipher.encrypt(k1, k.subarray(16, 32)); + const remainder = e.slice(0, 16).reduce((a, b) => a + b, 0) % 3; + if (remainder === 0) { + k = calculateSHA256(e, 0, e.length); + } else if (remainder === 1) { + k = calculateSHA384(e, 0, e.length); + } else if (remainder === 2) { + k = calculateSHA512(e, 0, e.length); + } + i++; + } + return k.subarray(0, 32); + } + checkOwnerPassword(password, ownerValidationSalt, userBytes, ownerPassword) { + const hashData = new Uint8Array(password.length + 56); + hashData.set(password, 0); + hashData.set(ownerValidationSalt, password.length); + hashData.set(userBytes, password.length + ownerValidationSalt.length); + const result = this._hash(password, hashData, userBytes); + return isArrayEqual(result, ownerPassword); + } + checkUserPassword(password, userValidationSalt, userPassword) { + const hashData = new Uint8Array(password.length + 8); + hashData.set(password, 0); + hashData.set(userValidationSalt, password.length); + const result = this._hash(password, hashData, []); + return isArrayEqual(result, userPassword); + } + getOwnerKey(password, ownerKeySalt, userBytes, ownerEncryption) { + const hashData = new Uint8Array(password.length + 56); + hashData.set(password, 0); + hashData.set(ownerKeySalt, password.length); + hashData.set(userBytes, password.length + ownerKeySalt.length); + const key = this._hash(password, hashData, userBytes); + const cipher = new AES256Cipher(key); + return cipher.decryptBlock(ownerEncryption, false, new Uint8Array(16)); + } + getUserKey(password, userKeySalt, userEncryption) { + const hashData = new Uint8Array(password.length + 8); + hashData.set(password, 0); + hashData.set(userKeySalt, password.length); + const key = this._hash(password, hashData, []); + const cipher = new AES256Cipher(key); + return cipher.decryptBlock(userEncryption, false, new Uint8Array(16)); + } +} +class CipherTransform { + constructor(stringCipherConstructor, streamCipherConstructor) { + this.StringCipherConstructor = stringCipherConstructor; + this.StreamCipherConstructor = streamCipherConstructor; + } + createStream(stream, length) { + const cipher = new this.StreamCipherConstructor(); + return new DecryptStream(stream, length, function cipherTransformDecryptStream(data, finalize) { + return cipher.decryptBlock(data, finalize); + }); + } + decryptString(s) { + const cipher = new this.StringCipherConstructor(); + let data = stringToBytes(s); + data = cipher.decryptBlock(data, true); + return bytesToString(data); + } + encryptString(s) { + const cipher = new this.StringCipherConstructor(); + if (cipher instanceof AESBaseCipher) { + const strLen = s.length; + const pad = 16 - strLen % 16; + s += String.fromCharCode(pad).repeat(pad); + const iv = new Uint8Array(16); + if (typeof crypto !== "undefined") { + crypto.getRandomValues(iv); + } else { + for (let i = 0; i < 16; i++) { + iv[i] = Math.floor(256 * Math.random()); + } + } + let data = stringToBytes(s); + data = cipher.encrypt(data, iv); + const buf = new Uint8Array(16 + data.length); + buf.set(iv); + buf.set(data, 16); + return bytesToString(buf); + } + let data = stringToBytes(s); + data = cipher.encrypt(data); + return bytesToString(data); + } +} +class CipherTransformFactory { + static #defaultPasswordBytes = new Uint8Array([0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a]); + #createEncryptionKey20(revision, password, ownerPassword, ownerValidationSalt, ownerKeySalt, uBytes, userPassword, userValidationSalt, userKeySalt, ownerEncryption, userEncryption, perms) { + if (password) { + const passwordLength = Math.min(127, password.length); + password = password.subarray(0, passwordLength); + } else { + password = []; + } + const pdfAlgorithm = revision === 6 ? new PDF20() : new PDF17(); + if (pdfAlgorithm.checkUserPassword(password, userValidationSalt, userPassword)) { + return pdfAlgorithm.getUserKey(password, userKeySalt, userEncryption); + } else if (password.length && pdfAlgorithm.checkOwnerPassword(password, ownerValidationSalt, uBytes, ownerPassword)) { + return pdfAlgorithm.getOwnerKey(password, ownerKeySalt, uBytes, ownerEncryption); + } + return null; + } + #prepareKeyData(fileId, password, ownerPassword, userPassword, flags, revision, keyLength, encryptMetadata) { + const hashDataSize = 40 + ownerPassword.length + fileId.length; + const hashData = new Uint8Array(hashDataSize); + let i = 0, + j, + n; + if (password) { + n = Math.min(32, password.length); + for (; i < n; ++i) { + hashData[i] = password[i]; + } + } + j = 0; + while (i < 32) { + hashData[i++] = CipherTransformFactory.#defaultPasswordBytes[j++]; + } + for (j = 0, n = ownerPassword.length; j < n; ++j) { + hashData[i++] = ownerPassword[j]; + } + hashData[i++] = flags & 0xff; + hashData[i++] = flags >> 8 & 0xff; + hashData[i++] = flags >> 16 & 0xff; + hashData[i++] = flags >>> 24 & 0xff; + for (j = 0, n = fileId.length; j < n; ++j) { + hashData[i++] = fileId[j]; + } + if (revision >= 4 && !encryptMetadata) { + hashData[i++] = 0xff; + hashData[i++] = 0xff; + hashData[i++] = 0xff; + hashData[i++] = 0xff; + } + let hash = calculateMD5(hashData, 0, i); + const keyLengthInBytes = keyLength >> 3; + if (revision >= 3) { + for (j = 0; j < 50; ++j) { + hash = calculateMD5(hash, 0, keyLengthInBytes); + } + } + const encryptionKey = hash.subarray(0, keyLengthInBytes); + let cipher, checkData; + if (revision >= 3) { + for (i = 0; i < 32; ++i) { + hashData[i] = CipherTransformFactory.#defaultPasswordBytes[i]; + } + for (j = 0, n = fileId.length; j < n; ++j) { + hashData[i++] = fileId[j]; + } + cipher = new ARCFourCipher(encryptionKey); + checkData = cipher.encryptBlock(calculateMD5(hashData, 0, i)); + n = encryptionKey.length; + const derivedKey = new Uint8Array(n); + for (j = 1; j <= 19; ++j) { + for (let k = 0; k < n; ++k) { + derivedKey[k] = encryptionKey[k] ^ j; + } + cipher = new ARCFourCipher(derivedKey); + checkData = cipher.encryptBlock(checkData); + } + for (j = 0, n = checkData.length; j < n; ++j) { + if (userPassword[j] !== checkData[j]) { + return null; + } + } + } else { + cipher = new ARCFourCipher(encryptionKey); + checkData = cipher.encryptBlock(CipherTransformFactory.#defaultPasswordBytes); + for (j = 0, n = checkData.length; j < n; ++j) { + if (userPassword[j] !== checkData[j]) { + return null; + } + } + } + return encryptionKey; + } + #decodeUserPassword(password, ownerPassword, revision, keyLength) { + const hashData = new Uint8Array(32); + let i = 0; + const n = Math.min(32, password.length); + for (; i < n; ++i) { + hashData[i] = password[i]; + } + let j = 0; + while (i < 32) { + hashData[i++] = CipherTransformFactory.#defaultPasswordBytes[j++]; + } + let hash = calculateMD5(hashData, 0, i); + const keyLengthInBytes = keyLength >> 3; + if (revision >= 3) { + for (j = 0; j < 50; ++j) { + hash = calculateMD5(hash, 0, hash.length); + } + } + let cipher, userPassword; + if (revision >= 3) { + userPassword = ownerPassword; + const derivedKey = new Uint8Array(keyLengthInBytes); + for (j = 19; j >= 0; j--) { + for (let k = 0; k < keyLengthInBytes; ++k) { + derivedKey[k] = hash[k] ^ j; + } + cipher = new ARCFourCipher(derivedKey); + userPassword = cipher.encryptBlock(userPassword); + } + } else { + cipher = new ARCFourCipher(hash.subarray(0, keyLengthInBytes)); + userPassword = cipher.encryptBlock(ownerPassword); + } + return userPassword; + } + #buildObjectKey(num, gen, encryptionKey, isAes = false) { + const key = new Uint8Array(encryptionKey.length + 9); + const n = encryptionKey.length; + let i; + for (i = 0; i < n; ++i) { + key[i] = encryptionKey[i]; + } + key[i++] = num & 0xff; + key[i++] = num >> 8 & 0xff; + key[i++] = num >> 16 & 0xff; + key[i++] = gen & 0xff; + key[i++] = gen >> 8 & 0xff; + if (isAes) { + key[i++] = 0x73; + key[i++] = 0x41; + key[i++] = 0x6c; + key[i++] = 0x54; + } + const hash = calculateMD5(key, 0, i); + return hash.subarray(0, Math.min(encryptionKey.length + 5, 16)); + } + #buildCipherConstructor(cf, name, num, gen, key) { + if (!(name instanceof Name)) { + throw new FormatError("Invalid crypt filter name."); + } + const self = this; + const cryptFilter = cf.get(name.name); + const cfm = cryptFilter?.get("CFM"); + if (!cfm || cfm.name === "None") { + return function () { + return new NullCipher(); + }; + } + if (cfm.name === "V2") { + return function () { + return new ARCFourCipher(self.#buildObjectKey(num, gen, key, false)); + }; + } + if (cfm.name === "AESV2") { + return function () { + return new AES128Cipher(self.#buildObjectKey(num, gen, key, true)); + }; + } + if (cfm.name === "AESV3") { + return function () { + return new AES256Cipher(key); + }; + } + throw new FormatError("Unknown crypto method"); + } + constructor(dict, fileId, password) { + const filter = dict.get("Filter"); + if (!isName(filter, "Standard")) { + throw new FormatError("unknown encryption method"); + } + this.filterName = filter.name; + this.dict = dict; + const algorithm = dict.get("V"); + if (!Number.isInteger(algorithm) || algorithm !== 1 && algorithm !== 2 && algorithm !== 4 && algorithm !== 5) { + throw new FormatError("unsupported encryption algorithm"); + } + this.algorithm = algorithm; + let keyLength = dict.get("Length"); + if (!keyLength) { + if (algorithm <= 3) { + keyLength = 40; + } else { + const cfDict = dict.get("CF"); + const streamCryptoName = dict.get("StmF"); + if (cfDict instanceof Dict && streamCryptoName instanceof Name) { + cfDict.suppressEncryption = true; + const handlerDict = cfDict.get(streamCryptoName.name); + keyLength = handlerDict?.get("Length") || 128; + if (keyLength < 40) { + keyLength <<= 3; + } + } + } + } + if (!Number.isInteger(keyLength) || keyLength < 40 || keyLength % 8 !== 0) { + throw new FormatError("invalid key length"); + } + const ownerBytes = stringToBytes(dict.get("O")), + userBytes = stringToBytes(dict.get("U")); + const ownerPassword = ownerBytes.subarray(0, 32); + const userPassword = userBytes.subarray(0, 32); + const flags = dict.get("P"); + const revision = dict.get("R"); + const encryptMetadata = (algorithm === 4 || algorithm === 5) && dict.get("EncryptMetadata") !== false; + this.encryptMetadata = encryptMetadata; + const fileIdBytes = stringToBytes(fileId); + let passwordBytes; + if (password) { + if (revision === 6) { + try { + password = utf8StringToString(password); + } catch { + warn("CipherTransformFactory: Unable to convert UTF8 encoded password."); + } + } + passwordBytes = stringToBytes(password); + } + let encryptionKey; + if (algorithm !== 5) { + encryptionKey = this.#prepareKeyData(fileIdBytes, passwordBytes, ownerPassword, userPassword, flags, revision, keyLength, encryptMetadata); + } else { + const ownerValidationSalt = ownerBytes.subarray(32, 40); + const ownerKeySalt = ownerBytes.subarray(40, 48); + const uBytes = userBytes.subarray(0, 48); + const userValidationSalt = userBytes.subarray(32, 40); + const userKeySalt = userBytes.subarray(40, 48); + const ownerEncryption = stringToBytes(dict.get("OE")); + const userEncryption = stringToBytes(dict.get("UE")); + const perms = stringToBytes(dict.get("Perms")); + encryptionKey = this.#createEncryptionKey20(revision, passwordBytes, ownerPassword, ownerValidationSalt, ownerKeySalt, uBytes, userPassword, userValidationSalt, userKeySalt, ownerEncryption, userEncryption, perms); + } + if (!encryptionKey && !password) { + throw new PasswordException("No password given", PasswordResponses.NEED_PASSWORD); + } else if (!encryptionKey && password) { + const decodedPassword = this.#decodeUserPassword(passwordBytes, ownerPassword, revision, keyLength); + encryptionKey = this.#prepareKeyData(fileIdBytes, decodedPassword, ownerPassword, userPassword, flags, revision, keyLength, encryptMetadata); + } + if (!encryptionKey) { + throw new PasswordException("Incorrect Password", PasswordResponses.INCORRECT_PASSWORD); + } + this.encryptionKey = encryptionKey; + if (algorithm >= 4) { + const cf = dict.get("CF"); + if (cf instanceof Dict) { + cf.suppressEncryption = true; + } + this.cf = cf; + this.stmf = dict.get("StmF") || Name.get("Identity"); + this.strf = dict.get("StrF") || Name.get("Identity"); + this.eff = dict.get("EFF") || this.stmf; + } + } + createCipherTransform(num, gen) { + if (this.algorithm === 4 || this.algorithm === 5) { + return new CipherTransform(this.#buildCipherConstructor(this.cf, this.strf, num, gen, this.encryptionKey), this.#buildCipherConstructor(this.cf, this.stmf, num, gen, this.encryptionKey)); + } + const key = this.#buildObjectKey(num, gen, this.encryptionKey, false); + const cipherConstructor = function () { + return new ARCFourCipher(key); + }; + return new CipherTransform(cipherConstructor, cipherConstructor); + } +} + +;// ./src/core/dataset_reader.js + + + +function decodeString(str) { + try { + return stringToUTF8String(str); + } catch (ex) { + warn(`UTF-8 decoding failed: "${ex}".`); + return str; + } +} +class DatasetXMLParser extends SimpleXMLParser { + constructor(options) { + super(options); + this.node = null; + } + onEndElement(name) { + const node = super.onEndElement(name); + if (node && name === "xfa:datasets") { + this.node = node; + throw new Error("Aborting DatasetXMLParser."); + } + } +} +class DatasetReader { + constructor(data) { + if (data.datasets) { + this.node = new SimpleXMLParser({ + hasAttributes: true + }).parseFromString(data.datasets).documentElement; + } else { + const parser = new DatasetXMLParser({ + hasAttributes: true + }); + try { + parser.parseFromString(data["xdp:xdp"]); + } catch {} + this.node = parser.node; + } + } + getValue(path) { + if (!this.node || !path) { + return ""; + } + const node = this.node.searchNode(parseXFAPath(path), 0); + if (!node) { + return ""; + } + const first = node.firstChild; + if (first?.nodeName === "value") { + return node.children.map(child => decodeString(child.textContent)); + } + return decodeString(node.textContent); + } +} + +;// ./src/core/xref.js + + + + + + +class XRef { + #firstXRefStmPos = null; + constructor(stream, pdfManager) { + this.stream = stream; + this.pdfManager = pdfManager; + this.entries = []; + this._xrefStms = new Set(); + this._cacheMap = new Map(); + this._pendingRefs = new RefSet(); + this._newPersistentRefNum = null; + this._newTemporaryRefNum = null; + this._persistentRefsCache = null; + } + getNewPersistentRef(obj) { + if (this._newPersistentRefNum === null) { + this._newPersistentRefNum = this.entries.length || 1; + } + const num = this._newPersistentRefNum++; + this._cacheMap.set(num, obj); + return Ref.get(num, 0); + } + getNewTemporaryRef() { + if (this._newTemporaryRefNum === null) { + this._newTemporaryRefNum = this.entries.length || 1; + if (this._newPersistentRefNum) { + this._persistentRefsCache = new Map(); + for (let i = this._newTemporaryRefNum; i < this._newPersistentRefNum; i++) { + this._persistentRefsCache.set(i, this._cacheMap.get(i)); + this._cacheMap.delete(i); + } + } + } + return Ref.get(this._newTemporaryRefNum++, 0); + } + resetNewTemporaryRef() { + this._newTemporaryRefNum = null; + if (this._persistentRefsCache) { + for (const [num, obj] of this._persistentRefsCache) { + this._cacheMap.set(num, obj); + } + } + this._persistentRefsCache = null; + } + setStartXRef(startXRef) { + this.startXRefQueue = [startXRef]; + } + parse(recoveryMode = false) { + let trailerDict; + if (!recoveryMode) { + trailerDict = this.readXRef(); + } else { + warn("Indexing all PDF objects"); + trailerDict = this.indexObjects(); + } + trailerDict.assignXref(this); + this.trailer = trailerDict; + let encrypt; + try { + encrypt = trailerDict.get("Encrypt"); + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn(`XRef.parse - Invalid "Encrypt" reference: "${ex}".`); + } + if (encrypt instanceof Dict) { + const ids = trailerDict.get("ID"); + const fileId = ids?.length ? ids[0] : ""; + encrypt.suppressEncryption = true; + this.encrypt = new CipherTransformFactory(encrypt, fileId, this.pdfManager.password); + } + let root; + try { + root = trailerDict.get("Root"); + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn(`XRef.parse - Invalid "Root" reference: "${ex}".`); + } + if (root instanceof Dict) { + try { + const pages = root.get("Pages"); + if (pages instanceof Dict) { + this.root = root; + return; + } + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn(`XRef.parse - Invalid "Pages" reference: "${ex}".`); + } + } + if (!recoveryMode) { + throw new XRefParseException(); + } + throw new InvalidPDFException("Invalid Root reference."); + } + processXRefTable(parser) { + if (!("tableState" in this)) { + this.tableState = { + entryNum: 0, + streamPos: parser.lexer.stream.pos, + parserBuf1: parser.buf1, + parserBuf2: parser.buf2 + }; + } + const obj = this.readXRefTable(parser); + if (!isCmd(obj, "trailer")) { + throw new FormatError("Invalid XRef table: could not find trailer dictionary"); + } + let dict = parser.getObj(); + if (!(dict instanceof Dict) && dict.dict) { + dict = dict.dict; + } + if (!(dict instanceof Dict)) { + throw new FormatError("Invalid XRef table: could not parse trailer dictionary"); + } + delete this.tableState; + return dict; + } + readXRefTable(parser) { + const stream = parser.lexer.stream; + const tableState = this.tableState; + stream.pos = tableState.streamPos; + parser.buf1 = tableState.parserBuf1; + parser.buf2 = tableState.parserBuf2; + let obj; + while (true) { + if (!("firstEntryNum" in tableState) || !("entryCount" in tableState)) { + if (isCmd(obj = parser.getObj(), "trailer")) { + break; + } + tableState.firstEntryNum = obj; + tableState.entryCount = parser.getObj(); + } + let first = tableState.firstEntryNum; + const count = tableState.entryCount; + if (!Number.isInteger(first) || !Number.isInteger(count)) { + throw new FormatError("Invalid XRef table: wrong types in subsection header"); + } + for (let i = tableState.entryNum; i < count; i++) { + tableState.streamPos = stream.pos; + tableState.entryNum = i; + tableState.parserBuf1 = parser.buf1; + tableState.parserBuf2 = parser.buf2; + const entry = {}; + entry.offset = parser.getObj(); + entry.gen = parser.getObj(); + const type = parser.getObj(); + if (type instanceof Cmd) { + switch (type.cmd) { + case "f": + entry.free = true; + break; + case "n": + entry.uncompressed = true; + break; + } + } + if (!Number.isInteger(entry.offset) || !Number.isInteger(entry.gen) || !(entry.free || entry.uncompressed)) { + throw new FormatError(`Invalid entry in XRef subsection: ${first}, ${count}`); + } + if (i === 0 && entry.free && first === 1) { + first = 0; + } + if (!this.entries[i + first]) { + this.entries[i + first] = entry; + } + } + tableState.entryNum = 0; + tableState.streamPos = stream.pos; + tableState.parserBuf1 = parser.buf1; + tableState.parserBuf2 = parser.buf2; + delete tableState.firstEntryNum; + delete tableState.entryCount; + } + if (this.entries[0] && !this.entries[0].free) { + throw new FormatError("Invalid XRef table: unexpected first object"); + } + return obj; + } + processXRefStream(stream) { + if (!("streamState" in this)) { + const { + dict, + pos + } = stream; + const byteWidths = dict.get("W"); + const range = dict.get("Index") || [0, dict.get("Size")]; + this.streamState = { + entryRanges: range, + byteWidths, + entryNum: 0, + streamPos: pos + }; + } + this.readXRefStream(stream); + delete this.streamState; + return stream.dict; + } + readXRefStream(stream) { + const streamState = this.streamState; + stream.pos = streamState.streamPos; + const [typeFieldWidth, offsetFieldWidth, generationFieldWidth] = streamState.byteWidths; + const entryRanges = streamState.entryRanges; + while (entryRanges.length > 0) { + const [first, n] = entryRanges; + if (!Number.isInteger(first) || !Number.isInteger(n)) { + throw new FormatError(`Invalid XRef range fields: ${first}, ${n}`); + } + if (!Number.isInteger(typeFieldWidth) || !Number.isInteger(offsetFieldWidth) || !Number.isInteger(generationFieldWidth)) { + throw new FormatError(`Invalid XRef entry fields length: ${first}, ${n}`); + } + for (let i = streamState.entryNum; i < n; ++i) { + streamState.entryNum = i; + streamState.streamPos = stream.pos; + let type = 0, + offset = 0, + generation = 0; + for (let j = 0; j < typeFieldWidth; ++j) { + const typeByte = stream.getByte(); + if (typeByte === -1) { + throw new FormatError("Invalid XRef byteWidths 'type'."); + } + type = type << 8 | typeByte; + } + if (typeFieldWidth === 0) { + type = 1; + } + for (let j = 0; j < offsetFieldWidth; ++j) { + const offsetByte = stream.getByte(); + if (offsetByte === -1) { + throw new FormatError("Invalid XRef byteWidths 'offset'."); + } + offset = offset << 8 | offsetByte; + } + for (let j = 0; j < generationFieldWidth; ++j) { + const generationByte = stream.getByte(); + if (generationByte === -1) { + throw new FormatError("Invalid XRef byteWidths 'generation'."); + } + generation = generation << 8 | generationByte; + } + const entry = {}; + entry.offset = offset; + entry.gen = generation; + switch (type) { + case 0: + entry.free = true; + break; + case 1: + entry.uncompressed = true; + break; + case 2: + break; + default: + throw new FormatError(`Invalid XRef entry type: ${type}`); + } + if (!this.entries[first + i]) { + this.entries[first + i] = entry; + } + } + streamState.entryNum = 0; + streamState.streamPos = stream.pos; + entryRanges.splice(0, 2); + } + } + indexObjects() { + const TAB = 0x9, + LF = 0xa, + CR = 0xd, + SPACE = 0x20; + const PERCENT = 0x25, + LT = 0x3c; + function readToken(data, offset) { + let token = "", + ch = data[offset]; + while (ch !== LF && ch !== CR && ch !== LT) { + if (++offset >= data.length) { + break; + } + token += String.fromCharCode(ch); + ch = data[offset]; + } + return token; + } + function skipUntil(data, offset, what) { + const length = what.length, + dataLength = data.length; + let skipped = 0; + while (offset < dataLength) { + let i = 0; + while (i < length && data[offset + i] === what[i]) { + ++i; + } + if (i >= length) { + break; + } + offset++; + skipped++; + } + return skipped; + } + const gEndobjRegExp = /\b(endobj|\d+\s+\d+\s+obj|xref|trailer\s*<<)\b/g; + const gStartxrefRegExp = /\b(startxref|\d+\s+\d+\s+obj)\b/g; + const objRegExp = /^(\d+)\s+(\d+)\s+obj\b/; + const trailerBytes = new Uint8Array([116, 114, 97, 105, 108, 101, 114]); + const startxrefBytes = new Uint8Array([115, 116, 97, 114, 116, 120, 114, 101, 102]); + const xrefBytes = new Uint8Array([47, 88, 82, 101, 102]); + this.entries.length = 0; + this._cacheMap.clear(); + const stream = this.stream; + stream.pos = 0; + const buffer = stream.getBytes(), + bufferStr = bytesToString(buffer), + length = buffer.length; + let position = stream.start; + const trailers = [], + xrefStms = []; + while (position < length) { + let ch = buffer[position]; + if (ch === TAB || ch === LF || ch === CR || ch === SPACE) { + ++position; + continue; + } + if (ch === PERCENT) { + do { + ++position; + if (position >= length) { + break; + } + ch = buffer[position]; + } while (ch !== LF && ch !== CR); + continue; + } + const token = readToken(buffer, position); + let m; + if (token.startsWith("xref") && (token.length === 4 || /\s/.test(token[4]))) { + position += skipUntil(buffer, position, trailerBytes); + trailers.push(position); + position += skipUntil(buffer, position, startxrefBytes); + } else if (m = objRegExp.exec(token)) { + const num = m[1] | 0, + gen = m[2] | 0; + const startPos = position + token.length; + let contentLength, + updateEntries = false; + if (!this.entries[num]) { + updateEntries = true; + } else if (this.entries[num].gen === gen) { + try { + const parser = new Parser({ + lexer: new Lexer(stream.makeSubStream(startPos)) + }); + parser.getObj(); + updateEntries = true; + } catch (ex) { + if (ex instanceof ParserEOFException) { + warn(`indexObjects -- checking object (${token}): "${ex}".`); + } else { + updateEntries = true; + } + } + } + if (updateEntries) { + this.entries[num] = { + offset: position - stream.start, + gen, + uncompressed: true + }; + } + gEndobjRegExp.lastIndex = startPos; + const match = gEndobjRegExp.exec(bufferStr); + if (match) { + const endPos = gEndobjRegExp.lastIndex + 1; + contentLength = endPos - position; + if (match[1] !== "endobj") { + warn(`indexObjects: Found "${match[1]}" inside of another "obj", ` + 'caused by missing "endobj" -- trying to recover.'); + contentLength -= match[1].length + 1; + } + } else { + contentLength = length - position; + } + const content = buffer.subarray(position, position + contentLength); + const xrefTagOffset = skipUntil(content, 0, xrefBytes); + if (xrefTagOffset < contentLength && content[xrefTagOffset + 5] < 64) { + xrefStms.push(position - stream.start); + this._xrefStms.add(position - stream.start); + } + position += contentLength; + } else if (token.startsWith("trailer") && (token.length === 7 || /\s/.test(token[7]))) { + trailers.push(position); + const startPos = position + token.length; + let contentLength; + gStartxrefRegExp.lastIndex = startPos; + const match = gStartxrefRegExp.exec(bufferStr); + if (match) { + const endPos = gStartxrefRegExp.lastIndex + 1; + contentLength = endPos - position; + if (match[1] !== "startxref") { + warn(`indexObjects: Found "${match[1]}" after "trailer", ` + 'caused by missing "startxref" -- trying to recover.'); + contentLength -= match[1].length + 1; + } + } else { + contentLength = length - position; + } + position += contentLength; + } else { + position += token.length + 1; + } + } + for (const xrefStm of xrefStms) { + this.startXRefQueue.push(xrefStm); + this.readXRef(true); + } + const trailerDicts = []; + let isEncrypted = false; + for (const trailer of trailers) { + stream.pos = trailer; + const parser = new Parser({ + lexer: new Lexer(stream), + xref: this, + allowStreams: true, + recoveryMode: true + }); + const obj = parser.getObj(); + if (!isCmd(obj, "trailer")) { + continue; + } + const dict = parser.getObj(); + if (!(dict instanceof Dict)) { + continue; + } + trailerDicts.push(dict); + if (dict.has("Encrypt")) { + isEncrypted = true; + } + } + let trailerDict, trailerError; + for (const dict of [...trailerDicts, "genFallback", ...trailerDicts]) { + if (dict === "genFallback") { + if (!trailerError) { + break; + } + this._generationFallback = true; + continue; + } + let validPagesDict = false; + try { + const rootDict = dict.get("Root"); + if (!(rootDict instanceof Dict)) { + continue; + } + const pagesDict = rootDict.get("Pages"); + if (!(pagesDict instanceof Dict)) { + continue; + } + const pagesCount = pagesDict.get("Count"); + if (Number.isInteger(pagesCount)) { + validPagesDict = true; + } + } catch (ex) { + trailerError = ex; + continue; + } + if (validPagesDict && (!isEncrypted || dict.has("Encrypt")) && dict.has("ID")) { + return dict; + } + trailerDict = dict; + } + if (trailerDict) { + return trailerDict; + } + if (this.topDict) { + return this.topDict; + } + if (!trailerDicts.length) { + for (const [num, entry] of this.entries.entries()) { + if (!entry) { + continue; + } + const ref = Ref.get(num, entry.gen); + let obj; + try { + obj = this.fetch(ref); + } catch { + continue; + } + if (obj instanceof BaseStream) { + obj = obj.dict; + } + if (obj instanceof Dict && obj.has("Root")) { + return obj; + } + } + } + throw new InvalidPDFException("Invalid PDF structure."); + } + readXRef(recoveryMode = false) { + const stream = this.stream; + const startXRefParsedCache = new Set(); + while (this.startXRefQueue.length) { + try { + const startXRef = this.startXRefQueue[0]; + if (startXRefParsedCache.has(startXRef)) { + warn("readXRef - skipping XRef table since it was already parsed."); + this.startXRefQueue.shift(); + continue; + } + startXRefParsedCache.add(startXRef); + stream.pos = startXRef + stream.start; + const parser = new Parser({ + lexer: new Lexer(stream), + xref: this, + allowStreams: true + }); + let obj = parser.getObj(); + let dict; + if (isCmd(obj, "xref")) { + dict = this.processXRefTable(parser); + if (!this.topDict) { + this.topDict = dict; + } + obj = dict.get("XRefStm"); + if (Number.isInteger(obj) && !this._xrefStms.has(obj)) { + this._xrefStms.add(obj); + this.startXRefQueue.push(obj); + this.#firstXRefStmPos ??= obj; + } + } else if (Number.isInteger(obj)) { + if (!Number.isInteger(parser.getObj()) || !isCmd(parser.getObj(), "obj") || !((obj = parser.getObj()) instanceof BaseStream)) { + throw new FormatError("Invalid XRef stream"); + } + dict = this.processXRefStream(obj); + if (!this.topDict) { + this.topDict = dict; + } + if (!dict) { + throw new FormatError("Failed to read XRef stream"); + } + } else { + throw new FormatError("Invalid XRef stream header"); + } + obj = dict.get("Prev"); + if (Number.isInteger(obj)) { + this.startXRefQueue.push(obj); + } else if (obj instanceof Ref) { + this.startXRefQueue.push(obj.num); + } + } catch (e) { + if (e instanceof MissingDataException) { + throw e; + } + info("(while reading XRef): " + e); + } + this.startXRefQueue.shift(); + } + if (this.topDict) { + return this.topDict; + } + if (recoveryMode) { + return undefined; + } + throw new XRefParseException(); + } + get lastXRefStreamPos() { + return this.#firstXRefStmPos ?? (this._xrefStms.size > 0 ? Math.max(...this._xrefStms) : null); + } + getEntry(i) { + const xrefEntry = this.entries[i]; + if (xrefEntry && !xrefEntry.free && xrefEntry.offset) { + return xrefEntry; + } + return null; + } + fetchIfRef(obj, suppressEncryption = false) { + if (obj instanceof Ref) { + return this.fetch(obj, suppressEncryption); + } + return obj; + } + fetch(ref, suppressEncryption = false) { + if (!(ref instanceof Ref)) { + throw new Error("ref object is not a reference"); + } + const num = ref.num; + const cacheEntry = this._cacheMap.get(num); + if (cacheEntry !== undefined) { + if (cacheEntry instanceof Dict && !cacheEntry.objId) { + cacheEntry.objId = ref.toString(); + } + return cacheEntry; + } + let xrefEntry = this.getEntry(num); + if (xrefEntry === null) { + this._cacheMap.set(num, xrefEntry); + return xrefEntry; + } + if (this._pendingRefs.has(ref)) { + this._pendingRefs.remove(ref); + warn(`Ignoring circular reference: ${ref}.`); + return CIRCULAR_REF; + } + this._pendingRefs.put(ref); + try { + xrefEntry = xrefEntry.uncompressed ? this.fetchUncompressed(ref, xrefEntry, suppressEncryption) : this.fetchCompressed(ref, xrefEntry, suppressEncryption); + this._pendingRefs.remove(ref); + } catch (ex) { + this._pendingRefs.remove(ref); + throw ex; + } + if (xrefEntry instanceof Dict) { + xrefEntry.objId = ref.toString(); + } else if (xrefEntry instanceof BaseStream) { + xrefEntry.dict.objId = ref.toString(); + } + return xrefEntry; + } + fetchUncompressed(ref, xrefEntry, suppressEncryption = false) { + const gen = ref.gen; + let num = ref.num; + if (xrefEntry.gen !== gen) { + const msg = `Inconsistent generation in XRef: ${ref}`; + if (this._generationFallback && xrefEntry.gen < gen) { + warn(msg); + return this.fetchUncompressed(Ref.get(num, xrefEntry.gen), xrefEntry, suppressEncryption); + } + throw new XRefEntryException(msg); + } + const stream = this.stream.makeSubStream(xrefEntry.offset + this.stream.start); + const parser = new Parser({ + lexer: new Lexer(stream), + xref: this, + allowStreams: true + }); + const obj1 = parser.getObj(); + const obj2 = parser.getObj(); + const obj3 = parser.getObj(); + if (obj1 !== num || obj2 !== gen || !(obj3 instanceof Cmd)) { + throw new XRefEntryException(`Bad (uncompressed) XRef entry: ${ref}`); + } + if (obj3.cmd !== "obj") { + if (obj3.cmd.startsWith("obj")) { + num = parseInt(obj3.cmd.substring(3), 10); + if (!Number.isNaN(num)) { + return num; + } + } + throw new XRefEntryException(`Bad (uncompressed) XRef entry: ${ref}`); + } + xrefEntry = this.encrypt && !suppressEncryption ? parser.getObj(this.encrypt.createCipherTransform(num, gen)) : parser.getObj(); + if (!(xrefEntry instanceof BaseStream)) { + this._cacheMap.set(num, xrefEntry); + } + return xrefEntry; + } + fetchCompressed(ref, xrefEntry, suppressEncryption = false) { + const tableOffset = xrefEntry.offset; + const stream = this.fetch(Ref.get(tableOffset, 0)); + if (!(stream instanceof BaseStream)) { + throw new FormatError("bad ObjStm stream"); + } + const first = stream.dict.get("First"); + const n = stream.dict.get("N"); + if (!Number.isInteger(first) || !Number.isInteger(n)) { + throw new FormatError("invalid first and n parameters for ObjStm stream"); + } + let parser = new Parser({ + lexer: new Lexer(stream), + xref: this, + allowStreams: true + }); + const nums = new Array(n); + const offsets = new Array(n); + for (let i = 0; i < n; ++i) { + const num = parser.getObj(); + if (!Number.isInteger(num)) { + throw new FormatError(`invalid object number in the ObjStm stream: ${num}`); + } + const offset = parser.getObj(); + if (!Number.isInteger(offset)) { + throw new FormatError(`invalid object offset in the ObjStm stream: ${offset}`); + } + nums[i] = num; + offsets[i] = offset; + } + const start = (stream.start || 0) + first; + const entries = new Array(n); + for (let i = 0; i < n; ++i) { + const length = i < n - 1 ? offsets[i + 1] - offsets[i] : undefined; + if (length < 0) { + throw new FormatError("Invalid offset in the ObjStm stream."); + } + parser = new Parser({ + lexer: new Lexer(stream.makeSubStream(start + offsets[i], length, stream.dict)), + xref: this, + allowStreams: true + }); + const obj = parser.getObj(); + entries[i] = obj; + if (obj instanceof BaseStream) { + continue; + } + const num = nums[i], + entry = this.entries[num]; + if (entry && entry.offset === tableOffset && entry.gen === i) { + this._cacheMap.set(num, obj); + } + } + xrefEntry = entries[xrefEntry.gen]; + if (xrefEntry === undefined) { + throw new XRefEntryException(`Bad (compressed) XRef entry: ${ref}`); + } + return xrefEntry; + } + async fetchIfRefAsync(obj, suppressEncryption) { + if (obj instanceof Ref) { + return this.fetchAsync(obj, suppressEncryption); + } + return obj; + } + async fetchAsync(ref, suppressEncryption) { + try { + return this.fetch(ref, suppressEncryption); + } catch (ex) { + if (!(ex instanceof MissingDataException)) { + throw ex; + } + await this.pdfManager.requestRange(ex.begin, ex.end); + return this.fetchAsync(ref, suppressEncryption); + } + } + getCatalogObj() { + return this.root; + } +} + +;// ./src/core/document.js + + + + + + + + + + + + + + + + + + + +const LETTER_SIZE_MEDIABOX = [0, 0, 612, 792]; +class Page { + constructor({ + pdfManager, + xref, + pageIndex, + pageDict, + ref, + globalIdFactory, + fontCache, + builtInCMapCache, + standardFontDataCache, + globalImageCache, + systemFontCache, + nonBlendModesSet, + xfaFactory + }) { + this.pdfManager = pdfManager; + this.pageIndex = pageIndex; + this.pageDict = pageDict; + this.xref = xref; + this.ref = ref; + this.fontCache = fontCache; + this.builtInCMapCache = builtInCMapCache; + this.standardFontDataCache = standardFontDataCache; + this.globalImageCache = globalImageCache; + this.systemFontCache = systemFontCache; + this.nonBlendModesSet = nonBlendModesSet; + this.evaluatorOptions = pdfManager.evaluatorOptions; + this.resourcesPromise = null; + this.xfaFactory = xfaFactory; + const idCounters = { + obj: 0 + }; + this._localIdFactory = class extends globalIdFactory { + static createObjId() { + return `p${pageIndex}_${++idCounters.obj}`; + } + static getPageObjId() { + return `p${ref.toString()}`; + } + }; + } + _getInheritableProperty(key, getArray = false) { + const value = getInheritableProperty({ + dict: this.pageDict, + key, + getArray, + stopWhenFound: false + }); + if (!Array.isArray(value)) { + return value; + } + if (value.length === 1 || !(value[0] instanceof Dict)) { + return value[0]; + } + return Dict.merge({ + xref: this.xref, + dictArray: value + }); + } + get content() { + return this.pageDict.getArray("Contents"); + } + get resources() { + const resources = this._getInheritableProperty("Resources"); + return shadow(this, "resources", resources instanceof Dict ? resources : Dict.empty); + } + _getBoundingBox(name) { + if (this.xfaData) { + return this.xfaData.bbox; + } + const box = lookupNormalRect(this._getInheritableProperty(name, true), null); + if (box) { + if (box[2] - box[0] > 0 && box[3] - box[1] > 0) { + return box; + } + warn(`Empty, or invalid, /${name} entry.`); + } + return null; + } + get mediaBox() { + return shadow(this, "mediaBox", this._getBoundingBox("MediaBox") || LETTER_SIZE_MEDIABOX); + } + get cropBox() { + return shadow(this, "cropBox", this._getBoundingBox("CropBox") || this.mediaBox); + } + get userUnit() { + const obj = this.pageDict.get("UserUnit"); + return shadow(this, "userUnit", typeof obj === "number" && obj > 0 ? obj : 1.0); + } + get view() { + const { + cropBox, + mediaBox + } = this; + if (cropBox !== mediaBox && !isArrayEqual(cropBox, mediaBox)) { + const box = Util.intersect(cropBox, mediaBox); + if (box && box[2] - box[0] > 0 && box[3] - box[1] > 0) { + return shadow(this, "view", box); + } + warn("Empty /CropBox and /MediaBox intersection."); + } + return shadow(this, "view", mediaBox); + } + get rotate() { + let rotate = this._getInheritableProperty("Rotate") || 0; + if (rotate % 90 !== 0) { + rotate = 0; + } else if (rotate >= 360) { + rotate %= 360; + } else if (rotate < 0) { + rotate = (rotate % 360 + 360) % 360; + } + return shadow(this, "rotate", rotate); + } + _onSubStreamError(reason, objId) { + if (this.evaluatorOptions.ignoreErrors) { + warn(`getContentStream - ignoring sub-stream (${objId}): "${reason}".`); + return; + } + throw reason; + } + getContentStream() { + return this.pdfManager.ensure(this, "content").then(content => { + if (content instanceof BaseStream) { + return content; + } + if (Array.isArray(content)) { + return new StreamsSequenceStream(content, this._onSubStreamError.bind(this)); + } + return new NullStream(); + }); + } + get xfaData() { + return shadow(this, "xfaData", this.xfaFactory ? { + bbox: this.xfaFactory.getBoundingBox(this.pageIndex) + } : null); + } + async #replaceIdByRef(annotations, deletedAnnotations, existingAnnotations) { + const promises = []; + for (const annotation of annotations) { + if (annotation.id) { + const ref = Ref.fromString(annotation.id); + if (!ref) { + warn(`A non-linked annotation cannot be modified: ${annotation.id}`); + continue; + } + if (annotation.deleted) { + deletedAnnotations.put(ref, ref); + if (annotation.popupRef) { + const popupRef = Ref.fromString(annotation.popupRef); + if (popupRef) { + deletedAnnotations.put(popupRef, popupRef); + } + } + continue; + } + existingAnnotations?.put(ref); + annotation.ref = ref; + promises.push(this.xref.fetchAsync(ref).then(obj => { + if (obj instanceof Dict) { + annotation.oldAnnotation = obj.clone(); + } + }, () => { + warn(`Cannot fetch \`oldAnnotation\` for: ${ref}.`); + })); + delete annotation.id; + } + } + await Promise.all(promises); + } + async saveNewAnnotations(handler, task, annotations, imagePromises, changes) { + if (this.xfaFactory) { + throw new Error("XFA: Cannot save new annotations."); + } + const partialEvaluator = new PartialEvaluator({ + xref: this.xref, + handler, + pageIndex: this.pageIndex, + idFactory: this._localIdFactory, + fontCache: this.fontCache, + builtInCMapCache: this.builtInCMapCache, + standardFontDataCache: this.standardFontDataCache, + globalImageCache: this.globalImageCache, + systemFontCache: this.systemFontCache, + options: this.evaluatorOptions + }); + const deletedAnnotations = new RefSetCache(); + const existingAnnotations = new RefSet(); + await this.#replaceIdByRef(annotations, deletedAnnotations, existingAnnotations); + const pageDict = this.pageDict; + const annotationsArray = this.annotations.filter(a => !(a instanceof Ref && deletedAnnotations.has(a))); + const newData = await AnnotationFactory.saveNewAnnotations(partialEvaluator, task, annotations, imagePromises, changes); + for (const { + ref + } of newData.annotations) { + if (ref instanceof Ref && !existingAnnotations.has(ref)) { + annotationsArray.push(ref); + } + } + const dict = pageDict.clone(); + dict.set("Annots", annotationsArray); + changes.put(this.ref, { + data: dict + }); + for (const deletedRef of deletedAnnotations) { + changes.put(deletedRef, { + data: null + }); + } + } + save(handler, task, annotationStorage, changes) { + const partialEvaluator = new PartialEvaluator({ + xref: this.xref, + handler, + pageIndex: this.pageIndex, + idFactory: this._localIdFactory, + fontCache: this.fontCache, + builtInCMapCache: this.builtInCMapCache, + standardFontDataCache: this.standardFontDataCache, + globalImageCache: this.globalImageCache, + systemFontCache: this.systemFontCache, + options: this.evaluatorOptions + }); + return this._parsedAnnotations.then(function (annotations) { + const promises = []; + for (const annotation of annotations) { + promises.push(annotation.save(partialEvaluator, task, annotationStorage, changes).catch(function (reason) { + warn("save - ignoring annotation data during " + `"${task.name}" task: "${reason}".`); + return null; + })); + } + return Promise.all(promises); + }); + } + loadResources(keys) { + this.resourcesPromise ||= this.pdfManager.ensure(this, "resources"); + return this.resourcesPromise.then(() => { + const objectLoader = new ObjectLoader(this.resources, keys, this.xref); + return objectLoader.load(); + }); + } + getOperatorList({ + handler, + sink, + task, + intent, + cacheKey, + annotationStorage = null, + modifiedIds = null + }) { + const contentStreamPromise = this.getContentStream(); + const resourcesPromise = this.loadResources(["ColorSpace", "ExtGState", "Font", "Pattern", "Properties", "Shading", "XObject"]); + const partialEvaluator = new PartialEvaluator({ + xref: this.xref, + handler, + pageIndex: this.pageIndex, + idFactory: this._localIdFactory, + fontCache: this.fontCache, + builtInCMapCache: this.builtInCMapCache, + standardFontDataCache: this.standardFontDataCache, + globalImageCache: this.globalImageCache, + systemFontCache: this.systemFontCache, + options: this.evaluatorOptions + }); + const newAnnotsByPage = !this.xfaFactory ? getNewAnnotationsMap(annotationStorage) : null; + const newAnnots = newAnnotsByPage?.get(this.pageIndex); + let newAnnotationsPromise = Promise.resolve(null); + let deletedAnnotations = null; + if (newAnnots) { + const annotationGlobalsPromise = this.pdfManager.ensureDoc("annotationGlobals"); + let imagePromises; + const missingBitmaps = new Set(); + for (const { + bitmapId, + bitmap + } of newAnnots) { + if (bitmapId && !bitmap && !missingBitmaps.has(bitmapId)) { + missingBitmaps.add(bitmapId); + } + } + const { + isOffscreenCanvasSupported + } = this.evaluatorOptions; + if (missingBitmaps.size > 0) { + const annotationWithBitmaps = newAnnots.slice(); + for (const [key, annotation] of annotationStorage) { + if (!key.startsWith(AnnotationEditorPrefix)) { + continue; + } + if (annotation.bitmap && missingBitmaps.has(annotation.bitmapId)) { + annotationWithBitmaps.push(annotation); + } + } + imagePromises = AnnotationFactory.generateImages(annotationWithBitmaps, this.xref, isOffscreenCanvasSupported); + } else { + imagePromises = AnnotationFactory.generateImages(newAnnots, this.xref, isOffscreenCanvasSupported); + } + deletedAnnotations = new RefSet(); + newAnnotationsPromise = Promise.all([annotationGlobalsPromise, this.#replaceIdByRef(newAnnots, deletedAnnotations, null)]).then(([annotationGlobals]) => { + if (!annotationGlobals) { + return null; + } + return AnnotationFactory.printNewAnnotations(annotationGlobals, partialEvaluator, task, newAnnots, imagePromises); + }); + } + const pageListPromise = Promise.all([contentStreamPromise, resourcesPromise]).then(([contentStream]) => { + const opList = new OperatorList(intent, sink); + handler.send("StartRenderPage", { + transparency: partialEvaluator.hasBlendModes(this.resources, this.nonBlendModesSet), + pageIndex: this.pageIndex, + cacheKey + }); + return partialEvaluator.getOperatorList({ + stream: contentStream, + task, + resources: this.resources, + operatorList: opList + }).then(function () { + return opList; + }); + }); + return Promise.all([pageListPromise, this._parsedAnnotations, newAnnotationsPromise]).then(function ([pageOpList, annotations, newAnnotations]) { + if (newAnnotations) { + annotations = annotations.filter(a => !(a.ref && deletedAnnotations.has(a.ref))); + for (let i = 0, ii = newAnnotations.length; i < ii; i++) { + const newAnnotation = newAnnotations[i]; + if (newAnnotation.refToReplace) { + const j = annotations.findIndex(a => a.ref && isRefsEqual(a.ref, newAnnotation.refToReplace)); + if (j >= 0) { + annotations.splice(j, 1, newAnnotation); + newAnnotations.splice(i--, 1); + ii--; + } + } + } + annotations = annotations.concat(newAnnotations); + } + if (annotations.length === 0 || intent & RenderingIntentFlag.ANNOTATIONS_DISABLE) { + pageOpList.flush(true); + return { + length: pageOpList.totalLength + }; + } + const renderForms = !!(intent & RenderingIntentFlag.ANNOTATIONS_FORMS), + isEditing = !!(intent & RenderingIntentFlag.IS_EDITING), + intentAny = !!(intent & RenderingIntentFlag.ANY), + intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY), + intentPrint = !!(intent & RenderingIntentFlag.PRINT); + const opListPromises = []; + for (const annotation of annotations) { + if (intentAny || intentDisplay && annotation.mustBeViewed(annotationStorage, renderForms) && annotation.mustBeViewedWhenEditing(isEditing, modifiedIds) || intentPrint && annotation.mustBePrinted(annotationStorage)) { + opListPromises.push(annotation.getOperatorList(partialEvaluator, task, intent, annotationStorage).catch(function (reason) { + warn("getOperatorList - ignoring annotation data during " + `"${task.name}" task: "${reason}".`); + return { + opList: null, + separateForm: false, + separateCanvas: false + }; + })); + } + } + return Promise.all(opListPromises).then(function (opLists) { + let form = false, + canvas = false; + for (const { + opList, + separateForm, + separateCanvas + } of opLists) { + pageOpList.addOpList(opList); + form ||= separateForm; + canvas ||= separateCanvas; + } + pageOpList.flush(true, { + form, + canvas + }); + return { + length: pageOpList.totalLength + }; + }); + }); + } + async extractTextContent({ + handler, + task, + includeMarkedContent, + disableNormalization, + sink + }) { + const contentStreamPromise = this.getContentStream(); + const resourcesPromise = this.loadResources(["ExtGState", "Font", "Properties", "XObject"]); + const langPromise = this.pdfManager.ensureCatalog("lang"); + const [contentStream,, lang] = await Promise.all([contentStreamPromise, resourcesPromise, langPromise]); + const partialEvaluator = new PartialEvaluator({ + xref: this.xref, + handler, + pageIndex: this.pageIndex, + idFactory: this._localIdFactory, + fontCache: this.fontCache, + builtInCMapCache: this.builtInCMapCache, + standardFontDataCache: this.standardFontDataCache, + globalImageCache: this.globalImageCache, + systemFontCache: this.systemFontCache, + options: this.evaluatorOptions + }); + return partialEvaluator.getTextContent({ + stream: contentStream, + task, + resources: this.resources, + includeMarkedContent, + disableNormalization, + sink, + viewBox: this.view, + lang + }); + } + async getStructTree() { + const structTreeRoot = await this.pdfManager.ensureCatalog("structTreeRoot"); + if (!structTreeRoot) { + return null; + } + await this._parsedAnnotations; + const structTree = await this.pdfManager.ensure(this, "_parseStructTree", [structTreeRoot]); + return this.pdfManager.ensure(structTree, "serializable"); + } + _parseStructTree(structTreeRoot) { + const tree = new StructTreePage(structTreeRoot, this.pageDict); + tree.parse(this.ref); + return tree; + } + async getAnnotationsData(handler, task, intent) { + const annotations = await this._parsedAnnotations; + if (annotations.length === 0) { + return annotations; + } + const annotationsData = [], + textContentPromises = []; + let partialEvaluator; + const intentAny = !!(intent & RenderingIntentFlag.ANY), + intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY), + intentPrint = !!(intent & RenderingIntentFlag.PRINT); + for (const annotation of annotations) { + const isVisible = intentAny || intentDisplay && annotation.viewable; + if (isVisible || intentPrint && annotation.printable) { + annotationsData.push(annotation.data); + } + if (annotation.hasTextContent && isVisible) { + partialEvaluator ||= new PartialEvaluator({ + xref: this.xref, + handler, + pageIndex: this.pageIndex, + idFactory: this._localIdFactory, + fontCache: this.fontCache, + builtInCMapCache: this.builtInCMapCache, + standardFontDataCache: this.standardFontDataCache, + globalImageCache: this.globalImageCache, + systemFontCache: this.systemFontCache, + options: this.evaluatorOptions + }); + textContentPromises.push(annotation.extractTextContent(partialEvaluator, task, [-Infinity, -Infinity, Infinity, Infinity]).catch(function (reason) { + warn(`getAnnotationsData - ignoring textContent during "${task.name}" task: "${reason}".`); + })); + } + } + await Promise.all(textContentPromises); + return annotationsData; + } + get annotations() { + const annots = this._getInheritableProperty("Annots"); + return shadow(this, "annotations", Array.isArray(annots) ? annots : []); + } + get _parsedAnnotations() { + const promise = this.pdfManager.ensure(this, "annotations").then(async annots => { + if (annots.length === 0) { + return annots; + } + const [annotationGlobals, fieldObjects] = await Promise.all([this.pdfManager.ensureDoc("annotationGlobals"), this.pdfManager.ensureDoc("fieldObjects")]); + if (!annotationGlobals) { + return []; + } + const orphanFields = fieldObjects?.orphanFields; + const annotationPromises = []; + for (const annotationRef of annots) { + annotationPromises.push(AnnotationFactory.create(this.xref, annotationRef, annotationGlobals, this._localIdFactory, false, orphanFields, this.ref).catch(function (reason) { + warn(`_parsedAnnotations: "${reason}".`); + return null; + })); + } + const sortedAnnotations = []; + let popupAnnotations, widgetAnnotations; + for (const annotation of await Promise.all(annotationPromises)) { + if (!annotation) { + continue; + } + if (annotation instanceof WidgetAnnotation) { + (widgetAnnotations ||= []).push(annotation); + continue; + } + if (annotation instanceof PopupAnnotation) { + (popupAnnotations ||= []).push(annotation); + continue; + } + sortedAnnotations.push(annotation); + } + if (widgetAnnotations) { + sortedAnnotations.push(...widgetAnnotations); + } + if (popupAnnotations) { + sortedAnnotations.push(...popupAnnotations); + } + return sortedAnnotations; + }); + return shadow(this, "_parsedAnnotations", promise); + } + get jsActions() { + const actions = collectActions(this.xref, this.pageDict, PageActionEventType); + return shadow(this, "jsActions", actions); + } +} +const PDF_HEADER_SIGNATURE = new Uint8Array([0x25, 0x50, 0x44, 0x46, 0x2d]); +const STARTXREF_SIGNATURE = new Uint8Array([0x73, 0x74, 0x61, 0x72, 0x74, 0x78, 0x72, 0x65, 0x66]); +const ENDOBJ_SIGNATURE = new Uint8Array([0x65, 0x6e, 0x64, 0x6f, 0x62, 0x6a]); +function find(stream, signature, limit = 1024, backwards = false) { + const signatureLength = signature.length; + const scanBytes = stream.peekBytes(limit); + const scanLength = scanBytes.length - signatureLength; + if (scanLength <= 0) { + return false; + } + if (backwards) { + const signatureEnd = signatureLength - 1; + let pos = scanBytes.length - 1; + while (pos >= signatureEnd) { + let j = 0; + while (j < signatureLength && scanBytes[pos - j] === signature[signatureEnd - j]) { + j++; + } + if (j >= signatureLength) { + stream.pos += pos - signatureEnd; + return true; + } + pos--; + } + } else { + let pos = 0; + while (pos <= scanLength) { + let j = 0; + while (j < signatureLength && scanBytes[pos + j] === signature[j]) { + j++; + } + if (j >= signatureLength) { + stream.pos += pos; + return true; + } + pos++; + } + } + return false; +} +class PDFDocument { + constructor(pdfManager, stream) { + if (stream.length <= 0) { + throw new InvalidPDFException("The PDF file is empty, i.e. its size is zero bytes."); + } + this.pdfManager = pdfManager; + this.stream = stream; + this.xref = new XRef(stream, pdfManager); + this._pagePromises = new Map(); + this._version = null; + const idCounters = { + font: 0 + }; + this._globalIdFactory = class { + static getDocId() { + return `g_${pdfManager.docId}`; + } + static createFontId() { + return `f${++idCounters.font}`; + } + static createObjId() { + unreachable("Abstract method `createObjId` called."); + } + static getPageObjId() { + unreachable("Abstract method `getPageObjId` called."); + } + }; + } + parse(recoveryMode) { + this.xref.parse(recoveryMode); + this.catalog = new Catalog(this.pdfManager, this.xref); + } + get linearization() { + let linearization = null; + try { + linearization = Linearization.create(this.stream); + } catch (err) { + if (err instanceof MissingDataException) { + throw err; + } + info(err); + } + return shadow(this, "linearization", linearization); + } + get startXRef() { + const stream = this.stream; + let startXRef = 0; + if (this.linearization) { + stream.reset(); + if (find(stream, ENDOBJ_SIGNATURE)) { + stream.skip(6); + let ch = stream.peekByte(); + while (isWhiteSpace(ch)) { + stream.pos++; + ch = stream.peekByte(); + } + startXRef = stream.pos - stream.start; + } + } else { + const step = 1024; + const startXRefLength = STARTXREF_SIGNATURE.length; + let found = false, + pos = stream.end; + while (!found && pos > 0) { + pos -= step - startXRefLength; + if (pos < 0) { + pos = 0; + } + stream.pos = pos; + found = find(stream, STARTXREF_SIGNATURE, step, true); + } + if (found) { + stream.skip(9); + let ch; + do { + ch = stream.getByte(); + } while (isWhiteSpace(ch)); + let str = ""; + while (ch >= 0x20 && ch <= 0x39) { + str += String.fromCharCode(ch); + ch = stream.getByte(); + } + startXRef = parseInt(str, 10); + if (isNaN(startXRef)) { + startXRef = 0; + } + } + } + return shadow(this, "startXRef", startXRef); + } + checkHeader() { + const stream = this.stream; + stream.reset(); + if (!find(stream, PDF_HEADER_SIGNATURE)) { + return; + } + stream.moveStart(); + stream.skip(PDF_HEADER_SIGNATURE.length); + let version = "", + ch; + while ((ch = stream.getByte()) > 0x20 && version.length < 7) { + version += String.fromCharCode(ch); + } + if (PDF_VERSION_REGEXP.test(version)) { + this._version = version; + } else { + warn(`Invalid PDF header version: ${version}`); + } + } + parseStartXRef() { + this.xref.setStartXRef(this.startXRef); + } + get numPages() { + let num = 0; + if (this.catalog.hasActualNumPages) { + num = this.catalog.numPages; + } else if (this.xfaFactory) { + num = this.xfaFactory.getNumPages(); + } else if (this.linearization) { + num = this.linearization.numPages; + } else { + num = this.catalog.numPages; + } + return shadow(this, "numPages", num); + } + _hasOnlyDocumentSignatures(fields, recursionDepth = 0) { + const RECURSION_LIMIT = 10; + if (!Array.isArray(fields)) { + return false; + } + return fields.every(field => { + field = this.xref.fetchIfRef(field); + if (!(field instanceof Dict)) { + return false; + } + if (field.has("Kids")) { + if (++recursionDepth > RECURSION_LIMIT) { + warn("_hasOnlyDocumentSignatures: maximum recursion depth reached"); + return false; + } + return this._hasOnlyDocumentSignatures(field.get("Kids"), recursionDepth); + } + const isSignature = isName(field.get("FT"), "Sig"); + const rectangle = field.get("Rect"); + const isInvisible = Array.isArray(rectangle) && rectangle.every(value => value === 0); + return isSignature && isInvisible; + }); + } + get _xfaStreams() { + const acroForm = this.catalog.acroForm; + if (!acroForm) { + return null; + } + const xfa = acroForm.get("XFA"); + const entries = { + "xdp:xdp": "", + template: "", + datasets: "", + config: "", + connectionSet: "", + localeSet: "", + stylesheet: "", + "/xdp:xdp": "" + }; + if (xfa instanceof BaseStream && !xfa.isEmpty) { + entries["xdp:xdp"] = xfa; + return entries; + } + if (!Array.isArray(xfa) || xfa.length === 0) { + return null; + } + for (let i = 0, ii = xfa.length; i < ii; i += 2) { + let name; + if (i === 0) { + name = "xdp:xdp"; + } else if (i === ii - 2) { + name = "/xdp:xdp"; + } else { + name = xfa[i]; + } + if (!entries.hasOwnProperty(name)) { + continue; + } + const data = this.xref.fetchIfRef(xfa[i + 1]); + if (!(data instanceof BaseStream) || data.isEmpty) { + continue; + } + entries[name] = data; + } + return entries; + } + get xfaDatasets() { + const streams = this._xfaStreams; + if (!streams) { + return shadow(this, "xfaDatasets", null); + } + for (const key of ["datasets", "xdp:xdp"]) { + const stream = streams[key]; + if (!stream) { + continue; + } + try { + const str = stringToUTF8String(stream.getString()); + const data = { + [key]: str + }; + return shadow(this, "xfaDatasets", new DatasetReader(data)); + } catch { + warn("XFA - Invalid utf-8 string."); + break; + } + } + return shadow(this, "xfaDatasets", null); + } + get xfaData() { + const streams = this._xfaStreams; + if (!streams) { + return null; + } + const data = Object.create(null); + for (const [key, stream] of Object.entries(streams)) { + if (!stream) { + continue; + } + try { + data[key] = stringToUTF8String(stream.getString()); + } catch { + warn("XFA - Invalid utf-8 string."); + return null; + } + } + return data; + } + get xfaFactory() { + let data; + if (this.pdfManager.enableXfa && this.catalog.needsRendering && this.formInfo.hasXfa && !this.formInfo.hasAcroForm) { + data = this.xfaData; + } + return shadow(this, "xfaFactory", data ? new XFAFactory(data) : null); + } + get isPureXfa() { + return this.xfaFactory ? this.xfaFactory.isValid() : false; + } + get htmlForXfa() { + return this.xfaFactory ? this.xfaFactory.getPages() : null; + } + async loadXfaImages() { + const xfaImagesDict = await this.pdfManager.ensureCatalog("xfaImages"); + if (!xfaImagesDict) { + return; + } + const keys = xfaImagesDict.getKeys(); + const objectLoader = new ObjectLoader(xfaImagesDict, keys, this.xref); + await objectLoader.load(); + const xfaImages = new Map(); + for (const key of keys) { + const stream = xfaImagesDict.get(key); + if (stream instanceof BaseStream) { + xfaImages.set(key, stream.getBytes()); + } + } + this.xfaFactory.setImages(xfaImages); + } + async loadXfaFonts(handler, task) { + const acroForm = await this.pdfManager.ensureCatalog("acroForm"); + if (!acroForm) { + return; + } + const resources = await acroForm.getAsync("DR"); + if (!(resources instanceof Dict)) { + return; + } + const objectLoader = new ObjectLoader(resources, ["Font"], this.xref); + await objectLoader.load(); + const fontRes = resources.get("Font"); + if (!(fontRes instanceof Dict)) { + return; + } + const options = Object.assign(Object.create(null), this.pdfManager.evaluatorOptions); + options.useSystemFonts = false; + const partialEvaluator = new PartialEvaluator({ + xref: this.xref, + handler, + pageIndex: -1, + idFactory: this._globalIdFactory, + fontCache: this.catalog.fontCache, + builtInCMapCache: this.catalog.builtInCMapCache, + standardFontDataCache: this.catalog.standardFontDataCache, + options + }); + const operatorList = new OperatorList(); + const pdfFonts = []; + const initialState = { + get font() { + return pdfFonts.at(-1); + }, + set font(font) { + pdfFonts.push(font); + }, + clone() { + return this; + } + }; + const promises = []; + for (const [fontName, font] of fontRes) { + const descriptor = font.get("FontDescriptor"); + if (!(descriptor instanceof Dict)) { + continue; + } + let fontFamily = descriptor.get("FontFamily"); + fontFamily = fontFamily.replaceAll(/[ ]+(\d)/g, "$1"); + const fontWeight = descriptor.get("FontWeight"); + const italicAngle = -descriptor.get("ItalicAngle"); + const cssFontInfo = { + fontFamily, + fontWeight, + italicAngle + }; + if (!validateCSSFont(cssFontInfo)) { + continue; + } + promises.push(partialEvaluator.handleSetFont(resources, [Name.get(fontName), 1], null, operatorList, task, initialState, null, cssFontInfo).catch(function (reason) { + warn(`loadXfaFonts: "${reason}".`); + return null; + })); + } + await Promise.all(promises); + const missingFonts = this.xfaFactory.setFonts(pdfFonts); + if (!missingFonts) { + return; + } + options.ignoreErrors = true; + promises.length = 0; + pdfFonts.length = 0; + const reallyMissingFonts = new Set(); + for (const missing of missingFonts) { + if (!getXfaFontName(`${missing}-Regular`)) { + reallyMissingFonts.add(missing); + } + } + if (reallyMissingFonts.size) { + missingFonts.push("PdfJS-Fallback"); + } + for (const missing of missingFonts) { + if (reallyMissingFonts.has(missing)) { + continue; + } + for (const fontInfo of [{ + name: "Regular", + fontWeight: 400, + italicAngle: 0 + }, { + name: "Bold", + fontWeight: 700, + italicAngle: 0 + }, { + name: "Italic", + fontWeight: 400, + italicAngle: 12 + }, { + name: "BoldItalic", + fontWeight: 700, + italicAngle: 12 + }]) { + const name = `${missing}-${fontInfo.name}`; + const dict = getXfaFontDict(name); + promises.push(partialEvaluator.handleSetFont(resources, [Name.get(name), 1], null, operatorList, task, initialState, dict, { + fontFamily: missing, + fontWeight: fontInfo.fontWeight, + italicAngle: fontInfo.italicAngle + }).catch(function (reason) { + warn(`loadXfaFonts: "${reason}".`); + return null; + })); + } + } + await Promise.all(promises); + this.xfaFactory.appendFonts(pdfFonts, reallyMissingFonts); + } + async serializeXfaData(annotationStorage) { + return this.xfaFactory ? this.xfaFactory.serializeData(annotationStorage) : null; + } + get version() { + return this.catalog.version || this._version; + } + get formInfo() { + const formInfo = { + hasFields: false, + hasAcroForm: false, + hasXfa: false, + hasSignatures: false + }; + const acroForm = this.catalog.acroForm; + if (!acroForm) { + return shadow(this, "formInfo", formInfo); + } + try { + const fields = acroForm.get("Fields"); + const hasFields = Array.isArray(fields) && fields.length > 0; + formInfo.hasFields = hasFields; + const xfa = acroForm.get("XFA"); + formInfo.hasXfa = Array.isArray(xfa) && xfa.length > 0 || xfa instanceof BaseStream && !xfa.isEmpty; + const sigFlags = acroForm.get("SigFlags"); + const hasSignatures = !!(sigFlags & 0x1); + const hasOnlyDocumentSignatures = hasSignatures && this._hasOnlyDocumentSignatures(fields); + formInfo.hasAcroForm = hasFields && !hasOnlyDocumentSignatures; + formInfo.hasSignatures = hasSignatures; + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn(`Cannot fetch form information: "${ex}".`); + } + return shadow(this, "formInfo", formInfo); + } + get documentInfo() { + const docInfo = { + PDFFormatVersion: this.version, + Language: this.catalog.lang, + EncryptFilterName: this.xref.encrypt ? this.xref.encrypt.filterName : null, + IsLinearized: !!this.linearization, + IsAcroFormPresent: this.formInfo.hasAcroForm, + IsXFAPresent: this.formInfo.hasXfa, + IsCollectionPresent: !!this.catalog.collection, + IsSignaturesPresent: this.formInfo.hasSignatures + }; + let infoDict; + try { + infoDict = this.xref.trailer.get("Info"); + } catch (err) { + if (err instanceof MissingDataException) { + throw err; + } + info("The document information dictionary is invalid."); + } + if (!(infoDict instanceof Dict)) { + return shadow(this, "documentInfo", docInfo); + } + for (const key of infoDict.getKeys()) { + const value = infoDict.get(key); + switch (key) { + case "Title": + case "Author": + case "Subject": + case "Keywords": + case "Creator": + case "Producer": + case "CreationDate": + case "ModDate": + if (typeof value === "string") { + docInfo[key] = stringToPDFString(value); + continue; + } + break; + case "Trapped": + if (value instanceof Name) { + docInfo[key] = value; + continue; + } + break; + default: + let customValue; + switch (typeof value) { + case "string": + customValue = stringToPDFString(value); + break; + case "number": + case "boolean": + customValue = value; + break; + default: + if (value instanceof Name) { + customValue = value; + } + break; + } + if (customValue === undefined) { + warn(`Bad value, for custom key "${key}", in Info: ${value}.`); + continue; + } + if (!docInfo.Custom) { + docInfo.Custom = Object.create(null); + } + docInfo.Custom[key] = customValue; + continue; + } + warn(`Bad value, for key "${key}", in Info: ${value}.`); + } + return shadow(this, "documentInfo", docInfo); + } + get fingerprints() { + const FINGERPRINT_FIRST_BYTES = 1024; + const EMPTY_FINGERPRINT = "\x00".repeat(16); + function validate(data) { + return typeof data === "string" && data.length === 16 && data !== EMPTY_FINGERPRINT; + } + const id = this.xref.trailer.get("ID"); + let hashOriginal, hashModified; + if (Array.isArray(id) && validate(id[0])) { + hashOriginal = stringToBytes(id[0]); + if (id[1] !== id[0] && validate(id[1])) { + hashModified = stringToBytes(id[1]); + } + } else { + hashOriginal = calculateMD5(this.stream.getByteRange(0, FINGERPRINT_FIRST_BYTES), 0, FINGERPRINT_FIRST_BYTES); + } + return shadow(this, "fingerprints", [toHexUtil(hashOriginal), hashModified ? toHexUtil(hashModified) : null]); + } + async _getLinearizationPage(pageIndex) { + const { + catalog, + linearization, + xref + } = this; + const ref = Ref.get(linearization.objectNumberFirst, 0); + try { + const obj = await xref.fetchAsync(ref); + if (obj instanceof Dict) { + let type = obj.getRaw("Type"); + if (type instanceof Ref) { + type = await xref.fetchAsync(type); + } + if (isName(type, "Page") || !obj.has("Type") && !obj.has("Kids") && obj.has("Contents")) { + if (!catalog.pageKidsCountCache.has(ref)) { + catalog.pageKidsCountCache.put(ref, 1); + } + if (!catalog.pageIndexCache.has(ref)) { + catalog.pageIndexCache.put(ref, 0); + } + return [obj, ref]; + } + } + throw new FormatError("The Linearization dictionary doesn't point to a valid Page dictionary."); + } catch (reason) { + warn(`_getLinearizationPage: "${reason.message}".`); + return catalog.getPageDict(pageIndex); + } + } + getPage(pageIndex) { + const cachedPromise = this._pagePromises.get(pageIndex); + if (cachedPromise) { + return cachedPromise; + } + const { + catalog, + linearization, + xfaFactory + } = this; + let promise; + if (xfaFactory) { + promise = Promise.resolve([Dict.empty, null]); + } else if (linearization?.pageFirst === pageIndex) { + promise = this._getLinearizationPage(pageIndex); + } else { + promise = catalog.getPageDict(pageIndex); + } + promise = promise.then(([pageDict, ref]) => { + return new Page({ + pdfManager: this.pdfManager, + xref: this.xref, + pageIndex, + pageDict, + ref, + globalIdFactory: this._globalIdFactory, + fontCache: catalog.fontCache, + builtInCMapCache: catalog.builtInCMapCache, + standardFontDataCache: catalog.standardFontDataCache, + globalImageCache: catalog.globalImageCache, + systemFontCache: catalog.systemFontCache, + nonBlendModesSet: catalog.nonBlendModesSet, + xfaFactory + }); + }); + this._pagePromises.set(pageIndex, promise); + return promise; + } + async checkFirstPage(recoveryMode = false) { + if (recoveryMode) { + return; + } + try { + await this.getPage(0); + } catch (reason) { + if (reason instanceof XRefEntryException) { + this._pagePromises.delete(0); + await this.cleanup(); + throw new XRefParseException(); + } + } + } + async checkLastPage(recoveryMode = false) { + const { + catalog, + pdfManager + } = this; + catalog.setActualNumPages(); + let numPages; + try { + await Promise.all([pdfManager.ensureDoc("xfaFactory"), pdfManager.ensureDoc("linearization"), pdfManager.ensureCatalog("numPages")]); + if (this.xfaFactory) { + return; + } else if (this.linearization) { + numPages = this.linearization.numPages; + } else { + numPages = catalog.numPages; + } + if (!Number.isInteger(numPages)) { + throw new FormatError("Page count is not an integer."); + } else if (numPages <= 1) { + return; + } + await this.getPage(numPages - 1); + } catch (reason) { + this._pagePromises.delete(numPages - 1); + await this.cleanup(); + if (reason instanceof XRefEntryException && !recoveryMode) { + throw new XRefParseException(); + } + warn(`checkLastPage - invalid /Pages tree /Count: ${numPages}.`); + let pagesTree; + try { + pagesTree = await catalog.getAllPageDicts(recoveryMode); + } catch (reasonAll) { + if (reasonAll instanceof XRefEntryException && !recoveryMode) { + throw new XRefParseException(); + } + catalog.setActualNumPages(1); + return; + } + for (const [pageIndex, [pageDict, ref]] of pagesTree) { + let promise; + if (pageDict instanceof Error) { + promise = Promise.reject(pageDict); + promise.catch(() => {}); + } else { + promise = Promise.resolve(new Page({ + pdfManager, + xref: this.xref, + pageIndex, + pageDict, + ref, + globalIdFactory: this._globalIdFactory, + fontCache: catalog.fontCache, + builtInCMapCache: catalog.builtInCMapCache, + standardFontDataCache: catalog.standardFontDataCache, + globalImageCache: catalog.globalImageCache, + systemFontCache: catalog.systemFontCache, + nonBlendModesSet: catalog.nonBlendModesSet, + xfaFactory: null + })); + } + this._pagePromises.set(pageIndex, promise); + } + catalog.setActualNumPages(pagesTree.size); + } + } + fontFallback(id, handler) { + return this.catalog.fontFallback(id, handler); + } + async cleanup(manuallyTriggered = false) { + return this.catalog ? this.catalog.cleanup(manuallyTriggered) : clearGlobalCaches(); + } + async #collectFieldObjects(name, parentRef, fieldRef, promises, annotationGlobals, visitedRefs, orphanFields) { + const { + xref + } = this; + if (!(fieldRef instanceof Ref) || visitedRefs.has(fieldRef)) { + return; + } + visitedRefs.put(fieldRef); + const field = await xref.fetchAsync(fieldRef); + if (!(field instanceof Dict)) { + return; + } + if (field.has("T")) { + const partName = stringToPDFString(await field.getAsync("T")); + name = name === "" ? partName : `${name}.${partName}`; + } else { + let obj = field; + while (true) { + obj = obj.getRaw("Parent") || parentRef; + if (obj instanceof Ref) { + if (visitedRefs.has(obj)) { + break; + } + obj = await xref.fetchAsync(obj); + } + if (!(obj instanceof Dict)) { + break; + } + if (obj.has("T")) { + const partName = stringToPDFString(await obj.getAsync("T")); + name = name === "" ? partName : `${name}.${partName}`; + break; + } + } + } + if (parentRef && !field.has("Parent") && isName(field.get("Subtype"), "Widget")) { + orphanFields.put(fieldRef, parentRef); + } + if (!promises.has(name)) { + promises.set(name, []); + } + promises.get(name).push(AnnotationFactory.create(xref, fieldRef, annotationGlobals, null, true, orphanFields, null).then(annotation => annotation?.getFieldObject()).catch(function (reason) { + warn(`#collectFieldObjects: "${reason}".`); + return null; + })); + if (!field.has("Kids")) { + return; + } + const kids = await field.getAsync("Kids"); + if (Array.isArray(kids)) { + for (const kid of kids) { + await this.#collectFieldObjects(name, fieldRef, kid, promises, annotationGlobals, visitedRefs, orphanFields); + } + } + } + get fieldObjects() { + const promise = this.pdfManager.ensureDoc("formInfo").then(async formInfo => { + if (!formInfo.hasFields) { + return null; + } + const [annotationGlobals, acroForm] = await Promise.all([this.pdfManager.ensureDoc("annotationGlobals"), this.pdfManager.ensureCatalog("acroForm")]); + if (!annotationGlobals) { + return null; + } + const visitedRefs = new RefSet(); + const allFields = Object.create(null); + const fieldPromises = new Map(); + const orphanFields = new RefSetCache(); + for (const fieldRef of await acroForm.getAsync("Fields")) { + await this.#collectFieldObjects("", null, fieldRef, fieldPromises, annotationGlobals, visitedRefs, orphanFields); + } + const allPromises = []; + for (const [name, promises] of fieldPromises) { + allPromises.push(Promise.all(promises).then(fields => { + fields = fields.filter(field => !!field); + if (fields.length > 0) { + allFields[name] = fields; + } + })); + } + await Promise.all(allPromises); + return { + allFields, + orphanFields + }; + }); + return shadow(this, "fieldObjects", promise); + } + get hasJSActions() { + const promise = this.pdfManager.ensureDoc("_parseHasJSActions"); + return shadow(this, "hasJSActions", promise); + } + async _parseHasJSActions() { + const [catalogJsActions, fieldObjects] = await Promise.all([this.pdfManager.ensureCatalog("jsActions"), this.pdfManager.ensureDoc("fieldObjects")]); + if (catalogJsActions) { + return true; + } + if (fieldObjects) { + return Object.values(fieldObjects.allFields).some(fieldObject => fieldObject.some(object => object.actions !== null)); + } + return false; + } + get calculationOrderIds() { + const calculationOrder = this.catalog.acroForm?.get("CO"); + if (!Array.isArray(calculationOrder) || calculationOrder.length === 0) { + return shadow(this, "calculationOrderIds", null); + } + const ids = []; + for (const id of calculationOrder) { + if (id instanceof Ref) { + ids.push(id.toString()); + } + } + return shadow(this, "calculationOrderIds", ids.length ? ids : null); + } + get annotationGlobals() { + return shadow(this, "annotationGlobals", AnnotationFactory.createGlobals(this.pdfManager)); + } +} + +;// ./src/core/pdf_manager.js + + + + + +function parseDocBaseUrl(url) { + if (url) { + const absoluteUrl = createValidAbsoluteUrl(url); + if (absoluteUrl) { + return absoluteUrl.href; + } + warn(`Invalid absolute docBaseUrl: "${url}".`); + } + return null; +} +class BasePdfManager { + constructor(args) { + this._docBaseUrl = parseDocBaseUrl(args.docBaseUrl); + this._docId = args.docId; + this._password = args.password; + this.enableXfa = args.enableXfa; + args.evaluatorOptions.isOffscreenCanvasSupported &&= FeatureTest.isOffscreenCanvasSupported; + args.evaluatorOptions.isImageDecoderSupported &&= FeatureTest.isImageDecoderSupported; + this.evaluatorOptions = Object.freeze(args.evaluatorOptions); + } + get docId() { + return this._docId; + } + get password() { + return this._password; + } + get docBaseUrl() { + return this._docBaseUrl; + } + get catalog() { + return this.pdfDocument.catalog; + } + ensureDoc(prop, args) { + return this.ensure(this.pdfDocument, prop, args); + } + ensureXRef(prop, args) { + return this.ensure(this.pdfDocument.xref, prop, args); + } + ensureCatalog(prop, args) { + return this.ensure(this.pdfDocument.catalog, prop, args); + } + getPage(pageIndex) { + return this.pdfDocument.getPage(pageIndex); + } + fontFallback(id, handler) { + return this.pdfDocument.fontFallback(id, handler); + } + loadXfaFonts(handler, task) { + return this.pdfDocument.loadXfaFonts(handler, task); + } + loadXfaImages() { + return this.pdfDocument.loadXfaImages(); + } + serializeXfaData(annotationStorage) { + return this.pdfDocument.serializeXfaData(annotationStorage); + } + cleanup(manuallyTriggered = false) { + return this.pdfDocument.cleanup(manuallyTriggered); + } + async ensure(obj, prop, args) { + unreachable("Abstract method `ensure` called"); + } + requestRange(begin, end) { + unreachable("Abstract method `requestRange` called"); + } + requestLoadedStream(noFetch = false) { + unreachable("Abstract method `requestLoadedStream` called"); + } + sendProgressiveData(chunk) { + unreachable("Abstract method `sendProgressiveData` called"); + } + updatePassword(password) { + this._password = password; + } + terminate(reason) { + unreachable("Abstract method `terminate` called"); + } +} +class LocalPdfManager extends BasePdfManager { + constructor(args) { + super(args); + const stream = new Stream(args.source); + this.pdfDocument = new PDFDocument(this, stream); + this._loadedStreamPromise = Promise.resolve(stream); + } + async ensure(obj, prop, args) { + const value = obj[prop]; + if (typeof value === "function") { + return value.apply(obj, args); + } + return value; + } + requestRange(begin, end) { + return Promise.resolve(); + } + requestLoadedStream(noFetch = false) { + return this._loadedStreamPromise; + } + terminate(reason) {} +} +class NetworkPdfManager extends BasePdfManager { + constructor(args) { + super(args); + this.streamManager = new ChunkedStreamManager(args.source, { + msgHandler: args.handler, + length: args.length, + disableAutoFetch: args.disableAutoFetch, + rangeChunkSize: args.rangeChunkSize + }); + this.pdfDocument = new PDFDocument(this, this.streamManager.getStream()); + } + async ensure(obj, prop, args) { + try { + const value = obj[prop]; + if (typeof value === "function") { + return value.apply(obj, args); + } + return value; + } catch (ex) { + if (!(ex instanceof MissingDataException)) { + throw ex; + } + await this.requestRange(ex.begin, ex.end); + return this.ensure(obj, prop, args); + } + } + requestRange(begin, end) { + return this.streamManager.requestRange(begin, end); + } + requestLoadedStream(noFetch = false) { + return this.streamManager.requestAllChunks(noFetch); + } + sendProgressiveData(chunk) { + this.streamManager.onReceiveData({ + chunk + }); + } + terminate(reason) { + this.streamManager.abort(reason); + } +} + +;// ./src/shared/message_handler.js + +const CallbackKind = { + DATA: 1, + ERROR: 2 +}; +const StreamKind = { + CANCEL: 1, + CANCEL_COMPLETE: 2, + CLOSE: 3, + ENQUEUE: 4, + ERROR: 5, + PULL: 6, + PULL_COMPLETE: 7, + START_COMPLETE: 8 +}; +function onFn() {} +function wrapReason(ex) { + if (ex instanceof AbortException || ex instanceof InvalidPDFException || ex instanceof MissingPDFException || ex instanceof PasswordException || ex instanceof UnexpectedResponseException || ex instanceof UnknownErrorException) { + return ex; + } + if (!(ex instanceof Error || typeof ex === "object" && ex !== null)) { + unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.'); + } + switch (ex.name) { + case "AbortException": + return new AbortException(ex.message); + case "InvalidPDFException": + return new InvalidPDFException(ex.message); + case "MissingPDFException": + return new MissingPDFException(ex.message); + case "PasswordException": + return new PasswordException(ex.message, ex.code); + case "UnexpectedResponseException": + return new UnexpectedResponseException(ex.message, ex.status); + case "UnknownErrorException": + return new UnknownErrorException(ex.message, ex.details); + } + return new UnknownErrorException(ex.message, ex.toString()); +} +class MessageHandler { + #messageAC = new AbortController(); + constructor(sourceName, targetName, comObj) { + this.sourceName = sourceName; + this.targetName = targetName; + this.comObj = comObj; + this.callbackId = 1; + this.streamId = 1; + this.streamSinks = Object.create(null); + this.streamControllers = Object.create(null); + this.callbackCapabilities = Object.create(null); + this.actionHandler = Object.create(null); + comObj.addEventListener("message", this.#onMessage.bind(this), { + signal: this.#messageAC.signal + }); + } + #onMessage({ + data + }) { + if (data.targetName !== this.sourceName) { + return; + } + if (data.stream) { + this.#processStreamMessage(data); + return; + } + if (data.callback) { + const callbackId = data.callbackId; + const capability = this.callbackCapabilities[callbackId]; + if (!capability) { + throw new Error(`Cannot resolve callback ${callbackId}`); + } + delete this.callbackCapabilities[callbackId]; + if (data.callback === CallbackKind.DATA) { + capability.resolve(data.data); + } else if (data.callback === CallbackKind.ERROR) { + capability.reject(wrapReason(data.reason)); + } else { + throw new Error("Unexpected callback case"); + } + return; + } + const action = this.actionHandler[data.action]; + if (!action) { + throw new Error(`Unknown action from worker: ${data.action}`); + } + if (data.callbackId) { + const sourceName = this.sourceName, + targetName = data.sourceName, + comObj = this.comObj; + Promise.try(action, data.data).then(function (result) { + comObj.postMessage({ + sourceName, + targetName, + callback: CallbackKind.DATA, + callbackId: data.callbackId, + data: result + }); + }, function (reason) { + comObj.postMessage({ + sourceName, + targetName, + callback: CallbackKind.ERROR, + callbackId: data.callbackId, + reason: wrapReason(reason) + }); + }); + return; + } + if (data.streamId) { + this.#createStreamSink(data); + return; + } + action(data.data); + } + on(actionName, handler) { + const ah = this.actionHandler; + if (ah[actionName]) { + throw new Error(`There is already an actionName called "${actionName}"`); + } + ah[actionName] = handler; + } + send(actionName, data, transfers) { + this.comObj.postMessage({ + sourceName: this.sourceName, + targetName: this.targetName, + action: actionName, + data + }, transfers); + } + sendWithPromise(actionName, data, transfers) { + const callbackId = this.callbackId++; + const capability = Promise.withResolvers(); + this.callbackCapabilities[callbackId] = capability; + try { + this.comObj.postMessage({ + sourceName: this.sourceName, + targetName: this.targetName, + action: actionName, + callbackId, + data + }, transfers); + } catch (ex) { + capability.reject(ex); + } + return capability.promise; + } + sendWithStream(actionName, data, queueingStrategy, transfers) { + const streamId = this.streamId++, + sourceName = this.sourceName, + targetName = this.targetName, + comObj = this.comObj; + return new ReadableStream({ + start: controller => { + const startCapability = Promise.withResolvers(); + this.streamControllers[streamId] = { + controller, + startCall: startCapability, + pullCall: null, + cancelCall: null, + isClosed: false + }; + comObj.postMessage({ + sourceName, + targetName, + action: actionName, + streamId, + data, + desiredSize: controller.desiredSize + }, transfers); + return startCapability.promise; + }, + pull: controller => { + const pullCapability = Promise.withResolvers(); + this.streamControllers[streamId].pullCall = pullCapability; + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.PULL, + streamId, + desiredSize: controller.desiredSize + }); + return pullCapability.promise; + }, + cancel: reason => { + assert(reason instanceof Error, "cancel must have a valid reason"); + const cancelCapability = Promise.withResolvers(); + this.streamControllers[streamId].cancelCall = cancelCapability; + this.streamControllers[streamId].isClosed = true; + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.CANCEL, + streamId, + reason: wrapReason(reason) + }); + return cancelCapability.promise; + } + }, queueingStrategy); + } + #createStreamSink(data) { + const streamId = data.streamId, + sourceName = this.sourceName, + targetName = data.sourceName, + comObj = this.comObj; + const self = this, + action = this.actionHandler[data.action]; + const streamSink = { + enqueue(chunk, size = 1, transfers) { + if (this.isCancelled) { + return; + } + const lastDesiredSize = this.desiredSize; + this.desiredSize -= size; + if (lastDesiredSize > 0 && this.desiredSize <= 0) { + this.sinkCapability = Promise.withResolvers(); + this.ready = this.sinkCapability.promise; + } + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.ENQUEUE, + streamId, + chunk + }, transfers); + }, + close() { + if (this.isCancelled) { + return; + } + this.isCancelled = true; + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.CLOSE, + streamId + }); + delete self.streamSinks[streamId]; + }, + error(reason) { + assert(reason instanceof Error, "error must have a valid reason"); + if (this.isCancelled) { + return; + } + this.isCancelled = true; + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.ERROR, + streamId, + reason: wrapReason(reason) + }); + }, + sinkCapability: Promise.withResolvers(), + onPull: null, + onCancel: null, + isCancelled: false, + desiredSize: data.desiredSize, + ready: null + }; + streamSink.sinkCapability.resolve(); + streamSink.ready = streamSink.sinkCapability.promise; + this.streamSinks[streamId] = streamSink; + Promise.try(action, data.data, streamSink).then(function () { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.START_COMPLETE, + streamId, + success: true + }); + }, function (reason) { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.START_COMPLETE, + streamId, + reason: wrapReason(reason) + }); + }); + } + #processStreamMessage(data) { + const streamId = data.streamId, + sourceName = this.sourceName, + targetName = data.sourceName, + comObj = this.comObj; + const streamController = this.streamControllers[streamId], + streamSink = this.streamSinks[streamId]; + switch (data.stream) { + case StreamKind.START_COMPLETE: + if (data.success) { + streamController.startCall.resolve(); + } else { + streamController.startCall.reject(wrapReason(data.reason)); + } + break; + case StreamKind.PULL_COMPLETE: + if (data.success) { + streamController.pullCall.resolve(); + } else { + streamController.pullCall.reject(wrapReason(data.reason)); + } + break; + case StreamKind.PULL: + if (!streamSink) { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.PULL_COMPLETE, + streamId, + success: true + }); + break; + } + if (streamSink.desiredSize <= 0 && data.desiredSize > 0) { + streamSink.sinkCapability.resolve(); + } + streamSink.desiredSize = data.desiredSize; + Promise.try(streamSink.onPull || onFn).then(function () { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.PULL_COMPLETE, + streamId, + success: true + }); + }, function (reason) { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.PULL_COMPLETE, + streamId, + reason: wrapReason(reason) + }); + }); + break; + case StreamKind.ENQUEUE: + assert(streamController, "enqueue should have stream controller"); + if (streamController.isClosed) { + break; + } + streamController.controller.enqueue(data.chunk); + break; + case StreamKind.CLOSE: + assert(streamController, "close should have stream controller"); + if (streamController.isClosed) { + break; + } + streamController.isClosed = true; + streamController.controller.close(); + this.#deleteStreamController(streamController, streamId); + break; + case StreamKind.ERROR: + assert(streamController, "error should have stream controller"); + streamController.controller.error(wrapReason(data.reason)); + this.#deleteStreamController(streamController, streamId); + break; + case StreamKind.CANCEL_COMPLETE: + if (data.success) { + streamController.cancelCall.resolve(); + } else { + streamController.cancelCall.reject(wrapReason(data.reason)); + } + this.#deleteStreamController(streamController, streamId); + break; + case StreamKind.CANCEL: + if (!streamSink) { + break; + } + const dataReason = wrapReason(data.reason); + Promise.try(streamSink.onCancel || onFn, dataReason).then(function () { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.CANCEL_COMPLETE, + streamId, + success: true + }); + }, function (reason) { + comObj.postMessage({ + sourceName, + targetName, + stream: StreamKind.CANCEL_COMPLETE, + streamId, + reason: wrapReason(reason) + }); + }); + streamSink.sinkCapability.reject(dataReason); + streamSink.isCancelled = true; + delete this.streamSinks[streamId]; + break; + default: + throw new Error("Unexpected stream case"); + } + } + async #deleteStreamController(streamController, streamId) { + await Promise.allSettled([streamController.startCall?.promise, streamController.pullCall?.promise, streamController.cancelCall?.promise]); + delete this.streamControllers[streamId]; + } + destroy() { + this.#messageAC?.abort(); + this.#messageAC = null; + } +} + +;// ./src/core/writer.js + + + + + + + +async function writeObject(ref, obj, buffer, { + encrypt = null +}) { + const transform = encrypt?.createCipherTransform(ref.num, ref.gen); + buffer.push(`${ref.num} ${ref.gen} obj\n`); + if (obj instanceof Dict) { + await writeDict(obj, buffer, transform); + } else if (obj instanceof BaseStream) { + await writeStream(obj, buffer, transform); + } else if (Array.isArray(obj) || ArrayBuffer.isView(obj)) { + await writeArray(obj, buffer, transform); + } + buffer.push("\nendobj\n"); +} +async function writeDict(dict, buffer, transform) { + buffer.push("<<"); + for (const key of dict.getKeys()) { + buffer.push(` /${escapePDFName(key)} `); + await writeValue(dict.getRaw(key), buffer, transform); + } + buffer.push(">>"); +} +async function writeStream(stream, buffer, transform) { + let bytes = stream.getBytes(); + const { + dict + } = stream; + const [filter, params] = await Promise.all([dict.getAsync("Filter"), dict.getAsync("DecodeParms")]); + const filterZero = Array.isArray(filter) ? await dict.xref.fetchIfRefAsync(filter[0]) : filter; + const isFilterZeroFlateDecode = isName(filterZero, "FlateDecode"); + const MIN_LENGTH_FOR_COMPRESSING = 256; + if (bytes.length >= MIN_LENGTH_FOR_COMPRESSING || isFilterZeroFlateDecode) { + try { + const cs = new CompressionStream("deflate"); + const writer = cs.writable.getWriter(); + await writer.ready; + writer.write(bytes).then(async () => { + await writer.ready; + await writer.close(); + }).catch(() => {}); + const buf = await new Response(cs.readable).arrayBuffer(); + bytes = new Uint8Array(buf); + let newFilter, newParams; + if (!filter) { + newFilter = Name.get("FlateDecode"); + } else if (!isFilterZeroFlateDecode) { + newFilter = Array.isArray(filter) ? [Name.get("FlateDecode"), ...filter] : [Name.get("FlateDecode"), filter]; + if (params) { + newParams = Array.isArray(params) ? [null, ...params] : [null, params]; + } + } + if (newFilter) { + dict.set("Filter", newFilter); + } + if (newParams) { + dict.set("DecodeParms", newParams); + } + } catch (ex) { + info(`writeStream - cannot compress data: "${ex}".`); + } + } + let string = bytesToString(bytes); + if (transform) { + string = transform.encryptString(string); + } + dict.set("Length", string.length); + await writeDict(dict, buffer, transform); + buffer.push(" stream\n", string, "\nendstream"); +} +async function writeArray(array, buffer, transform) { + buffer.push("["); + let first = true; + for (const val of array) { + if (!first) { + buffer.push(" "); + } else { + first = false; + } + await writeValue(val, buffer, transform); + } + buffer.push("]"); +} +async function writeValue(value, buffer, transform) { + if (value instanceof Name) { + buffer.push(`/${escapePDFName(value.name)}`); + } else if (value instanceof Ref) { + buffer.push(`${value.num} ${value.gen} R`); + } else if (Array.isArray(value) || ArrayBuffer.isView(value)) { + await writeArray(value, buffer, transform); + } else if (typeof value === "string") { + if (transform) { + value = transform.encryptString(value); + } + buffer.push(`(${escapeString(value)})`); + } else if (typeof value === "number") { + buffer.push(numberToString(value)); + } else if (typeof value === "boolean") { + buffer.push(value.toString()); + } else if (value instanceof Dict) { + await writeDict(value, buffer, transform); + } else if (value instanceof BaseStream) { + await writeStream(value, buffer, transform); + } else if (value === null) { + buffer.push("null"); + } else { + warn(`Unhandled value in writer: ${typeof value}, please file a bug.`); + } +} +function writeInt(number, size, offset, buffer) { + for (let i = size + offset - 1; i > offset - 1; i--) { + buffer[i] = number & 0xff; + number >>= 8; + } + return offset + size; +} +function writeString(string, offset, buffer) { + for (let i = 0, len = string.length; i < len; i++) { + buffer[offset + i] = string.charCodeAt(i) & 0xff; + } +} +function computeMD5(filesize, xrefInfo) { + const time = Math.floor(Date.now() / 1000); + const filename = xrefInfo.filename || ""; + const md5Buffer = [time.toString(), filename, filesize.toString()]; + let md5BufferLen = md5Buffer.reduce((a, str) => a + str.length, 0); + for (const value of Object.values(xrefInfo.info)) { + md5Buffer.push(value); + md5BufferLen += value.length; + } + const array = new Uint8Array(md5BufferLen); + let offset = 0; + for (const str of md5Buffer) { + writeString(str, offset, array); + offset += str.length; + } + return bytesToString(calculateMD5(array)); +} +function writeXFADataForAcroform(str, changes) { + const xml = new SimpleXMLParser({ + hasAttributes: true + }).parseFromString(str); + for (const { + xfa + } of changes) { + if (!xfa) { + continue; + } + const { + path, + value + } = xfa; + if (!path) { + continue; + } + const nodePath = parseXFAPath(path); + let node = xml.documentElement.searchNode(nodePath, 0); + if (!node && nodePath.length > 1) { + node = xml.documentElement.searchNode([nodePath.at(-1)], 0); + } + if (node) { + node.childNodes = Array.isArray(value) ? value.map(val => new SimpleDOMNode("value", val)) : [new SimpleDOMNode("#text", value)]; + } else { + warn(`Node not found for path: ${path}`); + } + } + const buffer = []; + xml.documentElement.dump(buffer); + return buffer.join(""); +} +async function updateAcroform({ + xref, + acroForm, + acroFormRef, + hasXfa, + hasXfaDatasetsEntry, + xfaDatasetsRef, + needAppearances, + changes +}) { + if (hasXfa && !hasXfaDatasetsEntry && !xfaDatasetsRef) { + warn("XFA - Cannot save it"); + } + if (!needAppearances && (!hasXfa || !xfaDatasetsRef || hasXfaDatasetsEntry)) { + return; + } + const dict = acroForm.clone(); + if (hasXfa && !hasXfaDatasetsEntry) { + const newXfa = acroForm.get("XFA").slice(); + newXfa.splice(2, 0, "datasets"); + newXfa.splice(3, 0, xfaDatasetsRef); + dict.set("XFA", newXfa); + } + if (needAppearances) { + dict.set("NeedAppearances", true); + } + changes.put(acroFormRef, { + data: dict + }); +} +function updateXFA({ + xfaData, + xfaDatasetsRef, + changes, + xref +}) { + if (xfaData === null) { + const datasets = xref.fetchIfRef(xfaDatasetsRef); + xfaData = writeXFADataForAcroform(datasets.getString(), changes); + } + const xfaDataStream = new StringStream(xfaData); + xfaDataStream.dict = new Dict(xref); + xfaDataStream.dict.set("Type", Name.get("EmbeddedFile")); + changes.put(xfaDatasetsRef, { + data: xfaDataStream + }); +} +async function getXRefTable(xrefInfo, baseOffset, newRefs, newXref, buffer) { + buffer.push("xref\n"); + const indexes = getIndexes(newRefs); + let indexesPosition = 0; + for (const { + ref, + data + } of newRefs) { + if (ref.num === indexes[indexesPosition]) { + buffer.push(`${indexes[indexesPosition]} ${indexes[indexesPosition + 1]}\n`); + indexesPosition += 2; + } + if (data !== null) { + buffer.push(`${baseOffset.toString().padStart(10, "0")} ${Math.min(ref.gen, 0xffff).toString().padStart(5, "0")} n\r\n`); + baseOffset += data.length; + } else { + buffer.push(`0000000000 ${Math.min(ref.gen + 1, 0xffff).toString().padStart(5, "0")} f\r\n`); + } + } + computeIDs(baseOffset, xrefInfo, newXref); + buffer.push("trailer\n"); + await writeDict(newXref, buffer); + buffer.push("\nstartxref\n", baseOffset.toString(), "\n%%EOF\n"); +} +function getIndexes(newRefs) { + const indexes = []; + for (const { + ref + } of newRefs) { + if (ref.num === indexes.at(-2) + indexes.at(-1)) { + indexes[indexes.length - 1] += 1; + } else { + indexes.push(ref.num, 1); + } + } + return indexes; +} +async function getXRefStreamTable(xrefInfo, baseOffset, newRefs, newXref, buffer) { + const xrefTableData = []; + let maxOffset = 0; + let maxGen = 0; + for (const { + ref, + data + } of newRefs) { + let gen; + maxOffset = Math.max(maxOffset, baseOffset); + if (data !== null) { + gen = Math.min(ref.gen, 0xffff); + xrefTableData.push([1, baseOffset, gen]); + baseOffset += data.length; + } else { + gen = Math.min(ref.gen + 1, 0xffff); + xrefTableData.push([0, 0, gen]); + } + maxGen = Math.max(maxGen, gen); + } + newXref.set("Index", getIndexes(newRefs)); + const offsetSize = getSizeInBytes(maxOffset); + const maxGenSize = getSizeInBytes(maxGen); + const sizes = [1, offsetSize, maxGenSize]; + newXref.set("W", sizes); + computeIDs(baseOffset, xrefInfo, newXref); + const structSize = sizes.reduce((a, x) => a + x, 0); + const data = new Uint8Array(structSize * xrefTableData.length); + const stream = new Stream(data); + stream.dict = newXref; + let offset = 0; + for (const [type, objOffset, gen] of xrefTableData) { + offset = writeInt(type, sizes[0], offset, data); + offset = writeInt(objOffset, sizes[1], offset, data); + offset = writeInt(gen, sizes[2], offset, data); + } + await writeObject(xrefInfo.newRef, stream, buffer, {}); + buffer.push("startxref\n", baseOffset.toString(), "\n%%EOF\n"); +} +function computeIDs(baseOffset, xrefInfo, newXref) { + if (Array.isArray(xrefInfo.fileIds) && xrefInfo.fileIds.length > 0) { + const md5 = computeMD5(baseOffset, xrefInfo); + newXref.set("ID", [xrefInfo.fileIds[0], md5]); + } +} +function getTrailerDict(xrefInfo, changes, useXrefStream) { + const newXref = new Dict(null); + newXref.set("Prev", xrefInfo.startXRef); + const refForXrefTable = xrefInfo.newRef; + if (useXrefStream) { + changes.put(refForXrefTable, { + data: "" + }); + newXref.set("Size", refForXrefTable.num + 1); + newXref.set("Type", Name.get("XRef")); + } else { + newXref.set("Size", refForXrefTable.num); + } + if (xrefInfo.rootRef !== null) { + newXref.set("Root", xrefInfo.rootRef); + } + if (xrefInfo.infoRef !== null) { + newXref.set("Info", xrefInfo.infoRef); + } + if (xrefInfo.encryptRef !== null) { + newXref.set("Encrypt", xrefInfo.encryptRef); + } + return newXref; +} +async function writeChanges(changes, xref, buffer = []) { + const newRefs = []; + for (const [ref, { + data + }] of changes.items()) { + if (data === null || typeof data === "string") { + newRefs.push({ + ref, + data + }); + continue; + } + await writeObject(ref, data, buffer, xref); + newRefs.push({ + ref, + data: buffer.join("") + }); + buffer.length = 0; + } + return newRefs.sort((a, b) => a.ref.num - b.ref.num); +} +async function incrementalUpdate({ + originalData, + xrefInfo, + changes, + xref = null, + hasXfa = false, + xfaDatasetsRef = null, + hasXfaDatasetsEntry = false, + needAppearances, + acroFormRef = null, + acroForm = null, + xfaData = null, + useXrefStream = false +}) { + await updateAcroform({ + xref, + acroForm, + acroFormRef, + hasXfa, + hasXfaDatasetsEntry, + xfaDatasetsRef, + needAppearances, + changes + }); + if (hasXfa) { + updateXFA({ + xfaData, + xfaDatasetsRef, + changes, + xref + }); + } + const newXref = getTrailerDict(xrefInfo, changes, useXrefStream); + const buffer = []; + const newRefs = await writeChanges(changes, xref, buffer); + let baseOffset = originalData.length; + const lastByte = originalData.at(-1); + if (lastByte !== 0x0a && lastByte !== 0x0d) { + buffer.push("\n"); + baseOffset += 1; + } + for (const { + data + } of newRefs) { + if (data !== null) { + buffer.push(data); + } + } + await (useXrefStream ? getXRefStreamTable(xrefInfo, baseOffset, newRefs, newXref, buffer) : getXRefTable(xrefInfo, baseOffset, newRefs, newXref, buffer)); + const totalLength = buffer.reduce((a, str) => a + str.length, originalData.length); + const array = new Uint8Array(totalLength); + array.set(originalData); + let offset = originalData.length; + for (const str of buffer) { + writeString(str, offset, array); + offset += str.length; + } + return array; +} + +;// ./src/core/worker_stream.js + +class PDFWorkerStream { + constructor(msgHandler) { + this._msgHandler = msgHandler; + this._contentLength = null; + this._fullRequestReader = null; + this._rangeRequestReaders = []; + } + getFullReader() { + assert(!this._fullRequestReader, "PDFWorkerStream.getFullReader can only be called once."); + this._fullRequestReader = new PDFWorkerStreamReader(this._msgHandler); + return this._fullRequestReader; + } + getRangeReader(begin, end) { + const reader = new PDFWorkerStreamRangeReader(begin, end, this._msgHandler); + this._rangeRequestReaders.push(reader); + return reader; + } + cancelAllRequests(reason) { + this._fullRequestReader?.cancel(reason); + for (const reader of this._rangeRequestReaders.slice(0)) { + reader.cancel(reason); + } + } +} +class PDFWorkerStreamReader { + constructor(msgHandler) { + this._msgHandler = msgHandler; + this.onProgress = null; + this._contentLength = null; + this._isRangeSupported = false; + this._isStreamingSupported = false; + const readableStream = this._msgHandler.sendWithStream("GetReader"); + this._reader = readableStream.getReader(); + this._headersReady = this._msgHandler.sendWithPromise("ReaderHeadersReady").then(data => { + this._isStreamingSupported = data.isStreamingSupported; + this._isRangeSupported = data.isRangeSupported; + this._contentLength = data.contentLength; + }); + } + get headersReady() { + return this._headersReady; + } + get contentLength() { + return this._contentLength; + } + get isStreamingSupported() { + return this._isStreamingSupported; + } + get isRangeSupported() { + return this._isRangeSupported; + } + async read() { + const { + value, + done + } = await this._reader.read(); + if (done) { + return { + value: undefined, + done: true + }; + } + return { + value: value.buffer, + done: false + }; + } + cancel(reason) { + this._reader.cancel(reason); + } +} +class PDFWorkerStreamRangeReader { + constructor(begin, end, msgHandler) { + this._msgHandler = msgHandler; + this.onProgress = null; + const readableStream = this._msgHandler.sendWithStream("GetRangeReader", { + begin, + end + }); + this._reader = readableStream.getReader(); + } + get isStreamingSupported() { + return false; + } + async read() { + const { + value, + done + } = await this._reader.read(); + if (done) { + return { + value: undefined, + done: true + }; + } + return { + value: value.buffer, + done: false + }; + } + cancel(reason) { + this._reader.cancel(reason); + } +} + +;// ./src/core/worker.js + + + + + + + + + + +class WorkerTask { + constructor(name) { + this.name = name; + this.terminated = false; + this._capability = Promise.withResolvers(); + } + get finished() { + return this._capability.promise; + } + finish() { + this._capability.resolve(); + } + terminate() { + this.terminated = true; + } + ensureNotTerminated() { + if (this.terminated) { + throw new Error("Worker task was terminated"); + } + } +} +class WorkerMessageHandler { + static { + if (typeof window === "undefined" && !isNodeJS && typeof self !== "undefined" && typeof self.postMessage === "function" && "onmessage" in self) { + this.initializeFromPort(self); + } + } + static setup(handler, port) { + let testMessageProcessed = false; + handler.on("test", data => { + if (testMessageProcessed) { + return; + } + testMessageProcessed = true; + handler.send("test", data instanceof Uint8Array); + }); + handler.on("configure", data => { + setVerbosityLevel(data.verbosity); + }); + handler.on("GetDocRequest", data => this.createDocumentHandler(data, port)); + } + static createDocumentHandler(docParams, port) { + let pdfManager; + let terminated = false; + let cancelXHRs = null; + const WorkerTasks = new Set(); + const verbosity = getVerbosityLevel(); + const { + docId, + apiVersion + } = docParams; + const workerVersion = "4.10.38"; + if (apiVersion !== workerVersion) { + throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`); + } + const enumerableProperties = []; + for (const property in []) { + enumerableProperties.push(property); + } + if (enumerableProperties.length) { + throw new Error("The `Array.prototype` contains unexpected enumerable properties: " + enumerableProperties.join(", ") + "; thus breaking e.g. `for...in` iteration of `Array`s."); + } + const workerHandlerName = docId + "_worker"; + let handler = new MessageHandler(workerHandlerName, docId, port); + function ensureNotTerminated() { + if (terminated) { + throw new Error("Worker was terminated"); + } + } + function startWorkerTask(task) { + WorkerTasks.add(task); + } + function finishWorkerTask(task) { + task.finish(); + WorkerTasks.delete(task); + } + async function loadDocument(recoveryMode) { + await pdfManager.ensureDoc("checkHeader"); + await pdfManager.ensureDoc("parseStartXRef"); + await pdfManager.ensureDoc("parse", [recoveryMode]); + await pdfManager.ensureDoc("checkFirstPage", [recoveryMode]); + await pdfManager.ensureDoc("checkLastPage", [recoveryMode]); + const isPureXfa = await pdfManager.ensureDoc("isPureXfa"); + if (isPureXfa) { + const task = new WorkerTask("loadXfaFonts"); + startWorkerTask(task); + await Promise.all([pdfManager.loadXfaFonts(handler, task).catch(reason => {}).then(() => finishWorkerTask(task)), pdfManager.loadXfaImages()]); + } + const [numPages, fingerprints] = await Promise.all([pdfManager.ensureDoc("numPages"), pdfManager.ensureDoc("fingerprints")]); + const htmlForXfa = isPureXfa ? await pdfManager.ensureDoc("htmlForXfa") : null; + return { + numPages, + fingerprints, + htmlForXfa + }; + } + async function getPdfManager({ + data, + password, + disableAutoFetch, + rangeChunkSize, + length, + docBaseUrl, + enableXfa, + evaluatorOptions + }) { + const pdfManagerArgs = { + source: null, + disableAutoFetch, + docBaseUrl, + docId, + enableXfa, + evaluatorOptions, + handler, + length, + password, + rangeChunkSize + }; + if (data) { + pdfManagerArgs.source = data; + return new LocalPdfManager(pdfManagerArgs); + } + const pdfStream = new PDFWorkerStream(handler), + fullRequest = pdfStream.getFullReader(); + const pdfManagerCapability = Promise.withResolvers(); + let newPdfManager, + cachedChunks = [], + loaded = 0; + fullRequest.headersReady.then(function () { + if (!fullRequest.isRangeSupported) { + return; + } + pdfManagerArgs.source = pdfStream; + pdfManagerArgs.length = fullRequest.contentLength; + pdfManagerArgs.disableAutoFetch ||= fullRequest.isStreamingSupported; + newPdfManager = new NetworkPdfManager(pdfManagerArgs); + for (const chunk of cachedChunks) { + newPdfManager.sendProgressiveData(chunk); + } + cachedChunks = []; + pdfManagerCapability.resolve(newPdfManager); + cancelXHRs = null; + }).catch(function (reason) { + pdfManagerCapability.reject(reason); + cancelXHRs = null; + }); + new Promise(function (resolve, reject) { + const readChunk = function ({ + value, + done + }) { + try { + ensureNotTerminated(); + if (done) { + if (!newPdfManager) { + const pdfFile = arrayBuffersToBytes(cachedChunks); + cachedChunks = []; + if (length && pdfFile.length !== length) { + warn("reported HTTP length is different from actual"); + } + pdfManagerArgs.source = pdfFile; + newPdfManager = new LocalPdfManager(pdfManagerArgs); + pdfManagerCapability.resolve(newPdfManager); + } + cancelXHRs = null; + return; + } + loaded += value.byteLength; + if (!fullRequest.isStreamingSupported) { + handler.send("DocProgress", { + loaded, + total: Math.max(loaded, fullRequest.contentLength || 0) + }); + } + if (newPdfManager) { + newPdfManager.sendProgressiveData(value); + } else { + cachedChunks.push(value); + } + fullRequest.read().then(readChunk, reject); + } catch (e) { + reject(e); + } + }; + fullRequest.read().then(readChunk, reject); + }).catch(function (e) { + pdfManagerCapability.reject(e); + cancelXHRs = null; + }); + cancelXHRs = reason => { + pdfStream.cancelAllRequests(reason); + }; + return pdfManagerCapability.promise; + } + function setupDoc(data) { + function onSuccess(doc) { + ensureNotTerminated(); + handler.send("GetDoc", { + pdfInfo: doc + }); + } + function onFailure(ex) { + ensureNotTerminated(); + if (ex instanceof PasswordException) { + const task = new WorkerTask(`PasswordException: response ${ex.code}`); + startWorkerTask(task); + handler.sendWithPromise("PasswordRequest", ex).then(function ({ + password + }) { + finishWorkerTask(task); + pdfManager.updatePassword(password); + pdfManagerReady(); + }).catch(function () { + finishWorkerTask(task); + handler.send("DocException", ex); + }); + } else { + handler.send("DocException", wrapReason(ex)); + } + } + function pdfManagerReady() { + ensureNotTerminated(); + loadDocument(false).then(onSuccess, function (reason) { + ensureNotTerminated(); + if (!(reason instanceof XRefParseException)) { + onFailure(reason); + return; + } + pdfManager.requestLoadedStream().then(function () { + ensureNotTerminated(); + loadDocument(true).then(onSuccess, onFailure); + }); + }); + } + ensureNotTerminated(); + getPdfManager(data).then(function (newPdfManager) { + if (terminated) { + newPdfManager.terminate(new AbortException("Worker was terminated.")); + throw new Error("Worker was terminated"); + } + pdfManager = newPdfManager; + pdfManager.requestLoadedStream(true).then(stream => { + handler.send("DataLoaded", { + length: stream.bytes.byteLength + }); + }); + }).then(pdfManagerReady, onFailure); + } + handler.on("GetPage", function (data) { + return pdfManager.getPage(data.pageIndex).then(function (page) { + return Promise.all([pdfManager.ensure(page, "rotate"), pdfManager.ensure(page, "ref"), pdfManager.ensure(page, "userUnit"), pdfManager.ensure(page, "view")]).then(function ([rotate, ref, userUnit, view]) { + return { + rotate, + ref, + refStr: ref?.toString() ?? null, + userUnit, + view + }; + }); + }); + }); + handler.on("GetPageIndex", function (data) { + const pageRef = Ref.get(data.num, data.gen); + return pdfManager.ensureCatalog("getPageIndex", [pageRef]); + }); + handler.on("GetDestinations", function (data) { + return pdfManager.ensureCatalog("destinations"); + }); + handler.on("GetDestination", function (data) { + return pdfManager.ensureCatalog("getDestination", [data.id]); + }); + handler.on("GetPageLabels", function (data) { + return pdfManager.ensureCatalog("pageLabels"); + }); + handler.on("GetPageLayout", function (data) { + return pdfManager.ensureCatalog("pageLayout"); + }); + handler.on("GetPageMode", function (data) { + return pdfManager.ensureCatalog("pageMode"); + }); + handler.on("GetViewerPreferences", function (data) { + return pdfManager.ensureCatalog("viewerPreferences"); + }); + handler.on("GetOpenAction", function (data) { + return pdfManager.ensureCatalog("openAction"); + }); + handler.on("GetAttachments", function (data) { + return pdfManager.ensureCatalog("attachments"); + }); + handler.on("GetDocJSActions", function (data) { + return pdfManager.ensureCatalog("jsActions"); + }); + handler.on("GetPageJSActions", function ({ + pageIndex + }) { + return pdfManager.getPage(pageIndex).then(function (page) { + return pdfManager.ensure(page, "jsActions"); + }); + }); + handler.on("GetOutline", function (data) { + return pdfManager.ensureCatalog("documentOutline"); + }); + handler.on("GetOptionalContentConfig", function (data) { + return pdfManager.ensureCatalog("optionalContentConfig"); + }); + handler.on("GetPermissions", function (data) { + return pdfManager.ensureCatalog("permissions"); + }); + handler.on("GetMetadata", function (data) { + return Promise.all([pdfManager.ensureDoc("documentInfo"), pdfManager.ensureCatalog("metadata")]); + }); + handler.on("GetMarkInfo", function (data) { + return pdfManager.ensureCatalog("markInfo"); + }); + handler.on("GetData", function (data) { + return pdfManager.requestLoadedStream().then(function (stream) { + return stream.bytes; + }); + }); + handler.on("GetAnnotations", function ({ + pageIndex, + intent + }) { + return pdfManager.getPage(pageIndex).then(function (page) { + const task = new WorkerTask(`GetAnnotations: page ${pageIndex}`); + startWorkerTask(task); + return page.getAnnotationsData(handler, task, intent).then(data => { + finishWorkerTask(task); + return data; + }, reason => { + finishWorkerTask(task); + throw reason; + }); + }); + }); + handler.on("GetFieldObjects", function (data) { + return pdfManager.ensureDoc("fieldObjects").then(fieldObjects => fieldObjects?.allFields || null); + }); + handler.on("HasJSActions", function (data) { + return pdfManager.ensureDoc("hasJSActions"); + }); + handler.on("GetCalculationOrderIds", function (data) { + return pdfManager.ensureDoc("calculationOrderIds"); + }); + handler.on("SaveDocument", async function ({ + isPureXfa, + numPages, + annotationStorage, + filename + }) { + const globalPromises = [pdfManager.requestLoadedStream(), pdfManager.ensureCatalog("acroForm"), pdfManager.ensureCatalog("acroFormRef"), pdfManager.ensureDoc("startXRef"), pdfManager.ensureDoc("xref"), pdfManager.ensureDoc("linearization"), pdfManager.ensureCatalog("structTreeRoot")]; + const changes = new RefSetCache(); + const promises = []; + const newAnnotationsByPage = !isPureXfa ? getNewAnnotationsMap(annotationStorage) : null; + const [stream, acroForm, acroFormRef, startXRef, xref, linearization, _structTreeRoot] = await Promise.all(globalPromises); + const catalogRef = xref.trailer.getRaw("Root") || null; + let structTreeRoot; + if (newAnnotationsByPage) { + if (!_structTreeRoot) { + if (await StructTreeRoot.canCreateStructureTree({ + catalogRef, + pdfManager, + newAnnotationsByPage + })) { + structTreeRoot = null; + } + } else if (await _structTreeRoot.canUpdateStructTree({ + pdfManager, + xref, + newAnnotationsByPage + })) { + structTreeRoot = _structTreeRoot; + } + const imagePromises = AnnotationFactory.generateImages(annotationStorage.values(), xref, pdfManager.evaluatorOptions.isOffscreenCanvasSupported); + const newAnnotationPromises = structTreeRoot === undefined ? promises : []; + for (const [pageIndex, annotations] of newAnnotationsByPage) { + newAnnotationPromises.push(pdfManager.getPage(pageIndex).then(page => { + const task = new WorkerTask(`Save (editor): page ${pageIndex}`); + startWorkerTask(task); + return page.saveNewAnnotations(handler, task, annotations, imagePromises, changes).finally(function () { + finishWorkerTask(task); + }); + })); + } + if (structTreeRoot === null) { + promises.push(Promise.all(newAnnotationPromises).then(async () => { + await StructTreeRoot.createStructureTree({ + newAnnotationsByPage, + xref, + catalogRef, + pdfManager, + changes + }); + })); + } else if (structTreeRoot) { + promises.push(Promise.all(newAnnotationPromises).then(async () => { + await structTreeRoot.updateStructureTree({ + newAnnotationsByPage, + pdfManager, + changes + }); + })); + } + } + if (isPureXfa) { + promises.push(pdfManager.serializeXfaData(annotationStorage)); + } else { + for (let pageIndex = 0; pageIndex < numPages; pageIndex++) { + promises.push(pdfManager.getPage(pageIndex).then(function (page) { + const task = new WorkerTask(`Save: page ${pageIndex}`); + startWorkerTask(task); + return page.save(handler, task, annotationStorage, changes).finally(function () { + finishWorkerTask(task); + }); + })); + } + } + const refs = await Promise.all(promises); + let xfaData = null; + if (isPureXfa) { + xfaData = refs[0]; + if (!xfaData) { + return stream.bytes; + } + } else if (changes.size === 0) { + return stream.bytes; + } + const needAppearances = acroFormRef && acroForm instanceof Dict && changes.values().some(ref => ref.needAppearances); + const xfa = acroForm instanceof Dict && acroForm.get("XFA") || null; + let xfaDatasetsRef = null; + let hasXfaDatasetsEntry = false; + if (Array.isArray(xfa)) { + for (let i = 0, ii = xfa.length; i < ii; i += 2) { + if (xfa[i] === "datasets") { + xfaDatasetsRef = xfa[i + 1]; + hasXfaDatasetsEntry = true; + } + } + if (xfaDatasetsRef === null) { + xfaDatasetsRef = xref.getNewTemporaryRef(); + } + } else if (xfa) { + warn("Unsupported XFA type."); + } + let newXrefInfo = Object.create(null); + if (xref.trailer) { + const infoObj = Object.create(null); + const xrefInfo = xref.trailer.get("Info") || null; + if (xrefInfo instanceof Dict) { + for (const [key, value] of xrefInfo) { + if (typeof value === "string") { + infoObj[key] = stringToPDFString(value); + } + } + } + newXrefInfo = { + rootRef: catalogRef, + encryptRef: xref.trailer.getRaw("Encrypt") || null, + newRef: xref.getNewTemporaryRef(), + infoRef: xref.trailer.getRaw("Info") || null, + info: infoObj, + fileIds: xref.trailer.get("ID") || null, + startXRef: linearization ? startXRef : xref.lastXRefStreamPos ?? startXRef, + filename + }; + } + return incrementalUpdate({ + originalData: stream.bytes, + xrefInfo: newXrefInfo, + changes, + xref, + hasXfa: !!xfa, + xfaDatasetsRef, + hasXfaDatasetsEntry, + needAppearances, + acroFormRef, + acroForm, + xfaData, + useXrefStream: isDict(xref.topDict, "XRef") + }).finally(() => { + xref.resetNewTemporaryRef(); + }); + }); + handler.on("GetOperatorList", function (data, sink) { + const pageIndex = data.pageIndex; + pdfManager.getPage(pageIndex).then(function (page) { + const task = new WorkerTask(`GetOperatorList: page ${pageIndex}`); + startWorkerTask(task); + const start = verbosity >= VerbosityLevel.INFOS ? Date.now() : 0; + page.getOperatorList({ + handler, + sink, + task, + intent: data.intent, + cacheKey: data.cacheKey, + annotationStorage: data.annotationStorage, + modifiedIds: data.modifiedIds + }).then(function (operatorListInfo) { + finishWorkerTask(task); + if (start) { + info(`page=${pageIndex + 1} - getOperatorList: time=` + `${Date.now() - start}ms, len=${operatorListInfo.length}`); + } + sink.close(); + }, function (reason) { + finishWorkerTask(task); + if (task.terminated) { + return; + } + sink.error(reason); + }); + }); + }); + handler.on("GetTextContent", function (data, sink) { + const { + pageIndex, + includeMarkedContent, + disableNormalization + } = data; + pdfManager.getPage(pageIndex).then(function (page) { + const task = new WorkerTask("GetTextContent: page " + pageIndex); + startWorkerTask(task); + const start = verbosity >= VerbosityLevel.INFOS ? Date.now() : 0; + page.extractTextContent({ + handler, + task, + sink, + includeMarkedContent, + disableNormalization + }).then(function () { + finishWorkerTask(task); + if (start) { + info(`page=${pageIndex + 1} - getTextContent: time=` + `${Date.now() - start}ms`); + } + sink.close(); + }, function (reason) { + finishWorkerTask(task); + if (task.terminated) { + return; + } + sink.error(reason); + }); + }); + }); + handler.on("GetStructTree", function (data) { + return pdfManager.getPage(data.pageIndex).then(function (page) { + return pdfManager.ensure(page, "getStructTree"); + }); + }); + handler.on("FontFallback", function (data) { + return pdfManager.fontFallback(data.id, handler); + }); + handler.on("Cleanup", function (data) { + return pdfManager.cleanup(true); + }); + handler.on("Terminate", function (data) { + terminated = true; + const waitOn = []; + if (pdfManager) { + pdfManager.terminate(new AbortException("Worker was terminated.")); + const cleanupPromise = pdfManager.cleanup(); + waitOn.push(cleanupPromise); + pdfManager = null; + } else { + clearGlobalCaches(); + } + cancelXHRs?.(new AbortException("Worker was terminated.")); + for (const task of WorkerTasks) { + waitOn.push(task.finished); + task.terminate(); + } + return Promise.all(waitOn).then(function () { + handler.destroy(); + handler = null; + }); + }); + handler.on("Ready", function (data) { + setupDoc(docParams); + docParams = null; + }); + return workerHandlerName; + } + static initializeFromPort(port) { + const handler = new MessageHandler("worker", "main", port); + this.setup(handler, port); + handler.send("ready", null); + } +} + +;// ./src/pdf.worker.js + +const pdfjsVersion = "4.10.38"; +const pdfjsBuild = "f9bea397f"; + +var __webpack_exports__WorkerMessageHandler = __webpack_exports__.WorkerMessageHandler; +export { __webpack_exports__WorkerMessageHandler as WorkerMessageHandler }; + +//# sourceMappingURL=pdf.worker.mjs.map \ No newline at end of file diff --git a/public/pdfjs/web/debugger.css b/public/pdfjs/web/debugger.css new file mode 100644 index 0000000..b9d9f81 --- /dev/null +++ b/public/pdfjs/web/debugger.css @@ -0,0 +1,111 @@ +/* Copyright 2014 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +:root { + --panel-width: 300px; +} + +#PDFBug, +#PDFBug :is(input, button, select) { + font: message-box; +} +#PDFBug { + background-color: rgb(255 255 255); + border: 1px solid rgb(102 102 102); + position: fixed; + top: 32px; + right: 0; + bottom: 0; + font-size: 10px; + padding: 0; + width: var(--panel-width); +} +#PDFBug .controls { + background: rgb(238 238 238); + border-bottom: 1px solid rgb(102 102 102); + padding: 3px; +} +#PDFBug .panels { + inset: 27px 0 0; + overflow: auto; + position: absolute; +} +#PDFBug .panels > div { + padding: 5px; +} +#PDFBug button.active { + font-weight: bold; +} +.debuggerShowText, +.debuggerHideText:hover { + background-color: rgb(255 255 0 / 0.25); +} +#PDFBug .stats { + font-family: courier; + font-size: 10px; + white-space: pre; +} +#PDFBug .stats .title { + font-weight: bold; +} +#PDFBug table { + font-size: 10px; + white-space: pre; +} +#PDFBug table.showText { + border-collapse: collapse; + text-align: center; +} +#PDFBug table.showText, +#PDFBug table.showText :is(tr, td) { + border: 1px solid black; + padding: 1px; +} +#PDFBug table.showText td.advance { + color: grey; +} + +#viewer.textLayer-visible .textLayer { + opacity: 1; +} + +#viewer.textLayer-visible .canvasWrapper { + background-color: rgb(128 255 128); +} + +#viewer.textLayer-visible .canvasWrapper canvas { + mix-blend-mode: screen; +} + +#viewer.textLayer-visible .textLayer span { + background-color: rgb(255 255 0 / 0.1); + color: rgb(0 0 0); + border: solid 1px rgb(255 0 0 / 0.5); + box-sizing: border-box; +} + +#viewer.textLayer-visible .textLayer span[aria-owns] { + background-color: rgb(255 0 0 / 0.3); +} + +#viewer.textLayer-hover .textLayer span:hover { + background-color: rgb(255 255 255); + color: rgb(0 0 0); +} + +#viewer.textLayer-shadow .textLayer span { + background-color: rgb(255 255 255 / 0.6); + color: rgb(0 0 0); +} diff --git a/public/pdfjs/web/debugger.mjs b/public/pdfjs/web/debugger.mjs new file mode 100644 index 0000000..59c1871 --- /dev/null +++ b/public/pdfjs/web/debugger.mjs @@ -0,0 +1,623 @@ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { OPS } = globalThis.pdfjsLib || (await import("pdfjs-lib")); + +const opMap = Object.create(null); +for (const key in OPS) { + opMap[OPS[key]] = key; +} + +const FontInspector = (function FontInspectorClosure() { + let fonts; + let active = false; + const fontAttribute = "data-font-name"; + function removeSelection() { + const divs = document.querySelectorAll(`span[${fontAttribute}]`); + for (const div of divs) { + div.className = ""; + } + } + function resetSelection() { + const divs = document.querySelectorAll(`span[${fontAttribute}]`); + for (const div of divs) { + div.className = "debuggerHideText"; + } + } + function selectFont(fontName, show) { + const divs = document.querySelectorAll( + `span[${fontAttribute}=${fontName}]` + ); + for (const div of divs) { + div.className = show ? "debuggerShowText" : "debuggerHideText"; + } + } + function textLayerClick(e) { + if ( + !e.target.dataset.fontName || + e.target.tagName.toUpperCase() !== "SPAN" + ) { + return; + } + const fontName = e.target.dataset.fontName; + const selects = document.getElementsByTagName("input"); + for (const select of selects) { + if (select.dataset.fontName !== fontName) { + continue; + } + select.checked = !select.checked; + selectFont(fontName, select.checked); + select.scrollIntoView(); + } + } + return { + // Properties/functions needed by PDFBug. + id: "FontInspector", + name: "Font Inspector", + panel: null, + manager: null, + init() { + const panel = this.panel; + const tmp = document.createElement("button"); + tmp.addEventListener("click", resetSelection); + tmp.textContent = "Refresh"; + panel.append(tmp); + + fonts = document.createElement("div"); + panel.append(fonts); + }, + cleanup() { + fonts.textContent = ""; + }, + enabled: false, + get active() { + return active; + }, + set active(value) { + active = value; + if (active) { + document.body.addEventListener("click", textLayerClick, true); + resetSelection(); + } else { + document.body.removeEventListener("click", textLayerClick, true); + removeSelection(); + } + }, + // FontInspector specific functions. + fontAdded(fontObj, url) { + function properties(obj, list) { + const moreInfo = document.createElement("table"); + for (const entry of list) { + const tr = document.createElement("tr"); + const td1 = document.createElement("td"); + td1.textContent = entry; + tr.append(td1); + const td2 = document.createElement("td"); + td2.textContent = obj[entry].toString(); + tr.append(td2); + moreInfo.append(tr); + } + return moreInfo; + } + + const moreInfo = fontObj.css + ? properties(fontObj, ["baseFontName"]) + : properties(fontObj, ["name", "type"]); + + const fontName = fontObj.loadedName; + const font = document.createElement("div"); + const name = document.createElement("span"); + name.textContent = fontName; + let download; + if (!fontObj.css) { + download = document.createElement("a"); + if (url) { + url = /url\(['"]?([^)"']+)/.exec(url); + download.href = url[1]; + } else if (fontObj.data) { + download.href = URL.createObjectURL( + new Blob([fontObj.data], { type: fontObj.mimetype }) + ); + } + download.textContent = "Download"; + } + + const logIt = document.createElement("a"); + logIt.href = ""; + logIt.textContent = "Log"; + logIt.addEventListener("click", function (event) { + event.preventDefault(); + console.log(fontObj); + }); + const select = document.createElement("input"); + select.setAttribute("type", "checkbox"); + select.dataset.fontName = fontName; + select.addEventListener("click", function () { + selectFont(fontName, select.checked); + }); + if (download) { + font.append(select, name, " ", download, " ", logIt, moreInfo); + } else { + font.append(select, name, " ", logIt, moreInfo); + } + fonts.append(font); + // Somewhat of a hack, should probably add a hook for when the text layer + // is done rendering. + setTimeout(() => { + if (this.active) { + resetSelection(); + } + }, 2000); + }, + }; +})(); + +// Manages all the page steppers. +const StepperManager = (function StepperManagerClosure() { + let steppers = []; + let stepperDiv = null; + let stepperControls = null; + let stepperChooser = null; + let breakPoints = Object.create(null); + return { + // Properties/functions needed by PDFBug. + id: "Stepper", + name: "Stepper", + panel: null, + manager: null, + init() { + const self = this; + stepperControls = document.createElement("div"); + stepperChooser = document.createElement("select"); + stepperChooser.addEventListener("change", function (event) { + self.selectStepper(this.value); + }); + stepperControls.append(stepperChooser); + stepperDiv = document.createElement("div"); + this.panel.append(stepperControls, stepperDiv); + if (sessionStorage.getItem("pdfjsBreakPoints")) { + breakPoints = JSON.parse(sessionStorage.getItem("pdfjsBreakPoints")); + } + }, + cleanup() { + stepperChooser.textContent = ""; + stepperDiv.textContent = ""; + steppers = []; + }, + enabled: false, + active: false, + // Stepper specific functions. + create(pageIndex) { + const debug = document.createElement("div"); + debug.id = "stepper" + pageIndex; + debug.hidden = true; + debug.className = "stepper"; + stepperDiv.append(debug); + const b = document.createElement("option"); + b.textContent = "Page " + (pageIndex + 1); + b.value = pageIndex; + stepperChooser.append(b); + const initBreakPoints = breakPoints[pageIndex] || []; + const stepper = new Stepper(debug, pageIndex, initBreakPoints); + steppers.push(stepper); + if (steppers.length === 1) { + this.selectStepper(pageIndex, false); + } + return stepper; + }, + selectStepper(pageIndex, selectPanel) { + pageIndex |= 0; + if (selectPanel) { + this.manager.selectPanel(this); + } + for (const stepper of steppers) { + stepper.panel.hidden = stepper.pageIndex !== pageIndex; + } + for (const option of stepperChooser.options) { + option.selected = (option.value | 0) === pageIndex; + } + }, + saveBreakPoints(pageIndex, bps) { + breakPoints[pageIndex] = bps; + sessionStorage.setItem("pdfjsBreakPoints", JSON.stringify(breakPoints)); + }, + }; +})(); + +// The stepper for each page's operatorList. +class Stepper { + // Shorter way to create element and optionally set textContent. + #c(tag, textContent) { + const d = document.createElement(tag); + if (textContent) { + d.textContent = textContent; + } + return d; + } + + #simplifyArgs(args) { + if (typeof args === "string") { + const MAX_STRING_LENGTH = 75; + return args.length <= MAX_STRING_LENGTH + ? args + : args.substring(0, MAX_STRING_LENGTH) + "..."; + } + if (typeof args !== "object" || args === null) { + return args; + } + if ("length" in args) { + // array + const MAX_ITEMS = 10, + simpleArgs = []; + let i, ii; + for (i = 0, ii = Math.min(MAX_ITEMS, args.length); i < ii; i++) { + simpleArgs.push(this.#simplifyArgs(args[i])); + } + if (i < args.length) { + simpleArgs.push("..."); + } + return simpleArgs; + } + const simpleObj = {}; + for (const key in args) { + simpleObj[key] = this.#simplifyArgs(args[key]); + } + return simpleObj; + } + + constructor(panel, pageIndex, initialBreakPoints) { + this.panel = panel; + this.breakPoint = 0; + this.nextBreakPoint = null; + this.pageIndex = pageIndex; + this.breakPoints = initialBreakPoints; + this.currentIdx = -1; + this.operatorListIdx = 0; + this.indentLevel = 0; + } + + init(operatorList) { + const panel = this.panel; + const content = this.#c("div", "c=continue, s=step"); + const table = this.#c("table"); + content.append(table); + table.cellSpacing = 0; + const headerRow = this.#c("tr"); + table.append(headerRow); + headerRow.append( + this.#c("th", "Break"), + this.#c("th", "Idx"), + this.#c("th", "fn"), + this.#c("th", "args") + ); + panel.append(content); + this.table = table; + this.updateOperatorList(operatorList); + } + + updateOperatorList(operatorList) { + const self = this; + + function cboxOnClick() { + const x = +this.dataset.idx; + if (this.checked) { + self.breakPoints.push(x); + } else { + self.breakPoints.splice(self.breakPoints.indexOf(x), 1); + } + StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints); + } + + const MAX_OPERATORS_COUNT = 15000; + if (this.operatorListIdx > MAX_OPERATORS_COUNT) { + return; + } + + const chunk = document.createDocumentFragment(); + const operatorsToDisplay = Math.min( + MAX_OPERATORS_COUNT, + operatorList.fnArray.length + ); + for (let i = this.operatorListIdx; i < operatorsToDisplay; i++) { + const line = this.#c("tr"); + line.className = "line"; + line.dataset.idx = i; + chunk.append(line); + const checked = this.breakPoints.includes(i); + const args = operatorList.argsArray[i] || []; + + const breakCell = this.#c("td"); + const cbox = this.#c("input"); + cbox.type = "checkbox"; + cbox.className = "points"; + cbox.checked = checked; + cbox.dataset.idx = i; + cbox.onclick = cboxOnClick; + + breakCell.append(cbox); + line.append(breakCell, this.#c("td", i.toString())); + const fn = opMap[operatorList.fnArray[i]]; + let decArgs = args; + if (fn === "showText") { + const glyphs = args[0]; + const charCodeRow = this.#c("tr"); + const fontCharRow = this.#c("tr"); + const unicodeRow = this.#c("tr"); + for (const glyph of glyphs) { + if (typeof glyph === "object" && glyph !== null) { + charCodeRow.append(this.#c("td", glyph.originalCharCode)); + fontCharRow.append(this.#c("td", glyph.fontChar)); + unicodeRow.append(this.#c("td", glyph.unicode)); + } else { + // null or number + const advanceEl = this.#c("td", glyph); + advanceEl.classList.add("advance"); + charCodeRow.append(advanceEl); + fontCharRow.append(this.#c("td")); + unicodeRow.append(this.#c("td")); + } + } + decArgs = this.#c("td"); + const table = this.#c("table"); + table.classList.add("showText"); + decArgs.append(table); + table.append(charCodeRow, fontCharRow, unicodeRow); + } else if (fn === "restore" && this.indentLevel > 0) { + this.indentLevel--; + } + line.append(this.#c("td", " ".repeat(this.indentLevel * 2) + fn)); + if (fn === "save") { + this.indentLevel++; + } + + if (decArgs instanceof HTMLElement) { + line.append(decArgs); + } else { + line.append(this.#c("td", JSON.stringify(this.#simplifyArgs(decArgs)))); + } + } + if (operatorsToDisplay < operatorList.fnArray.length) { + const lastCell = this.#c("td", "..."); + lastCell.colspan = 4; + chunk.append(lastCell); + } + this.operatorListIdx = operatorList.fnArray.length; + this.table.append(chunk); + } + + getNextBreakPoint() { + this.breakPoints.sort(function (a, b) { + return a - b; + }); + for (const breakPoint of this.breakPoints) { + if (breakPoint > this.currentIdx) { + return breakPoint; + } + } + return null; + } + + breakIt(idx, callback) { + StepperManager.selectStepper(this.pageIndex, true); + this.currentIdx = idx; + + const listener = evt => { + switch (evt.keyCode) { + case 83: // step + document.removeEventListener("keydown", listener); + this.nextBreakPoint = this.currentIdx + 1; + this.goTo(-1); + callback(); + break; + case 67: // continue + document.removeEventListener("keydown", listener); + this.nextBreakPoint = this.getNextBreakPoint(); + this.goTo(-1); + callback(); + break; + } + }; + document.addEventListener("keydown", listener); + this.goTo(idx); + } + + goTo(idx) { + const allRows = this.panel.getElementsByClassName("line"); + for (const row of allRows) { + if ((row.dataset.idx | 0) === idx) { + row.style.backgroundColor = "rgb(251,250,207)"; + row.scrollIntoView(); + } else { + row.style.backgroundColor = null; + } + } + } +} + +const Stats = (function Stats() { + let stats = []; + function clear(node) { + node.textContent = ""; // Remove any `node` contents from the DOM. + } + function getStatIndex(pageNumber) { + for (const [i, stat] of stats.entries()) { + if (stat.pageNumber === pageNumber) { + return i; + } + } + return false; + } + return { + // Properties/functions needed by PDFBug. + id: "Stats", + name: "Stats", + panel: null, + manager: null, + init() {}, + enabled: false, + active: false, + // Stats specific functions. + add(pageNumber, stat) { + if (!stat) { + return; + } + const statsIndex = getStatIndex(pageNumber); + if (statsIndex !== false) { + stats[statsIndex].div.remove(); + stats.splice(statsIndex, 1); + } + const wrapper = document.createElement("div"); + wrapper.className = "stats"; + const title = document.createElement("div"); + title.className = "title"; + title.textContent = "Page: " + pageNumber; + const statsDiv = document.createElement("div"); + statsDiv.textContent = stat.toString(); + wrapper.append(title, statsDiv); + stats.push({ pageNumber, div: wrapper }); + stats.sort(function (a, b) { + return a.pageNumber - b.pageNumber; + }); + clear(this.panel); + for (const entry of stats) { + this.panel.append(entry.div); + } + }, + cleanup() { + stats = []; + clear(this.panel); + }, + }; +})(); + +// Manages all the debugging tools. +class PDFBug { + static #buttons = []; + + static #activePanel = null; + + static tools = [FontInspector, StepperManager, Stats]; + + static enable(ids) { + const all = ids.length === 1 && ids[0] === "all"; + const tools = this.tools; + for (const tool of tools) { + if (all || ids.includes(tool.id)) { + tool.enabled = true; + } + } + if (!all) { + // Sort the tools by the order they are enabled. + tools.sort(function (a, b) { + let indexA = ids.indexOf(a.id); + indexA = indexA < 0 ? tools.length : indexA; + let indexB = ids.indexOf(b.id); + indexB = indexB < 0 ? tools.length : indexB; + return indexA - indexB; + }); + } + } + + static init(container, ids) { + this.loadCSS(); + this.enable(ids); + /* + * Basic Layout: + * PDFBug + * Controls + * Panels + * Panel + * Panel + * ... + */ + const ui = document.createElement("div"); + ui.id = "PDFBug"; + + const controls = document.createElement("div"); + controls.setAttribute("class", "controls"); + ui.append(controls); + + const panels = document.createElement("div"); + panels.setAttribute("class", "panels"); + ui.append(panels); + + container.append(ui); + container.style.right = "var(--panel-width)"; + + // Initialize all the debugging tools. + for (const tool of this.tools) { + const panel = document.createElement("div"); + const panelButton = document.createElement("button"); + panelButton.textContent = tool.name; + panelButton.addEventListener("click", event => { + event.preventDefault(); + this.selectPanel(tool); + }); + controls.append(panelButton); + panels.append(panel); + tool.panel = panel; + tool.manager = this; + if (tool.enabled) { + tool.init(); + } else { + panel.textContent = + `${tool.name} is disabled. To enable add "${tool.id}" to ` + + "the pdfBug parameter and refresh (separate multiple by commas)."; + } + this.#buttons.push(panelButton); + } + this.selectPanel(0); + } + + static loadCSS() { + const { url } = import.meta; + + const link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = url.replace(/\.mjs$/, ".css"); + + document.head.append(link); + } + + static cleanup() { + for (const tool of this.tools) { + if (tool.enabled) { + tool.cleanup(); + } + } + } + + static selectPanel(index) { + if (typeof index !== "number") { + index = this.tools.indexOf(index); + } + if (index === this.#activePanel) { + return; + } + this.#activePanel = index; + for (const [j, tool] of this.tools.entries()) { + const isActive = j === index; + this.#buttons[j].classList.toggle("active", isActive); + tool.active = isActive; + tool.panel.hidden = !isActive; + } + } +} + +globalThis.FontInspector = FontInspector; +globalThis.StepperManager = StepperManager; +globalThis.Stats = Stats; + +export { PDFBug }; diff --git a/public/pdfjs/web/images/altText_add.svg b/public/pdfjs/web/images/altText_add.svg new file mode 100644 index 0000000..3451b53 --- /dev/null +++ b/public/pdfjs/web/images/altText_add.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/altText_disclaimer.svg b/public/pdfjs/web/images/altText_disclaimer.svg new file mode 100644 index 0000000..6fe79e7 --- /dev/null +++ b/public/pdfjs/web/images/altText_disclaimer.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/altText_done.svg b/public/pdfjs/web/images/altText_done.svg new file mode 100644 index 0000000..f54924e --- /dev/null +++ b/public/pdfjs/web/images/altText_done.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/altText_spinner.svg b/public/pdfjs/web/images/altText_spinner.svg new file mode 100644 index 0000000..fedb472 --- /dev/null +++ b/public/pdfjs/web/images/altText_spinner.svg @@ -0,0 +1,30 @@ + + + + + + + + + diff --git a/public/pdfjs/web/images/altText_warning.svg b/public/pdfjs/web/images/altText_warning.svg new file mode 100644 index 0000000..03014ce --- /dev/null +++ b/public/pdfjs/web/images/altText_warning.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/annotation-check.svg b/public/pdfjs/web/images/annotation-check.svg new file mode 100644 index 0000000..71cd16d --- /dev/null +++ b/public/pdfjs/web/images/annotation-check.svg @@ -0,0 +1,11 @@ + + + + diff --git a/public/pdfjs/web/images/annotation-comment.svg b/public/pdfjs/web/images/annotation-comment.svg new file mode 100644 index 0000000..86f1f17 --- /dev/null +++ b/public/pdfjs/web/images/annotation-comment.svg @@ -0,0 +1,16 @@ + + + + + diff --git a/public/pdfjs/web/images/annotation-help.svg b/public/pdfjs/web/images/annotation-help.svg new file mode 100644 index 0000000..00938fe --- /dev/null +++ b/public/pdfjs/web/images/annotation-help.svg @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/public/pdfjs/web/images/annotation-insert.svg b/public/pdfjs/web/images/annotation-insert.svg new file mode 100644 index 0000000..519ef68 --- /dev/null +++ b/public/pdfjs/web/images/annotation-insert.svg @@ -0,0 +1,10 @@ + + + + diff --git a/public/pdfjs/web/images/annotation-key.svg b/public/pdfjs/web/images/annotation-key.svg new file mode 100644 index 0000000..8d09d53 --- /dev/null +++ b/public/pdfjs/web/images/annotation-key.svg @@ -0,0 +1,11 @@ + + + + diff --git a/public/pdfjs/web/images/annotation-newparagraph.svg b/public/pdfjs/web/images/annotation-newparagraph.svg new file mode 100644 index 0000000..38d2497 --- /dev/null +++ b/public/pdfjs/web/images/annotation-newparagraph.svg @@ -0,0 +1,11 @@ + + + + diff --git a/public/pdfjs/web/images/annotation-noicon.svg b/public/pdfjs/web/images/annotation-noicon.svg new file mode 100644 index 0000000..c07d108 --- /dev/null +++ b/public/pdfjs/web/images/annotation-noicon.svg @@ -0,0 +1,7 @@ + + + diff --git a/public/pdfjs/web/images/annotation-note.svg b/public/pdfjs/web/images/annotation-note.svg new file mode 100644 index 0000000..7017365 --- /dev/null +++ b/public/pdfjs/web/images/annotation-note.svg @@ -0,0 +1,42 @@ + + + + + + + + diff --git a/public/pdfjs/web/images/annotation-paperclip.svg b/public/pdfjs/web/images/annotation-paperclip.svg new file mode 100644 index 0000000..2bed225 --- /dev/null +++ b/public/pdfjs/web/images/annotation-paperclip.svg @@ -0,0 +1,6 @@ + + + + diff --git a/public/pdfjs/web/images/annotation-paragraph.svg b/public/pdfjs/web/images/annotation-paragraph.svg new file mode 100644 index 0000000..6ae5212 --- /dev/null +++ b/public/pdfjs/web/images/annotation-paragraph.svg @@ -0,0 +1,16 @@ + + + + + diff --git a/public/pdfjs/web/images/annotation-pushpin.svg b/public/pdfjs/web/images/annotation-pushpin.svg new file mode 100644 index 0000000..6e0896c --- /dev/null +++ b/public/pdfjs/web/images/annotation-pushpin.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/public/pdfjs/web/images/cursor-editorFreeHighlight.svg b/public/pdfjs/web/images/cursor-editorFreeHighlight.svg new file mode 100644 index 0000000..513f6bd --- /dev/null +++ b/public/pdfjs/web/images/cursor-editorFreeHighlight.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/pdfjs/web/images/cursor-editorFreeText.svg b/public/pdfjs/web/images/cursor-editorFreeText.svg new file mode 100644 index 0000000..de2838e --- /dev/null +++ b/public/pdfjs/web/images/cursor-editorFreeText.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/cursor-editorInk.svg b/public/pdfjs/web/images/cursor-editorInk.svg new file mode 100644 index 0000000..1dadb5c --- /dev/null +++ b/public/pdfjs/web/images/cursor-editorInk.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/pdfjs/web/images/cursor-editorTextHighlight.svg b/public/pdfjs/web/images/cursor-editorTextHighlight.svg new file mode 100644 index 0000000..800340c --- /dev/null +++ b/public/pdfjs/web/images/cursor-editorTextHighlight.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/pdfjs/web/images/editor-toolbar-delete.svg b/public/pdfjs/web/images/editor-toolbar-delete.svg new file mode 100644 index 0000000..f84520d --- /dev/null +++ b/public/pdfjs/web/images/editor-toolbar-delete.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/public/pdfjs/web/images/findbarButton-next.svg b/public/pdfjs/web/images/findbarButton-next.svg new file mode 100644 index 0000000..8cb39be --- /dev/null +++ b/public/pdfjs/web/images/findbarButton-next.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/findbarButton-previous.svg b/public/pdfjs/web/images/findbarButton-previous.svg new file mode 100644 index 0000000..b610879 --- /dev/null +++ b/public/pdfjs/web/images/findbarButton-previous.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/gv-toolbarButton-download.svg b/public/pdfjs/web/images/gv-toolbarButton-download.svg new file mode 100644 index 0000000..d56cf3c --- /dev/null +++ b/public/pdfjs/web/images/gv-toolbarButton-download.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/loading-icon.gif b/public/pdfjs/web/images/loading-icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..1c72ebb554be018511ae972c3f2361dff02dce02 GIT binary patch literal 2545 zcma*pX;2es8VB%~zPr=ibVMCx-JQ^BhLDAsK)^**h(ZDp9YGuzZ%~j!}+w%FI;|aC7){7CdVvG)P{bng1y9Te*f}~*`1kQl$jwb z$tlW~rRS!X?#xfm_&6tTdp_`cjgYwbRFLNdoJCN$S-yhg`ZnC-yvedRSmOh%;Y`Gl6bY$Z-}#C=#F4%9!I1b zWQ~f+9P?;vhCxWwlwl=lrWG|7IYo;{jjmzJ5R9?f>n%-d@>kLINUc z4wM5dAO;kq<$}Dk{2-u0$I6@2N}&cUx9nmV1dYc8jfC}%=F9WCg^OQK9C6poh#2!A z3^EU*UFZvS^)?bu3T?J;@Ahb~%I?+@4!l5!*TjC}GIslNan-RCrrd~PdHYnNLJk+m&`$Y+NV(e>CCu%R#_8GqY4cv#j`#uRWdsg9DxWy(?oOvgCU}&@jy%c!H&-Q zqXJxajAtmQRoRa9V-RFXXh-bK*;Fum{BjpkYQGX~i@OZ^Dx0n&H}kvGKqQ?w(6iGXu_g08T|_hp#ZvFzIwKF*a=oMJ~3UGAjZ?g}GOxm44td zXoyYrU*I=y*vHv89hkYH(v5R#wc)BC3dZJKb3K)f>zaM3%JP(mpecViP0eKKYf3zy z->jx_mc?mCtPEvCQ?uppk?eLJt}_IR7giW%Jr)RyI!+E-voIs*lXI*z`GQc_&D#X( z{6G};HPYj6O|$lXxBJeDaweqa{4L=tOZCjTI^&UOxXg})LRG_cr^B9Rqt(i5ORbQX zq`_xCRsH>xEYY%&*Nyi#{S_JZNlTm#K56`RI%7^amom;*h90Si&g1CfaFV3D|a!`3Y-GKKbL*KSbl z>I96`TR@CqPJl(>QqB~RvK~-U)`e`l4LIqj+IU^~yyIe*|BRVB>4Bup%j{tLdKz4j zY^<8P8m~GRGz*yv0&-RJE+-keJ+%m3wNeopzsltWd->eWmBVwUr)pX` zK~CD<;~Z*Uy3W`3+MrEYxm5qYQ!z%YI;y7DTG`UVH0;@{M{!B&id_}3DBQ?zsotuR zEGLdRx25nLm%-wjlnEi;-aN_1S7???rO~WgA67jjr&(vRa3y$u#kqJbeKnw z{!T!1li9>M+sJ6AUe+*9d}2uGjhzd z|L1Rtp8uTGYyZoQ*`DS^m2dw-X{a)l+3m?ncvn^+O>)hdd3(hMtlhkRGns{<8c0I! zDDjpmwtj?@!6kA|iu3q+Ai;@JR+ zfk+ln&YFC{4bhK6IxVgLs4W%^8Lk`qzWU*L>yq0A3;l}{!wKZ!ue)C)SKI)9dl1hl zhIRLV@8E}rwvE{gX(}$f6x*k)_`*Ijt1=EU-Ls6-(phomeQBgtUs z5Xz~Cd*nE)Ac!0i4ep}Z1AugMB(&F?)#CU{Qc{Sp^vKsdL}vRB30H+Bbzrn`M##H3 z{W8dc_mDroEE+p8_}mnJtzZ4!RNe)zhB)Ds;S57nYSJxtek>^~&(7B+N5MPf2+2xx z5Dl&4X|c@f{Kd|z1r+N|$DmsoVp*3yOdxT^J^-VAk)Z@$4^XrPrFP-Co+MXZ+KJ(W z{JNYvraLLWA;&tRhIKOvhW|HC|L-dLvAUF(MG0(Nl?4tB{RzN7I(}Cb%hwN{crFC8 zji#aJElKvDFV+&VI1V?oUMA>*kto0^;3W8FQBSZ|{ z$v~TqE=(8DZa^i$^oht&h};P1N&wMXorKh*Z68gPV&ouy>%f36Oqkwemyeas$Qbz# zV?7Jy%o7KY6^I=P@eCji%W`o5sf(5hySYo9$l4e2`(hIV_?=H-#R6}0$WVA|*(K@3 z=5?@RlcLh(meW%A4)hGzcvEpm(_w?>zhL*i&s9$2>r zAtk{8Cia|+Y+V!uX9BtpXoF%lswuRKsM!pSs!?yhlCy!269K0|b M?FSZn2B>%I-}ej|s{jB1 literal 0 HcmV?d00001 diff --git a/public/pdfjs/web/images/loading.svg b/public/pdfjs/web/images/loading.svg new file mode 100644 index 0000000..0a15ff6 --- /dev/null +++ b/public/pdfjs/web/images/loading.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/pdfjs/web/images/messageBar_closingButton.svg b/public/pdfjs/web/images/messageBar_closingButton.svg new file mode 100644 index 0000000..8a40715 --- /dev/null +++ b/public/pdfjs/web/images/messageBar_closingButton.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/messageBar_warning.svg b/public/pdfjs/web/images/messageBar_warning.svg new file mode 100644 index 0000000..011cfcf --- /dev/null +++ b/public/pdfjs/web/images/messageBar_warning.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-documentProperties.svg b/public/pdfjs/web/images/secondaryToolbarButton-documentProperties.svg new file mode 100644 index 0000000..dd3917b --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-documentProperties.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-firstPage.svg b/public/pdfjs/web/images/secondaryToolbarButton-firstPage.svg new file mode 100644 index 0000000..f5c917f --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-firstPage.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-handTool.svg b/public/pdfjs/web/images/secondaryToolbarButton-handTool.svg new file mode 100644 index 0000000..b7073b5 --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-handTool.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-lastPage.svg b/public/pdfjs/web/images/secondaryToolbarButton-lastPage.svg new file mode 100644 index 0000000..c04f650 --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-lastPage.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-rotateCcw.svg b/public/pdfjs/web/images/secondaryToolbarButton-rotateCcw.svg new file mode 100644 index 0000000..da73a1b --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-rotateCcw.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-rotateCw.svg b/public/pdfjs/web/images/secondaryToolbarButton-rotateCw.svg new file mode 100644 index 0000000..c41ce73 --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-rotateCw.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-scrollHorizontal.svg b/public/pdfjs/web/images/secondaryToolbarButton-scrollHorizontal.svg new file mode 100644 index 0000000..fb440b9 --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-scrollHorizontal.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-scrollPage.svg b/public/pdfjs/web/images/secondaryToolbarButton-scrollPage.svg new file mode 100644 index 0000000..64a9f50 --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-scrollPage.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-scrollVertical.svg b/public/pdfjs/web/images/secondaryToolbarButton-scrollVertical.svg new file mode 100644 index 0000000..dc7e805 --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-scrollVertical.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-scrollWrapped.svg b/public/pdfjs/web/images/secondaryToolbarButton-scrollWrapped.svg new file mode 100644 index 0000000..75fe26b --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-scrollWrapped.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-selectTool.svg b/public/pdfjs/web/images/secondaryToolbarButton-selectTool.svg new file mode 100644 index 0000000..94d5141 --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-selectTool.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-spreadEven.svg b/public/pdfjs/web/images/secondaryToolbarButton-spreadEven.svg new file mode 100644 index 0000000..ce201e3 --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-spreadEven.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-spreadNone.svg b/public/pdfjs/web/images/secondaryToolbarButton-spreadNone.svg new file mode 100644 index 0000000..e8d487f --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-spreadNone.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/secondaryToolbarButton-spreadOdd.svg b/public/pdfjs/web/images/secondaryToolbarButton-spreadOdd.svg new file mode 100644 index 0000000..9211a42 --- /dev/null +++ b/public/pdfjs/web/images/secondaryToolbarButton-spreadOdd.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-bookmark.svg b/public/pdfjs/web/images/toolbarButton-bookmark.svg new file mode 100644 index 0000000..c4c37c9 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-bookmark.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-currentOutlineItem.svg b/public/pdfjs/web/images/toolbarButton-currentOutlineItem.svg new file mode 100644 index 0000000..01e6762 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-currentOutlineItem.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-download.svg b/public/pdfjs/web/images/toolbarButton-download.svg new file mode 100644 index 0000000..e2e850a --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-download.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/pdfjs/web/images/toolbarButton-editorFreeText.svg b/public/pdfjs/web/images/toolbarButton-editorFreeText.svg new file mode 100644 index 0000000..13a67bd --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-editorFreeText.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/pdfjs/web/images/toolbarButton-editorHighlight.svg b/public/pdfjs/web/images/toolbarButton-editorHighlight.svg new file mode 100644 index 0000000..b3cd7fd --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-editorHighlight.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/public/pdfjs/web/images/toolbarButton-editorInk.svg b/public/pdfjs/web/images/toolbarButton-editorInk.svg new file mode 100644 index 0000000..b579eec --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-editorInk.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/pdfjs/web/images/toolbarButton-editorStamp.svg b/public/pdfjs/web/images/toolbarButton-editorStamp.svg new file mode 100644 index 0000000..a1fef49 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-editorStamp.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/public/pdfjs/web/images/toolbarButton-menuArrow.svg b/public/pdfjs/web/images/toolbarButton-menuArrow.svg new file mode 100644 index 0000000..82ffeaa --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-menuArrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-openFile.svg b/public/pdfjs/web/images/toolbarButton-openFile.svg new file mode 100644 index 0000000..e773781 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-openFile.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-pageDown.svg b/public/pdfjs/web/images/toolbarButton-pageDown.svg new file mode 100644 index 0000000..1fc12e7 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-pageDown.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-pageUp.svg b/public/pdfjs/web/images/toolbarButton-pageUp.svg new file mode 100644 index 0000000..0936b9a --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-pageUp.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-presentationMode.svg b/public/pdfjs/web/images/toolbarButton-presentationMode.svg new file mode 100644 index 0000000..901d567 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-presentationMode.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-print.svg b/public/pdfjs/web/images/toolbarButton-print.svg new file mode 100644 index 0000000..97a3904 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-print.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-search.svg b/public/pdfjs/web/images/toolbarButton-search.svg new file mode 100644 index 0000000..0cc7ae2 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-search.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-secondaryToolbarToggle.svg b/public/pdfjs/web/images/toolbarButton-secondaryToolbarToggle.svg new file mode 100644 index 0000000..cace863 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-secondaryToolbarToggle.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-sidebarToggle.svg b/public/pdfjs/web/images/toolbarButton-sidebarToggle.svg new file mode 100644 index 0000000..1d8d0e4 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-sidebarToggle.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-viewAttachments.svg b/public/pdfjs/web/images/toolbarButton-viewAttachments.svg new file mode 100644 index 0000000..ab73f6e --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-viewAttachments.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-viewLayers.svg b/public/pdfjs/web/images/toolbarButton-viewLayers.svg new file mode 100644 index 0000000..1d72668 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-viewLayers.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-viewOutline.svg b/public/pdfjs/web/images/toolbarButton-viewOutline.svg new file mode 100644 index 0000000..7ed1bd9 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-viewOutline.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-viewThumbnail.svg b/public/pdfjs/web/images/toolbarButton-viewThumbnail.svg new file mode 100644 index 0000000..040d123 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-viewThumbnail.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-zoomIn.svg b/public/pdfjs/web/images/toolbarButton-zoomIn.svg new file mode 100644 index 0000000..30ec51a --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-zoomIn.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/toolbarButton-zoomOut.svg b/public/pdfjs/web/images/toolbarButton-zoomOut.svg new file mode 100644 index 0000000..f273b59 --- /dev/null +++ b/public/pdfjs/web/images/toolbarButton-zoomOut.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pdfjs/web/images/treeitem-collapsed.svg b/public/pdfjs/web/images/treeitem-collapsed.svg new file mode 100644 index 0000000..831cddf --- /dev/null +++ b/public/pdfjs/web/images/treeitem-collapsed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/pdfjs/web/images/treeitem-expanded.svg b/public/pdfjs/web/images/treeitem-expanded.svg new file mode 100644 index 0000000..2d45f0c --- /dev/null +++ b/public/pdfjs/web/images/treeitem-expanded.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/pdfjs/web/locale/ach/viewer.ftl b/public/pdfjs/web/locale/ach/viewer.ftl new file mode 100644 index 0000000..36769b7 --- /dev/null +++ b/public/pdfjs/web/locale/ach/viewer.ftl @@ -0,0 +1,225 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pot buk mukato +pdfjs-previous-button-label = Mukato +pdfjs-next-button = + .title = Pot buk malubo +pdfjs-next-button-label = Malubo +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pot buk +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = pi { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } me { $pagesCount }) +pdfjs-zoom-out-button = + .title = Jwik Matidi +pdfjs-zoom-out-button-label = Jwik Matidi +pdfjs-zoom-in-button = + .title = Kwot Madit +pdfjs-zoom-in-button-label = Kwot Madit +pdfjs-zoom-select = + .title = Kwoti +pdfjs-presentation-mode-button = + .title = Lokke i kit me tyer +pdfjs-presentation-mode-button-label = Kit me tyer +pdfjs-open-file-button = + .title = Yab Pwail +pdfjs-open-file-button-label = Yab +pdfjs-print-button = + .title = Go +pdfjs-print-button-label = Go + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Gintic +pdfjs-tools-button-label = Gintic +pdfjs-first-page-button = + .title = Cit i pot buk mukwongo +pdfjs-first-page-button-label = Cit i pot buk mukwongo +pdfjs-last-page-button = + .title = Cit i pot buk magiko +pdfjs-last-page-button-label = Cit i pot buk magiko +pdfjs-page-rotate-cw-button = + .title = Wire i tung lacuc +pdfjs-page-rotate-cw-button-label = Wire i tung lacuc +pdfjs-page-rotate-ccw-button = + .title = Wire i tung lacam +pdfjs-page-rotate-ccw-button-label = Wire i tung lacam +pdfjs-cursor-text-select-tool-button = + .title = Cak gitic me yero coc +pdfjs-cursor-text-select-tool-button-label = Gitic me yero coc +pdfjs-cursor-hand-tool-button = + .title = Cak gitic me cing +pdfjs-cursor-hand-tool-button-label = Gitic cing + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Jami me gin acoya… +pdfjs-document-properties-button-label = Jami me gin acoya… +pdfjs-document-properties-file-name = Nying pwail: +pdfjs-document-properties-file-size = Dit pa pwail: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Wiye: +pdfjs-document-properties-author = Ngat mucoyo: +pdfjs-document-properties-subject = Subjek: +pdfjs-document-properties-keywords = Lok mapire tek: +pdfjs-document-properties-creation-date = Nino dwe me cwec: +pdfjs-document-properties-modification-date = Nino dwe me yub: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Lacwec: +pdfjs-document-properties-producer = Layub PDF: +pdfjs-document-properties-version = Kit PDF: +pdfjs-document-properties-page-count = Kwan me pot buk: +pdfjs-document-properties-page-size = Dit pa potbuk: +pdfjs-document-properties-page-size-unit-inches = i +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = atir +pdfjs-document-properties-page-size-orientation-landscape = arii +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Waraga +pdfjs-document-properties-page-size-name-legal = Cik + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +pdfjs-document-properties-linearized-yes = Eyo +pdfjs-document-properties-linearized-no = Pe +pdfjs-document-properties-close-button = Lor + +## Print + +pdfjs-print-progress-message = Yubo coc me agoya… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Juki +pdfjs-printing-not-supported = Ciko: Layeny ma pe teno goyo liweng. +pdfjs-printing-not-ready = Ciko: PDF pe ocane weng me agoya. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Lok gintic ma inget +pdfjs-toggle-sidebar-button-label = Lok gintic ma inget +pdfjs-document-outline-button = + .title = Nyut Wiyewiye me Gin acoya (dii-kiryo me yaro/kano jami weng) +pdfjs-document-outline-button-label = Pek pa gin acoya +pdfjs-attachments-button = + .title = Nyut twec +pdfjs-attachments-button-label = Twec +pdfjs-thumbs-button = + .title = Nyut cal +pdfjs-thumbs-button-label = Cal +pdfjs-findbar-button = + .title = Nong iye gin acoya +pdfjs-findbar-button-label = Nong + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pot buk { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Cal me pot buk { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Nong + .placeholder = Nong i dokumen… +pdfjs-find-previous-button = + .title = Nong timme pa lok mukato +pdfjs-find-previous-button-label = Mukato +pdfjs-find-next-button = + .title = Nong timme pa lok malubo +pdfjs-find-next-button-label = Malubo +pdfjs-find-highlight-checkbox = Ket Lanyut I Weng +pdfjs-find-match-case-checkbox-label = Lok marwate +pdfjs-find-reached-top = Oo iwi gin acoya, omede ki i tere +pdfjs-find-reached-bottom = Oo i agiki me gin acoya, omede ki iwiye +pdfjs-find-not-found = Lok pe ononge + +## Predefined zoom values + +pdfjs-page-scale-width = Lac me iye pot buk +pdfjs-page-scale-fit = Porre me pot buk +pdfjs-page-scale-auto = Kwot pire kene +pdfjs-page-scale-actual = Dite kikome +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Bal otime kun cano PDF. +pdfjs-invalid-file-error = Pwail me PDF ma pe atir onyo obale woko. +pdfjs-missing-file-error = Pwail me PDF tye ka rem. +pdfjs-unexpected-response-error = Lagam mape kigeno pa lapok tic. +pdfjs-rendering-error = Bal otime i kare me nyuto pot buk. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Lok angea manok] + +## Password + +pdfjs-password-label = Ket mung me donyo me yabo pwail me PDF man. +pdfjs-password-invalid = Mung me donyo pe atir. Tim ber i tem doki. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Juki +pdfjs-web-fonts-disabled = Kijuko dit pa coc me kakube woko: pe romo tic ki dit pa coc me PDF ma kiketo i kine. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/af/viewer.ftl b/public/pdfjs/web/locale/af/viewer.ftl new file mode 100644 index 0000000..7c4346f --- /dev/null +++ b/public/pdfjs/web/locale/af/viewer.ftl @@ -0,0 +1,212 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Vorige bladsy +pdfjs-previous-button-label = Vorige +pdfjs-next-button = + .title = Volgende bladsy +pdfjs-next-button-label = Volgende +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Bladsy +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = van { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } van { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zoem uit +pdfjs-zoom-out-button-label = Zoem uit +pdfjs-zoom-in-button = + .title = Zoem in +pdfjs-zoom-in-button-label = Zoem in +pdfjs-zoom-select = + .title = Zoem +pdfjs-presentation-mode-button = + .title = Wissel na voorleggingsmodus +pdfjs-presentation-mode-button-label = Voorleggingsmodus +pdfjs-open-file-button = + .title = Open lêer +pdfjs-open-file-button-label = Open +pdfjs-print-button = + .title = Druk +pdfjs-print-button-label = Druk + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Nutsgoed +pdfjs-tools-button-label = Nutsgoed +pdfjs-first-page-button = + .title = Gaan na eerste bladsy +pdfjs-first-page-button-label = Gaan na eerste bladsy +pdfjs-last-page-button = + .title = Gaan na laaste bladsy +pdfjs-last-page-button-label = Gaan na laaste bladsy +pdfjs-page-rotate-cw-button = + .title = Roteer kloksgewys +pdfjs-page-rotate-cw-button-label = Roteer kloksgewys +pdfjs-page-rotate-ccw-button = + .title = Roteer anti-kloksgewys +pdfjs-page-rotate-ccw-button-label = Roteer anti-kloksgewys +pdfjs-cursor-text-select-tool-button = + .title = Aktiveer gereedskap om teks te merk +pdfjs-cursor-text-select-tool-button-label = Teksmerkgereedskap +pdfjs-cursor-hand-tool-button = + .title = Aktiveer handjie +pdfjs-cursor-hand-tool-button-label = Handjie + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumenteienskappe… +pdfjs-document-properties-button-label = Dokumenteienskappe… +pdfjs-document-properties-file-name = Lêernaam: +pdfjs-document-properties-file-size = Lêergrootte: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } kG ({ $size_b } grepe) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MG ({ $size_b } grepe) +pdfjs-document-properties-title = Titel: +pdfjs-document-properties-author = Outeur: +pdfjs-document-properties-subject = Onderwerp: +pdfjs-document-properties-keywords = Sleutelwoorde: +pdfjs-document-properties-creation-date = Skeppingsdatum: +pdfjs-document-properties-modification-date = Wysigingsdatum: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Skepper: +pdfjs-document-properties-producer = PDF-vervaardiger: +pdfjs-document-properties-version = PDF-weergawe: +pdfjs-document-properties-page-count = Aantal bladsye: + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + + +## + +pdfjs-document-properties-close-button = Sluit + +## Print + +pdfjs-print-progress-message = Berei tans dokument voor om te druk… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Kanselleer +pdfjs-printing-not-supported = Waarskuwing: Dié blaaier ondersteun nie drukwerk ten volle nie. +pdfjs-printing-not-ready = Waarskuwing: Die PDF is nog nie volledig gelaai vir drukwerk nie. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Sypaneel aan/af +pdfjs-toggle-sidebar-button-label = Sypaneel aan/af +pdfjs-document-outline-button = + .title = Wys dokumentskema (dubbelklik om alle items oop/toe te vou) +pdfjs-document-outline-button-label = Dokumentoorsig +pdfjs-attachments-button = + .title = Wys aanhegsels +pdfjs-attachments-button-label = Aanhegsels +pdfjs-thumbs-button = + .title = Wys duimnaels +pdfjs-thumbs-button-label = Duimnaels +pdfjs-findbar-button = + .title = Soek in dokument +pdfjs-findbar-button-label = Vind + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Bladsy { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Duimnael van bladsy { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Vind + .placeholder = Soek in dokument… +pdfjs-find-previous-button = + .title = Vind die vorige voorkoms van die frase +pdfjs-find-previous-button-label = Vorige +pdfjs-find-next-button = + .title = Vind die volgende voorkoms van die frase +pdfjs-find-next-button-label = Volgende +pdfjs-find-highlight-checkbox = Verlig almal +pdfjs-find-match-case-checkbox-label = Kassensitief +pdfjs-find-reached-top = Bokant van dokument is bereik; gaan voort van onder af +pdfjs-find-reached-bottom = Einde van dokument is bereik; gaan voort van bo af +pdfjs-find-not-found = Frase nie gevind nie + +## Predefined zoom values + +pdfjs-page-scale-width = Bladsywydte +pdfjs-page-scale-fit = Pas bladsy +pdfjs-page-scale-auto = Outomatiese zoem +pdfjs-page-scale-actual = Werklike grootte +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = 'n Fout het voorgekom met die laai van die PDF. +pdfjs-invalid-file-error = Ongeldige of korrupte PDF-lêer. +pdfjs-missing-file-error = PDF-lêer is weg. +pdfjs-unexpected-response-error = Onverwagse antwoord van bediener. +pdfjs-rendering-error = 'n Fout het voorgekom toe die bladsy weergegee is. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type }-annotasie] + +## Password + +pdfjs-password-label = Gee die wagwoord om dié PDF-lêer mee te open. +pdfjs-password-invalid = Ongeldige wagwoord. Probeer gerus weer. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Kanselleer +pdfjs-web-fonts-disabled = Webfonte is gedeaktiveer: kan nie PDF-fonte wat ingebed is, gebruik nie. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/an/viewer.ftl b/public/pdfjs/web/locale/an/viewer.ftl new file mode 100644 index 0000000..6733147 --- /dev/null +++ b/public/pdfjs/web/locale/an/viewer.ftl @@ -0,0 +1,257 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pachina anterior +pdfjs-previous-button-label = Anterior +pdfjs-next-button = + .title = Pachina siguient +pdfjs-next-button-label = Siguient +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pachina +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = de { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount }) +pdfjs-zoom-out-button = + .title = Achiquir +pdfjs-zoom-out-button-label = Achiquir +pdfjs-zoom-in-button = + .title = Agrandir +pdfjs-zoom-in-button-label = Agrandir +pdfjs-zoom-select = + .title = Grandaria +pdfjs-presentation-mode-button = + .title = Cambear t'o modo de presentación +pdfjs-presentation-mode-button-label = Modo de presentación +pdfjs-open-file-button = + .title = Ubrir o fichero +pdfjs-open-file-button-label = Ubrir +pdfjs-print-button = + .title = Imprentar +pdfjs-print-button-label = Imprentar + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Ferramientas +pdfjs-tools-button-label = Ferramientas +pdfjs-first-page-button = + .title = Ir ta la primer pachina +pdfjs-first-page-button-label = Ir ta la primer pachina +pdfjs-last-page-button = + .title = Ir ta la zaguer pachina +pdfjs-last-page-button-label = Ir ta la zaguer pachina +pdfjs-page-rotate-cw-button = + .title = Chirar enta la dreita +pdfjs-page-rotate-cw-button-label = Chira enta la dreita +pdfjs-page-rotate-ccw-button = + .title = Chirar enta la zurda +pdfjs-page-rotate-ccw-button-label = Chirar enta la zurda +pdfjs-cursor-text-select-tool-button = + .title = Activar la ferramienta de selección de texto +pdfjs-cursor-text-select-tool-button-label = Ferramienta de selección de texto +pdfjs-cursor-hand-tool-button = + .title = Activar la ferramienta man +pdfjs-cursor-hand-tool-button-label = Ferramienta man +pdfjs-scroll-vertical-button = + .title = Usar lo desplazamiento vertical +pdfjs-scroll-vertical-button-label = Desplazamiento vertical +pdfjs-scroll-horizontal-button = + .title = Usar lo desplazamiento horizontal +pdfjs-scroll-horizontal-button-label = Desplazamiento horizontal +pdfjs-scroll-wrapped-button = + .title = Activaar lo desplazamiento contino +pdfjs-scroll-wrapped-button-label = Desplazamiento contino +pdfjs-spread-none-button = + .title = No unir vistas de pachinas +pdfjs-spread-none-button-label = Una pachina nomás +pdfjs-spread-odd-button = + .title = Mostrar vista de pachinas, con as impars a la zurda +pdfjs-spread-odd-button-label = Doble pachina, impar a la zurda +pdfjs-spread-even-button = + .title = Amostrar vista de pachinas, con as pars a la zurda +pdfjs-spread-even-button-label = Doble pachina, para a la zurda + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Propiedatz d'o documento... +pdfjs-document-properties-button-label = Propiedatz d'o documento... +pdfjs-document-properties-file-name = Nombre de fichero: +pdfjs-document-properties-file-size = Grandaria d'o fichero: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Titol: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Afer: +pdfjs-document-properties-keywords = Parolas clau: +pdfjs-document-properties-creation-date = Calendata de creyación: +pdfjs-document-properties-modification-date = Calendata de modificación: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Creyador: +pdfjs-document-properties-producer = Creyador de PDF: +pdfjs-document-properties-version = Versión de PDF: +pdfjs-document-properties-page-count = Numero de pachinas: +pdfjs-document-properties-page-size = Mida de pachina: +pdfjs-document-properties-page-size-unit-inches = pulgadas +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = vertical +pdfjs-document-properties-page-size-orientation-landscape = horizontal +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Carta +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } x { $height } { $unit } { $orientation } +pdfjs-document-properties-page-size-dimension-name-string = { $width } x { $height } { $unit } { $name }, { $orientation } + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Vista web rapida: +pdfjs-document-properties-linearized-yes = Sí +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Zarrar + +## Print + +pdfjs-print-progress-message = Se ye preparando la documentación pa imprentar… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cancelar +pdfjs-printing-not-supported = Pare cuenta: Iste navegador no maneya totalment as impresions. +pdfjs-printing-not-ready = Aviso: Encara no se ha cargau completament o PDF ta imprentar-lo. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Amostrar u amagar a barra lateral +pdfjs-toggle-sidebar-notification-button = + .title = Cambiar barra lateral (lo documento contiene esquema/adchuntos/capas) +pdfjs-toggle-sidebar-button-label = Amostrar a barra lateral +pdfjs-document-outline-button = + .title = Amostrar esquema d'o documento (fer doble clic pa expandir/compactar totz los items) +pdfjs-document-outline-button-label = Esquema d'o documento +pdfjs-attachments-button = + .title = Amostrar os adchuntos +pdfjs-attachments-button-label = Adchuntos +pdfjs-layers-button = + .title = Amostrar capas (doble clic para reiniciar totas las capas a lo estau per defecto) +pdfjs-layers-button-label = Capas +pdfjs-thumbs-button = + .title = Amostrar as miniaturas +pdfjs-thumbs-button-label = Miniaturas +pdfjs-findbar-button = + .title = Trobar en o documento +pdfjs-findbar-button-label = Trobar +pdfjs-additional-layers = Capas adicionals + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pachina { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura d'a pachina { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Trobar + .placeholder = Trobar en o documento… +pdfjs-find-previous-button = + .title = Trobar l'anterior coincidencia d'a frase +pdfjs-find-previous-button-label = Anterior +pdfjs-find-next-button = + .title = Trobar a siguient coincidencia d'a frase +pdfjs-find-next-button-label = Siguient +pdfjs-find-highlight-checkbox = Resaltar-lo tot +pdfjs-find-match-case-checkbox-label = Coincidencia de mayusclas/minusclas +pdfjs-find-entire-word-checkbox-label = Parolas completas +pdfjs-find-reached-top = S'ha plegau a l'inicio d'o documento, se contina dende baixo +pdfjs-find-reached-bottom = S'ha plegau a la fin d'o documento, se contina dende alto +pdfjs-find-not-found = No s'ha trobau a frase + +## Predefined zoom values + +pdfjs-page-scale-width = Amplaria d'a pachina +pdfjs-page-scale-fit = Achuste d'a pachina +pdfjs-page-scale-auto = Grandaria automatica +pdfjs-page-scale-actual = Grandaria actual +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = S'ha produciu una error en cargar o PDF. +pdfjs-invalid-file-error = O PDF no ye valido u ye estorbau. +pdfjs-missing-file-error = No i ha fichero PDF. +pdfjs-unexpected-response-error = Respuesta a lo servicio inasperada. +pdfjs-rendering-error = Ha ocurriu una error en renderizar a pachina. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anotación { $type }] + +## Password + +pdfjs-password-label = Introduzca a clau ta ubrir iste fichero PDF. +pdfjs-password-invalid = Clau invalida. Torna a intentar-lo. +pdfjs-password-ok-button = Acceptar +pdfjs-password-cancel-button = Cancelar +pdfjs-web-fonts-disabled = As fuents web son desactivadas: no se puet incrustar fichers PDF. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/ar/viewer.ftl b/public/pdfjs/web/locale/ar/viewer.ftl new file mode 100644 index 0000000..8d14767 --- /dev/null +++ b/public/pdfjs/web/locale/ar/viewer.ftl @@ -0,0 +1,425 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = الصفحة السابقة +pdfjs-previous-button-label = السابقة +pdfjs-next-button = + .title = الصفحة التالية +pdfjs-next-button-label = التالية +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = صفحة +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = من { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } من { $pagesCount }) +pdfjs-zoom-out-button = + .title = بعّد +pdfjs-zoom-out-button-label = بعّد +pdfjs-zoom-in-button = + .title = قرّب +pdfjs-zoom-in-button-label = قرّب +pdfjs-zoom-select = + .title = التقريب +pdfjs-presentation-mode-button = + .title = انتقل لوضع العرض التقديمي +pdfjs-presentation-mode-button-label = وضع العرض التقديمي +pdfjs-open-file-button = + .title = افتح ملفًا +pdfjs-open-file-button-label = افتح +pdfjs-print-button = + .title = اطبع +pdfjs-print-button-label = اطبع +pdfjs-save-button = + .title = احفظ +pdfjs-save-button-label = احفظ +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = نزّل +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = نزّل +pdfjs-bookmark-button = + .title = الصفحة الحالية (عرض URL من الصفحة الحالية) +pdfjs-bookmark-button-label = الصفحة الحالية + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = الأدوات +pdfjs-tools-button-label = الأدوات +pdfjs-first-page-button = + .title = انتقل إلى الصفحة الأولى +pdfjs-first-page-button-label = انتقل إلى الصفحة الأولى +pdfjs-last-page-button = + .title = انتقل إلى الصفحة الأخيرة +pdfjs-last-page-button-label = انتقل إلى الصفحة الأخيرة +pdfjs-page-rotate-cw-button = + .title = أدر باتجاه عقارب الساعة +pdfjs-page-rotate-cw-button-label = أدر باتجاه عقارب الساعة +pdfjs-page-rotate-ccw-button = + .title = أدر بعكس اتجاه عقارب الساعة +pdfjs-page-rotate-ccw-button-label = أدر بعكس اتجاه عقارب الساعة +pdfjs-cursor-text-select-tool-button = + .title = فعّل أداة اختيار النص +pdfjs-cursor-text-select-tool-button-label = أداة اختيار النص +pdfjs-cursor-hand-tool-button = + .title = فعّل أداة اليد +pdfjs-cursor-hand-tool-button-label = أداة اليد +pdfjs-scroll-page-button = + .title = استخدم تمرير الصفحة +pdfjs-scroll-page-button-label = تمرير الصفحة +pdfjs-scroll-vertical-button = + .title = استخدم التمرير الرأسي +pdfjs-scroll-vertical-button-label = التمرير الرأسي +pdfjs-scroll-horizontal-button = + .title = استخدم التمرير الأفقي +pdfjs-scroll-horizontal-button-label = التمرير الأفقي +pdfjs-scroll-wrapped-button = + .title = استخدم التمرير الملتف +pdfjs-scroll-wrapped-button-label = التمرير الملتف +pdfjs-spread-none-button = + .title = لا تدمج هوامش الصفحات مع بعضها البعض +pdfjs-spread-none-button-label = بلا هوامش +pdfjs-spread-odd-button = + .title = ادمج هوامش الصفحات الفردية +pdfjs-spread-odd-button-label = هوامش الصفحات الفردية +pdfjs-spread-even-button = + .title = ادمج هوامش الصفحات الزوجية +pdfjs-spread-even-button-label = هوامش الصفحات الزوجية + +## Document properties dialog + +pdfjs-document-properties-button = + .title = خصائص المستند… +pdfjs-document-properties-button-label = خصائص المستند… +pdfjs-document-properties-file-name = اسم الملف: +pdfjs-document-properties-file-size = حجم الملف: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } ك.بايت ({ $size_b } بايت) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } م.بايت ({ $size_b } بايت) +pdfjs-document-properties-title = العنوان: +pdfjs-document-properties-author = المؤلف: +pdfjs-document-properties-subject = الموضوع: +pdfjs-document-properties-keywords = الكلمات الأساسية: +pdfjs-document-properties-creation-date = تاريخ الإنشاء: +pdfjs-document-properties-modification-date = تاريخ التعديل: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }، { $time } +pdfjs-document-properties-creator = المنشئ: +pdfjs-document-properties-producer = منتج PDF: +pdfjs-document-properties-version = إصدارة PDF: +pdfjs-document-properties-page-count = عدد الصفحات: +pdfjs-document-properties-page-size = مقاس الورقة: +pdfjs-document-properties-page-size-unit-inches = بوصة +pdfjs-document-properties-page-size-unit-millimeters = ملم +pdfjs-document-properties-page-size-orientation-portrait = طوليّ +pdfjs-document-properties-page-size-orientation-landscape = عرضيّ +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = خطاب +pdfjs-document-properties-page-size-name-legal = قانونيّ + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = ‏{ $width } × ‏{ $height } ‏{ $unit } (‏{ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = ‏{ $width } × ‏{ $height } ‏{ $unit } (‏{ $name }، { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = العرض السريع عبر الوِب: +pdfjs-document-properties-linearized-yes = نعم +pdfjs-document-properties-linearized-no = لا +pdfjs-document-properties-close-button = أغلق + +## Print + +pdfjs-print-progress-message = يُحضّر المستند للطباعة… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }٪ +pdfjs-print-progress-close-button = ألغِ +pdfjs-printing-not-supported = تحذير: لا يدعم هذا المتصفح الطباعة بشكل كامل. +pdfjs-printing-not-ready = تحذير: ملف PDF لم يُحمّل كاملًا للطباعة. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = بدّل ظهور الشريط الجانبي +pdfjs-toggle-sidebar-notification-button = + .title = بدّل ظهور الشريط الجانبي (يحتوي المستند على مخطط أو مرفقات أو طبقات) +pdfjs-toggle-sidebar-button-label = بدّل ظهور الشريط الجانبي +pdfjs-document-outline-button = + .title = اعرض فهرس المستند (نقر مزدوج لتمديد أو تقليص كل العناصر) +pdfjs-document-outline-button-label = مخطط المستند +pdfjs-attachments-button = + .title = اعرض المرفقات +pdfjs-attachments-button-label = المُرفقات +pdfjs-layers-button = + .title = اعرض الطبقات (انقر مرتين لتصفير كل الطبقات إلى الحالة المبدئية) +pdfjs-layers-button-label = ‏‏الطبقات +pdfjs-thumbs-button = + .title = اعرض مُصغرات +pdfjs-thumbs-button-label = مُصغّرات +pdfjs-current-outline-item-button = + .title = ابحث عن عنصر المخطّط التفصيلي الحالي +pdfjs-current-outline-item-button-label = عنصر المخطّط التفصيلي الحالي +pdfjs-findbar-button = + .title = ابحث في المستند +pdfjs-findbar-button-label = ابحث +pdfjs-additional-layers = الطبقات الإضافية + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = صفحة { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = مصغّرة صفحة { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = ابحث + .placeholder = ابحث في المستند… +pdfjs-find-previous-button = + .title = ابحث عن التّواجد السّابق للعبارة +pdfjs-find-previous-button-label = السابق +pdfjs-find-next-button = + .title = ابحث عن التّواجد التّالي للعبارة +pdfjs-find-next-button-label = التالي +pdfjs-find-highlight-checkbox = أبرِز الكل +pdfjs-find-match-case-checkbox-label = طابق حالة الأحرف +pdfjs-find-match-diacritics-checkbox-label = طابِق الحركات +pdfjs-find-entire-word-checkbox-label = كلمات كاملة +pdfjs-find-reached-top = تابعت من الأسفل بعدما وصلت إلى بداية المستند +pdfjs-find-reached-bottom = تابعت من الأعلى بعدما وصلت إلى نهاية المستند +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [zero] لا مطابقة + [one] { $current } من أصل { $total } مطابقة + [two] { $current } من أصل { $total } مطابقة + [few] { $current } من أصل { $total } مطابقة + [many] { $current } من أصل { $total } مطابقة + *[other] { $current } من أصل { $total } مطابقة + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [zero] { $limit } مطابقة + [one] أكثر من { $limit } مطابقة + [two] أكثر من { $limit } مطابقة + [few] أكثر من { $limit } مطابقة + [many] أكثر من { $limit } مطابقة + *[other] أكثر من { $limit } مطابقات + } +pdfjs-find-not-found = لا وجود للعبارة + +## Predefined zoom values + +pdfjs-page-scale-width = عرض الصفحة +pdfjs-page-scale-fit = ملائمة الصفحة +pdfjs-page-scale-auto = تقريب تلقائي +pdfjs-page-scale-actual = الحجم الفعلي +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }٪ + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = صفحة { $page } + +## Loading indicator messages + +pdfjs-loading-error = حدث عطل أثناء تحميل ملف PDF. +pdfjs-invalid-file-error = ملف PDF تالف أو غير صحيح. +pdfjs-missing-file-error = ملف PDF غير موجود. +pdfjs-unexpected-response-error = استجابة خادوم غير متوقعة. +pdfjs-rendering-error = حدث خطأ أثناء عرض الصفحة. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }، { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [تعليق { $type }] + +## Password + +pdfjs-password-label = أدخل لكلمة السر لفتح هذا الملف. +pdfjs-password-invalid = كلمة سر خطأ. من فضلك أعد المحاولة. +pdfjs-password-ok-button = حسنا +pdfjs-password-cancel-button = ألغِ +pdfjs-web-fonts-disabled = خطوط الوب مُعطّلة: تعذّر استخدام خطوط PDF المُضمّنة. + +## Editing + +pdfjs-editor-free-text-button = + .title = نص +pdfjs-editor-free-text-button-label = نص +pdfjs-editor-ink-button = + .title = ارسم +pdfjs-editor-ink-button-label = ارسم +pdfjs-editor-stamp-button = + .title = أضِف أو حرّر الصور +pdfjs-editor-stamp-button-label = أضِف أو حرّر الصور +pdfjs-editor-highlight-button = + .title = أبرِز +pdfjs-editor-highlight-button-label = أبرِز +pdfjs-highlight-floating-button1 = + .title = أبرِز + .aria-label = أبرِز +pdfjs-highlight-floating-button-label = أبرِز + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = أزِل الرسم +pdfjs-editor-remove-freetext-button = + .title = أزِل النص +pdfjs-editor-remove-stamp-button = + .title = أزِل الصورة +pdfjs-editor-remove-highlight-button = + .title = أزِل الإبراز + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = اللون +pdfjs-editor-free-text-size-input = الحجم +pdfjs-editor-ink-color-input = اللون +pdfjs-editor-ink-thickness-input = السماكة +pdfjs-editor-ink-opacity-input = العتامة +pdfjs-editor-stamp-add-image-button = + .title = أضِف صورة +pdfjs-editor-stamp-add-image-button-label = أضِف صورة +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = السماكة +pdfjs-editor-free-highlight-thickness-title = + .title = غيّر السُمك عند إبراز عناصر أُخرى غير النص +pdfjs-free-text = + .aria-label = محرِّر النص +pdfjs-free-text-default-content = ابدأ الكتابة… +pdfjs-ink = + .aria-label = محرِّر الرسم +pdfjs-ink-canvas = + .aria-label = صورة أنشأها المستخدم + +## Alt-text dialog + +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button-label = نص بديل +pdfjs-editor-alt-text-edit-button-label = تحرير النص البديل +pdfjs-editor-alt-text-dialog-label = اختر خيار +pdfjs-editor-alt-text-dialog-description = يساعد النص البديل عندما لا يتمكن الأشخاص من رؤية الصورة أو عندما لا يتم تحميلها. +pdfjs-editor-alt-text-add-description-label = أضِف وصف +pdfjs-editor-alt-text-add-description-description = استهدف جملتين تصفان الموضوع أو الإعداد أو الإجراءات. +pdfjs-editor-alt-text-mark-decorative-label = علّمها على أنها زخرفية +pdfjs-editor-alt-text-mark-decorative-description = يُستخدم هذا في الصور المزخرفة، مثل الحدود أو العلامات المائية. +pdfjs-editor-alt-text-cancel-button = ألغِ +pdfjs-editor-alt-text-save-button = احفظ +pdfjs-editor-alt-text-decorative-tooltip = عُلّمت على أنها زخرفية +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = على سبيل المثال، "يجلس شاب على الطاولة لتناول وجبة" + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = الزاوية اليُسرى العُليا — غيّر الحجم +pdfjs-editor-resizer-label-top-middle = أعلى الوسط - غيّر الحجم +pdfjs-editor-resizer-label-top-right = الزاوية اليُمنى العُليا - غيّر الحجم +pdfjs-editor-resizer-label-middle-right = اليمين الأوسط - غيّر الحجم +pdfjs-editor-resizer-label-bottom-right = الزاوية اليُمنى السُفلى - غيّر الحجم +pdfjs-editor-resizer-label-bottom-middle = أسفل الوسط - غيّر الحجم +pdfjs-editor-resizer-label-bottom-left = الزاوية اليُسرى السُفلية - غيّر الحجم +pdfjs-editor-resizer-label-middle-left = مُنتصف اليسار - غيّر الحجم +pdfjs-editor-resizer-top-left = + .aria-label = الزاوية اليُسرى العُليا — غيّر الحجم +pdfjs-editor-resizer-top-middle = + .aria-label = أعلى الوسط - غيّر الحجم +pdfjs-editor-resizer-top-right = + .aria-label = الزاوية اليُمنى العُليا - غيّر الحجم +pdfjs-editor-resizer-middle-right = + .aria-label = اليمين الأوسط - غيّر الحجم +pdfjs-editor-resizer-bottom-right = + .aria-label = الزاوية اليُمنى السُفلى - غيّر الحجم +pdfjs-editor-resizer-bottom-middle = + .aria-label = أسفل الوسط - غيّر الحجم +pdfjs-editor-resizer-bottom-left = + .aria-label = الزاوية اليُسرى السُفلية - غيّر الحجم +pdfjs-editor-resizer-middle-left = + .aria-label = مُنتصف اليسار - غيّر الحجم + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = أبرِز اللون +pdfjs-editor-colorpicker-button = + .title = غيّر اللون +pdfjs-editor-colorpicker-dropdown = + .aria-label = اختيارات الألوان +pdfjs-editor-colorpicker-yellow = + .title = أصفر +pdfjs-editor-colorpicker-green = + .title = أخضر +pdfjs-editor-colorpicker-blue = + .title = أزرق +pdfjs-editor-colorpicker-pink = + .title = وردي +pdfjs-editor-colorpicker-red = + .title = أحمر + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = أظهِر الكل +pdfjs-editor-highlight-show-all-button = + .title = أظهِر الكل + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + diff --git a/public/pdfjs/web/locale/ast/viewer.ftl b/public/pdfjs/web/locale/ast/viewer.ftl new file mode 100644 index 0000000..2503caf --- /dev/null +++ b/public/pdfjs/web/locale/ast/viewer.ftl @@ -0,0 +1,201 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Páxina anterior +pdfjs-previous-button-label = Anterior +pdfjs-next-button = + .title = Páxina siguiente +pdfjs-next-button-label = Siguiente +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Páxina +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = de { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount }) +pdfjs-zoom-out-button = + .title = Alloñar +pdfjs-zoom-out-button-label = Alloña +pdfjs-zoom-in-button = + .title = Averar +pdfjs-zoom-in-button-label = Avera +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Cambiar al mou de presentación +pdfjs-presentation-mode-button-label = Mou de presentación +pdfjs-open-file-button-label = Abrir +pdfjs-print-button = + .title = Imprentar +pdfjs-print-button-label = Imprentar + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Ferramientes +pdfjs-tools-button-label = Ferramientes +pdfjs-first-page-button-label = Dir a la primer páxina +pdfjs-last-page-button-label = Dir a la última páxina +pdfjs-page-rotate-cw-button = + .title = Voltia a la derecha +pdfjs-page-rotate-cw-button-label = Voltiar a la derecha +pdfjs-page-rotate-ccw-button = + .title = Voltia a la esquierda +pdfjs-page-rotate-ccw-button-label = Voltiar a la esquierda +pdfjs-cursor-text-select-tool-button = + .title = Activa la ferramienta d'esbilla de testu +pdfjs-cursor-text-select-tool-button-label = Ferramienta d'esbilla de testu +pdfjs-cursor-hand-tool-button = + .title = Activa la ferramienta de mano +pdfjs-cursor-hand-tool-button-label = Ferramienta de mano +pdfjs-scroll-vertical-button = + .title = Usa'l desplazamientu vertical +pdfjs-scroll-vertical-button-label = Desplazamientu vertical +pdfjs-scroll-horizontal-button = + .title = Usa'l desplazamientu horizontal +pdfjs-scroll-horizontal-button-label = Desplazamientu horizontal +pdfjs-scroll-wrapped-button = + .title = Usa'l desplazamientu continuu +pdfjs-scroll-wrapped-button-label = Desplazamientu continuu +pdfjs-spread-none-button-label = Fueyes individuales +pdfjs-spread-odd-button-label = Fueyes pares +pdfjs-spread-even-button-label = Fueyes impares + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Propiedaes del documentu… +pdfjs-document-properties-button-label = Propiedaes del documentu… +pdfjs-document-properties-file-name = Nome del ficheru: +pdfjs-document-properties-file-size = Tamañu del ficheru: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Títulu: +pdfjs-document-properties-keywords = Pallabres clave: +pdfjs-document-properties-creation-date = Data de creación: +pdfjs-document-properties-modification-date = Data de modificación: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-producer = Productor del PDF: +pdfjs-document-properties-version = Versión del PDF: +pdfjs-document-properties-page-count = Númberu de páxines: +pdfjs-document-properties-page-size = Tamañu de páxina: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = vertical +pdfjs-document-properties-page-size-orientation-landscape = horizontal +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Vista web rápida: +pdfjs-document-properties-linearized-yes = Sí +pdfjs-document-properties-linearized-no = Non +pdfjs-document-properties-close-button = Zarrar + +## Print + +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Encaboxar + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Alternar la barra llateral +pdfjs-attachments-button = + .title = Amosar los axuntos +pdfjs-attachments-button-label = Axuntos +pdfjs-layers-button-label = Capes +pdfjs-thumbs-button = + .title = Amosar les miniatures +pdfjs-thumbs-button-label = Miniatures +pdfjs-findbar-button-label = Atopar +pdfjs-additional-layers = Capes adicionales + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Páxina { $page } + +## Find panel button title and messages + +pdfjs-find-previous-button-label = Anterior +pdfjs-find-next-button-label = Siguiente +pdfjs-find-entire-word-checkbox-label = Pallabres completes +pdfjs-find-reached-top = Algamóse'l comienzu de la páxina, síguese dende abaxo +pdfjs-find-reached-bottom = Algamóse la fin del documentu, síguese dende arriba + +## Predefined zoom values + +pdfjs-page-scale-auto = Zoom automáticu +pdfjs-page-scale-actual = Tamañu real +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Páxina { $page } + +## Loading indicator messages + +pdfjs-loading-error = Asocedió un fallu mentanto se cargaba'l PDF. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } + +## Password + +pdfjs-password-ok-button = Aceptar +pdfjs-password-cancel-button = Encaboxar + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/az/viewer.ftl b/public/pdfjs/web/locale/az/viewer.ftl new file mode 100644 index 0000000..773aae4 --- /dev/null +++ b/public/pdfjs/web/locale/az/viewer.ftl @@ -0,0 +1,257 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Əvvəlki səhifə +pdfjs-previous-button-label = Əvvəlkini tap +pdfjs-next-button = + .title = Növbəti səhifə +pdfjs-next-button-label = İrəli +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Səhifə +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = / { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount }) +pdfjs-zoom-out-button = + .title = Uzaqlaş +pdfjs-zoom-out-button-label = Uzaqlaş +pdfjs-zoom-in-button = + .title = Yaxınlaş +pdfjs-zoom-in-button-label = Yaxınlaş +pdfjs-zoom-select = + .title = Yaxınlaşdırma +pdfjs-presentation-mode-button = + .title = Təqdimat Rejiminə Keç +pdfjs-presentation-mode-button-label = Təqdimat Rejimi +pdfjs-open-file-button = + .title = Fayl Aç +pdfjs-open-file-button-label = Aç +pdfjs-print-button = + .title = Yazdır +pdfjs-print-button-label = Yazdır + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Alətlər +pdfjs-tools-button-label = Alətlər +pdfjs-first-page-button = + .title = İlk Səhifəyə get +pdfjs-first-page-button-label = İlk Səhifəyə get +pdfjs-last-page-button = + .title = Son Səhifəyə get +pdfjs-last-page-button-label = Son Səhifəyə get +pdfjs-page-rotate-cw-button = + .title = Saat İstiqamətində Fırlat +pdfjs-page-rotate-cw-button-label = Saat İstiqamətində Fırlat +pdfjs-page-rotate-ccw-button = + .title = Saat İstiqamətinin Əksinə Fırlat +pdfjs-page-rotate-ccw-button-label = Saat İstiqamətinin Əksinə Fırlat +pdfjs-cursor-text-select-tool-button = + .title = Yazı seçmə alətini aktivləşdir +pdfjs-cursor-text-select-tool-button-label = Yazı seçmə aləti +pdfjs-cursor-hand-tool-button = + .title = Əl alətini aktivləşdir +pdfjs-cursor-hand-tool-button-label = Əl aləti +pdfjs-scroll-vertical-button = + .title = Şaquli sürüşdürmə işlət +pdfjs-scroll-vertical-button-label = Şaquli sürüşdürmə +pdfjs-scroll-horizontal-button = + .title = Üfüqi sürüşdürmə işlət +pdfjs-scroll-horizontal-button-label = Üfüqi sürüşdürmə +pdfjs-scroll-wrapped-button = + .title = Bükülü sürüşdürmə işlət +pdfjs-scroll-wrapped-button-label = Bükülü sürüşdürmə +pdfjs-spread-none-button = + .title = Yan-yana birləşdirilmiş səhifələri işlətmə +pdfjs-spread-none-button-label = Birləşdirmə +pdfjs-spread-odd-button = + .title = Yan-yana birləşdirilmiş səhifələri tək nömrəli səhifələrdən başlat +pdfjs-spread-odd-button-label = Tək nömrəli +pdfjs-spread-even-button = + .title = Yan-yana birləşdirilmiş səhifələri cüt nömrəli səhifələrdən başlat +pdfjs-spread-even-button-label = Cüt nömrəli + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Sənəd xüsusiyyətləri… +pdfjs-document-properties-button-label = Sənəd xüsusiyyətləri… +pdfjs-document-properties-file-name = Fayl adı: +pdfjs-document-properties-file-size = Fayl ölçüsü: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bayt) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bayt) +pdfjs-document-properties-title = Başlık: +pdfjs-document-properties-author = Müəllif: +pdfjs-document-properties-subject = Mövzu: +pdfjs-document-properties-keywords = Açar sözlər: +pdfjs-document-properties-creation-date = Yaradılış Tarixi : +pdfjs-document-properties-modification-date = Dəyişdirilmə Tarixi : +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Yaradan: +pdfjs-document-properties-producer = PDF yaradıcısı: +pdfjs-document-properties-version = PDF versiyası: +pdfjs-document-properties-page-count = Səhifə sayı: +pdfjs-document-properties-page-size = Səhifə Ölçüsü: +pdfjs-document-properties-page-size-unit-inches = inç +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = portret +pdfjs-document-properties-page-size-orientation-landscape = albom +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Məktub +pdfjs-document-properties-page-size-name-legal = Hüquqi + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Fast Web View: +pdfjs-document-properties-linearized-yes = Bəli +pdfjs-document-properties-linearized-no = Xeyr +pdfjs-document-properties-close-button = Qapat + +## Print + +pdfjs-print-progress-message = Sənəd çap üçün hazırlanır… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Ləğv et +pdfjs-printing-not-supported = Xəbərdarlıq: Çap bu səyyah tərəfindən tam olaraq dəstəklənmir. +pdfjs-printing-not-ready = Xəbərdarlıq: PDF çap üçün tam yüklənməyib. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Yan Paneli Aç/Bağla +pdfjs-toggle-sidebar-notification-button = + .title = Yan paneli çevir (sənəddə icmal/bağlamalar/laylar mövcuddur) +pdfjs-toggle-sidebar-button-label = Yan Paneli Aç/Bağla +pdfjs-document-outline-button = + .title = Sənədin eskizini göstər (bütün bəndləri açmaq/yığmaq üçün iki dəfə klikləyin) +pdfjs-document-outline-button-label = Sənəd strukturu +pdfjs-attachments-button = + .title = Bağlamaları göstər +pdfjs-attachments-button-label = Bağlamalar +pdfjs-layers-button = + .title = Layları göstər (bütün layları ilkin halına sıfırlamaq üçün iki dəfə klikləyin) +pdfjs-layers-button-label = Laylar +pdfjs-thumbs-button = + .title = Kiçik şəkilləri göstər +pdfjs-thumbs-button-label = Kiçik şəkillər +pdfjs-findbar-button = + .title = Sənəddə Tap +pdfjs-findbar-button-label = Tap +pdfjs-additional-layers = Əlavə laylar + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Səhifə{ $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page } səhifəsinin kiçik vəziyyəti + +## Find panel button title and messages + +pdfjs-find-input = + .title = Tap + .placeholder = Sənəddə tap… +pdfjs-find-previous-button = + .title = Bir öncəki uyğun gələn sözü tapır +pdfjs-find-previous-button-label = Geri +pdfjs-find-next-button = + .title = Bir sonrakı uyğun gələn sözü tapır +pdfjs-find-next-button-label = İrəli +pdfjs-find-highlight-checkbox = İşarələ +pdfjs-find-match-case-checkbox-label = Böyük/kiçik hərfə həssaslıq +pdfjs-find-entire-word-checkbox-label = Tam sözlər +pdfjs-find-reached-top = Sənədin yuxarısına çatdı, aşağıdan davam edir +pdfjs-find-reached-bottom = Sənədin sonuna çatdı, yuxarıdan davam edir +pdfjs-find-not-found = Uyğunlaşma tapılmadı + +## Predefined zoom values + +pdfjs-page-scale-width = Səhifə genişliyi +pdfjs-page-scale-fit = Səhifəni sığdır +pdfjs-page-scale-auto = Avtomatik yaxınlaşdır +pdfjs-page-scale-actual = Hazırkı Həcm +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = PDF yüklenərkən bir səhv yarandı. +pdfjs-invalid-file-error = Səhv və ya zədələnmiş olmuş PDF fayl. +pdfjs-missing-file-error = PDF fayl yoxdur. +pdfjs-unexpected-response-error = Gözlənilməz server cavabı. +pdfjs-rendering-error = Səhifə göstərilərkən səhv yarandı. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotasiyası] + +## Password + +pdfjs-password-label = Bu PDF faylı açmaq üçün parolu daxil edin. +pdfjs-password-invalid = Parol səhvdir. Bir daha yoxlayın. +pdfjs-password-ok-button = Tamam +pdfjs-password-cancel-button = Ləğv et +pdfjs-web-fonts-disabled = Web Şriftlər söndürülüb: yerləşdirilmiş PDF şriftlərini istifadə etmək mümkün deyil. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/be/viewer.ftl b/public/pdfjs/web/locale/be/viewer.ftl new file mode 100644 index 0000000..3f029d9 --- /dev/null +++ b/public/pdfjs/web/locale/be/viewer.ftl @@ -0,0 +1,518 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Папярэдняя старонка +pdfjs-previous-button-label = Папярэдняя +pdfjs-next-button = + .title = Наступная старонка +pdfjs-next-button-label = Наступная +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Старонка +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = з { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } з { $pagesCount }) +pdfjs-zoom-out-button = + .title = Паменшыць +pdfjs-zoom-out-button-label = Паменшыць +pdfjs-zoom-in-button = + .title = Павялічыць +pdfjs-zoom-in-button-label = Павялічыць +pdfjs-zoom-select = + .title = Павялічэнне тэксту +pdfjs-presentation-mode-button = + .title = Пераключыцца ў рэжым паказу +pdfjs-presentation-mode-button-label = Рэжым паказу +pdfjs-open-file-button = + .title = Адкрыць файл +pdfjs-open-file-button-label = Адкрыць +pdfjs-print-button = + .title = Друкаваць +pdfjs-print-button-label = Друкаваць +pdfjs-save-button = + .title = Захаваць +pdfjs-save-button-label = Захаваць +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Сцягнуць +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Сцягнуць +pdfjs-bookmark-button = + .title = Дзейная старонка (паглядзець URL-адрас з дзейнай старонкі) +pdfjs-bookmark-button-label = Цяперашняя старонка + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Прылады +pdfjs-tools-button-label = Прылады +pdfjs-first-page-button = + .title = Перайсці на першую старонку +pdfjs-first-page-button-label = Перайсці на першую старонку +pdfjs-last-page-button = + .title = Перайсці на апошнюю старонку +pdfjs-last-page-button-label = Перайсці на апошнюю старонку +pdfjs-page-rotate-cw-button = + .title = Павярнуць па сонцу +pdfjs-page-rotate-cw-button-label = Павярнуць па сонцу +pdfjs-page-rotate-ccw-button = + .title = Павярнуць супраць сонца +pdfjs-page-rotate-ccw-button-label = Павярнуць супраць сонца +pdfjs-cursor-text-select-tool-button = + .title = Уключыць прыладу выбару тэксту +pdfjs-cursor-text-select-tool-button-label = Прылада выбару тэксту +pdfjs-cursor-hand-tool-button = + .title = Уключыць ручную прыладу +pdfjs-cursor-hand-tool-button-label = Ручная прылада +pdfjs-scroll-page-button = + .title = Выкарыстоўваць пракрутку старонкi +pdfjs-scroll-page-button-label = Пракрутка старонкi +pdfjs-scroll-vertical-button = + .title = Ужываць вертыкальную пракрутку +pdfjs-scroll-vertical-button-label = Вертыкальная пракрутка +pdfjs-scroll-horizontal-button = + .title = Ужываць гарызантальную пракрутку +pdfjs-scroll-horizontal-button-label = Гарызантальная пракрутка +pdfjs-scroll-wrapped-button = + .title = Ужываць маштабавальную пракрутку +pdfjs-scroll-wrapped-button-label = Маштабавальная пракрутка +pdfjs-spread-none-button = + .title = Не выкарыстоўваць разгорнутыя старонкі +pdfjs-spread-none-button-label = Без разгорнутых старонак +pdfjs-spread-odd-button = + .title = Разгорнутыя старонкі пачынаючы з няцотных нумароў +pdfjs-spread-odd-button-label = Няцотныя старонкі злева +pdfjs-spread-even-button = + .title = Разгорнутыя старонкі пачынаючы з цотных нумароў +pdfjs-spread-even-button-label = Цотныя старонкі злева + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Уласцівасці дакумента… +pdfjs-document-properties-button-label = Уласцівасці дакумента… +pdfjs-document-properties-file-name = Назва файла: +pdfjs-document-properties-file-size = Памер файла: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } КБ ({ $b } байтаў) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } МБ ({ $b } байтаў) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байт) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } байт) +pdfjs-document-properties-title = Загаловак: +pdfjs-document-properties-author = Аўтар: +pdfjs-document-properties-subject = Тэма: +pdfjs-document-properties-keywords = Ключавыя словы: +pdfjs-document-properties-creation-date = Дата стварэння: +pdfjs-document-properties-modification-date = Дата змянення: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Стваральнік: +pdfjs-document-properties-producer = Вырабнік PDF: +pdfjs-document-properties-version = Версія PDF: +pdfjs-document-properties-page-count = Колькасць старонак: +pdfjs-document-properties-page-size = Памер старонкі: +pdfjs-document-properties-page-size-unit-inches = цаляў +pdfjs-document-properties-page-size-unit-millimeters = мм +pdfjs-document-properties-page-size-orientation-portrait = кніжная +pdfjs-document-properties-page-size-orientation-landscape = альбомная +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Хуткі прагляд у Інтэрнэце: +pdfjs-document-properties-linearized-yes = Так +pdfjs-document-properties-linearized-no = Не +pdfjs-document-properties-close-button = Закрыць + +## Print + +pdfjs-print-progress-message = Падрыхтоўка дакумента да друку… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Скасаваць +pdfjs-printing-not-supported = Папярэджанне: друк не падтрымліваецца цалкам гэтым браўзерам. +pdfjs-printing-not-ready = Увага: PDF не сцягнуты цалкам для друкавання. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Паказаць/схаваць бакавую панэль +pdfjs-toggle-sidebar-notification-button = + .title = Паказаць/схаваць бакавую панэль (дакумент мае змест/укладанні/пласты) +pdfjs-toggle-sidebar-button-label = Паказаць/схаваць бакавую панэль +pdfjs-document-outline-button = + .title = Паказаць структуру дакумента (двайная пстрычка, каб разгарнуць /згарнуць усе элементы) +pdfjs-document-outline-button-label = Структура дакумента +pdfjs-attachments-button = + .title = Паказаць далучэнні +pdfjs-attachments-button-label = Далучэнні +pdfjs-layers-button = + .title = Паказаць пласты (націсніце двойчы, каб скінуць усе пласты да прадвызначанага стану) +pdfjs-layers-button-label = Пласты +pdfjs-thumbs-button = + .title = Паказ мініяцюр +pdfjs-thumbs-button-label = Мініяцюры +pdfjs-current-outline-item-button = + .title = Знайсці бягучы элемент структуры +pdfjs-current-outline-item-button-label = Бягучы элемент структуры +pdfjs-findbar-button = + .title = Пошук у дакуменце +pdfjs-findbar-button-label = Знайсці +pdfjs-additional-layers = Дадатковыя пласты + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Старонка { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Мініяцюра старонкі { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Шукаць + .placeholder = Шукаць у дакуменце… +pdfjs-find-previous-button = + .title = Знайсці папярэдні выпадак выразу +pdfjs-find-previous-button-label = Папярэдні +pdfjs-find-next-button = + .title = Знайсці наступны выпадак выразу +pdfjs-find-next-button-label = Наступны +pdfjs-find-highlight-checkbox = Падфарбаваць усе +pdfjs-find-match-case-checkbox-label = Адрозніваць вялікія/малыя літары +pdfjs-find-match-diacritics-checkbox-label = З улікам дыякрытык +pdfjs-find-entire-word-checkbox-label = Словы цалкам +pdfjs-find-reached-top = Дасягнуты пачатак дакумента, працяг з канца +pdfjs-find-reached-bottom = Дасягнуты канец дакумента, працяг з пачатку +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } з { $total } супадзенняў + [few] { $current } з { $total } супадзенняў + *[many] { $current } з { $total } супадзенняў + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Больш за { $limit } супадзенне + [few] Больш за { $limit } супадзенні + *[many] Больш за { $limit } супадзенняў + } +pdfjs-find-not-found = Выраз не знойдзены + +## Predefined zoom values + +pdfjs-page-scale-width = Шырыня старонкі +pdfjs-page-scale-fit = Уцісненне старонкі +pdfjs-page-scale-auto = Аўтаматычнае павелічэнне +pdfjs-page-scale-actual = Сапраўдны памер +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Старонка { $page } + +## Loading indicator messages + +pdfjs-loading-error = Здарылася памылка ў часе загрузкі PDF. +pdfjs-invalid-file-error = Няспраўны або пашкоджаны файл PDF. +pdfjs-missing-file-error = Адсутны файл PDF. +pdfjs-unexpected-response-error = Нечаканы адказ сервера. +pdfjs-rendering-error = Здарылася памылка падчас адлюстравання старонкі. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Увядзіце пароль, каб адкрыць гэты файл PDF. +pdfjs-password-invalid = Нядзейсны пароль. Паспрабуйце зноў. +pdfjs-password-ok-button = Добра +pdfjs-password-cancel-button = Скасаваць +pdfjs-web-fonts-disabled = Шрыфты Сеціва забаронены: немагчыма ўжываць укладзеныя шрыфты PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Тэкст +pdfjs-editor-free-text-button-label = Тэкст +pdfjs-editor-ink-button = + .title = Маляваць +pdfjs-editor-ink-button-label = Маляваць +pdfjs-editor-stamp-button = + .title = Дадаць або змяніць выявы +pdfjs-editor-stamp-button-label = Дадаць або змяніць выявы +pdfjs-editor-highlight-button = + .title = Вылучэнне +pdfjs-editor-highlight-button-label = Вылучэнне +pdfjs-highlight-floating-button1 = + .title = Падфарбаваць + .aria-label = Падфарбаваць +pdfjs-highlight-floating-button-label = Падфарбаваць + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Выдаліць малюнак +pdfjs-editor-remove-freetext-button = + .title = Выдаліць тэкст +pdfjs-editor-remove-stamp-button = + .title = Выдаліць выяву +pdfjs-editor-remove-highlight-button = + .title = Выдаліць падфарбоўку + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Колер +pdfjs-editor-free-text-size-input = Памер +pdfjs-editor-ink-color-input = Колер +pdfjs-editor-ink-thickness-input = Таўшчыня +pdfjs-editor-ink-opacity-input = Непразрыстасць +pdfjs-editor-stamp-add-image-button = + .title = Дадаць выяву +pdfjs-editor-stamp-add-image-button-label = Дадаць выяву +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Таўшчыня +pdfjs-editor-free-highlight-thickness-title = + .title = Змяняць таўшчыню пры вылучэнні іншых элементаў, акрамя тэксту +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Тэкставы рэдактар + .default-content = Пачніце ўводзіць… +pdfjs-free-text = + .aria-label = Тэкставы рэдактар +pdfjs-free-text-default-content = Пачніце набор тэксту… +pdfjs-ink = + .aria-label = Графічны рэдактар +pdfjs-ink-canvas = + .aria-label = Выява, створаная карыстальнікам + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Альтэрнатыўны тэкст +pdfjs-editor-alt-text-edit-button = + .aria-label = Змяніць альтэрнатыўны тэкст +pdfjs-editor-alt-text-edit-button-label = Змяніць альтэрнатыўны тэкст +pdfjs-editor-alt-text-dialog-label = Выберыце варыянт +pdfjs-editor-alt-text-dialog-description = Альтэрнатыўны тэкст дапамагае, калі людзі не бачаць выяву або калі яна не загружаецца. +pdfjs-editor-alt-text-add-description-label = Дадаць апісанне +pdfjs-editor-alt-text-add-description-description = Старайцеся скласці 1-2 сказы, якія апісваюць прадмет, абстаноўку або дзеянні. +pdfjs-editor-alt-text-mark-decorative-label = Пазначыць як дэкаратыўны +pdfjs-editor-alt-text-mark-decorative-description = Выкарыстоўваецца для дэкаратыўных выяваў, такіх як рамкі або вадзяныя знакі. +pdfjs-editor-alt-text-cancel-button = Скасаваць +pdfjs-editor-alt-text-save-button = Захаваць +pdfjs-editor-alt-text-decorative-tooltip = Пазначаны як дэкаратыўны +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Напрыклад, «Малады чалавек садзіцца за стол есці» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Альтэрнатыўны тэкст + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Верхні левы кут — змяніць памер +pdfjs-editor-resizer-label-top-middle = Уверсе пасярэдзіне — змяніць памер +pdfjs-editor-resizer-label-top-right = Верхні правы кут — змяніць памер +pdfjs-editor-resizer-label-middle-right = Пасярэдзіне справа — змяніць памер +pdfjs-editor-resizer-label-bottom-right = Правы ніжні кут — змяніць памер +pdfjs-editor-resizer-label-bottom-middle = Пасярэдзіне ўнізе — змяніць памер +pdfjs-editor-resizer-label-bottom-left = Левы ніжні кут — змяніць памер +pdfjs-editor-resizer-label-middle-left = Пасярэдзіне злева — змяніць памер +pdfjs-editor-resizer-top-left = + .aria-label = Верхні левы кут — змяніць памер +pdfjs-editor-resizer-top-middle = + .aria-label = Уверсе пасярэдзіне — змяніць памер +pdfjs-editor-resizer-top-right = + .aria-label = Верхні правы кут — змяніць памер +pdfjs-editor-resizer-middle-right = + .aria-label = Пасярэдзіне справа — змяніць памер +pdfjs-editor-resizer-bottom-right = + .aria-label = Правы ніжні кут — змяніць памер +pdfjs-editor-resizer-bottom-middle = + .aria-label = Пасярэдзіне ўнізе — змяніць памер +pdfjs-editor-resizer-bottom-left = + .aria-label = Левы ніжні кут — змяніць памер +pdfjs-editor-resizer-middle-left = + .aria-label = Пасярэдзіне злева — змяніць памер + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Колер падфарбоўкі +pdfjs-editor-colorpicker-button = + .title = Змяніць колер +pdfjs-editor-colorpicker-dropdown = + .aria-label = Выбар колеру +pdfjs-editor-colorpicker-yellow = + .title = Жоўты +pdfjs-editor-colorpicker-green = + .title = Зялёны +pdfjs-editor-colorpicker-blue = + .title = Блакітны +pdfjs-editor-colorpicker-pink = + .title = Ружовы +pdfjs-editor-colorpicker-red = + .title = Чырвоны + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Паказаць усе +pdfjs-editor-highlight-show-all-button = + .title = Паказаць усе + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Рэдагаваць тэкст для атрыбута alt (апісанне выявы) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Дадаць тэкст для атрыбута alt (апісанне выявы) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Напішыце сваё апісанне тут… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Кароткае апісанне для людзей, якія не бачаць выяву, ці калі выява не загружаецца. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Гэты тэкст для атрыбута alt быў створаны аўтаматычна і можа быць недакладным +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Даведацца больш +pdfjs-editor-new-alt-text-create-automatically-button-label = Ствараць тэкст для атрыбута alt аўтаматычна +pdfjs-editor-new-alt-text-not-now-button = Не зараз +pdfjs-editor-new-alt-text-error-title = Не ўдалося аўтаматычна стварыць тэкст для атрыбута alt +pdfjs-editor-new-alt-text-error-description = Калі ласка, напішыце ўласны тэкст для атрыбута alt або паўтарыце спробу пазней. +pdfjs-editor-new-alt-text-error-close-button = Закрыць +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Сцягванне мадэлі ШІ для тэксту для атрыбута alt ({ $downloadedSize } з { $totalSize } МБ) + .aria-valuetext = Сцягванне мадэлі ШІ для тэксту для атрыбута alt ({ $downloadedSize } з { $totalSize } МБ) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Тэкст для атрыбута alt дададзены +pdfjs-editor-new-alt-text-added-button-label = Тэкст для атрыбута alt дададзены +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Адсутнічае тэкст для атрыбута alt +pdfjs-editor-new-alt-text-missing-button-label = Адсутнічае тэкст для атрыбута alt +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Водгук на тэкст для атрыбута alt +pdfjs-editor-new-alt-text-to-review-button-label = Водгук на тэкст для атрыбута alt +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Створаны аўтаматычна: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Налады альтэрнатыўнага тэксту для выявы +pdfjs-image-alt-text-settings-button-label = Налады альтэрнатыўнага тэксту для выявы +pdfjs-editor-alt-text-settings-dialog-label = Налады альтэрнатыўнага тэксту для выявы +pdfjs-editor-alt-text-settings-automatic-title = Аўтаматычны тэкст для атрыбута alt +pdfjs-editor-alt-text-settings-create-model-button-label = Ствараць тэкст для атрыбута alt аўтаматычна +pdfjs-editor-alt-text-settings-create-model-description = Прапануе апісанні, каб дапамагчы людзям, якія не бачаць выяву, ці калі выява не загружаецца. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Мадэль ШІ для тэксту для атрыбута alt ({ $totalSize } МБ) +pdfjs-editor-alt-text-settings-ai-model-description = Працуе лакальна на вашай прыладзе, таму вашы звесткі застаюцца прыватнымі. Патрабуецца для аўтаматычнага альтэрнатыўнага тэксту. +pdfjs-editor-alt-text-settings-delete-model-button = Выдаліць +pdfjs-editor-alt-text-settings-download-model-button = Сцягнуць +pdfjs-editor-alt-text-settings-downloading-model-button = Сцягванне… +pdfjs-editor-alt-text-settings-editor-title = Рэдактар тэксту для атрыбута alt +pdfjs-editor-alt-text-settings-show-dialog-button-label = Адразу паказваць рэдактар тэксту для атрыбута alt пры даданні выявы +pdfjs-editor-alt-text-settings-show-dialog-description = Дапамагае пераканацца, што ўсе вашы выявы маюць альтэрнатыўны тэкст. +pdfjs-editor-alt-text-settings-close-button = Закрыць + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Падсвятленне выдалена +pdfjs-editor-undo-bar-message-freetext = Тэкст выдалены +pdfjs-editor-undo-bar-message-ink = Малюнак выдалены +pdfjs-editor-undo-bar-message-stamp = Відарыс выдалены +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } анатацыя выдалена + [few] { $count } анатацыі выдалена + *[many] { $count } анатацый выдалена + } +pdfjs-editor-undo-bar-undo-button = + .title = Адмяніць +pdfjs-editor-undo-bar-undo-button-label = Адмяніць +pdfjs-editor-undo-bar-close-button = + .title = Закрыць +pdfjs-editor-undo-bar-close-button-label = Закрыць diff --git a/public/pdfjs/web/locale/bg/viewer.ftl b/public/pdfjs/web/locale/bg/viewer.ftl new file mode 100644 index 0000000..8b1124e --- /dev/null +++ b/public/pdfjs/web/locale/bg/viewer.ftl @@ -0,0 +1,418 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Предишна страница +pdfjs-previous-button-label = Предишна +pdfjs-next-button = + .title = Следваща страница +pdfjs-next-button-label = Следваща +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Страница +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = от { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } от { $pagesCount }) +pdfjs-zoom-out-button = + .title = Намаляване +pdfjs-zoom-out-button-label = Намаляване +pdfjs-zoom-in-button = + .title = Увеличаване +pdfjs-zoom-in-button-label = Увеличаване +pdfjs-zoom-select = + .title = Мащабиране +pdfjs-presentation-mode-button = + .title = Превключване към режим на представяне +pdfjs-presentation-mode-button-label = Режим на представяне +pdfjs-open-file-button = + .title = Отваряне на файл +pdfjs-open-file-button-label = Отваряне +pdfjs-print-button = + .title = Отпечатване +pdfjs-print-button-label = Отпечатване +pdfjs-save-button = + .title = Запазване +pdfjs-save-button-label = Запазване +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Изтегляне +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Изтегляне +pdfjs-bookmark-button = + .title = Текуща страница (преглед на адреса на страницата) +pdfjs-bookmark-button-label = Текуща страница + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Инструменти +pdfjs-tools-button-label = Инструменти +pdfjs-first-page-button = + .title = Към първата страница +pdfjs-first-page-button-label = Към първата страница +pdfjs-last-page-button = + .title = Към последната страница +pdfjs-last-page-button-label = Към последната страница +pdfjs-page-rotate-cw-button = + .title = Завъртане по час. стрелка +pdfjs-page-rotate-cw-button-label = Завъртане по часовниковата стрелка +pdfjs-page-rotate-ccw-button = + .title = Завъртане обратно на час. стрелка +pdfjs-page-rotate-ccw-button-label = Завъртане обратно на часовниковата стрелка +pdfjs-cursor-text-select-tool-button = + .title = Включване на инструмента за избор на текст +pdfjs-cursor-text-select-tool-button-label = Инструмент за избор на текст +pdfjs-cursor-hand-tool-button = + .title = Включване на инструмента ръка +pdfjs-cursor-hand-tool-button-label = Инструмент ръка +pdfjs-scroll-page-button = + .title = Използване на плъзгане на страници +pdfjs-scroll-page-button-label = Плъзгане на страници +pdfjs-scroll-vertical-button = + .title = Използване на вертикално плъзгане +pdfjs-scroll-vertical-button-label = Вертикално плъзгане +pdfjs-scroll-horizontal-button = + .title = Използване на хоризонтално +pdfjs-scroll-horizontal-button-label = Хоризонтално плъзгане +pdfjs-scroll-wrapped-button = + .title = Използване на мащабируемо плъзгане +pdfjs-scroll-wrapped-button-label = Мащабируемо плъзгане +pdfjs-spread-none-button = + .title = Режимът на сдвояване е изключен +pdfjs-spread-none-button-label = Без сдвояване +pdfjs-spread-odd-button = + .title = Сдвояване, започвайки от нечетните страници +pdfjs-spread-odd-button-label = Нечетните отляво +pdfjs-spread-even-button = + .title = Сдвояване, започвайки от четните страници +pdfjs-spread-even-button-label = Четните отляво + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Свойства на документа… +pdfjs-document-properties-button-label = Свойства на документа… +pdfjs-document-properties-file-name = Име на файл: +pdfjs-document-properties-file-size = Големина на файл: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } КБ ({ $b } байта) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } МБ ({ $b } байта) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байта) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } байта) +pdfjs-document-properties-title = Заглавие: +pdfjs-document-properties-author = Автор: +pdfjs-document-properties-subject = Тема: +pdfjs-document-properties-keywords = Ключови думи: +pdfjs-document-properties-creation-date = Дата на създаване: +pdfjs-document-properties-modification-date = Дата на промяна: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Създател: +pdfjs-document-properties-producer = PDF произведен от: +pdfjs-document-properties-version = Издание на PDF: +pdfjs-document-properties-page-count = Брой страници: +pdfjs-document-properties-page-size = Размер на страницата: +pdfjs-document-properties-page-size-unit-inches = инч +pdfjs-document-properties-page-size-unit-millimeters = мм +pdfjs-document-properties-page-size-orientation-portrait = портрет +pdfjs-document-properties-page-size-orientation-landscape = пейзаж +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Правни въпроси + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Бърз преглед: +pdfjs-document-properties-linearized-yes = Да +pdfjs-document-properties-linearized-no = Не +pdfjs-document-properties-close-button = Затваряне + +## Print + +pdfjs-print-progress-message = Подготвяне на документа за отпечатване… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Отказ +pdfjs-printing-not-supported = Внимание: Този четец няма пълна поддръжка на отпечатване. +pdfjs-printing-not-ready = Внимание: Този PDF файл не е напълно зареден за печат. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Превключване на страничната лента +pdfjs-toggle-sidebar-notification-button = + .title = Превключване на страничната лента (документът има структура/прикачени файлове/слоеве) +pdfjs-toggle-sidebar-button-label = Превключване на страничната лента +pdfjs-document-outline-button = + .title = Показване на структурата на документа (двукратно щракване за свиване/разгъване на всичко) +pdfjs-document-outline-button-label = Структура на документа +pdfjs-attachments-button = + .title = Показване на притурките +pdfjs-attachments-button-label = Притурки +pdfjs-layers-button = + .title = Показване на слоевете (двукратно щракване за възстановяване на всички слоеве към състоянието по подразбиране) +pdfjs-layers-button-label = Слоеве +pdfjs-thumbs-button = + .title = Показване на миниатюрите +pdfjs-thumbs-button-label = Миниатюри +pdfjs-current-outline-item-button = + .title = Намиране на текущия елемент от структурата +pdfjs-current-outline-item-button-label = Текущ елемент от структурата +pdfjs-findbar-button = + .title = Намиране в документа +pdfjs-findbar-button-label = Търсене +pdfjs-additional-layers = Допълнителни слоеве + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Страница { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Миниатюра на страница { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Търсене + .placeholder = Търсене в документа… +pdfjs-find-previous-button = + .title = Намиране на предишно съвпадение на фразата +pdfjs-find-previous-button-label = Предишна +pdfjs-find-next-button = + .title = Намиране на следващо съвпадение на фразата +pdfjs-find-next-button-label = Следваща +pdfjs-find-highlight-checkbox = Открояване на всички +pdfjs-find-match-case-checkbox-label = Съвпадение на регистъра +pdfjs-find-match-diacritics-checkbox-label = Без производни букви +pdfjs-find-entire-word-checkbox-label = Цели думи +pdfjs-find-reached-top = Достигнато е началото на документа, продължаване от края +pdfjs-find-reached-bottom = Достигнат е краят на документа, продължаване от началото +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } от { $total } съвпадение + *[other] { $current } от { $total } съвпадения + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Повече от { $limit } съвпадение + *[other] Повече от { $limit } съвпадения + } +pdfjs-find-not-found = Фразата не е намерена + +## Predefined zoom values + +pdfjs-page-scale-width = Ширина на страницата +pdfjs-page-scale-fit = Вместване в страницата +pdfjs-page-scale-auto = Автоматично мащабиране +pdfjs-page-scale-actual = Действителен размер +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Страница { $page } + +## Loading indicator messages + +pdfjs-loading-error = Получи се грешка при зареждане на PDF-а. +pdfjs-invalid-file-error = Невалиден или повреден PDF файл. +pdfjs-missing-file-error = Липсващ PDF файл. +pdfjs-unexpected-response-error = Неочакван отговор от сървъра. +pdfjs-rendering-error = Грешка при изчертаване на страницата. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Анотация { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Въведете парола за отваряне на този PDF файл. +pdfjs-password-invalid = Невалидна парола. Моля, опитайте отново. +pdfjs-password-ok-button = Добре +pdfjs-password-cancel-button = Отказ +pdfjs-web-fonts-disabled = Уеб-шрифтовете са забранени: разрешаване на използването на вградените PDF шрифтове. + +## Editing + +pdfjs-editor-free-text-button = + .title = Текст +pdfjs-editor-free-text-button-label = Текст +pdfjs-editor-ink-button = + .title = Рисуване +pdfjs-editor-ink-button-label = Рисуване +pdfjs-editor-stamp-button = + .title = Добавяне или променяне на изображения +pdfjs-editor-stamp-button-label = Добавяне или променяне на изображения + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Премахване на рисунката +pdfjs-editor-remove-freetext-button = + .title = Премахване на текста +pdfjs-editor-remove-stamp-button = + .title = Пермахване на изображението +pdfjs-editor-remove-highlight-button = + .title = Премахване на открояването + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Цвят +pdfjs-editor-free-text-size-input = Размер +pdfjs-editor-ink-color-input = Цвят +pdfjs-editor-ink-thickness-input = Дебелина +pdfjs-editor-ink-opacity-input = Прозрачност +pdfjs-editor-stamp-add-image-button = + .title = Добавяне на изображение +pdfjs-editor-stamp-add-image-button-label = Добавяне на изображение +pdfjs-free-text = + .aria-label = Текстов редактор +pdfjs-free-text-default-content = Започнете да пишете… +pdfjs-ink = + .aria-label = Промяна на рисунка +pdfjs-ink-canvas = + .aria-label = Изображение, създадено от потребител + +## Alt-text dialog + +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button-label = Алтернативен текст +pdfjs-editor-alt-text-edit-button-label = Промяна на алтернативния текст +pdfjs-editor-alt-text-dialog-label = Изберете от възможностите +pdfjs-editor-alt-text-dialog-description = Алтернативният текст помага на потребителите, когато не могат да видят изображението или то не се зарежда. +pdfjs-editor-alt-text-add-description-label = Добавяне на описание +pdfjs-editor-alt-text-add-description-description = Стремете се към 1-2 изречения, описващи предмета, настройката или действията. +pdfjs-editor-alt-text-mark-decorative-label = Отбелязване като декоративно +pdfjs-editor-alt-text-mark-decorative-description = Използва се за орнаменти или декоративни изображения, като контури и водни знаци. +pdfjs-editor-alt-text-cancel-button = Отказ +pdfjs-editor-alt-text-save-button = Запазване +pdfjs-editor-alt-text-decorative-tooltip = Отбелязване като декоративно +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Например, „Млад мъж седи на маса и се храни“ + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Горен ляв ъгъл — преоразмеряване +pdfjs-editor-resizer-label-top-middle = Горе в средата — преоразмеряване +pdfjs-editor-resizer-label-top-right = Горен десен ъгъл — преоразмеряване +pdfjs-editor-resizer-label-middle-right = Дясно в средата — преоразмеряване +pdfjs-editor-resizer-label-bottom-right = Долен десен ъгъл — преоразмеряване +pdfjs-editor-resizer-label-bottom-middle = Долу в средата — преоразмеряване +pdfjs-editor-resizer-label-bottom-left = Долен ляв ъгъл — преоразмеряване +pdfjs-editor-resizer-label-middle-left = Ляво в средата — преоразмеряване +pdfjs-editor-resizer-top-left = + .aria-label = Горен ляв ъгъл — преоразмеряване +pdfjs-editor-resizer-top-middle = + .aria-label = Горе в средата — преоразмеряване +pdfjs-editor-resizer-top-right = + .aria-label = Горен десен ъгъл — преоразмеряване +pdfjs-editor-resizer-middle-right = + .aria-label = Дясно в средата — преоразмеряване +pdfjs-editor-resizer-bottom-right = + .aria-label = Долен десен ъгъл — преоразмеряване +pdfjs-editor-resizer-bottom-middle = + .aria-label = Долу в средата — преоразмеряване +pdfjs-editor-resizer-bottom-left = + .aria-label = Долен ляв ъгъл — преоразмеряване +pdfjs-editor-resizer-middle-left = + .aria-label = Ляво в средата — преоразмеряване + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Цвят на открояване +pdfjs-editor-colorpicker-button = + .title = Промяна на цвят +pdfjs-editor-colorpicker-dropdown = + .aria-label = Избор на цвят +pdfjs-editor-colorpicker-yellow = + .title = Жълто +pdfjs-editor-colorpicker-green = + .title = Зелено +pdfjs-editor-colorpicker-blue = + .title = Синьо +pdfjs-editor-colorpicker-pink = + .title = Розово +pdfjs-editor-colorpicker-red = + .title = Червено + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +pdfjs-editor-new-alt-text-not-now-button = Не сега + +## Image alt-text settings + diff --git a/public/pdfjs/web/locale/bn/viewer.ftl b/public/pdfjs/web/locale/bn/viewer.ftl new file mode 100644 index 0000000..1e20ecb --- /dev/null +++ b/public/pdfjs/web/locale/bn/viewer.ftl @@ -0,0 +1,247 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = পূর্ববর্তী পাতা +pdfjs-previous-button-label = পূর্ববর্তী +pdfjs-next-button = + .title = পরবর্তী পাতা +pdfjs-next-button-label = পরবর্তী +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = পাতা +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount } এর +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pagesCount } এর { $pageNumber }) +pdfjs-zoom-out-button = + .title = ছোট আকারে প্রদর্শন +pdfjs-zoom-out-button-label = ছোট আকারে প্রদর্শন +pdfjs-zoom-in-button = + .title = বড় আকারে প্রদর্শন +pdfjs-zoom-in-button-label = বড় আকারে প্রদর্শন +pdfjs-zoom-select = + .title = বড় আকারে প্রদর্শন +pdfjs-presentation-mode-button = + .title = উপস্থাপনা মোডে স্যুইচ করুন +pdfjs-presentation-mode-button-label = উপস্থাপনা মোড +pdfjs-open-file-button = + .title = ফাইল খুলুন +pdfjs-open-file-button-label = খুলুন +pdfjs-print-button = + .title = মুদ্রণ +pdfjs-print-button-label = মুদ্রণ + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = টুল +pdfjs-tools-button-label = টুল +pdfjs-first-page-button = + .title = প্রথম পাতায় যাও +pdfjs-first-page-button-label = প্রথম পাতায় যাও +pdfjs-last-page-button = + .title = শেষ পাতায় যাও +pdfjs-last-page-button-label = শেষ পাতায় যাও +pdfjs-page-rotate-cw-button = + .title = ঘড়ির কাঁটার দিকে ঘোরাও +pdfjs-page-rotate-cw-button-label = ঘড়ির কাঁটার দিকে ঘোরাও +pdfjs-page-rotate-ccw-button = + .title = ঘড়ির কাঁটার বিপরীতে ঘোরাও +pdfjs-page-rotate-ccw-button-label = ঘড়ির কাঁটার বিপরীতে ঘোরাও +pdfjs-cursor-text-select-tool-button = + .title = লেখা নির্বাচক টুল সক্রিয় করুন +pdfjs-cursor-text-select-tool-button-label = লেখা নির্বাচক টুল +pdfjs-cursor-hand-tool-button = + .title = হ্যান্ড টুল সক্রিয় করুন +pdfjs-cursor-hand-tool-button-label = হ্যান্ড টুল +pdfjs-scroll-vertical-button = + .title = উলম্ব স্ক্রলিং ব্যবহার করুন +pdfjs-scroll-vertical-button-label = উলম্ব স্ক্রলিং +pdfjs-scroll-horizontal-button = + .title = অনুভূমিক স্ক্রলিং ব্যবহার করুন +pdfjs-scroll-horizontal-button-label = অনুভূমিক স্ক্রলিং +pdfjs-scroll-wrapped-button = + .title = Wrapped স্ক্রোলিং ব্যবহার করুন +pdfjs-scroll-wrapped-button-label = Wrapped স্ক্রোলিং +pdfjs-spread-none-button = + .title = পেজ স্প্রেডগুলোতে যোগদান করবেন না +pdfjs-spread-none-button-label = Spreads নেই +pdfjs-spread-odd-button-label = বিজোড় Spreads +pdfjs-spread-even-button-label = জোড় Spreads + +## Document properties dialog + +pdfjs-document-properties-button = + .title = নথি বৈশিষ্ট্য… +pdfjs-document-properties-button-label = নথি বৈশিষ্ট্য… +pdfjs-document-properties-file-name = ফাইলের নাম: +pdfjs-document-properties-file-size = ফাইলের আকার: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } কেবি ({ $size_b } বাইট) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } এমবি ({ $size_b } বাইট) +pdfjs-document-properties-title = শিরোনাম: +pdfjs-document-properties-author = লেখক: +pdfjs-document-properties-subject = বিষয়: +pdfjs-document-properties-keywords = কীওয়ার্ড: +pdfjs-document-properties-creation-date = তৈরির তারিখ: +pdfjs-document-properties-modification-date = পরিবর্তনের তারিখ: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = প্রস্তুতকারক: +pdfjs-document-properties-producer = পিডিএফ প্রস্তুতকারক: +pdfjs-document-properties-version = পিডিএফ সংষ্করণ: +pdfjs-document-properties-page-count = মোট পাতা: +pdfjs-document-properties-page-size = পাতার সাইজ: +pdfjs-document-properties-page-size-unit-inches = এর মধ্যে +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = উলম্ব +pdfjs-document-properties-page-size-orientation-landscape = অনুভূমিক +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = লেটার +pdfjs-document-properties-page-size-name-legal = লীগাল + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Fast Web View: +pdfjs-document-properties-linearized-yes = হ্যাঁ +pdfjs-document-properties-linearized-no = না +pdfjs-document-properties-close-button = বন্ধ + +## Print + +pdfjs-print-progress-message = মুদ্রণের জন্য নথি প্রস্তুত করা হচ্ছে… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = বাতিল +pdfjs-printing-not-supported = সতর্কতা: এই ব্রাউজারে মুদ্রণ সম্পূর্ণভাবে সমর্থিত নয়। +pdfjs-printing-not-ready = সতর্কীকরণ: পিডিএফটি মুদ্রণের জন্য সম্পূর্ণ লোড হয়নি। + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = সাইডবার টগল করুন +pdfjs-toggle-sidebar-button-label = সাইডবার টগল করুন +pdfjs-document-outline-button = + .title = নথির আউটলাইন দেখাও (সব আইটেম প্রসারিত/সঙ্কুচিত করতে ডবল ক্লিক করুন) +pdfjs-document-outline-button-label = নথির রূপরেখা +pdfjs-attachments-button = + .title = সংযুক্তি দেখাও +pdfjs-attachments-button-label = সংযুক্তি +pdfjs-thumbs-button = + .title = থাম্বনেইল সমূহ প্রদর্শন করুন +pdfjs-thumbs-button-label = থাম্বনেইল সমূহ +pdfjs-findbar-button = + .title = নথির মধ্যে খুঁজুন +pdfjs-findbar-button-label = খুঁজুন + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = পাতা { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page } পাতার থাম্বনেইল + +## Find panel button title and messages + +pdfjs-find-input = + .title = খুঁজুন + .placeholder = নথির মধ্যে খুঁজুন… +pdfjs-find-previous-button = + .title = বাক্যাংশের পূর্ববর্তী উপস্থিতি অনুসন্ধান +pdfjs-find-previous-button-label = পূর্ববর্তী +pdfjs-find-next-button = + .title = বাক্যাংশের পরবর্তী উপস্থিতি অনুসন্ধান +pdfjs-find-next-button-label = পরবর্তী +pdfjs-find-highlight-checkbox = সব হাইলাইট করুন +pdfjs-find-match-case-checkbox-label = অক্ষরের ছাঁদ মেলানো +pdfjs-find-entire-word-checkbox-label = সম্পূর্ণ শব্দ +pdfjs-find-reached-top = পাতার শুরুতে পৌছে গেছে, নীচ থেকে আরম্ভ করা হয়েছে +pdfjs-find-reached-bottom = পাতার শেষে পৌছে গেছে, উপর থেকে আরম্ভ করা হয়েছে +pdfjs-find-not-found = বাক্যাংশ পাওয়া যায়নি + +## Predefined zoom values + +pdfjs-page-scale-width = পাতার প্রস্থ +pdfjs-page-scale-fit = পাতা ফিট করুন +pdfjs-page-scale-auto = স্বয়ংক্রিয় জুম +pdfjs-page-scale-actual = প্রকৃত আকার +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = পিডিএফ লোড করার সময় ত্রুটি দেখা দিয়েছে। +pdfjs-invalid-file-error = অকার্যকর অথবা ক্ষতিগ্রস্ত পিডিএফ ফাইল। +pdfjs-missing-file-error = নিখোঁজ PDF ফাইল। +pdfjs-unexpected-response-error = অপ্রত্যাশীত সার্ভার প্রতিক্রিয়া। +pdfjs-rendering-error = পাতা উপস্থাপনার সময় ত্রুটি দেখা দিয়েছে। + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } টীকা] + +## Password + +pdfjs-password-label = পিডিএফ ফাইলটি ওপেন করতে পাসওয়ার্ড দিন। +pdfjs-password-invalid = ভুল পাসওয়ার্ড। অনুগ্রহ করে আবার চেষ্টা করুন। +pdfjs-password-ok-button = ঠিক আছে +pdfjs-password-cancel-button = বাতিল +pdfjs-web-fonts-disabled = ওয়েব ফন্ট নিষ্ক্রিয়: সংযুক্ত পিডিএফ ফন্ট ব্যবহার করা যাচ্ছে না। + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/bo/viewer.ftl b/public/pdfjs/web/locale/bo/viewer.ftl new file mode 100644 index 0000000..824eab4 --- /dev/null +++ b/public/pdfjs/web/locale/bo/viewer.ftl @@ -0,0 +1,247 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = དྲ་ངོས་སྔོན་མ +pdfjs-previous-button-label = སྔོན་མ +pdfjs-next-button = + .title = དྲ་ངོས་རྗེས་མ +pdfjs-next-button-label = རྗེས་མ +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = ཤོག་ངོས +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = of { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zoom Out +pdfjs-zoom-out-button-label = Zoom Out +pdfjs-zoom-in-button = + .title = Zoom In +pdfjs-zoom-in-button-label = Zoom In +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Switch to Presentation Mode +pdfjs-presentation-mode-button-label = Presentation Mode +pdfjs-open-file-button = + .title = Open File +pdfjs-open-file-button-label = Open +pdfjs-print-button = + .title = Print +pdfjs-print-button-label = Print + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Tools +pdfjs-tools-button-label = Tools +pdfjs-first-page-button = + .title = Go to First Page +pdfjs-first-page-button-label = Go to First Page +pdfjs-last-page-button = + .title = Go to Last Page +pdfjs-last-page-button-label = Go to Last Page +pdfjs-page-rotate-cw-button = + .title = Rotate Clockwise +pdfjs-page-rotate-cw-button-label = Rotate Clockwise +pdfjs-page-rotate-ccw-button = + .title = Rotate Counterclockwise +pdfjs-page-rotate-ccw-button-label = Rotate Counterclockwise +pdfjs-cursor-text-select-tool-button = + .title = Enable Text Selection Tool +pdfjs-cursor-text-select-tool-button-label = Text Selection Tool +pdfjs-cursor-hand-tool-button = + .title = Enable Hand Tool +pdfjs-cursor-hand-tool-button-label = Hand Tool +pdfjs-scroll-vertical-button = + .title = Use Vertical Scrolling +pdfjs-scroll-vertical-button-label = Vertical Scrolling +pdfjs-scroll-horizontal-button = + .title = Use Horizontal Scrolling +pdfjs-scroll-horizontal-button-label = Horizontal Scrolling +pdfjs-scroll-wrapped-button = + .title = Use Wrapped Scrolling +pdfjs-scroll-wrapped-button-label = Wrapped Scrolling +pdfjs-spread-none-button = + .title = Do not join page spreads +pdfjs-spread-none-button-label = No Spreads +pdfjs-spread-odd-button = + .title = Join page spreads starting with odd-numbered pages +pdfjs-spread-odd-button-label = Odd Spreads +pdfjs-spread-even-button = + .title = Join page spreads starting with even-numbered pages +pdfjs-spread-even-button-label = Even Spreads + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Document Properties… +pdfjs-document-properties-button-label = Document Properties… +pdfjs-document-properties-file-name = File name: +pdfjs-document-properties-file-size = File size: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Title: +pdfjs-document-properties-author = Author: +pdfjs-document-properties-subject = Subject: +pdfjs-document-properties-keywords = Keywords: +pdfjs-document-properties-creation-date = Creation Date: +pdfjs-document-properties-modification-date = Modification Date: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Creator: +pdfjs-document-properties-producer = PDF Producer: +pdfjs-document-properties-version = PDF Version: +pdfjs-document-properties-page-count = Page Count: +pdfjs-document-properties-page-size = Page Size: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = portrait +pdfjs-document-properties-page-size-orientation-landscape = landscape +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Fast Web View: +pdfjs-document-properties-linearized-yes = Yes +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Close + +## Print + +pdfjs-print-progress-message = Preparing document for printing… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cancel +pdfjs-printing-not-supported = Warning: Printing is not fully supported by this browser. +pdfjs-printing-not-ready = Warning: The PDF is not fully loaded for printing. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Toggle Sidebar +pdfjs-toggle-sidebar-button-label = Toggle Sidebar +pdfjs-document-outline-button = + .title = Show Document Outline (double-click to expand/collapse all items) +pdfjs-document-outline-button-label = Document Outline +pdfjs-attachments-button = + .title = Show Attachments +pdfjs-attachments-button-label = Attachments +pdfjs-thumbs-button = + .title = Show Thumbnails +pdfjs-thumbs-button-label = Thumbnails +pdfjs-findbar-button = + .title = Find in Document +pdfjs-findbar-button-label = Find + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Page { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Thumbnail of Page { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Find + .placeholder = Find in document… +pdfjs-find-previous-button = + .title = Find the previous occurrence of the phrase +pdfjs-find-previous-button-label = Previous +pdfjs-find-next-button = + .title = Find the next occurrence of the phrase +pdfjs-find-next-button-label = Next +pdfjs-find-highlight-checkbox = Highlight all +pdfjs-find-match-case-checkbox-label = Match case +pdfjs-find-entire-word-checkbox-label = Whole words +pdfjs-find-reached-top = Reached top of document, continued from bottom +pdfjs-find-reached-bottom = Reached end of document, continued from top +pdfjs-find-not-found = Phrase not found + +## Predefined zoom values + +pdfjs-page-scale-width = Page Width +pdfjs-page-scale-fit = Page Fit +pdfjs-page-scale-auto = Automatic Zoom +pdfjs-page-scale-actual = Actual Size +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = An error occurred while loading the PDF. +pdfjs-invalid-file-error = Invalid or corrupted PDF file. +pdfjs-missing-file-error = Missing PDF file. +pdfjs-unexpected-response-error = Unexpected server response. +pdfjs-rendering-error = An error occurred while rendering the page. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] + +## Password + +pdfjs-password-label = Enter the password to open this PDF file. +pdfjs-password-invalid = Invalid password. Please try again. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Cancel +pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/br/viewer.ftl b/public/pdfjs/web/locale/br/viewer.ftl new file mode 100644 index 0000000..60a3df0 --- /dev/null +++ b/public/pdfjs/web/locale/br/viewer.ftl @@ -0,0 +1,340 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pajenn a-raok +pdfjs-previous-button-label = A-raok +pdfjs-next-button = + .title = Pajenn war-lerc'h +pdfjs-next-button-label = War-lerc'h +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pajenn +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = eus { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } war { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zoum bihanaat +pdfjs-zoom-out-button-label = Zoum bihanaat +pdfjs-zoom-in-button = + .title = Zoum brasaat +pdfjs-zoom-in-button-label = Zoum brasaat +pdfjs-zoom-select = + .title = Zoum +pdfjs-presentation-mode-button = + .title = Trec'haoliñ etrezek ar mod kinnigadenn +pdfjs-presentation-mode-button-label = Mod kinnigadenn +pdfjs-open-file-button = + .title = Digeriñ ur restr +pdfjs-open-file-button-label = Digeriñ ur restr +pdfjs-print-button = + .title = Moullañ +pdfjs-print-button-label = Moullañ +pdfjs-save-button = + .title = Enrollañ +pdfjs-save-button-label = Enrollañ +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Pellgargañ +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Pellgargañ +pdfjs-bookmark-button-label = Pajenn a-vremañ + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Ostilhoù +pdfjs-tools-button-label = Ostilhoù +pdfjs-first-page-button = + .title = Mont d'ar bajenn gentañ +pdfjs-first-page-button-label = Mont d'ar bajenn gentañ +pdfjs-last-page-button = + .title = Mont d'ar bajenn diwezhañ +pdfjs-last-page-button-label = Mont d'ar bajenn diwezhañ +pdfjs-page-rotate-cw-button = + .title = C'hwelañ gant roud ar bizied +pdfjs-page-rotate-cw-button-label = C'hwelañ gant roud ar bizied +pdfjs-page-rotate-ccw-button = + .title = C'hwelañ gant roud gin ar bizied +pdfjs-page-rotate-ccw-button-label = C'hwelañ gant roud gin ar bizied +pdfjs-cursor-text-select-tool-button = + .title = Gweredekaat an ostilh diuzañ testenn +pdfjs-cursor-text-select-tool-button-label = Ostilh diuzañ testenn +pdfjs-cursor-hand-tool-button = + .title = Gweredekaat an ostilh dorn +pdfjs-cursor-hand-tool-button-label = Ostilh dorn +pdfjs-scroll-vertical-button = + .title = Arverañ an dibunañ a-blom +pdfjs-scroll-vertical-button-label = Dibunañ a-serzh +pdfjs-scroll-horizontal-button = + .title = Arverañ an dibunañ a-blaen +pdfjs-scroll-horizontal-button-label = Dibunañ a-blaen +pdfjs-scroll-wrapped-button = + .title = Arverañ an dibunañ paket +pdfjs-scroll-wrapped-button-label = Dibunañ paket +pdfjs-spread-none-button = + .title = Chom hep stagañ ar skignadurioù +pdfjs-spread-none-button-label = Skignadenn ebet +pdfjs-spread-odd-button = + .title = Lakaat ar pajennadoù en ur gregiñ gant ar pajennoù ampar +pdfjs-spread-odd-button-label = Pajennoù ampar +pdfjs-spread-even-button = + .title = Lakaat ar pajennadoù en ur gregiñ gant ar pajennoù par +pdfjs-spread-even-button-label = Pajennoù par + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Perzhioù an teul… +pdfjs-document-properties-button-label = Perzhioù an teul… +pdfjs-document-properties-file-name = Anv restr: +pdfjs-document-properties-file-size = Ment ar restr: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } Ke ({ $size_b } eizhbit) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } Me ({ $size_b } eizhbit) +pdfjs-document-properties-title = Titl: +pdfjs-document-properties-author = Aozer: +pdfjs-document-properties-subject = Danvez: +pdfjs-document-properties-keywords = Gerioù-alc'hwez: +pdfjs-document-properties-creation-date = Deiziad krouiñ: +pdfjs-document-properties-modification-date = Deiziad kemmañ: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Krouer: +pdfjs-document-properties-producer = Kenderc'her PDF: +pdfjs-document-properties-version = Handelv PDF: +pdfjs-document-properties-page-count = Niver a bajennoù: +pdfjs-document-properties-page-size = Ment ar bajenn: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = poltred +pdfjs-document-properties-page-size-orientation-landscape = gweledva +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Lizher +pdfjs-document-properties-page-size-name-legal = Lezennel + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Gwel Web Herrek: +pdfjs-document-properties-linearized-yes = Ya +pdfjs-document-properties-linearized-no = Ket +pdfjs-document-properties-close-button = Serriñ + +## Print + +pdfjs-print-progress-message = O prientiñ an teul evit moullañ... +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Nullañ +pdfjs-printing-not-supported = Kemenn: N'eo ket skoret penn-da-benn ar moullañ gant ar merdeer-mañ. +pdfjs-printing-not-ready = Kemenn: N'hall ket bezañ moullet ar restr PDF rak n'eo ket karget penn-da-benn. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Diskouez/kuzhat ar varrenn gostez +pdfjs-toggle-sidebar-notification-button = + .title = Trec'haoliñ ar varrenn-gostez (ur steuñv pe stagadennoù a zo en teul) +pdfjs-toggle-sidebar-button-label = Diskouez/kuzhat ar varrenn gostez +pdfjs-document-outline-button = + .title = Diskouez steuñv an teul (daouglikit evit brasaat/bihanaat an holl elfennoù) +pdfjs-document-outline-button-label = Sinedoù an teuliad +pdfjs-attachments-button = + .title = Diskouez ar c'henstagadurioù +pdfjs-attachments-button-label = Kenstagadurioù +pdfjs-layers-button = + .title = Diskouez ar gwiskadoù (daou-glikañ evit adderaouekaat an holl gwiskadoù d'o stad dre ziouer) +pdfjs-layers-button-label = Gwiskadoù +pdfjs-thumbs-button = + .title = Diskouez ar melvennoù +pdfjs-thumbs-button-label = Melvennoù +pdfjs-findbar-button = + .title = Klask e-barzh an teuliad +pdfjs-findbar-button-label = Klask +pdfjs-additional-layers = Gwiskadoù ouzhpenn + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pajenn { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Melvenn ar bajenn { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Klask + .placeholder = Klask e-barzh an teuliad +pdfjs-find-previous-button = + .title = Kavout an tamm frazenn kent o klotañ ganti +pdfjs-find-previous-button-label = Kent +pdfjs-find-next-button = + .title = Kavout an tamm frazenn war-lerc'h o klotañ ganti +pdfjs-find-next-button-label = War-lerc'h +pdfjs-find-highlight-checkbox = Usskediñ pep tra +pdfjs-find-match-case-checkbox-label = Teurel evezh ouzh ar pennlizherennoù +pdfjs-find-match-diacritics-checkbox-label = Doujañ d’an tiredoù +pdfjs-find-entire-word-checkbox-label = Gerioù a-bezh +pdfjs-find-reached-top = Tizhet eo bet derou ar bajenn, kenderc'hel diouzh an diaz +pdfjs-find-reached-bottom = Tizhet eo bet dibenn ar bajenn, kenderc'hel diouzh ar c'hrec'h +pdfjs-find-not-found = N'haller ket kavout ar frazenn + +## Predefined zoom values + +pdfjs-page-scale-width = Led ar bajenn +pdfjs-page-scale-fit = Pajenn a-bezh +pdfjs-page-scale-auto = Zoum emgefreek +pdfjs-page-scale-actual = Ment wir +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Pajenn { $page } + +## Loading indicator messages + +pdfjs-loading-error = Degouezhet ez eus bet ur fazi e-pad kargañ ar PDF. +pdfjs-invalid-file-error = Restr PDF didalvoudek pe kontronet. +pdfjs-missing-file-error = Restr PDF o vankout. +pdfjs-unexpected-response-error = Respont dic'hortoz a-berzh an dafariad +pdfjs-rendering-error = Degouezhet ez eus bet ur fazi e-pad skrammañ ar bajennad. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Notennañ] + +## Password + +pdfjs-password-label = Enankit ar ger-tremen evit digeriñ ar restr PDF-mañ. +pdfjs-password-invalid = Ger-tremen didalvoudek. Klaskit en-dro mar plij. +pdfjs-password-ok-button = Mat eo +pdfjs-password-cancel-button = Nullañ +pdfjs-web-fonts-disabled = Diweredekaet eo an nodrezhoù web: n'haller ket arverañ an nodrezhoù PDF enframmet. + +## Editing + +pdfjs-editor-free-text-button = + .title = Testenn +pdfjs-editor-free-text-button-label = Testenn +pdfjs-editor-ink-button = + .title = Tresañ +pdfjs-editor-ink-button-label = Tresañ +pdfjs-editor-stamp-button = + .title = Ouzhpennañ pe aozañ skeudennoù +pdfjs-editor-stamp-button-label = Ouzhpennañ pe aozañ skeudennoù + +## Remove button for the various kind of editor. + + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Liv +pdfjs-editor-free-text-size-input = Ment +pdfjs-editor-ink-color-input = Liv +pdfjs-editor-ink-thickness-input = Tevder +pdfjs-editor-ink-opacity-input = Boullder +pdfjs-editor-stamp-add-image-button = + .title = Ouzhpennañ ur skeudenn +pdfjs-editor-stamp-add-image-button-label = Ouzhpennañ ur skeudenn +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Tevded +pdfjs-free-text = + .aria-label = Aozer testennoù +pdfjs-ink = + .aria-label = Aozer tresoù +pdfjs-ink-canvas = + .aria-label = Skeudenn bet krouet gant an implijer·ez + +## Alt-text dialog + +pdfjs-editor-alt-text-add-description-label = Ouzhpennañ un deskrivadur +pdfjs-editor-alt-text-cancel-button = Nullañ +pdfjs-editor-alt-text-save-button = Enrollañ + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + + +## Color picker + +pdfjs-editor-colorpicker-button = + .title = Cheñch liv +pdfjs-editor-colorpicker-yellow = + .title = Melen +pdfjs-editor-colorpicker-blue = + .title = Glas +pdfjs-editor-colorpicker-pink = + .title = Roz +pdfjs-editor-colorpicker-red = + .title = Ruz + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Diskouez pep tra +pdfjs-editor-highlight-show-all-button = + .title = Diskouez pep tra + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Gouzout hiroc’h +pdfjs-editor-new-alt-text-error-close-button = Serriñ + +## Image alt-text settings + +pdfjs-editor-alt-text-settings-delete-model-button = Dilemel +pdfjs-editor-alt-text-settings-download-model-button = Pellgargañ +pdfjs-editor-alt-text-settings-downloading-model-button = O pellgargañ… +pdfjs-editor-alt-text-settings-close-button = Serriñ diff --git a/public/pdfjs/web/locale/brx/viewer.ftl b/public/pdfjs/web/locale/brx/viewer.ftl new file mode 100644 index 0000000..53ff72c --- /dev/null +++ b/public/pdfjs/web/locale/brx/viewer.ftl @@ -0,0 +1,218 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = आगोलनि बिलाइ +pdfjs-previous-button-label = आगोलनि +pdfjs-next-button = + .title = उननि बिलाइ +pdfjs-next-button-label = उननि +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = बिलाइ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount } नि +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pagesCount } नि { $pageNumber }) +pdfjs-zoom-out-button = + .title = फिसायै जुम खालाम +pdfjs-zoom-out-button-label = फिसायै जुम खालाम +pdfjs-zoom-in-button = + .title = गेदेरै जुम खालाम +pdfjs-zoom-in-button-label = गेदेरै जुम खालाम +pdfjs-zoom-select = + .title = जुम खालाम +pdfjs-presentation-mode-button = + .title = दिन्थिफुंनाय म'डआव थां +pdfjs-presentation-mode-button-label = दिन्थिफुंनाय म'ड +pdfjs-open-file-button = + .title = फाइलखौ खेव +pdfjs-open-file-button-label = खेव +pdfjs-print-button = + .title = साफाय +pdfjs-print-button-label = साफाय + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = टुल +pdfjs-tools-button-label = टुल +pdfjs-first-page-button = + .title = गिबि बिलाइआव थां +pdfjs-first-page-button-label = गिबि बिलाइआव थां +pdfjs-last-page-button = + .title = जोबथा बिलाइआव थां +pdfjs-last-page-button-label = जोबथा बिलाइआव थां +pdfjs-page-rotate-cw-button = + .title = घरि गिदिंनाय फार्से फिदिं +pdfjs-page-rotate-cw-button-label = घरि गिदिंनाय फार्से फिदिं +pdfjs-page-rotate-ccw-button = + .title = घरि गिदिंनाय उल्था फार्से फिदिं +pdfjs-page-rotate-ccw-button-label = घरि गिदिंनाय उल्था फार्से फिदिं + +## Document properties dialog + +pdfjs-document-properties-button = + .title = फोरमान बिलाइनि आखुथाय... +pdfjs-document-properties-button-label = फोरमान बिलाइनि आखुथाय... +pdfjs-document-properties-file-name = फाइलनि मुं: +pdfjs-document-properties-file-size = फाइलनि महर: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } बाइट) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } बाइट) +pdfjs-document-properties-title = बिमुं: +pdfjs-document-properties-author = लिरगिरि: +pdfjs-document-properties-subject = आयदा: +pdfjs-document-properties-keywords = गाहाय सोदोब: +pdfjs-document-properties-creation-date = सोरजिनाय अक्ट': +pdfjs-document-properties-modification-date = सुद्रायनाय अक्ट': +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = सोरजिग्रा: +pdfjs-document-properties-producer = PDF दिहुनग्रा: +pdfjs-document-properties-version = PDF बिसान: +pdfjs-document-properties-page-count = बिलाइनि हिसाब: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = प'र्ट्रेट +pdfjs-document-properties-page-size-orientation-landscape = लेण्डस्केप +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = लायजाम + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +pdfjs-document-properties-linearized-yes = नंगौ +pdfjs-document-properties-linearized-no = नङा +pdfjs-document-properties-close-button = बन्द खालाम + +## Print + +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = नेवसि +pdfjs-printing-not-supported = सांग्रांथि: साफायनाया बे ब्राउजारजों आबुङै हेफाजाब होजाया। +pdfjs-printing-not-ready = सांग्रांथि: PDF खौ साफायनायनि थाखाय फुरायै ल'ड खालामाखै। + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = टग्गल साइडबार +pdfjs-toggle-sidebar-button-label = टग्गल साइडबार +pdfjs-document-outline-button-label = फोरमान बिलाइ सिमा हांखो +pdfjs-attachments-button = + .title = नांजाब होनायखौ दिन्थि +pdfjs-attachments-button-label = नांजाब होनाय +pdfjs-thumbs-button = + .title = थामनेइलखौ दिन्थि +pdfjs-thumbs-button-label = थामनेइल +pdfjs-findbar-button = + .title = फोरमान बिलाइआव नागिरना दिहुन +pdfjs-findbar-button-label = नायगिरना दिहुन + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = बिलाइ { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = बिलाइ { $page } नि थामनेइल + +## Find panel button title and messages + +pdfjs-find-input = + .title = नायगिरना दिहुन + .placeholder = फोरमान बिलाइआव नागिरना दिहुन... +pdfjs-find-previous-button = + .title = बाथ्रा खोन्दोबनि सिगांनि नुजाथिनायखौ नागिर +pdfjs-find-previous-button-label = आगोलनि +pdfjs-find-next-button = + .title = बाथ्रा खोन्दोबनि उननि नुजाथिनायखौ नागिर +pdfjs-find-next-button-label = उननि +pdfjs-find-highlight-checkbox = गासैखौबो हाइलाइट खालाम +pdfjs-find-match-case-checkbox-label = गोरोबनाय केस +pdfjs-find-reached-top = थालो निफ्राय जागायनानै फोरमान बिलाइनि बिजौआव सौहैबाय +pdfjs-find-reached-bottom = बिजौ निफ्राय जागायनानै फोरमान बिलाइनि बिजौआव सौहैबाय +pdfjs-find-not-found = बाथ्रा खोन्दोब मोनाखै + +## Predefined zoom values + +pdfjs-page-scale-width = बिलाइनि गुवार +pdfjs-page-scale-fit = बिलाइ गोरोबनाय +pdfjs-page-scale-auto = गावनोगाव जुम +pdfjs-page-scale-actual = थार महर +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = PDF ल'ड खालामनाय समाव मोनसे गोरोन्थि जाबाय। +pdfjs-invalid-file-error = बाहायजायै एबा गाज्रि जानाय PDF फाइल +pdfjs-missing-file-error = गोमानाय PDF फाइल +pdfjs-unexpected-response-error = मिजिंथियै सार्भार फिननाय। +pdfjs-rendering-error = बिलाइखौ राव सोलायनाय समाव मोनसे गोरोन्थि जादों। + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } सोदोब बेखेवनाय] + +## Password + +pdfjs-password-label = बे PDF फाइलखौ खेवनो पासवार्ड हाबहो। +pdfjs-password-invalid = बाहायजायै पासवार्ड। अननानै फिन नाजा। +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = नेवसि +pdfjs-web-fonts-disabled = वेब फन्टखौ लोरबां खालामबाय: अरजाबहोनाय PDF फन्टखौ बाहायनो हायाखै। + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/bs/viewer.ftl b/public/pdfjs/web/locale/bs/viewer.ftl new file mode 100644 index 0000000..3944042 --- /dev/null +++ b/public/pdfjs/web/locale/bs/viewer.ftl @@ -0,0 +1,223 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Prethodna strana +pdfjs-previous-button-label = Prethodna +pdfjs-next-button = + .title = Sljedeća strna +pdfjs-next-button-label = Sljedeća +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Strana +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = od { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } od { $pagesCount }) +pdfjs-zoom-out-button = + .title = Umanji +pdfjs-zoom-out-button-label = Umanji +pdfjs-zoom-in-button = + .title = Uvećaj +pdfjs-zoom-in-button-label = Uvećaj +pdfjs-zoom-select = + .title = Uvećanje +pdfjs-presentation-mode-button = + .title = Prebaci se u prezentacijski režim +pdfjs-presentation-mode-button-label = Prezentacijski režim +pdfjs-open-file-button = + .title = Otvori fajl +pdfjs-open-file-button-label = Otvori +pdfjs-print-button = + .title = Štampaj +pdfjs-print-button-label = Štampaj + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Alati +pdfjs-tools-button-label = Alati +pdfjs-first-page-button = + .title = Idi na prvu stranu +pdfjs-first-page-button-label = Idi na prvu stranu +pdfjs-last-page-button = + .title = Idi na zadnju stranu +pdfjs-last-page-button-label = Idi na zadnju stranu +pdfjs-page-rotate-cw-button = + .title = Rotiraj u smjeru kazaljke na satu +pdfjs-page-rotate-cw-button-label = Rotiraj u smjeru kazaljke na satu +pdfjs-page-rotate-ccw-button = + .title = Rotiraj suprotno smjeru kazaljke na satu +pdfjs-page-rotate-ccw-button-label = Rotiraj suprotno smjeru kazaljke na satu +pdfjs-cursor-text-select-tool-button = + .title = Omogući alat za označavanje teksta +pdfjs-cursor-text-select-tool-button-label = Alat za označavanje teksta +pdfjs-cursor-hand-tool-button = + .title = Omogući ručni alat +pdfjs-cursor-hand-tool-button-label = Ručni alat + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Svojstva dokumenta... +pdfjs-document-properties-button-label = Svojstva dokumenta... +pdfjs-document-properties-file-name = Naziv fajla: +pdfjs-document-properties-file-size = Veličina fajla: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajta) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajta) +pdfjs-document-properties-title = Naslov: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Predmet: +pdfjs-document-properties-keywords = Ključne riječi: +pdfjs-document-properties-creation-date = Datum kreiranja: +pdfjs-document-properties-modification-date = Datum promjene: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Kreator: +pdfjs-document-properties-producer = PDF stvaratelj: +pdfjs-document-properties-version = PDF verzija: +pdfjs-document-properties-page-count = Broj stranica: +pdfjs-document-properties-page-size = Veličina stranice: +pdfjs-document-properties-page-size-unit-inches = u +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = uspravno +pdfjs-document-properties-page-size-orientation-landscape = vodoravno +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Pismo +pdfjs-document-properties-page-size-name-legal = Pravni + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +pdfjs-document-properties-close-button = Zatvori + +## Print + +pdfjs-print-progress-message = Pripremam dokument za štampu… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Otkaži +pdfjs-printing-not-supported = Upozorenje: Štampanje nije u potpunosti podržano u ovom browseru. +pdfjs-printing-not-ready = Upozorenje: PDF nije u potpunosti učitan za štampanje. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Uključi/isključi bočnu traku +pdfjs-toggle-sidebar-button-label = Uključi/isključi bočnu traku +pdfjs-document-outline-button = + .title = Prikaži outline dokumenta (dvoklik za skupljanje/širenje svih stavki) +pdfjs-document-outline-button-label = Konture dokumenta +pdfjs-attachments-button = + .title = Prikaži priloge +pdfjs-attachments-button-label = Prilozi +pdfjs-thumbs-button = + .title = Prikaži thumbnailove +pdfjs-thumbs-button-label = Thumbnailovi +pdfjs-findbar-button = + .title = Pronađi u dokumentu +pdfjs-findbar-button-label = Pronađi + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Strana { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Thumbnail strane { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Pronađi + .placeholder = Pronađi u dokumentu… +pdfjs-find-previous-button = + .title = Pronađi prethodno pojavljivanje fraze +pdfjs-find-previous-button-label = Prethodno +pdfjs-find-next-button = + .title = Pronađi sljedeće pojavljivanje fraze +pdfjs-find-next-button-label = Sljedeće +pdfjs-find-highlight-checkbox = Označi sve +pdfjs-find-match-case-checkbox-label = Osjetljivost na karaktere +pdfjs-find-reached-top = Dostigao sam vrh dokumenta, nastavljam sa dna +pdfjs-find-reached-bottom = Dostigao sam kraj dokumenta, nastavljam sa vrha +pdfjs-find-not-found = Fraza nije pronađena + +## Predefined zoom values + +pdfjs-page-scale-width = Širina strane +pdfjs-page-scale-fit = Uklopi stranu +pdfjs-page-scale-auto = Automatsko uvećanje +pdfjs-page-scale-actual = Stvarna veličina +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Došlo je do greške prilikom učitavanja PDF-a. +pdfjs-invalid-file-error = Neispravan ili oštećen PDF fajl. +pdfjs-missing-file-error = Nedostaje PDF fajl. +pdfjs-unexpected-response-error = Neočekivani odgovor servera. +pdfjs-rendering-error = Došlo je do greške prilikom renderiranja strane. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } pribilješka] + +## Password + +pdfjs-password-label = Upišite lozinku da biste otvorili ovaj PDF fajl. +pdfjs-password-invalid = Pogrešna lozinka. Pokušajte ponovo. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Otkaži +pdfjs-web-fonts-disabled = Web fontovi su onemogućeni: nemoguće koristiti ubačene PDF fontove. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/ca/viewer.ftl b/public/pdfjs/web/locale/ca/viewer.ftl new file mode 100644 index 0000000..7417741 --- /dev/null +++ b/public/pdfjs/web/locale/ca/viewer.ftl @@ -0,0 +1,313 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pàgina anterior +pdfjs-previous-button-label = Anterior +pdfjs-next-button = + .title = Pàgina següent +pdfjs-next-button-label = Següent +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pàgina +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = de { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount }) +pdfjs-zoom-out-button = + .title = Redueix +pdfjs-zoom-out-button-label = Redueix +pdfjs-zoom-in-button = + .title = Amplia +pdfjs-zoom-in-button-label = Amplia +pdfjs-zoom-select = + .title = Escala +pdfjs-presentation-mode-button = + .title = Canvia al mode de presentació +pdfjs-presentation-mode-button-label = Mode de presentació +pdfjs-open-file-button = + .title = Obre el fitxer +pdfjs-open-file-button-label = Obre +pdfjs-print-button = + .title = Imprimeix +pdfjs-print-button-label = Imprimeix +pdfjs-save-button = + .title = Desa +pdfjs-save-button-label = Desa +pdfjs-bookmark-button = + .title = Pàgina actual (mostra l'URL de la pàgina actual) +pdfjs-bookmark-button-label = Pàgina actual + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Eines +pdfjs-tools-button-label = Eines +pdfjs-first-page-button = + .title = Vés a la primera pàgina +pdfjs-first-page-button-label = Vés a la primera pàgina +pdfjs-last-page-button = + .title = Vés a l'última pàgina +pdfjs-last-page-button-label = Vés a l'última pàgina +pdfjs-page-rotate-cw-button = + .title = Gira cap a la dreta +pdfjs-page-rotate-cw-button-label = Gira cap a la dreta +pdfjs-page-rotate-ccw-button = + .title = Gira cap a l'esquerra +pdfjs-page-rotate-ccw-button-label = Gira cap a l'esquerra +pdfjs-cursor-text-select-tool-button = + .title = Habilita l'eina de selecció de text +pdfjs-cursor-text-select-tool-button-label = Eina de selecció de text +pdfjs-cursor-hand-tool-button = + .title = Habilita l'eina de mà +pdfjs-cursor-hand-tool-button-label = Eina de mà +pdfjs-scroll-page-button = + .title = Usa el desplaçament de pàgina +pdfjs-scroll-page-button-label = Desplaçament de pàgina +pdfjs-scroll-vertical-button = + .title = Utilitza el desplaçament vertical +pdfjs-scroll-vertical-button-label = Desplaçament vertical +pdfjs-scroll-horizontal-button = + .title = Utilitza el desplaçament horitzontal +pdfjs-scroll-horizontal-button-label = Desplaçament horitzontal +pdfjs-scroll-wrapped-button = + .title = Activa el desplaçament continu +pdfjs-scroll-wrapped-button-label = Desplaçament continu +pdfjs-spread-none-button = + .title = No agrupis les pàgines de dues en dues +pdfjs-spread-none-button-label = Una sola pàgina +pdfjs-spread-odd-button = + .title = Mostra dues pàgines començant per les pàgines de numeració senar +pdfjs-spread-odd-button-label = Doble pàgina (senar) +pdfjs-spread-even-button = + .title = Mostra dues pàgines començant per les pàgines de numeració parell +pdfjs-spread-even-button-label = Doble pàgina (parell) + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Propietats del document… +pdfjs-document-properties-button-label = Propietats del document… +pdfjs-document-properties-file-name = Nom del fitxer: +pdfjs-document-properties-file-size = Mida del fitxer: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Títol: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Assumpte: +pdfjs-document-properties-keywords = Paraules clau: +pdfjs-document-properties-creation-date = Data de creació: +pdfjs-document-properties-modification-date = Data de modificació: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Creador: +pdfjs-document-properties-producer = Generador de PDF: +pdfjs-document-properties-version = Versió de PDF: +pdfjs-document-properties-page-count = Nombre de pàgines: +pdfjs-document-properties-page-size = Mida de la pàgina: +pdfjs-document-properties-page-size-unit-inches = polzades +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = vertical +pdfjs-document-properties-page-size-orientation-landscape = apaïsat +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Carta +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Vista web ràpida: +pdfjs-document-properties-linearized-yes = Sí +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Tanca + +## Print + +pdfjs-print-progress-message = S'està preparant la impressió del document… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cancel·la +pdfjs-printing-not-supported = Avís: la impressió no és plenament funcional en aquest navegador. +pdfjs-printing-not-ready = Atenció: el PDF no s'ha acabat de carregar per imprimir-lo. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Mostra/amaga la barra lateral +pdfjs-toggle-sidebar-notification-button = + .title = Mostra/amaga la barra lateral (el document conté un esquema, adjuncions o capes) +pdfjs-toggle-sidebar-button-label = Mostra/amaga la barra lateral +pdfjs-document-outline-button = + .title = Mostra l'esquema del document (doble clic per ampliar/reduir tots els elements) +pdfjs-document-outline-button-label = Esquema del document +pdfjs-attachments-button = + .title = Mostra les adjuncions +pdfjs-attachments-button-label = Adjuncions +pdfjs-layers-button = + .title = Mostra les capes (doble clic per restablir totes les capes al seu estat per defecte) +pdfjs-layers-button-label = Capes +pdfjs-thumbs-button = + .title = Mostra les miniatures +pdfjs-thumbs-button-label = Miniatures +pdfjs-current-outline-item-button = + .title = Cerca l'element d'esquema actual +pdfjs-current-outline-item-button-label = Element d'esquema actual +pdfjs-findbar-button = + .title = Cerca al document +pdfjs-findbar-button-label = Cerca +pdfjs-additional-layers = Capes addicionals + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pàgina { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura de la pàgina { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Cerca + .placeholder = Cerca al document… +pdfjs-find-previous-button = + .title = Cerca l'anterior coincidència de l'expressió +pdfjs-find-previous-button-label = Anterior +pdfjs-find-next-button = + .title = Cerca la següent coincidència de l'expressió +pdfjs-find-next-button-label = Següent +pdfjs-find-highlight-checkbox = Ressalta-ho tot +pdfjs-find-match-case-checkbox-label = Distingeix entre majúscules i minúscules +pdfjs-find-match-diacritics-checkbox-label = Respecta els diacrítics +pdfjs-find-entire-word-checkbox-label = Paraules senceres +pdfjs-find-reached-top = S'ha arribat al principi del document, es continua pel final +pdfjs-find-reached-bottom = S'ha arribat al final del document, es continua pel principi +pdfjs-find-not-found = No s'ha trobat l'expressió + +## Predefined zoom values + +pdfjs-page-scale-width = Amplada de la pàgina +pdfjs-page-scale-fit = Ajusta la pàgina +pdfjs-page-scale-auto = Zoom automàtic +pdfjs-page-scale-actual = Mida real +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Pàgina { $page } + +## Loading indicator messages + +pdfjs-loading-error = S'ha produït un error en carregar el PDF. +pdfjs-invalid-file-error = El fitxer PDF no és vàlid o està malmès. +pdfjs-missing-file-error = Falta el fitxer PDF. +pdfjs-unexpected-response-error = Resposta inesperada del servidor. +pdfjs-rendering-error = S'ha produït un error mentre es renderitzava la pàgina. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anotació { $type }] + +## Password + +pdfjs-password-label = Introduïu la contrasenya per obrir aquest fitxer PDF. +pdfjs-password-invalid = La contrasenya no és vàlida. Torneu-ho a provar. +pdfjs-password-ok-button = D'acord +pdfjs-password-cancel-button = Cancel·la +pdfjs-web-fonts-disabled = Els tipus de lletra web estan desactivats: no es poden utilitzar els tipus de lletra incrustats al PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Text +pdfjs-editor-free-text-button-label = Text +pdfjs-editor-ink-button = + .title = Dibuixa +pdfjs-editor-ink-button-label = Dibuixa + +## Remove button for the various kind of editor. + + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Color +pdfjs-editor-free-text-size-input = Mida +pdfjs-editor-ink-color-input = Color +pdfjs-editor-ink-thickness-input = Gruix +pdfjs-editor-ink-opacity-input = Opacitat +pdfjs-free-text = + .aria-label = Editor de text +pdfjs-free-text-default-content = Escriviu… +pdfjs-ink = + .aria-label = Editor de dibuix +pdfjs-ink-canvas = + .aria-label = Imatge creada per l'usuari + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + diff --git a/public/pdfjs/web/locale/cak/viewer.ftl b/public/pdfjs/web/locale/cak/viewer.ftl new file mode 100644 index 0000000..f40c1e9 --- /dev/null +++ b/public/pdfjs/web/locale/cak/viewer.ftl @@ -0,0 +1,291 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Jun kan ruxaq +pdfjs-previous-button-label = Jun kan +pdfjs-next-button = + .title = Jun chik ruxaq +pdfjs-next-button-label = Jun chik +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Ruxaq +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = richin { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } richin { $pagesCount }) +pdfjs-zoom-out-button = + .title = Tich'utinirisäx +pdfjs-zoom-out-button-label = Tich'utinirisäx +pdfjs-zoom-in-button = + .title = Tinimirisäx +pdfjs-zoom-in-button-label = Tinimirisäx +pdfjs-zoom-select = + .title = Sum +pdfjs-presentation-mode-button = + .title = Tijal ri rub'anikil niwachin +pdfjs-presentation-mode-button-label = Pa rub'eyal niwachin +pdfjs-open-file-button = + .title = Tijaq Yakb'äl +pdfjs-open-file-button-label = Tijaq +pdfjs-print-button = + .title = Titz'ajb'äx +pdfjs-print-button-label = Titz'ajb'äx +pdfjs-save-button = + .title = Tiyak +pdfjs-save-button-label = Tiyak +pdfjs-bookmark-button-label = Ruxaq k'o wakami + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Samajib'äl +pdfjs-tools-button-label = Samajib'äl +pdfjs-first-page-button = + .title = Tib'e pa nab'ey ruxaq +pdfjs-first-page-button-label = Tib'e pa nab'ey ruxaq +pdfjs-last-page-button = + .title = Tib'e pa ruk'isib'äl ruxaq +pdfjs-last-page-button-label = Tib'e pa ruk'isib'äl ruxaq +pdfjs-page-rotate-cw-button = + .title = Tisutïx pan ajkiq'a' +pdfjs-page-rotate-cw-button-label = Tisutïx pan ajkiq'a' +pdfjs-page-rotate-ccw-button = + .title = Tisutïx pan ajxokon +pdfjs-page-rotate-ccw-button-label = Tisutïx pan ajxokon +pdfjs-cursor-text-select-tool-button = + .title = Titzij ri rusamajib'al Rucha'ik Rucholajem Tzij +pdfjs-cursor-text-select-tool-button-label = Rusamajib'al Rucha'ik Rucholajem Tzij +pdfjs-cursor-hand-tool-button = + .title = Titzij ri q'ab'aj samajib'äl +pdfjs-cursor-hand-tool-button-label = Q'ab'aj Samajib'äl +pdfjs-scroll-page-button = + .title = Tokisäx Ruxaq Q'axanem +pdfjs-scroll-page-button-label = Ruxaq Q'axanem +pdfjs-scroll-vertical-button = + .title = Tokisäx Pa'äl Q'axanem +pdfjs-scroll-vertical-button-label = Pa'äl Q'axanem +pdfjs-scroll-horizontal-button = + .title = Tokisäx Kotz'öl Q'axanem +pdfjs-scroll-horizontal-button-label = Kotz'öl Q'axanem +pdfjs-scroll-wrapped-button = + .title = Tokisäx Tzub'aj Q'axanem +pdfjs-scroll-wrapped-button-label = Tzub'aj Q'axanem +pdfjs-spread-none-button = + .title = Man ketun taq ruxaq pa rub'eyal wuj +pdfjs-spread-none-button-label = Majun Rub'eyal +pdfjs-spread-odd-button = + .title = Ke'atunu' ri taq ruxaq rik'in natikirisaj rik'in jun man k'ulaj ta rajilab'al +pdfjs-spread-odd-button-label = Man K'ulaj Ta Rub'eyal +pdfjs-spread-even-button = + .title = Ke'atunu' ri taq ruxaq rik'in natikirisaj rik'in jun k'ulaj rajilab'al +pdfjs-spread-even-button-label = K'ulaj Rub'eyal + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Taq richinil wuj… +pdfjs-document-properties-button-label = Taq richinil wuj… +pdfjs-document-properties-file-name = Rub'i' yakb'äl: +pdfjs-document-properties-file-size = Runimilem yakb'äl: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = B'i'aj: +pdfjs-document-properties-author = B'anel: +pdfjs-document-properties-subject = Taqikil: +pdfjs-document-properties-keywords = Kixe'el taq tzij: +pdfjs-document-properties-creation-date = Ruq'ijul xtz'uk: +pdfjs-document-properties-modification-date = Ruq'ijul xjalwachïx: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Q'inonel: +pdfjs-document-properties-producer = PDF b'anöy: +pdfjs-document-properties-version = PDF ruwäch: +pdfjs-document-properties-page-count = Jarupe' ruxaq: +pdfjs-document-properties-page-size = Runimilem ri Ruxaq: +pdfjs-document-properties-page-size-unit-inches = pa +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = rupalem +pdfjs-document-properties-page-size-orientation-landscape = rukotz'olem +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Loman wuj +pdfjs-document-properties-page-size-name-legal = Taqanel tzijol + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Anin Rutz'etik Ajk'amaya'l: +pdfjs-document-properties-linearized-yes = Ja' +pdfjs-document-properties-linearized-no = Mani +pdfjs-document-properties-close-button = Titz'apïx + +## Print + +pdfjs-print-progress-message = Ruchojmirisaxik wuj richin nitz'ajb'äx… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Tiq'at +pdfjs-printing-not-supported = Rutzijol k'ayewal: Ri rutz'ajb'axik man koch'el ta ronojel pa re okik'amaya'l re'. +pdfjs-printing-not-ready = Rutzijol k'ayewal: Ri PDF man xusamajij ta ronojel richin nitz'ajb'äx. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Tijal ri ajxikin kajtz'ik +pdfjs-toggle-sidebar-notification-button = + .title = Tik'ex ri ajxikin yuqkajtz'ik (ri wuj eruk'wan taq ruchi'/taqo/kuchuj) +pdfjs-toggle-sidebar-button-label = Tijal ri ajxikin kajtz'ik +pdfjs-document-outline-button = + .title = Tik'ut pe ruch'akulal wuj (kamul-pitz'oj richin nirik'/nich'utinirisäx ronojel ruch'akulal) +pdfjs-document-outline-button-label = Ruch'akulal wuj +pdfjs-attachments-button = + .title = Kek'ut pe ri taq taqoj +pdfjs-attachments-button-label = Taq taqoj +pdfjs-layers-button = + .title = Kek'ut taq Kuchuj (ka'i'-pitz' richin yetzolïx ronojel ri taq kuchuj e k'o wi) +pdfjs-layers-button-label = Taq kuchuj +pdfjs-thumbs-button = + .title = Kek'ut pe taq ch'utiq +pdfjs-thumbs-button-label = Koköj +pdfjs-current-outline-item-button = + .title = Kekanöx Taq Ch'akulal Kik'wan Chib'äl +pdfjs-current-outline-item-button-label = Taq Ch'akulal Kik'wan Chib'äl +pdfjs-findbar-button = + .title = Tikanöx chupam ri wuj +pdfjs-findbar-button-label = Tikanöx +pdfjs-additional-layers = Tz'aqat ta Kuchuj + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Ruxaq { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Ruch'utinirisaxik ruxaq { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Tikanöx + .placeholder = Tikanöx pa wuj… +pdfjs-find-previous-button = + .title = Tib'an b'enam pa ri jun kan q'aptzij xilitäj +pdfjs-find-previous-button-label = Jun kan +pdfjs-find-next-button = + .title = Tib'e pa ri jun chik pajtzij xilitäj +pdfjs-find-next-button-label = Jun chik +pdfjs-find-highlight-checkbox = Tiya' retal ronojel +pdfjs-find-match-case-checkbox-label = Tuk'äm ri' kik'in taq nimatz'ib' chuqa' taq ch'utitz'ib' +pdfjs-find-match-diacritics-checkbox-label = Tiya' Kikojol Tz'aqat taq Tz'ib' +pdfjs-find-entire-word-checkbox-label = Tz'aqät taq tzij +pdfjs-find-reached-top = Xb'eq'i' ri rutikirib'al wuj, xtikanöx k'a pa ruk'isib'äl +pdfjs-find-reached-bottom = Xb'eq'i' ri ruk'isib'äl wuj, xtikanöx pa rutikirib'al +pdfjs-find-not-found = Man xilitäj ta ri pajtzij + +## Predefined zoom values + +pdfjs-page-scale-width = Ruwa ruxaq +pdfjs-page-scale-fit = Tinuk' ruxaq +pdfjs-page-scale-auto = Yonil chi nimilem +pdfjs-page-scale-actual = Runimilem Wakami +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Ruxaq { $page } + +## Loading indicator messages + +pdfjs-loading-error = Xk'ulwachitäj jun sach'oj toq xnuk'ux ri PDF . +pdfjs-invalid-file-error = Man oke ta o yujtajinäq ri PDF yakb'äl. +pdfjs-missing-file-error = Man xilitäj ta ri PDF yakb'äl. +pdfjs-unexpected-response-error = Man oyob'en ta tz'olin rutzij ruk'u'x samaj. +pdfjs-rendering-error = Xk'ulwachitäj jun sachoj toq ninuk'wachij ri ruxaq. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Tz'ib'anïk] + +## Password + +pdfjs-password-label = Tatz'ib'aj ri ewan tzij richin najäq re yakb'äl re' pa PDF. +pdfjs-password-invalid = Man okel ta ri ewan tzij: Tatojtob'ej chik. +pdfjs-password-ok-button = Ütz +pdfjs-password-cancel-button = Tiq'at +pdfjs-web-fonts-disabled = E chupül ri taq ajk'amaya'l tz'ib': man tikirel ta nokisäx ri taq tz'ib' PDF pa ch'ikenïk + +## Editing + +pdfjs-editor-free-text-button = + .title = Rucholajem tz'ib' +pdfjs-editor-free-text-button-label = Rucholajem tz'ib' +pdfjs-editor-ink-button = + .title = Tiwachib'ëx +pdfjs-editor-ink-button-label = Tiwachib'ëx +# Editor Parameters +pdfjs-editor-free-text-color-input = B'onil +pdfjs-editor-free-text-size-input = Nimilem +pdfjs-editor-ink-color-input = B'onil +pdfjs-editor-ink-thickness-input = Rupimil +pdfjs-editor-ink-opacity-input = Q'equmal +pdfjs-free-text = + .aria-label = Nuk'unel tz'ib'atzij +pdfjs-free-text-default-content = Titikitisäx rutz'ib'axik… +pdfjs-ink = + .aria-label = Nuk'unel wachib'äl +pdfjs-ink-canvas = + .aria-label = Wachib'äl nuk'un ruma okisaxel + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/ckb/viewer.ftl b/public/pdfjs/web/locale/ckb/viewer.ftl new file mode 100644 index 0000000..ae87335 --- /dev/null +++ b/public/pdfjs/web/locale/ckb/viewer.ftl @@ -0,0 +1,242 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = پەڕەی پێشوو +pdfjs-previous-button-label = پێشوو +pdfjs-next-button = + .title = پەڕەی دوواتر +pdfjs-next-button-label = دوواتر +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = پەرە +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = لە { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } لە { $pagesCount }) +pdfjs-zoom-out-button = + .title = ڕۆچوونی +pdfjs-zoom-out-button-label = ڕۆچوونی +pdfjs-zoom-in-button = + .title = هێنانەپێش +pdfjs-zoom-in-button-label = هێنانەپێش +pdfjs-zoom-select = + .title = زووم +pdfjs-presentation-mode-button = + .title = گۆڕین بۆ دۆخی پێشکەشکردن +pdfjs-presentation-mode-button-label = دۆخی پێشکەشکردن +pdfjs-open-file-button = + .title = پەڕگە بکەرەوە +pdfjs-open-file-button-label = کردنەوە +pdfjs-print-button = + .title = چاپکردن +pdfjs-print-button-label = چاپکردن + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = ئامرازەکان +pdfjs-tools-button-label = ئامرازەکان +pdfjs-first-page-button = + .title = برۆ بۆ یەکەم پەڕە +pdfjs-first-page-button-label = بڕۆ بۆ یەکەم پەڕە +pdfjs-last-page-button = + .title = بڕۆ بۆ کۆتا پەڕە +pdfjs-last-page-button-label = بڕۆ بۆ کۆتا پەڕە +pdfjs-page-rotate-cw-button = + .title = ئاڕاستەی میلی کاتژمێر +pdfjs-page-rotate-cw-button-label = ئاڕاستەی میلی کاتژمێر +pdfjs-page-rotate-ccw-button = + .title = پێچەوانەی میلی کاتژمێر +pdfjs-page-rotate-ccw-button-label = پێچەوانەی میلی کاتژمێر +pdfjs-cursor-text-select-tool-button = + .title = توڵامرازی نیشانکەری دەق چالاک بکە +pdfjs-cursor-text-select-tool-button-label = توڵامرازی نیشانکەری دەق +pdfjs-cursor-hand-tool-button = + .title = توڵامرازی دەستی چالاک بکە +pdfjs-cursor-hand-tool-button-label = توڵامرازی دەستی +pdfjs-scroll-vertical-button = + .title = ناردنی ئەستوونی بەکاربێنە +pdfjs-scroll-vertical-button-label = ناردنی ئەستوونی +pdfjs-scroll-horizontal-button = + .title = ناردنی ئاسۆیی بەکاربێنە +pdfjs-scroll-horizontal-button-label = ناردنی ئاسۆیی +pdfjs-scroll-wrapped-button = + .title = ناردنی لوولکراو بەکاربێنە +pdfjs-scroll-wrapped-button-label = ناردنی لوولکراو + +## Document properties dialog + +pdfjs-document-properties-button = + .title = تایبەتمەندییەکانی بەڵگەنامە... +pdfjs-document-properties-button-label = تایبەتمەندییەکانی بەڵگەنامە... +pdfjs-document-properties-file-name = ناوی پەڕگە: +pdfjs-document-properties-file-size = قەبارەی پەڕگە: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } کب ({ $size_b } بایت) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } مب ({ $size_b } بایت) +pdfjs-document-properties-title = سەردێڕ: +pdfjs-document-properties-author = نووسەر +pdfjs-document-properties-subject = بابەت: +pdfjs-document-properties-keywords = کلیلەوشە: +pdfjs-document-properties-creation-date = بەرواری درووستکردن: +pdfjs-document-properties-modification-date = بەرواری دەستکاریکردن: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = درووستکەر: +pdfjs-document-properties-producer = بەرهەمهێنەری PDF: +pdfjs-document-properties-version = وەشانی PDF: +pdfjs-document-properties-page-count = ژمارەی پەرەکان: +pdfjs-document-properties-page-size = قەبارەی پەڕە: +pdfjs-document-properties-page-size-unit-inches = ئینچ +pdfjs-document-properties-page-size-unit-millimeters = ملم +pdfjs-document-properties-page-size-orientation-portrait = پۆرترەیت(درێژ) +pdfjs-document-properties-page-size-orientation-landscape = پانیی +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = نامە +pdfjs-document-properties-page-size-name-legal = یاسایی + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = پیشاندانی وێبی خێرا: +pdfjs-document-properties-linearized-yes = بەڵێ +pdfjs-document-properties-linearized-no = نەخێر +pdfjs-document-properties-close-button = داخستن + +## Print + +pdfjs-print-progress-message = بەڵگەنامە ئامادەدەکرێت بۆ چاپکردن... +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = پاشگەزبوونەوە +pdfjs-printing-not-supported = ئاگاداربە: چاپکردن بە تەواوی پشتگیر ناکرێت لەم وێبگەڕە. +pdfjs-printing-not-ready = ئاگاداربە: PDF بە تەواوی بارنەبووە بۆ چاپکردن. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = لاتەنیشت پیشاندان/شاردنەوە +pdfjs-toggle-sidebar-button-label = لاتەنیشت پیشاندان/شاردنەوە +pdfjs-document-outline-button-label = سنووری چوارچێوە +pdfjs-attachments-button = + .title = پاشکۆکان پیشان بدە +pdfjs-attachments-button-label = پاشکۆکان +pdfjs-layers-button-label = چینەکان +pdfjs-thumbs-button = + .title = وێنۆچکە پیشان بدە +pdfjs-thumbs-button-label = وێنۆچکە +pdfjs-findbar-button = + .title = لە بەڵگەنامە بگەرێ +pdfjs-findbar-button-label = دۆزینەوە +pdfjs-additional-layers = چینی زیاتر + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = پەڕەی { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = وێنۆچکەی پەڕەی { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = دۆزینەوە + .placeholder = لە بەڵگەنامە بگەرێ... +pdfjs-find-previous-button = + .title = هەبوونی پێشوو بدۆزرەوە لە ڕستەکەدا +pdfjs-find-previous-button-label = پێشوو +pdfjs-find-next-button = + .title = هەبوونی داهاتوو بدۆزەرەوە لە ڕستەکەدا +pdfjs-find-next-button-label = دوواتر +pdfjs-find-highlight-checkbox = هەمووی نیشانە بکە +pdfjs-find-match-case-checkbox-label = دۆخی لەیەکچوون +pdfjs-find-entire-word-checkbox-label = هەموو وشەکان +pdfjs-find-reached-top = گەشتیتە سەرەوەی بەڵگەنامە، لە خوارەوە دەستت پێکرد +pdfjs-find-reached-bottom = گەشتیتە کۆتایی بەڵگەنامە. لەسەرەوە دەستت پێکرد +pdfjs-find-not-found = نووسین نەدۆزرایەوە + +## Predefined zoom values + +pdfjs-page-scale-width = پانی پەڕە +pdfjs-page-scale-fit = پڕبوونی پەڕە +pdfjs-page-scale-auto = زوومی خۆکار +pdfjs-page-scale-actual = قەبارەی ڕاستی +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = هەڵەیەک ڕوویدا لە کاتی بارکردنی PDF. +pdfjs-invalid-file-error = پەڕگەی pdf تێکچووە یان نەگونجاوە. +pdfjs-missing-file-error = پەڕگەی pdf بوونی نیە. +pdfjs-unexpected-response-error = وەڵامی ڕاژەخوازی نەخوازراو. +pdfjs-rendering-error = هەڵەیەک ڕوویدا لە کاتی پوختەکردنی (ڕێندەر) پەڕە. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } سەرنج] + +## Password + +pdfjs-password-label = وشەی تێپەڕ بنووسە بۆ کردنەوەی پەڕگەی pdf. +pdfjs-password-invalid = وشەی تێپەڕ هەڵەیە. تکایە دووبارە هەوڵ بدەرەوە. +pdfjs-password-ok-button = باشە +pdfjs-password-cancel-button = پاشگەزبوونەوە +pdfjs-web-fonts-disabled = جۆرەپیتی وێب ناچالاکە: نەتوانی جۆرەپیتی تێخراوی ناو pdfـەکە بەکاربێت. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/cs/viewer.ftl b/public/pdfjs/web/locale/cs/viewer.ftl new file mode 100644 index 0000000..696fe30 --- /dev/null +++ b/public/pdfjs/web/locale/cs/viewer.ftl @@ -0,0 +1,521 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Přejde na předchozí stránku +pdfjs-previous-button-label = Předchozí +pdfjs-next-button = + .title = Přejde na následující stránku +pdfjs-next-button-label = Další +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Stránka +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = z { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } z { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zmenší velikost +pdfjs-zoom-out-button-label = Zmenšit +pdfjs-zoom-in-button = + .title = Zvětší velikost +pdfjs-zoom-in-button-label = Zvětšit +pdfjs-zoom-select = + .title = Nastaví velikost +pdfjs-presentation-mode-button = + .title = Přepne do režimu prezentace +pdfjs-presentation-mode-button-label = Režim prezentace +pdfjs-open-file-button = + .title = Otevře soubor +pdfjs-open-file-button-label = Otevřít +pdfjs-print-button = + .title = Vytiskne dokument +pdfjs-print-button-label = Vytisknout +pdfjs-save-button = + .title = Uložit +pdfjs-save-button-label = Uložit +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Stáhnout +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Stáhnout +pdfjs-bookmark-button = + .title = Aktuální stránka (zobrazit URL od aktuální stránky) +pdfjs-bookmark-button-label = Aktuální stránka + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Nástroje +pdfjs-tools-button-label = Nástroje +pdfjs-first-page-button = + .title = Přejde na první stránku +pdfjs-first-page-button-label = Přejít na první stránku +pdfjs-last-page-button = + .title = Přejde na poslední stránku +pdfjs-last-page-button-label = Přejít na poslední stránku +pdfjs-page-rotate-cw-button = + .title = Otočí po směru hodin +pdfjs-page-rotate-cw-button-label = Otočit po směru hodin +pdfjs-page-rotate-ccw-button = + .title = Otočí proti směru hodin +pdfjs-page-rotate-ccw-button-label = Otočit proti směru hodin +pdfjs-cursor-text-select-tool-button = + .title = Povolí výběr textu +pdfjs-cursor-text-select-tool-button-label = Výběr textu +pdfjs-cursor-hand-tool-button = + .title = Povolí nástroj ručička +pdfjs-cursor-hand-tool-button-label = Nástroj ručička +pdfjs-scroll-page-button = + .title = Posouvat po stránkách +pdfjs-scroll-page-button-label = Posouvání po stránkách +pdfjs-scroll-vertical-button = + .title = Použít svislé posouvání +pdfjs-scroll-vertical-button-label = Svislé posouvání +pdfjs-scroll-horizontal-button = + .title = Použít vodorovné posouvání +pdfjs-scroll-horizontal-button-label = Vodorovné posouvání +pdfjs-scroll-wrapped-button = + .title = Použít postupné posouvání +pdfjs-scroll-wrapped-button-label = Postupné posouvání +pdfjs-spread-none-button = + .title = Nesdružovat stránky +pdfjs-spread-none-button-label = Žádné sdružení +pdfjs-spread-odd-button = + .title = Sdruží stránky s umístěním lichých vlevo +pdfjs-spread-odd-button-label = Sdružení stránek (liché vlevo) +pdfjs-spread-even-button = + .title = Sdruží stránky s umístěním sudých vlevo +pdfjs-spread-even-button-label = Sdružení stránek (sudé vlevo) + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Vlastnosti dokumentu… +pdfjs-document-properties-button-label = Vlastnosti dokumentu… +pdfjs-document-properties-file-name = Název souboru: +pdfjs-document-properties-file-size = Velikost souboru: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } kB ({ $b } bajtů) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bajtů) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajtů) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajtů) +pdfjs-document-properties-title = Název stránky: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Předmět: +pdfjs-document-properties-keywords = Klíčová slova: +pdfjs-document-properties-creation-date = Datum vytvoření: +pdfjs-document-properties-modification-date = Datum úpravy: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Vytvořil: +pdfjs-document-properties-producer = Tvůrce PDF: +pdfjs-document-properties-version = Verze PDF: +pdfjs-document-properties-page-count = Počet stránek: +pdfjs-document-properties-page-size = Velikost stránky: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = na výšku +pdfjs-document-properties-page-size-orientation-landscape = na šířku +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Dopis +pdfjs-document-properties-page-size-name-legal = Právní dokument + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Rychlé zobrazování z webu: +pdfjs-document-properties-linearized-yes = Ano +pdfjs-document-properties-linearized-no = Ne +pdfjs-document-properties-close-button = Zavřít + +## Print + +pdfjs-print-progress-message = Příprava dokumentu pro tisk… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress } % +pdfjs-print-progress-close-button = Zrušit +pdfjs-printing-not-supported = Upozornění: Tisk není v tomto prohlížeči plně podporován. +pdfjs-printing-not-ready = Upozornění: Dokument PDF není kompletně načten. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Postranní lišta +pdfjs-toggle-sidebar-notification-button = + .title = Přepnout postranní lištu (dokument obsahuje osnovu/přílohy/vrstvy) +pdfjs-toggle-sidebar-button-label = Postranní lišta +pdfjs-document-outline-button = + .title = Zobrazí osnovu dokumentu (poklepání přepne zobrazení všech položek) +pdfjs-document-outline-button-label = Osnova dokumentu +pdfjs-attachments-button = + .title = Zobrazí přílohy +pdfjs-attachments-button-label = Přílohy +pdfjs-layers-button = + .title = Zobrazit vrstvy (poklepáním obnovíte všechny vrstvy do výchozího stavu) +pdfjs-layers-button-label = Vrstvy +pdfjs-thumbs-button = + .title = Zobrazí náhledy +pdfjs-thumbs-button-label = Náhledy +pdfjs-current-outline-item-button = + .title = Najít aktuální položku v osnově +pdfjs-current-outline-item-button-label = Aktuální položka v osnově +pdfjs-findbar-button = + .title = Najde v dokumentu +pdfjs-findbar-button-label = Najít +pdfjs-additional-layers = Další vrstvy + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Strana { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Náhled strany { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Najít + .placeholder = Najít v dokumentu… +pdfjs-find-previous-button = + .title = Najde předchozí výskyt hledaného textu +pdfjs-find-previous-button-label = Předchozí +pdfjs-find-next-button = + .title = Najde další výskyt hledaného textu +pdfjs-find-next-button-label = Další +pdfjs-find-highlight-checkbox = Zvýraznit +pdfjs-find-match-case-checkbox-label = Rozlišovat velikost +pdfjs-find-match-diacritics-checkbox-label = Rozlišovat diakritiku +pdfjs-find-entire-word-checkbox-label = Celá slova +pdfjs-find-reached-top = Dosažen začátek dokumentu, pokračuje se od konce +pdfjs-find-reached-bottom = Dosažen konec dokumentu, pokračuje se od začátku +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current }. z { $total } výskytu + [few] { $current }. z { $total } výskytů + [many] { $current }. z { $total } výskytů + *[other] { $current }. z { $total } výskytů + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Více než { $limit } výskyt + [few] Více než { $limit } výskyty + [many] Více než { $limit } výskytů + *[other] Více než { $limit } výskytů + } +pdfjs-find-not-found = Hledaný text nenalezen + +## Predefined zoom values + +pdfjs-page-scale-width = Podle šířky +pdfjs-page-scale-fit = Podle výšky +pdfjs-page-scale-auto = Automatická velikost +pdfjs-page-scale-actual = Skutečná velikost +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale } % + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Strana { $page } + +## Loading indicator messages + +pdfjs-loading-error = Při nahrávání PDF nastala chyba. +pdfjs-invalid-file-error = Neplatný nebo chybný soubor PDF. +pdfjs-missing-file-error = Chybí soubor PDF. +pdfjs-unexpected-response-error = Neočekávaná odpověď serveru. +pdfjs-rendering-error = Při vykreslování stránky nastala chyba. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anotace typu { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Pro otevření PDF souboru vložte heslo. +pdfjs-password-invalid = Neplatné heslo. Zkuste to znovu. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Zrušit +pdfjs-web-fonts-disabled = Webová písma jsou zakázána, proto není možné použít vložená písma PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Text +pdfjs-editor-free-text-button-label = Text +pdfjs-editor-ink-button = + .title = Kreslení +pdfjs-editor-ink-button-label = Kreslení +pdfjs-editor-stamp-button = + .title = Přidání či úprava obrázků +pdfjs-editor-stamp-button-label = Přidání či úprava obrázků +pdfjs-editor-highlight-button = + .title = Zvýraznění +pdfjs-editor-highlight-button-label = Zvýraznění +pdfjs-highlight-floating-button1 = + .title = Zvýraznit + .aria-label = Zvýraznit +pdfjs-highlight-floating-button-label = Zvýraznit + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Odebrat kresbu +pdfjs-editor-remove-freetext-button = + .title = Odebrat text +pdfjs-editor-remove-stamp-button = + .title = Odebrat obrázek +pdfjs-editor-remove-highlight-button = + .title = Odebrat zvýraznění + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Barva +pdfjs-editor-free-text-size-input = Velikost +pdfjs-editor-ink-color-input = Barva +pdfjs-editor-ink-thickness-input = Tloušťka +pdfjs-editor-ink-opacity-input = Průhlednost +pdfjs-editor-stamp-add-image-button = + .title = Přidat obrázek +pdfjs-editor-stamp-add-image-button-label = Přidat obrázek +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Tloušťka +pdfjs-editor-free-highlight-thickness-title = + .title = Změna tloušťky při zvýrazňování jiných položek než textu +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Textový editor + .default-content = Začněte psát... +pdfjs-free-text = + .aria-label = Textový editor +pdfjs-free-text-default-content = Začněte psát… +pdfjs-ink = + .aria-label = Editor kreslení +pdfjs-ink-canvas = + .aria-label = Uživatelem vytvořený obrázek + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Náhradní popis +pdfjs-editor-alt-text-edit-button = + .aria-label = Upravit alternativní text +pdfjs-editor-alt-text-edit-button-label = Upravit náhradní popis +pdfjs-editor-alt-text-dialog-label = Vyberte možnost +pdfjs-editor-alt-text-dialog-description = Náhradní popis pomáhá, když lidé obrázek nevidí nebo když se nenačítá. +pdfjs-editor-alt-text-add-description-label = Přidat popis +pdfjs-editor-alt-text-add-description-description = Snažte se o 1-2 věty, které popisují předmět, prostředí nebo činnosti. +pdfjs-editor-alt-text-mark-decorative-label = Označit jako dekorativní +pdfjs-editor-alt-text-mark-decorative-description = Používá se pro okrasné obrázky, jako jsou rámečky nebo vodoznaky. +pdfjs-editor-alt-text-cancel-button = Zrušit +pdfjs-editor-alt-text-save-button = Uložit +pdfjs-editor-alt-text-decorative-tooltip = Označen jako dekorativní +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Například: “Mladý muž si sedá ke stolu, aby se najedl.” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternativní text + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Levý horní roh — změna velikosti +pdfjs-editor-resizer-label-top-middle = Horní střed — změna velikosti +pdfjs-editor-resizer-label-top-right = Pravý horní roh — změna velikosti +pdfjs-editor-resizer-label-middle-right = Vpravo uprostřed — změna velikosti +pdfjs-editor-resizer-label-bottom-right = Pravý dolní roh — změna velikosti +pdfjs-editor-resizer-label-bottom-middle = Střed dole — změna velikosti +pdfjs-editor-resizer-label-bottom-left = Levý dolní roh — změna velikosti +pdfjs-editor-resizer-label-middle-left = Vlevo uprostřed — změna velikosti +pdfjs-editor-resizer-top-left = + .aria-label = Levý horní roh — změna velikosti +pdfjs-editor-resizer-top-middle = + .aria-label = Horní střed — změna velikosti +pdfjs-editor-resizer-top-right = + .aria-label = Pravý horní roh — změna velikosti +pdfjs-editor-resizer-middle-right = + .aria-label = Vpravo uprostřed — změna velikosti +pdfjs-editor-resizer-bottom-right = + .aria-label = Pravý dolní roh — změna velikosti +pdfjs-editor-resizer-bottom-middle = + .aria-label = Střed dole — změna velikosti +pdfjs-editor-resizer-bottom-left = + .aria-label = Levý dolní roh — změna velikosti +pdfjs-editor-resizer-middle-left = + .aria-label = Vlevo uprostřed — změna velikosti + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Barva zvýraznění +pdfjs-editor-colorpicker-button = + .title = Změna barvy +pdfjs-editor-colorpicker-dropdown = + .aria-label = Výběr barev +pdfjs-editor-colorpicker-yellow = + .title = Žlutá +pdfjs-editor-colorpicker-green = + .title = Zelená +pdfjs-editor-colorpicker-blue = + .title = Modrá +pdfjs-editor-colorpicker-pink = + .title = Růžová +pdfjs-editor-colorpicker-red = + .title = Červená + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Zobrazit vše +pdfjs-editor-highlight-show-all-button = + .title = Zobrazit vše + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Upravit alternativní text (popis obrázku) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Přidat alternativní text (popis obrázku) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Sem napište svůj popis… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Krátký popis pro lidi, kteří neuvidí obrázek nebo když se obrázek nenačítá. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Tento alternativní text byl vytvořen automaticky a může být nepřesný. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Více informací +pdfjs-editor-new-alt-text-create-automatically-button-label = Vytvořit alternativní text automaticky +pdfjs-editor-new-alt-text-not-now-button = Teď ne +pdfjs-editor-new-alt-text-error-title = Nepodařilo se automaticky vytvořit alternativní text +pdfjs-editor-new-alt-text-error-description = Napište prosím vlastní alternativní text nebo to zkuste znovu později. +pdfjs-editor-new-alt-text-error-close-button = Zavřít +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Stahuje se model AI pro alternativní texty ({ $downloadedSize } z { $totalSize } MB) + .aria-valuetext = Stahuje se model AI pro alternativní texty ({ $downloadedSize } z { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternativní text byl přidán +pdfjs-editor-new-alt-text-added-button-label = Alternativní text byl přidán +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Chybí alternativní text +pdfjs-editor-new-alt-text-missing-button-label = Chybí alternativní text +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Zkontrolovat alternativní text +pdfjs-editor-new-alt-text-to-review-button-label = Zkontrolovat alternativní text +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Vytvořeno automaticky: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Nastavení alternativního textu obrázku +pdfjs-image-alt-text-settings-button-label = Nastavení alternativního textu obrázku +pdfjs-editor-alt-text-settings-dialog-label = Nastavení alternativního textu obrázku +pdfjs-editor-alt-text-settings-automatic-title = Automatický alternativní text +pdfjs-editor-alt-text-settings-create-model-button-label = Vytvořit alternativní text automaticky +pdfjs-editor-alt-text-settings-create-model-description = Navrhuje popisy, které pomohou lidem, kteří nevidí obrázek nebo když se obrázek nenačte. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Model AI pro alternativní text ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Běží lokálně na vašem zařízení, takže vaše data zůstávají v bezpečí. Vyžadováno pro automatický alternativní text. +pdfjs-editor-alt-text-settings-delete-model-button = Smazat +pdfjs-editor-alt-text-settings-download-model-button = Stáhnout +pdfjs-editor-alt-text-settings-downloading-model-button = Probíhá stahování... +pdfjs-editor-alt-text-settings-editor-title = Editor alternativního textu +pdfjs-editor-alt-text-settings-show-dialog-button-label = Při přidávání obrázku hned zobrazit editor alternativního textu +pdfjs-editor-alt-text-settings-show-dialog-description = Pomůže vám zajistit, aby všechny vaše obrázky obsahovaly alternativní text. +pdfjs-editor-alt-text-settings-close-button = Zavřít + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Zvýraznění odebráno +pdfjs-editor-undo-bar-message-freetext = Text odstraněn +pdfjs-editor-undo-bar-message-ink = Kresba odstraněna +pdfjs-editor-undo-bar-message-stamp = Obrázek odebrán +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotace odebrána + [few] { $count } anotace odebrány + [many] { $count } anotací odebráno + *[other] { $count } anotací odebráno + } +pdfjs-editor-undo-bar-undo-button = + .title = Zpět +pdfjs-editor-undo-bar-undo-button-label = Zpět +pdfjs-editor-undo-bar-close-button = + .title = Zavřít +pdfjs-editor-undo-bar-close-button-label = Zavřít diff --git a/public/pdfjs/web/locale/cy/viewer.ftl b/public/pdfjs/web/locale/cy/viewer.ftl new file mode 100644 index 0000000..8363835 --- /dev/null +++ b/public/pdfjs/web/locale/cy/viewer.ftl @@ -0,0 +1,527 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Tudalen Flaenorol +pdfjs-previous-button-label = Blaenorol +pdfjs-next-button = + .title = Tudalen Nesaf +pdfjs-next-button-label = Nesaf +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Tudalen +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = o { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } o { $pagesCount }) +pdfjs-zoom-out-button = + .title = Lleihau +pdfjs-zoom-out-button-label = Lleihau +pdfjs-zoom-in-button = + .title = Cynyddu +pdfjs-zoom-in-button-label = Cynyddu +pdfjs-zoom-select = + .title = Chwyddo +pdfjs-presentation-mode-button = + .title = Newid i'r Modd Cyflwyno +pdfjs-presentation-mode-button-label = Modd Cyflwyno +pdfjs-open-file-button = + .title = Agor Ffeil +pdfjs-open-file-button-label = Agor +pdfjs-print-button = + .title = Argraffu +pdfjs-print-button-label = Argraffu +pdfjs-save-button = + .title = Cadw +pdfjs-save-button-label = Cadw +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Llwytho i lawr +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Llwytho i lawr +pdfjs-bookmark-button = + .title = Tudalen Gyfredol (Gweld URL o'r Dudalen Gyfredol) +pdfjs-bookmark-button-label = Tudalen Gyfredol + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Offer +pdfjs-tools-button-label = Offer +pdfjs-first-page-button = + .title = Mynd i'r Dudalen Gyntaf +pdfjs-first-page-button-label = Mynd i'r Dudalen Gyntaf +pdfjs-last-page-button = + .title = Mynd i'r Dudalen Olaf +pdfjs-last-page-button-label = Mynd i'r Dudalen Olaf +pdfjs-page-rotate-cw-button = + .title = Cylchdroi Clocwedd +pdfjs-page-rotate-cw-button-label = Cylchdroi Clocwedd +pdfjs-page-rotate-ccw-button = + .title = Cylchdroi Gwrthglocwedd +pdfjs-page-rotate-ccw-button-label = Cylchdroi Gwrthglocwedd +pdfjs-cursor-text-select-tool-button = + .title = Galluogi Dewis Offeryn Testun +pdfjs-cursor-text-select-tool-button-label = Offeryn Dewis Testun +pdfjs-cursor-hand-tool-button = + .title = Galluogi Offeryn Llaw +pdfjs-cursor-hand-tool-button-label = Offeryn Llaw +pdfjs-scroll-page-button = + .title = Defnyddio Sgrolio Tudalen +pdfjs-scroll-page-button-label = Sgrolio Tudalen +pdfjs-scroll-vertical-button = + .title = Defnyddio Sgrolio Fertigol +pdfjs-scroll-vertical-button-label = Sgrolio Fertigol +pdfjs-scroll-horizontal-button = + .title = Defnyddio Sgrolio Llorweddol +pdfjs-scroll-horizontal-button-label = Sgrolio Llorweddol +pdfjs-scroll-wrapped-button = + .title = Defnyddio Sgrolio Amlapio +pdfjs-scroll-wrapped-button-label = Sgrolio Amlapio +pdfjs-spread-none-button = + .title = Peidio uno trawsdaleniadau +pdfjs-spread-none-button-label = Dim Trawsdaleniadau +pdfjs-spread-odd-button = + .title = Uno trawsdaleniadau gan gychwyn gyda thudalennau odrif +pdfjs-spread-odd-button-label = Trawsdaleniadau Odrif +pdfjs-spread-even-button = + .title = Uno trawsdaleniadau gan gychwyn gyda thudalennau eilrif +pdfjs-spread-even-button-label = Trawsdaleniadau Eilrif + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Priodweddau Dogfen… +pdfjs-document-properties-button-label = Priodweddau Dogfen… +pdfjs-document-properties-file-name = Enw ffeil: +pdfjs-document-properties-file-size = Maint ffeil: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } beit) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } beit) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } beit) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } beit) +pdfjs-document-properties-title = Teitl: +pdfjs-document-properties-author = Awdur: +pdfjs-document-properties-subject = Pwnc: +pdfjs-document-properties-keywords = Allweddair: +pdfjs-document-properties-creation-date = Dyddiad Creu: +pdfjs-document-properties-modification-date = Dyddiad Addasu: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Crewr: +pdfjs-document-properties-producer = Cynhyrchydd PDF: +pdfjs-document-properties-version = Fersiwn PDF: +pdfjs-document-properties-page-count = Cyfrif Tudalen: +pdfjs-document-properties-page-size = Maint Tudalen: +pdfjs-document-properties-page-size-unit-inches = o fewn +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = portread +pdfjs-document-properties-page-size-orientation-landscape = tirlun +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Llythyr +pdfjs-document-properties-page-size-name-legal = Cyfreithiol + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Golwg Gwe Cyflym: +pdfjs-document-properties-linearized-yes = Iawn +pdfjs-document-properties-linearized-no = Na +pdfjs-document-properties-close-button = Cau + +## Print + +pdfjs-print-progress-message = Paratoi dogfen ar gyfer ei hargraffu… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Diddymu +pdfjs-printing-not-supported = Rhybudd: Nid yw argraffu yn cael ei gynnal yn llawn gan y porwr. +pdfjs-printing-not-ready = Rhybudd: Nid yw'r PDF wedi ei lwytho'n llawn ar gyfer argraffu. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Toglo'r Bar Ochr +pdfjs-toggle-sidebar-notification-button = + .title = Toglo'r Bar Ochr (mae'r ddogfen yn cynnwys amlinelliadau/atodiadau/haenau) +pdfjs-toggle-sidebar-button-label = Toglo'r Bar Ochr +pdfjs-document-outline-button = + .title = Dangos Amlinell Dogfen (clic dwbl i ymestyn/cau pob eitem) +pdfjs-document-outline-button-label = Amlinelliad Dogfen +pdfjs-attachments-button = + .title = Dangos Atodiadau +pdfjs-attachments-button-label = Atodiadau +pdfjs-layers-button = + .title = Dangos Haenau (cliciwch ddwywaith i ailosod yr holl haenau i'r cyflwr rhagosodedig) +pdfjs-layers-button-label = Haenau +pdfjs-thumbs-button = + .title = Dangos Lluniau Bach +pdfjs-thumbs-button-label = Lluniau Bach +pdfjs-current-outline-item-button = + .title = Canfod yr Eitem Amlinellol Gyfredol +pdfjs-current-outline-item-button-label = Yr Eitem Amlinellol Gyfredol +pdfjs-findbar-button = + .title = Canfod yn y Ddogfen +pdfjs-findbar-button-label = Canfod +pdfjs-additional-layers = Haenau Ychwanegol + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Tudalen { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Llun Bach Tudalen { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Canfod + .placeholder = Canfod yn y ddogfen… +pdfjs-find-previous-button = + .title = Canfod enghraifft flaenorol o'r ymadrodd +pdfjs-find-previous-button-label = Blaenorol +pdfjs-find-next-button = + .title = Canfod enghraifft nesaf yr ymadrodd +pdfjs-find-next-button-label = Nesaf +pdfjs-find-highlight-checkbox = Amlygu Popeth +pdfjs-find-match-case-checkbox-label = Cydweddu Maint +pdfjs-find-match-diacritics-checkbox-label = Diacritigau Cyfatebol +pdfjs-find-entire-word-checkbox-label = Geiriau Cyfan +pdfjs-find-reached-top = Wedi cyrraedd brig y dudalen, parhau o'r gwaelod +pdfjs-find-reached-bottom = Wedi cyrraedd diwedd y dudalen, parhau o'r brig +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [zero] { $current } o { $total } cydweddiadau + [one] { $current } o { $total } cydweddiad + [two] { $current } o { $total } gydweddiad + [few] { $current } o { $total } cydweddiad + [many] { $current } o { $total } chydweddiad + *[other] { $current } o { $total } cydweddiad + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [zero] Mwy nag { $limit } cydweddiadau + [one] Mwy nag { $limit } cydweddiad + [two] Mwy nag { $limit } gydweddiad + [few] Mwy nag { $limit } cydweddiad + [many] Mwy nag { $limit } chydweddiad + *[other] Mwy nag { $limit } cydweddiad + } +pdfjs-find-not-found = Heb ganfod ymadrodd + +## Predefined zoom values + +pdfjs-page-scale-width = Lled Tudalen +pdfjs-page-scale-fit = Ffit Tudalen +pdfjs-page-scale-auto = Chwyddo Awtomatig +pdfjs-page-scale-actual = Maint Gwirioneddol +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Tudalen { $page } + +## Loading indicator messages + +pdfjs-loading-error = Digwyddodd gwall wrth lwytho'r PDF. +pdfjs-invalid-file-error = Ffeil PDF annilys neu llwgr. +pdfjs-missing-file-error = Ffeil PDF coll. +pdfjs-unexpected-response-error = Ymateb annisgwyl gan y gweinydd. +pdfjs-rendering-error = Digwyddodd gwall wrth adeiladu'r dudalen. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anodiad { $type } ] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Rhowch gyfrinair i agor y PDF. +pdfjs-password-invalid = Cyfrinair annilys. Ceisiwch eto. +pdfjs-password-ok-button = Iawn +pdfjs-password-cancel-button = Diddymu +pdfjs-web-fonts-disabled = Ffontiau gwe wedi eu hanalluogi: methu defnyddio ffontiau PDF mewnblanedig. + +## Editing + +pdfjs-editor-free-text-button = + .title = Testun +pdfjs-editor-free-text-button-label = Testun +pdfjs-editor-ink-button = + .title = Lluniadu +pdfjs-editor-ink-button-label = Lluniadu +pdfjs-editor-stamp-button = + .title = Ychwanegu neu olygu delweddau +pdfjs-editor-stamp-button-label = Ychwanegu neu olygu delweddau +pdfjs-editor-highlight-button = + .title = Amlygu +pdfjs-editor-highlight-button-label = Amlygu +pdfjs-highlight-floating-button1 = + .title = Amlygu + .aria-label = Amlygu +pdfjs-highlight-floating-button-label = Amlygu + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Dileu lluniad +pdfjs-editor-remove-freetext-button = + .title = Dileu testun +pdfjs-editor-remove-stamp-button = + .title = Dileu delwedd +pdfjs-editor-remove-highlight-button = + .title = Tynnu amlygiad + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Lliw +pdfjs-editor-free-text-size-input = Maint +pdfjs-editor-ink-color-input = Lliw +pdfjs-editor-ink-thickness-input = Trwch +pdfjs-editor-ink-opacity-input = Didreiddedd +pdfjs-editor-stamp-add-image-button = + .title = Ychwanegu delwedd +pdfjs-editor-stamp-add-image-button-label = Ychwanegu delwedd +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Trwch +pdfjs-editor-free-highlight-thickness-title = + .title = Newid trwch wrth amlygu eitemau heblaw testun +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Golygydd Testun + .default-content = Cychwyn teipio… +pdfjs-free-text = + .aria-label = Golygydd Testun +pdfjs-free-text-default-content = Cychwyn teipio… +pdfjs-ink = + .aria-label = Golygydd Lluniadu +pdfjs-ink-canvas = + .aria-label = Delwedd wedi'i chreu gan ddefnyddwyr + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Testun amgen (alt) +pdfjs-editor-alt-text-edit-button = + .aria-label = Golygu testun amgen +pdfjs-editor-alt-text-edit-button-label = Golygu testun amgen +pdfjs-editor-alt-text-dialog-label = Dewisiadau +pdfjs-editor-alt-text-dialog-description = Mae testun amgen (testun alt) yn helpu pan na all pobl weld y ddelwedd neu pan nad yw'n llwytho. +pdfjs-editor-alt-text-add-description-label = Ychwanegu disgrifiad +pdfjs-editor-alt-text-add-description-description = Anelwch at 1-2 frawddeg sy'n disgrifio'r pwnc, y cefndir neu'r gweithredoedd. +pdfjs-editor-alt-text-mark-decorative-label = Marcio fel addurniadol +pdfjs-editor-alt-text-mark-decorative-description = Mae'n cael ei ddefnyddio ar gyfer delweddau addurniadol, fel borderi neu farciau dŵr. +pdfjs-editor-alt-text-cancel-button = Diddymu +pdfjs-editor-alt-text-save-button = Cadw +pdfjs-editor-alt-text-decorative-tooltip = Marcio fel addurniadol +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Er enghraifft, “Mae dyn ifanc yn eistedd wrth fwrdd i fwyta pryd bwyd” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Testun amgen (alt) + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Y gornel chwith uchaf — newid maint +pdfjs-editor-resizer-label-top-middle = Canol uchaf - newid maint +pdfjs-editor-resizer-label-top-right = Y gornel dde uchaf - newid maint +pdfjs-editor-resizer-label-middle-right = De canol - newid maint +pdfjs-editor-resizer-label-bottom-right = Y gornel dde isaf — newid maint +pdfjs-editor-resizer-label-bottom-middle = Canol gwaelod — newid maint +pdfjs-editor-resizer-label-bottom-left = Y gornel chwith isaf — newid maint +pdfjs-editor-resizer-label-middle-left = Chwith canol — newid maint +pdfjs-editor-resizer-top-left = + .aria-label = Y gornel chwith uchaf — newid maint +pdfjs-editor-resizer-top-middle = + .aria-label = Canol uchaf - newid maint +pdfjs-editor-resizer-top-right = + .aria-label = Y gornel dde uchaf - newid maint +pdfjs-editor-resizer-middle-right = + .aria-label = De canol - newid maint +pdfjs-editor-resizer-bottom-right = + .aria-label = Y gornel dde isaf — newid maint +pdfjs-editor-resizer-bottom-middle = + .aria-label = Canol gwaelod — newid maint +pdfjs-editor-resizer-bottom-left = + .aria-label = Y gornel chwith isaf — newid maint +pdfjs-editor-resizer-middle-left = + .aria-label = Chwith canol — newid maint + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Lliw amlygu +pdfjs-editor-colorpicker-button = + .title = Newid lliw +pdfjs-editor-colorpicker-dropdown = + .aria-label = Dewisiadau lliw +pdfjs-editor-colorpicker-yellow = + .title = Melyn +pdfjs-editor-colorpicker-green = + .title = Gwyrdd +pdfjs-editor-colorpicker-blue = + .title = Glas +pdfjs-editor-colorpicker-pink = + .title = Pinc +pdfjs-editor-colorpicker-red = + .title = Coch + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Dangos y cyfan +pdfjs-editor-highlight-show-all-button = + .title = Dangos y cyfan + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Golygu testun amgen (disgrifiad o ddelwedd) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Ychwanegwch destun amgen (disgrifiad delwedd) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Ysgrifennwch eich disgrifiad yma… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Disgrifiad byr ar gyfer pobl sydd ddim yn gallu gweld y ddelwedd neu pan nad yw'r ddelwedd yn llwytho. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Cafodd y testun amgen hwn ei greu'n awtomatig a gall fod yn anghywir. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Rhagor +pdfjs-editor-new-alt-text-create-automatically-button-label = Creu testun amgen yn awtomatig +pdfjs-editor-new-alt-text-not-now-button = Nid nawr +pdfjs-editor-new-alt-text-error-title = Methu â chreu testun amgen yn awtomatig +pdfjs-editor-new-alt-text-error-description = Ysgrifennwch eich testun amgen eich hun neu ceisiwch eto yn nes ymlaen. +pdfjs-editor-new-alt-text-error-close-button = Cau +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Wrthi'n llwytho i lawr model AI testun amgen ( { $downloadedSize } o { $totalSize } MB) + .aria-valuetext = Wrthi'n llwytho i lawr model AI testun amgen ( { $downloadedSize } o { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Ychwanegwyd testun amgen +pdfjs-editor-new-alt-text-added-button-label = Ychwanegwyd testun amgen +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Testun amgen coll +pdfjs-editor-new-alt-text-missing-button-label = Testun amgen coll +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Adolygu'r testun amgen +pdfjs-editor-new-alt-text-to-review-button-label = Adolygu'r testun amgen +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Crëwyd yn awtomatig: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Gosodiadau testun amgen delwedd +pdfjs-image-alt-text-settings-button-label = Gosodiadau testun amgen delwedd +pdfjs-editor-alt-text-settings-dialog-label = Gosodiadau testun amgen delwedd +pdfjs-editor-alt-text-settings-automatic-title = Testun amgen awtomatig +pdfjs-editor-alt-text-settings-create-model-button-label = Creu testun amgen yn awtomatig +pdfjs-editor-alt-text-settings-create-model-description = Yn awgrymu disgrifiadau i helpu pobl sydd ddim yn gallu gweld y ddelwedd neu pan nad yw'r ddelwedd yn llwytho. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Model AI testun amgen ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Yn rhedeg yn lleol ar eich dyfais fel bod eich data'n aros yn breifat. Yn ofynnol ar gyfer testun amgen awtomatig. +pdfjs-editor-alt-text-settings-delete-model-button = Dileu +pdfjs-editor-alt-text-settings-download-model-button = Llwytho i Lawr +pdfjs-editor-alt-text-settings-downloading-model-button = Wrthi'n llwytho i lawr… +pdfjs-editor-alt-text-settings-editor-title = Golygydd testun amgen +pdfjs-editor-alt-text-settings-show-dialog-button-label = Dangoswch y golygydd testun amgen yn syth wrth ychwanegu delwedd +pdfjs-editor-alt-text-settings-show-dialog-description = Yn eich helpu i wneud yn siŵr bod gan eich holl ddelweddau destun amgen. +pdfjs-editor-alt-text-settings-close-button = Cau + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Tynnwyd yr amlygu +pdfjs-editor-undo-bar-message-freetext = Tynnwyd y testun +pdfjs-editor-undo-bar-message-ink = Tynnwyd y lluniad +pdfjs-editor-undo-bar-message-stamp = Tynnwyd y ddelwedd +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [zero] { $count } anodiad wedi'u tynnu + [one] { $count } anodiad wedi'i dynnu + [two] { $count } anodiad wedi'u tynnu + [few] { $count } anodiad wedi'u tynnu + [many] { $count } anodiad wedi'u tynnu + *[other] { $count } anodiad wedi'u tynnu + } +pdfjs-editor-undo-bar-undo-button = + .title = Dadwneud +pdfjs-editor-undo-bar-undo-button-label = Dadwneud +pdfjs-editor-undo-bar-close-button = + .title = Cau +pdfjs-editor-undo-bar-close-button-label = Cau diff --git a/public/pdfjs/web/locale/da/viewer.ftl b/public/pdfjs/web/locale/da/viewer.ftl new file mode 100644 index 0000000..224eac1 --- /dev/null +++ b/public/pdfjs/web/locale/da/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Forrige side +pdfjs-previous-button-label = Forrige +pdfjs-next-button = + .title = Næste side +pdfjs-next-button-label = Næste +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Side +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = af { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } af { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zoom ud +pdfjs-zoom-out-button-label = Zoom ud +pdfjs-zoom-in-button = + .title = Zoom ind +pdfjs-zoom-in-button-label = Zoom ind +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Skift til fuldskærmsvisning +pdfjs-presentation-mode-button-label = Fuldskærmsvisning +pdfjs-open-file-button = + .title = Åbn fil +pdfjs-open-file-button-label = Åbn +pdfjs-print-button = + .title = Udskriv +pdfjs-print-button-label = Udskriv +pdfjs-save-button = + .title = Gem +pdfjs-save-button-label = Gem +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Hent +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Hent +pdfjs-bookmark-button = + .title = Aktuel side (vis URL fra den aktuelle side) +pdfjs-bookmark-button-label = Aktuel side + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Funktioner +pdfjs-tools-button-label = Funktioner +pdfjs-first-page-button = + .title = Gå til første side +pdfjs-first-page-button-label = Gå til første side +pdfjs-last-page-button = + .title = Gå til sidste side +pdfjs-last-page-button-label = Gå til sidste side +pdfjs-page-rotate-cw-button = + .title = Roter med uret +pdfjs-page-rotate-cw-button-label = Roter med uret +pdfjs-page-rotate-ccw-button = + .title = Roter mod uret +pdfjs-page-rotate-ccw-button-label = Roter mod uret +pdfjs-cursor-text-select-tool-button = + .title = Aktiver markeringsværktøj +pdfjs-cursor-text-select-tool-button-label = Markeringsværktøj +pdfjs-cursor-hand-tool-button = + .title = Aktiver håndværktøj +pdfjs-cursor-hand-tool-button-label = Håndværktøj +pdfjs-scroll-page-button = + .title = Brug sidescrolling +pdfjs-scroll-page-button-label = Sidescrolling +pdfjs-scroll-vertical-button = + .title = Brug vertikal scrolling +pdfjs-scroll-vertical-button-label = Vertikal scrolling +pdfjs-scroll-horizontal-button = + .title = Brug horisontal scrolling +pdfjs-scroll-horizontal-button-label = Horisontal scrolling +pdfjs-scroll-wrapped-button = + .title = Brug ombrudt scrolling +pdfjs-scroll-wrapped-button-label = Ombrudt scrolling +pdfjs-spread-none-button = + .title = Vis enkeltsider +pdfjs-spread-none-button-label = Enkeltsider +pdfjs-spread-odd-button = + .title = Vis opslag med ulige sidenumre til venstre +pdfjs-spread-odd-button-label = Opslag med forside +pdfjs-spread-even-button = + .title = Vis opslag med lige sidenumre til venstre +pdfjs-spread-even-button-label = Opslag uden forside + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumentegenskaber… +pdfjs-document-properties-button-label = Dokumentegenskaber… +pdfjs-document-properties-file-name = Filnavn: +pdfjs-document-properties-file-size = Filstørrelse: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Titel: +pdfjs-document-properties-author = Forfatter: +pdfjs-document-properties-subject = Emne: +pdfjs-document-properties-keywords = Nøgleord: +pdfjs-document-properties-creation-date = Oprettet: +pdfjs-document-properties-modification-date = Redigeret: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Program: +pdfjs-document-properties-producer = PDF-producent: +pdfjs-document-properties-version = PDF-version: +pdfjs-document-properties-page-count = Antal sider: +pdfjs-document-properties-page-size = Sidestørrelse: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = stående +pdfjs-document-properties-page-size-orientation-landscape = liggende +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Hurtig web-visning: +pdfjs-document-properties-linearized-yes = Ja +pdfjs-document-properties-linearized-no = Nej +pdfjs-document-properties-close-button = Luk + +## Print + +pdfjs-print-progress-message = Forbereder dokument til udskrivning… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Annuller +pdfjs-printing-not-supported = Advarsel: Udskrivning er ikke fuldt understøttet af browseren. +pdfjs-printing-not-ready = Advarsel: PDF-filen er ikke fuldt indlæst til udskrivning. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Slå sidepanel til eller fra +pdfjs-toggle-sidebar-notification-button = + .title = Slå sidepanel til eller fra (dokumentet indeholder disposition/vedhæftede filer/lag) +pdfjs-toggle-sidebar-button-label = Slå sidepanel til eller fra +pdfjs-document-outline-button = + .title = Vis dokumentets disposition (dobbeltklik for at udvide/sammenfolde alle elementer) +pdfjs-document-outline-button-label = Dokument-disposition +pdfjs-attachments-button = + .title = Vis vedhæftede filer +pdfjs-attachments-button-label = Vedhæftede filer +pdfjs-layers-button = + .title = Vis lag (dobbeltklik for at nulstille alle lag til standard-tilstanden) +pdfjs-layers-button-label = Lag +pdfjs-thumbs-button = + .title = Vis miniaturer +pdfjs-thumbs-button-label = Miniaturer +pdfjs-current-outline-item-button = + .title = Find det aktuelle dispositions-element +pdfjs-current-outline-item-button-label = Aktuelt dispositions-element +pdfjs-findbar-button = + .title = Find i dokument +pdfjs-findbar-button-label = Find +pdfjs-additional-layers = Yderligere lag + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Side { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniature af side { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Find + .placeholder = Find i dokument… +pdfjs-find-previous-button = + .title = Find den forrige forekomst +pdfjs-find-previous-button-label = Forrige +pdfjs-find-next-button = + .title = Find den næste forekomst +pdfjs-find-next-button-label = Næste +pdfjs-find-highlight-checkbox = Fremhæv alle +pdfjs-find-match-case-checkbox-label = Forskel på store og små bogstaver +pdfjs-find-match-diacritics-checkbox-label = Diakritiske tegn +pdfjs-find-entire-word-checkbox-label = Hele ord +pdfjs-find-reached-top = Toppen af siden blev nået, fortsatte fra bunden +pdfjs-find-reached-bottom = Bunden af siden blev nået, fortsatte fra toppen +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } af { $total } forekomst + *[other] { $current } af { $total } forekomster + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Mere end { $limit } forekomst + *[other] Mere end { $limit } forekomster + } +pdfjs-find-not-found = Der blev ikke fundet noget + +## Predefined zoom values + +pdfjs-page-scale-width = Sidebredde +pdfjs-page-scale-fit = Tilpas til side +pdfjs-page-scale-auto = Automatisk zoom +pdfjs-page-scale-actual = Faktisk størrelse +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Side { $page } + +## Loading indicator messages + +pdfjs-loading-error = Der opstod en fejl ved indlæsning af PDF-filen. +pdfjs-invalid-file-error = PDF-filen er ugyldig eller ødelagt. +pdfjs-missing-file-error = Manglende PDF-fil. +pdfjs-unexpected-response-error = Uventet svar fra serveren. +pdfjs-rendering-error = Der opstod en fejl ved generering af siden. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type }kommentar] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Angiv adgangskode til at åbne denne PDF-fil. +pdfjs-password-invalid = Ugyldig adgangskode. Prøv igen. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Fortryd +pdfjs-web-fonts-disabled = Webskrifttyper er deaktiverede. De indlejrede skrifttyper i PDF-filen kan ikke anvendes. + +## Editing + +pdfjs-editor-free-text-button = + .title = Tekst +pdfjs-editor-free-text-button-label = Tekst +pdfjs-editor-ink-button = + .title = Tegn +pdfjs-editor-ink-button-label = Tegn +pdfjs-editor-stamp-button = + .title = Tilføj eller rediger billeder +pdfjs-editor-stamp-button-label = Tilføj eller rediger billeder +pdfjs-editor-highlight-button = + .title = Fremhæv +pdfjs-editor-highlight-button-label = Fremhæv +pdfjs-highlight-floating-button1 = + .title = Fremhæv + .aria-label = Fremhæv +pdfjs-highlight-floating-button-label = Fremhæv + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Fjern tegning +pdfjs-editor-remove-freetext-button = + .title = Fjern tekst +pdfjs-editor-remove-stamp-button = + .title = Fjern billede +pdfjs-editor-remove-highlight-button = + .title = Fjern fremhævning + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Farve +pdfjs-editor-free-text-size-input = Størrelse +pdfjs-editor-ink-color-input = Farve +pdfjs-editor-ink-thickness-input = Tykkelse +pdfjs-editor-ink-opacity-input = Uigennemsigtighed +pdfjs-editor-stamp-add-image-button = + .title = Tilføj billede +pdfjs-editor-stamp-add-image-button-label = Tilføj billede +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Tykkelse +pdfjs-editor-free-highlight-thickness-title = + .title = Ændr tykkelse, når andre elementer end tekst fremhæves +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Teksteditor + .default-content = Begynd at skrive… +pdfjs-free-text = + .aria-label = Teksteditor +pdfjs-free-text-default-content = Begynd at skrive… +pdfjs-ink = + .aria-label = Tegnings-editor +pdfjs-ink-canvas = + .aria-label = Brugeroprettet billede + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alternativ tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Rediger alternativ tekst +pdfjs-editor-alt-text-edit-button-label = Rediger alternativ tekst +pdfjs-editor-alt-text-dialog-label = Vælg en indstilling +pdfjs-editor-alt-text-dialog-description = Alternativ tekst hjælper folk, som ikke kan se billedet eller når det ikke indlæses. +pdfjs-editor-alt-text-add-description-label = Tilføj en beskrivelse +pdfjs-editor-alt-text-add-description-description = Sigt efter en eller to sætninger, der beskriver emnet, omgivelserne eller handlinger. +pdfjs-editor-alt-text-mark-decorative-label = Marker som dekorativ +pdfjs-editor-alt-text-mark-decorative-description = Dette bruges for dekorative billeder som rammer eller vandmærker. +pdfjs-editor-alt-text-cancel-button = Annuller +pdfjs-editor-alt-text-save-button = Gem +pdfjs-editor-alt-text-decorative-tooltip = Markeret som dekorativ +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = For eksempel: "En ung mand sætter sig ved et bord for at spise et måltid mad" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternativ tekst + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Øverste venstre hjørne — tilpas størrelse +pdfjs-editor-resizer-label-top-middle = Øverste i midten — tilpas størrelse +pdfjs-editor-resizer-label-top-right = Øverste højre hjørne — tilpas størrelse +pdfjs-editor-resizer-label-middle-right = Midten til højre — tilpas størrelse +pdfjs-editor-resizer-label-bottom-right = Nederste højre hjørne - tilpas størrelse +pdfjs-editor-resizer-label-bottom-middle = Nederst i midten - tilpas størrelse +pdfjs-editor-resizer-label-bottom-left = Nederste venstre hjørne - tilpas størrelse +pdfjs-editor-resizer-label-middle-left = Midten til venstre — tilpas størrelse +pdfjs-editor-resizer-top-left = + .aria-label = Øverste venstre hjørne — tilpas størrelse +pdfjs-editor-resizer-top-middle = + .aria-label = Øverste i midten — tilpas størrelse +pdfjs-editor-resizer-top-right = + .aria-label = Øverste højre hjørne — tilpas størrelse +pdfjs-editor-resizer-middle-right = + .aria-label = Midten til højre — tilpas størrelse +pdfjs-editor-resizer-bottom-right = + .aria-label = Nederste højre hjørne - tilpas størrelse +pdfjs-editor-resizer-bottom-middle = + .aria-label = Nederst i midten - tilpas størrelse +pdfjs-editor-resizer-bottom-left = + .aria-label = Nederste venstre hjørne - tilpas størrelse +pdfjs-editor-resizer-middle-left = + .aria-label = Midten til venstre — tilpas størrelse + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Fremhævningsfarve +pdfjs-editor-colorpicker-button = + .title = Skift farve +pdfjs-editor-colorpicker-dropdown = + .aria-label = Farvevalg +pdfjs-editor-colorpicker-yellow = + .title = Gul +pdfjs-editor-colorpicker-green = + .title = Grøn +pdfjs-editor-colorpicker-blue = + .title = Blå +pdfjs-editor-colorpicker-pink = + .title = Lyserød +pdfjs-editor-colorpicker-red = + .title = Rød + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Vis alle +pdfjs-editor-highlight-show-all-button = + .title = Vis alle + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Rediger alternativ tekst (billedbeskrivelse) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Tilføj alternativ tekst (billedbeskrivelse) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Skriv din beskrivelse her... +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Kort beskrivelse til personer, der ikke kan se billedet, eller når billedet ikke indlæses. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Denne alternative tekst blev oprettet automatisk og kan være upræcis. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Læs mere +pdfjs-editor-new-alt-text-create-automatically-button-label = Opret alternativ tekst automatisk +pdfjs-editor-new-alt-text-not-now-button = Ikke nu +pdfjs-editor-new-alt-text-error-title = Kunne ikke oprette alternativ tekst automatisk +pdfjs-editor-new-alt-text-error-description = Skriv din egen alternative tekst, eller prøv igen senere. +pdfjs-editor-new-alt-text-error-close-button = Luk +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Henter alternativ tekst AI-model ({ $downloadedSize } af { $totalSize } MB) + .aria-valuetext = Henter alternativ tekst AI-model ({ $downloadedSize } af { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternativ tekst tilføjet +pdfjs-editor-new-alt-text-added-button-label = Alternativ tekst tilføjet +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Mangler alternativ tekst +pdfjs-editor-new-alt-text-missing-button-label = Mangler alternativ tekst +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Gennemgå alternativ tekst +pdfjs-editor-new-alt-text-to-review-button-label = Gennemgå alternativ tekst +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Oprettet automatisk: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Indstillinger for alternativ tekst til billeder +pdfjs-image-alt-text-settings-button-label = Indstillinger for alternativ tekst til billeder +pdfjs-editor-alt-text-settings-dialog-label = Indstillinger for alternativ tekst til billeder +pdfjs-editor-alt-text-settings-automatic-title = Automatisk alternativ tekst +pdfjs-editor-alt-text-settings-create-model-button-label = Opret alternativ tekst automatisk +pdfjs-editor-alt-text-settings-create-model-description = Foreslår beskrivelser for at hjælpe folk, der ikke kan se billedet, eller når billedet ikke indlæses. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = AI-model til at oprette alternative tekster ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Kører lokalt på din enhed, så dine data forbliver private. Påkrævet for at anvende automatisk alternativ tekst. +pdfjs-editor-alt-text-settings-delete-model-button = Slet +pdfjs-editor-alt-text-settings-download-model-button = Hent +pdfjs-editor-alt-text-settings-downloading-model-button = Henter… +pdfjs-editor-alt-text-settings-editor-title = Redigering af alternativ tekst +pdfjs-editor-alt-text-settings-show-dialog-button-label = Vis redigering af alternativ tekst med det samme, når et billede tilføjes +pdfjs-editor-alt-text-settings-show-dialog-description = Hjælper dig med at sikre, at alle dine billeder har alternativ tekst. +pdfjs-editor-alt-text-settings-close-button = Luk + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Fremhævning fjernet +pdfjs-editor-undo-bar-message-freetext = Tekst fjernet +pdfjs-editor-undo-bar-message-ink = Tegning fjernet +pdfjs-editor-undo-bar-message-stamp = Billede fjernet +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } kommentar fjernet + *[other] { $count } kommentarer fjernet + } +pdfjs-editor-undo-bar-undo-button = + .title = Fortryd +pdfjs-editor-undo-bar-undo-button-label = Fortryd +pdfjs-editor-undo-bar-close-button = + .title = Luk +pdfjs-editor-undo-bar-close-button-label = Luk diff --git a/public/pdfjs/web/locale/de/viewer.ftl b/public/pdfjs/web/locale/de/viewer.ftl new file mode 100644 index 0000000..ee26455 --- /dev/null +++ b/public/pdfjs/web/locale/de/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Eine Seite zurück +pdfjs-previous-button-label = Zurück +pdfjs-next-button = + .title = Eine Seite vor +pdfjs-next-button-label = Vor +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Seite +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = von { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } von { $pagesCount }) +pdfjs-zoom-out-button = + .title = Verkleinern +pdfjs-zoom-out-button-label = Verkleinern +pdfjs-zoom-in-button = + .title = Vergrößern +pdfjs-zoom-in-button-label = Vergrößern +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = In Präsentationsmodus wechseln +pdfjs-presentation-mode-button-label = Präsentationsmodus +pdfjs-open-file-button = + .title = Datei öffnen +pdfjs-open-file-button-label = Öffnen +pdfjs-print-button = + .title = Drucken +pdfjs-print-button-label = Drucken +pdfjs-save-button = + .title = Speichern +pdfjs-save-button-label = Speichern +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Herunterladen +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Herunterladen +pdfjs-bookmark-button = + .title = Aktuelle Seite (URL von aktueller Seite anzeigen) +pdfjs-bookmark-button-label = Aktuelle Seite + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Werkzeuge +pdfjs-tools-button-label = Werkzeuge +pdfjs-first-page-button = + .title = Erste Seite anzeigen +pdfjs-first-page-button-label = Erste Seite anzeigen +pdfjs-last-page-button = + .title = Letzte Seite anzeigen +pdfjs-last-page-button-label = Letzte Seite anzeigen +pdfjs-page-rotate-cw-button = + .title = Im Uhrzeigersinn drehen +pdfjs-page-rotate-cw-button-label = Im Uhrzeigersinn drehen +pdfjs-page-rotate-ccw-button = + .title = Gegen Uhrzeigersinn drehen +pdfjs-page-rotate-ccw-button-label = Gegen Uhrzeigersinn drehen +pdfjs-cursor-text-select-tool-button = + .title = Textauswahl-Werkzeug aktivieren +pdfjs-cursor-text-select-tool-button-label = Textauswahl-Werkzeug +pdfjs-cursor-hand-tool-button = + .title = Hand-Werkzeug aktivieren +pdfjs-cursor-hand-tool-button-label = Hand-Werkzeug +pdfjs-scroll-page-button = + .title = Seiten einzeln anordnen +pdfjs-scroll-page-button-label = Einzelseitenanordnung +pdfjs-scroll-vertical-button = + .title = Seiten übereinander anordnen +pdfjs-scroll-vertical-button-label = Vertikale Seitenanordnung +pdfjs-scroll-horizontal-button = + .title = Seiten nebeneinander anordnen +pdfjs-scroll-horizontal-button-label = Horizontale Seitenanordnung +pdfjs-scroll-wrapped-button = + .title = Seiten neben- und übereinander anordnen, abhängig vom Platz +pdfjs-scroll-wrapped-button-label = Kombinierte Seitenanordnung +pdfjs-spread-none-button = + .title = Seiten nicht nebeneinander anzeigen +pdfjs-spread-none-button-label = Einzelne Seiten +pdfjs-spread-odd-button = + .title = Jeweils eine ungerade und eine gerade Seite nebeneinander anzeigen +pdfjs-spread-odd-button-label = Ungerade + gerade Seite +pdfjs-spread-even-button = + .title = Jeweils eine gerade und eine ungerade Seite nebeneinander anzeigen +pdfjs-spread-even-button-label = Gerade + ungerade Seite + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumenteigenschaften +pdfjs-document-properties-button-label = Dokumenteigenschaften… +pdfjs-document-properties-file-name = Dateiname: +pdfjs-document-properties-file-size = Dateigröße: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } Bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } Bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } Bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } Bytes) +pdfjs-document-properties-title = Titel: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Thema: +pdfjs-document-properties-keywords = Stichwörter: +pdfjs-document-properties-creation-date = Erstelldatum: +pdfjs-document-properties-modification-date = Bearbeitungsdatum: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date } { $time } +pdfjs-document-properties-creator = Anwendung: +pdfjs-document-properties-producer = PDF erstellt mit: +pdfjs-document-properties-version = PDF-Version: +pdfjs-document-properties-page-count = Seitenzahl: +pdfjs-document-properties-page-size = Seitengröße: +pdfjs-document-properties-page-size-unit-inches = Zoll +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = Hochformat +pdfjs-document-properties-page-size-orientation-landscape = Querformat +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Schnelle Webanzeige: +pdfjs-document-properties-linearized-yes = Ja +pdfjs-document-properties-linearized-no = Nein +pdfjs-document-properties-close-button = Schließen + +## Print + +pdfjs-print-progress-message = Dokument wird für Drucken vorbereitet… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress } % +pdfjs-print-progress-close-button = Abbrechen +pdfjs-printing-not-supported = Warnung: Die Drucken-Funktion wird durch diesen Browser nicht vollständig unterstützt. +pdfjs-printing-not-ready = Warnung: Die PDF-Datei ist nicht vollständig geladen, dies ist für das Drucken aber empfohlen. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Sidebar umschalten +pdfjs-toggle-sidebar-notification-button = + .title = Sidebar umschalten (Dokument enthält Dokumentstruktur/Anhänge/Ebenen) +pdfjs-toggle-sidebar-button-label = Sidebar umschalten +pdfjs-document-outline-button = + .title = Dokumentstruktur anzeigen (Doppelklicken, um alle Einträge aus- bzw. einzuklappen) +pdfjs-document-outline-button-label = Dokumentstruktur +pdfjs-attachments-button = + .title = Anhänge anzeigen +pdfjs-attachments-button-label = Anhänge +pdfjs-layers-button = + .title = Ebenen anzeigen (Doppelklicken, um alle Ebenen auf den Standardzustand zurückzusetzen) +pdfjs-layers-button-label = Ebenen +pdfjs-thumbs-button = + .title = Miniaturansichten anzeigen +pdfjs-thumbs-button-label = Miniaturansichten +pdfjs-current-outline-item-button = + .title = Aktuelles Struktur-Element finden +pdfjs-current-outline-item-button-label = Aktuelles Struktur-Element +pdfjs-findbar-button = + .title = Dokument durchsuchen +pdfjs-findbar-button-label = Suchen +pdfjs-additional-layers = Zusätzliche Ebenen + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Seite { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniaturansicht von Seite { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Suchen + .placeholder = Dokument durchsuchen… +pdfjs-find-previous-button = + .title = Vorheriges Vorkommen des Suchbegriffs finden +pdfjs-find-previous-button-label = Zurück +pdfjs-find-next-button = + .title = Nächstes Vorkommen des Suchbegriffs finden +pdfjs-find-next-button-label = Weiter +pdfjs-find-highlight-checkbox = Alle hervorheben +pdfjs-find-match-case-checkbox-label = Groß-/Kleinschreibung beachten +pdfjs-find-match-diacritics-checkbox-label = Akzente +pdfjs-find-entire-word-checkbox-label = Ganze Wörter +pdfjs-find-reached-top = Anfang des Dokuments erreicht, fahre am Ende fort +pdfjs-find-reached-bottom = Ende des Dokuments erreicht, fahre am Anfang fort +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } von { $total } Übereinstimmung + *[other] { $current } von { $total } Übereinstimmungen + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Mehr als { $limit } Übereinstimmung + *[other] Mehr als { $limit } Übereinstimmungen + } +pdfjs-find-not-found = Suchbegriff nicht gefunden + +## Predefined zoom values + +pdfjs-page-scale-width = Seitenbreite +pdfjs-page-scale-fit = Seitengröße +pdfjs-page-scale-auto = Automatischer Zoom +pdfjs-page-scale-actual = Originalgröße +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale } % + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Seite { $page } + +## Loading indicator messages + +pdfjs-loading-error = Beim Laden der PDF-Datei trat ein Fehler auf. +pdfjs-invalid-file-error = Ungültige oder beschädigte PDF-Datei +pdfjs-missing-file-error = Fehlende PDF-Datei +pdfjs-unexpected-response-error = Unerwartete Antwort des Servers +pdfjs-rendering-error = Beim Darstellen der Seite trat ein Fehler auf. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anlage: { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Geben Sie zum Öffnen der PDF-Datei deren Passwort ein. +pdfjs-password-invalid = Falsches Passwort. Bitte versuchen Sie es erneut. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Abbrechen +pdfjs-web-fonts-disabled = Web-Schriftarten sind deaktiviert: Eingebettete PDF-Schriftarten konnten nicht geladen werden. + +## Editing + +pdfjs-editor-free-text-button = + .title = Text +pdfjs-editor-free-text-button-label = Text +pdfjs-editor-ink-button = + .title = Zeichnen +pdfjs-editor-ink-button-label = Zeichnen +pdfjs-editor-stamp-button = + .title = Grafiken hinzufügen oder bearbeiten +pdfjs-editor-stamp-button-label = Grafiken hinzufügen oder bearbeiten +pdfjs-editor-highlight-button = + .title = Hervorheben +pdfjs-editor-highlight-button-label = Hervorheben +pdfjs-highlight-floating-button1 = + .title = Hervorheben + .aria-label = Hervorheben +pdfjs-highlight-floating-button-label = Hervorheben + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Zeichnung entfernen +pdfjs-editor-remove-freetext-button = + .title = Text entfernen +pdfjs-editor-remove-stamp-button = + .title = Grafik entfernen +pdfjs-editor-remove-highlight-button = + .title = Hervorhebung entfernen + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Farbe +pdfjs-editor-free-text-size-input = Größe +pdfjs-editor-ink-color-input = Farbe +pdfjs-editor-ink-thickness-input = Linienstärke +pdfjs-editor-ink-opacity-input = Deckkraft +pdfjs-editor-stamp-add-image-button = + .title = Grafik hinzufügen +pdfjs-editor-stamp-add-image-button-label = Grafik hinzufügen +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Linienstärke +pdfjs-editor-free-highlight-thickness-title = + .title = Linienstärke beim Hervorheben anderer Elemente als Text ändern +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Texteditor + .default-content = Schreiben beginnen… +pdfjs-free-text = + .aria-label = Texteditor +pdfjs-free-text-default-content = Schreiben beginnen… +pdfjs-ink = + .aria-label = Zeichnungseditor +pdfjs-ink-canvas = + .aria-label = Vom Benutzer erstelltes Bild + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alternativ-Text +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternativ-Text bearbeiten +pdfjs-editor-alt-text-edit-button-label = Alternativ-Text bearbeiten +pdfjs-editor-alt-text-dialog-label = Option wählen +pdfjs-editor-alt-text-dialog-description = Alt-Text (Alternativtext) hilft, wenn Personen die Grafik nicht sehen können oder wenn sie nicht geladen wird. +pdfjs-editor-alt-text-add-description-label = Beschreibung hinzufügen +pdfjs-editor-alt-text-add-description-description = Ziel sind 1-2 Sätze, die das Thema, das Szenario oder Aktionen beschreiben. +pdfjs-editor-alt-text-mark-decorative-label = Als dekorativ markieren +pdfjs-editor-alt-text-mark-decorative-description = Dies wird für Ziergrafiken wie Ränder oder Wasserzeichen verwendet. +pdfjs-editor-alt-text-cancel-button = Abbrechen +pdfjs-editor-alt-text-save-button = Speichern +pdfjs-editor-alt-text-decorative-tooltip = Als dekorativ markiert +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Zum Beispiel: "Ein junger Mann setzt sich an einen Tisch, um zu essen." +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternativ-Text + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Linke obere Ecke - Größe ändern +pdfjs-editor-resizer-label-top-middle = Oben mittig - Größe ändern +pdfjs-editor-resizer-label-top-right = Rechts oben - Größe ändern +pdfjs-editor-resizer-label-middle-right = Mitte rechts - Größe ändern +pdfjs-editor-resizer-label-bottom-right = Rechte untere Ecke - Größe ändern +pdfjs-editor-resizer-label-bottom-middle = Unten mittig - Größe ändern +pdfjs-editor-resizer-label-bottom-left = Linke untere Ecke - Größe ändern +pdfjs-editor-resizer-label-middle-left = Mitte links - Größe ändern +pdfjs-editor-resizer-top-left = + .aria-label = Linke obere Ecke - Größe ändern +pdfjs-editor-resizer-top-middle = + .aria-label = Oben mittig - Größe ändern +pdfjs-editor-resizer-top-right = + .aria-label = Rechts oben - Größe ändern +pdfjs-editor-resizer-middle-right = + .aria-label = Mitte rechts - Größe ändern +pdfjs-editor-resizer-bottom-right = + .aria-label = Rechte untere Ecke - Größe ändern +pdfjs-editor-resizer-bottom-middle = + .aria-label = Unten mittig - Größe ändern +pdfjs-editor-resizer-bottom-left = + .aria-label = Linke untere Ecke - Größe ändern +pdfjs-editor-resizer-middle-left = + .aria-label = Mitte links - Größe ändern + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Hervorhebungsfarbe +pdfjs-editor-colorpicker-button = + .title = Farbe ändern +pdfjs-editor-colorpicker-dropdown = + .aria-label = Farbauswahl +pdfjs-editor-colorpicker-yellow = + .title = Gelb +pdfjs-editor-colorpicker-green = + .title = Grün +pdfjs-editor-colorpicker-blue = + .title = Blau +pdfjs-editor-colorpicker-pink = + .title = Pink +pdfjs-editor-colorpicker-red = + .title = Rot + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Alle anzeigen +pdfjs-editor-highlight-show-all-button = + .title = Alle anzeigen + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Alternativ-Text (Grafikbeschreibung) bearbeiten +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Alternativ-Text (Grafikbeschreibung) hinzufügen +pdfjs-editor-new-alt-text-textarea = + .placeholder = Schreiben Sie Ihre Beschreibung hier… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Kurze Beschreibung für Personen, die die Grafik nicht sehen können, oder wenn die Grafik nicht geladen wird. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Dieser Alternativ-Text wurde automatisch erstellt und könnte ungenau sein. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Weitere Informationen +pdfjs-editor-new-alt-text-create-automatically-button-label = Alternativ-Text automatisch erstellen +pdfjs-editor-new-alt-text-not-now-button = Nicht jetzt +pdfjs-editor-new-alt-text-error-title = Alternativ-Text konnte nicht automatisch erstellt werden +pdfjs-editor-new-alt-text-error-description = Bitte schreiben Sie Ihren eigenen Alternativ-Text oder versuchen Sie es später erneut. +pdfjs-editor-new-alt-text-error-close-button = Schließen +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Alternativ-Text-KI-Modell wird heruntergeladen ({ $downloadedSize } von { $totalSize } MB) + .aria-valuetext = Alternativ-Text-KI-Modell wird heruntergeladen ({ $downloadedSize } von { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternativ-Text hinzugefügt +pdfjs-editor-new-alt-text-added-button-label = Alternativ-Text hinzugefügt +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Fehlender Alternativ-Text +pdfjs-editor-new-alt-text-missing-button-label = Fehlender Alternativ-Text +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternativ-Text überprüfen +pdfjs-editor-new-alt-text-to-review-button-label = Alternativ-Text überprüfen +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Automatisch erstellt: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Alternativ-Text-Einstellungen für Grafiken +pdfjs-image-alt-text-settings-button-label = Alternativ-Text-Einstellungen für Grafiken +pdfjs-editor-alt-text-settings-dialog-label = Alternativ-Text-Einstellungen für Grafiken +pdfjs-editor-alt-text-settings-automatic-title = Automatischer Alternativ-Text +pdfjs-editor-alt-text-settings-create-model-button-label = Alternativ-Text automatisch erstellen +pdfjs-editor-alt-text-settings-create-model-description = Schlägt Beschreibungen vor, um Personen zu helfen, die die Grafik nicht sehen können, oder wenn die Grafik nicht geladen wird. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Alternativ-Text-KI-Modell ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Wird lokal auf Ihrem Gerät ausgeführt, sodass Ihre Daten privat bleiben. Erforderlich für automatischen Alternativ-Text. +pdfjs-editor-alt-text-settings-delete-model-button = Löschen +pdfjs-editor-alt-text-settings-download-model-button = Herunterladen +pdfjs-editor-alt-text-settings-downloading-model-button = Wird heruntergeladen… +pdfjs-editor-alt-text-settings-editor-title = Alternativ-Texteditor +pdfjs-editor-alt-text-settings-show-dialog-button-label = Alternativ-Texteditor beim Hinzufügen einer Grafik anzeigen +pdfjs-editor-alt-text-settings-show-dialog-description = Hilft Ihnen, sicherzustellen, dass alle Ihre Grafiken Alternativ-Text haben. +pdfjs-editor-alt-text-settings-close-button = Schließen + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Hervorhebung entfernt +pdfjs-editor-undo-bar-message-freetext = Text entfernt +pdfjs-editor-undo-bar-message-ink = Zeichnung entfernt +pdfjs-editor-undo-bar-message-stamp = Grafik entfernt +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } Anmerkung entfernt + *[other] { $count } Anmerkungen entfernt + } +pdfjs-editor-undo-bar-undo-button = + .title = Rückgängig +pdfjs-editor-undo-bar-undo-button-label = Rückgängig +pdfjs-editor-undo-bar-close-button = + .title = Schließen +pdfjs-editor-undo-bar-close-button-label = Schließen diff --git a/public/pdfjs/web/locale/dsb/viewer.ftl b/public/pdfjs/web/locale/dsb/viewer.ftl new file mode 100644 index 0000000..24ac94f --- /dev/null +++ b/public/pdfjs/web/locale/dsb/viewer.ftl @@ -0,0 +1,521 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pjerwjejšny bok +pdfjs-previous-button-label = Slědk +pdfjs-next-button = + .title = Pśiducy bok +pdfjs-next-button-label = Dalej +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Bok +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = z { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } z { $pagesCount }) +pdfjs-zoom-out-button = + .title = Pómjeńšyś +pdfjs-zoom-out-button-label = Pómjeńšyś +pdfjs-zoom-in-button = + .title = Pówětšyś +pdfjs-zoom-in-button-label = Pówětšyś +pdfjs-zoom-select = + .title = Skalěrowanje +pdfjs-presentation-mode-button = + .title = Do prezentaciskego modusa pśejś +pdfjs-presentation-mode-button-label = Prezentaciski modus +pdfjs-open-file-button = + .title = Dataju wócyniś +pdfjs-open-file-button-label = Wócyniś +pdfjs-print-button = + .title = Śišćaś +pdfjs-print-button-label = Śišćaś +pdfjs-save-button = + .title = Składowaś +pdfjs-save-button-label = Składowaś +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Ześěgnuś +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Ześěgnuś +pdfjs-bookmark-button = + .title = Aktualny bok (URL z aktualnego boka pokazaś) +pdfjs-bookmark-button-label = Aktualny bok + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Rědy +pdfjs-tools-button-label = Rědy +pdfjs-first-page-button = + .title = K prědnemu bokoju +pdfjs-first-page-button-label = K prědnemu bokoju +pdfjs-last-page-button = + .title = K slědnemu bokoju +pdfjs-last-page-button-label = K slědnemu bokoju +pdfjs-page-rotate-cw-button = + .title = Wobwjertnuś ako špěra źo +pdfjs-page-rotate-cw-button-label = Wobwjertnuś ako špěra źo +pdfjs-page-rotate-ccw-button = + .title = Wobwjertnuś nawopaki ako špěra źo +pdfjs-page-rotate-ccw-button-label = Wobwjertnuś nawopaki ako špěra źo +pdfjs-cursor-text-select-tool-button = + .title = Rěd za wuběranje teksta zmóžniś +pdfjs-cursor-text-select-tool-button-label = Rěd za wuběranje teksta +pdfjs-cursor-hand-tool-button = + .title = Rucny rěd zmóžniś +pdfjs-cursor-hand-tool-button-label = Rucny rěd +pdfjs-scroll-page-button = + .title = Kulanje boka wužywaś +pdfjs-scroll-page-button-label = Kulanje boka +pdfjs-scroll-vertical-button = + .title = Wertikalne suwanje wužywaś +pdfjs-scroll-vertical-button-label = Wertikalne suwanje +pdfjs-scroll-horizontal-button = + .title = Horicontalne suwanje wužywaś +pdfjs-scroll-horizontal-button-label = Horicontalne suwanje +pdfjs-scroll-wrapped-button = + .title = Pózlažke suwanje wužywaś +pdfjs-scroll-wrapped-button-label = Pózlažke suwanje +pdfjs-spread-none-button = + .title = Boki njezwězaś +pdfjs-spread-none-button-label = Žeden dwójny bok +pdfjs-spread-odd-button = + .title = Boki zachopinajucy z njerownymi bokami zwězaś +pdfjs-spread-odd-button-label = Njerowne boki +pdfjs-spread-even-button = + .title = Boki zachopinajucy z rownymi bokami zwězaś +pdfjs-spread-even-button-label = Rowne boki + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumentowe kakosći… +pdfjs-document-properties-button-label = Dokumentowe kakosći… +pdfjs-document-properties-file-name = Mě dataje: +pdfjs-document-properties-file-size = Wjelikosć dataje: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bajtow) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bajtow) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajtow) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajtow) +pdfjs-document-properties-title = Titel: +pdfjs-document-properties-author = Awtor: +pdfjs-document-properties-subject = Tema: +pdfjs-document-properties-keywords = Klucowe słowa: +pdfjs-document-properties-creation-date = Datum napóranja: +pdfjs-document-properties-modification-date = Datum změny: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Awtor: +pdfjs-document-properties-producer = PDF-gótowaŕ: +pdfjs-document-properties-version = PDF-wersija: +pdfjs-document-properties-page-count = Licba bokow: +pdfjs-document-properties-page-size = Wjelikosć boka: +pdfjs-document-properties-page-size-unit-inches = col +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = wusoki format +pdfjs-document-properties-page-size-orientation-landscape = prěcny format +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Fast Web View: +pdfjs-document-properties-linearized-yes = Jo +pdfjs-document-properties-linearized-no = Ně +pdfjs-document-properties-close-button = Zacyniś + +## Print + +pdfjs-print-progress-message = Dokument pśigótujo se za śišćanje… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Pśetergnuś +pdfjs-printing-not-supported = Warnowanje: Śišćanje njepódpěra se połnje pśez toś ten wobglědowak. +pdfjs-printing-not-ready = Warnowanje: PDF njejo se za śišćanje dopołnje zacytał. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Bócnicu pokazaś/schowaś +pdfjs-toggle-sidebar-notification-button = + .title = Bocnicu pśešaltowaś (dokument rozrědowanje/pśipiski/warstwy wopśimujo) +pdfjs-toggle-sidebar-button-label = Bócnicu pokazaś/schowaś +pdfjs-document-outline-button = + .title = Dokumentowe naraźenje pokazaś (dwójne kliknjenje, aby se wšykne zapiski pokazali/schowali) +pdfjs-document-outline-button-label = Dokumentowa struktura +pdfjs-attachments-button = + .title = Pśidanki pokazaś +pdfjs-attachments-button-label = Pśidanki +pdfjs-layers-button = + .title = Warstwy pokazaś (klikniśo dwójcy, aby wšykne warstwy na standardny staw slědk stajił) +pdfjs-layers-button-label = Warstwy +pdfjs-thumbs-button = + .title = Miniatury pokazaś +pdfjs-thumbs-button-label = Miniatury +pdfjs-current-outline-item-button = + .title = Aktualny rozrědowański zapisk pytaś +pdfjs-current-outline-item-button-label = Aktualny rozrědowański zapisk +pdfjs-findbar-button = + .title = W dokumenśe pytaś +pdfjs-findbar-button-label = Pytaś +pdfjs-additional-layers = Dalšne warstwy + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Bok { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura boka { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Pytaś + .placeholder = W dokumenśe pytaś… +pdfjs-find-previous-button = + .title = Pjerwjejšne wustupowanje pytańskego wuraza pytaś +pdfjs-find-previous-button-label = Slědk +pdfjs-find-next-button = + .title = Pśidujuce wustupowanje pytańskego wuraza pytaś +pdfjs-find-next-button-label = Dalej +pdfjs-find-highlight-checkbox = Wšykne wuzwignuś +pdfjs-find-match-case-checkbox-label = Na wjelikopisanje źiwaś +pdfjs-find-match-diacritics-checkbox-label = Diakritiske znamuška wužywaś +pdfjs-find-entire-word-checkbox-label = Cełe słowa +pdfjs-find-reached-top = Zachopjeńk dokumenta dostany, pókšacujo se z kóńcom +pdfjs-find-reached-bottom = Kóńc dokumenta dostany, pókšacujo se ze zachopjeńkom +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } z { $total } wótpowědnika + [two] { $current } z { $total } wótpowědnikowu + [few] { $current } z { $total } wótpowědnikow + *[other] { $current } z { $total } wótpowědnikow + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Wušej { $limit } wótpowědnik + [two] Wušej { $limit } wótpowědnika + [few] Wušej { $limit } wótpowědniki + *[other] Wušej { $limit } wótpowědniki + } +pdfjs-find-not-found = Pytański wuraz njejo se namakał + +## Predefined zoom values + +pdfjs-page-scale-width = Šyrokosć boka +pdfjs-page-scale-fit = Wjelikosć boka +pdfjs-page-scale-auto = Awtomatiske skalěrowanje +pdfjs-page-scale-actual = Aktualna wjelikosć +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Bok { $page } + +## Loading indicator messages + +pdfjs-loading-error = Pśi zacytowanju PDF jo zmólka nastała. +pdfjs-invalid-file-error = Njepłaśiwa abo wobškóźona PDF-dataja. +pdfjs-missing-file-error = Felujuca PDF-dataja. +pdfjs-unexpected-response-error = Njewócakane serwerowe wótegrono. +pdfjs-rendering-error = Pśi zwobraznjanju boka jo zmólka nastała. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Typ pśipiskow: { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Zapódajśo gronidło, aby PDF-dataju wócynił. +pdfjs-password-invalid = Njepłaśiwe gronidło. Pšosym wopytajśo hyšći raz. +pdfjs-password-ok-button = W pórěźe +pdfjs-password-cancel-button = Pśetergnuś +pdfjs-web-fonts-disabled = Webpisma su znjemóžnjone: njejo móžno, zasajźone PDF-pisma wužywaś. + +## Editing + +pdfjs-editor-free-text-button = + .title = Tekst +pdfjs-editor-free-text-button-label = Tekst +pdfjs-editor-ink-button = + .title = Kresliś +pdfjs-editor-ink-button-label = Kresliś +pdfjs-editor-stamp-button = + .title = Wobraze pśidaś abo wobźěłaś +pdfjs-editor-stamp-button-label = Wobraze pśidaś abo wobźěłaś +pdfjs-editor-highlight-button = + .title = Wuzwignuś +pdfjs-editor-highlight-button-label = Wuzwignuś +pdfjs-highlight-floating-button1 = + .title = Wuzwignuś + .aria-label = Wuzwignuś +pdfjs-highlight-floating-button-label = Wuzwignuś + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Kreslanku wótwónoźeś +pdfjs-editor-remove-freetext-button = + .title = Tekst wótwónoźeś +pdfjs-editor-remove-stamp-button = + .title = Wobraz wótwónoźeś +pdfjs-editor-remove-highlight-button = + .title = Wuzwignjenje wótpóraś + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Barwa +pdfjs-editor-free-text-size-input = Wjelikosć +pdfjs-editor-ink-color-input = Barwa +pdfjs-editor-ink-thickness-input = Tłustosć +pdfjs-editor-ink-opacity-input = Opacita +pdfjs-editor-stamp-add-image-button = + .title = Wobraz pśidaś +pdfjs-editor-stamp-add-image-button-label = Wobraz pśidaś +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Tłustosć +pdfjs-editor-free-highlight-thickness-title = + .title = Tłustosć změniś, gaž se zapiski wuzwiguju, kótarež tekst njejsu +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstowy editor + .default-content = Zachopśo pisaś … +pdfjs-free-text = + .aria-label = Tekstowy editor +pdfjs-free-text-default-content = Zachopśo pisaś… +pdfjs-ink = + .aria-label = Kresleński editor +pdfjs-ink-canvas = + .aria-label = Wobraz napórany wót wužywarja + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alternatiwny tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternatiwny tekst wobźěłaś +pdfjs-editor-alt-text-edit-button-label = Alternatiwny tekst wobźěłaś +pdfjs-editor-alt-text-dialog-label = Nastajenje wubraś +pdfjs-editor-alt-text-dialog-description = Alternatiwny tekst pomaga, gaž luźe njamógu wobraz wiźeś abo gaž se wobraz njezacytajo. +pdfjs-editor-alt-text-add-description-label = Wopisanje pśidaś +pdfjs-editor-alt-text-add-description-description = Pišćo 1 sadu abo 2 saźe, kótarejž temu, nastajenje abo akcije wopisujotej. +pdfjs-editor-alt-text-mark-decorative-label = Ako dekoratiwny markěrowaś +pdfjs-editor-alt-text-mark-decorative-description = To se za pyšnjece wobraze wužywa, na pśikład ramiki abo wódowe znamjenja. +pdfjs-editor-alt-text-cancel-button = Pśetergnuś +pdfjs-editor-alt-text-save-button = Składowaś +pdfjs-editor-alt-text-decorative-tooltip = Ako dekoratiwny markěrowany +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Na pśikład, „Młody muski za blidom sejźi, aby jěź jědł“ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternatiwny tekst + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Górjejce nalěwo – wjelikosć změniś +pdfjs-editor-resizer-label-top-middle = Górjejce wesrjejź – wjelikosć změniś +pdfjs-editor-resizer-label-top-right = Górjejce napšawo – wjelikosć změniś +pdfjs-editor-resizer-label-middle-right = Wesrjejź napšawo – wjelikosć změniś +pdfjs-editor-resizer-label-bottom-right = Dołojce napšawo – wjelikosć změniś +pdfjs-editor-resizer-label-bottom-middle = Dołojce wesrjejź – wjelikosć změniś +pdfjs-editor-resizer-label-bottom-left = Dołojce nalěwo – wjelikosć změniś +pdfjs-editor-resizer-label-middle-left = Wesrjejź nalěwo – wjelikosć změniś +pdfjs-editor-resizer-top-left = + .aria-label = Górjejce nalěwo – wjelikosć změniś +pdfjs-editor-resizer-top-middle = + .aria-label = Górjejce wesrjejź – wjelikosć změniś +pdfjs-editor-resizer-top-right = + .aria-label = Górjejce napšawo – wjelikosć změniś +pdfjs-editor-resizer-middle-right = + .aria-label = Wesrjejź napšawo – wjelikosć změniś +pdfjs-editor-resizer-bottom-right = + .aria-label = Dołojce napšawo – wjelikosć změniś +pdfjs-editor-resizer-bottom-middle = + .aria-label = Dołojce wesrjejź – wjelikosć změniś +pdfjs-editor-resizer-bottom-left = + .aria-label = Dołojce nalěwo – wjelikosć změniś +pdfjs-editor-resizer-middle-left = + .aria-label = Wesrjejź nalěwo – wjelikosć změniś + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Barwa wuzwignjenja +pdfjs-editor-colorpicker-button = + .title = Barwu změniś +pdfjs-editor-colorpicker-dropdown = + .aria-label = Wuběrk barwow +pdfjs-editor-colorpicker-yellow = + .title = Žołty +pdfjs-editor-colorpicker-green = + .title = Zeleny +pdfjs-editor-colorpicker-blue = + .title = Módry +pdfjs-editor-colorpicker-pink = + .title = Pink +pdfjs-editor-colorpicker-red = + .title = Cerwjeny + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Wšykne pokazaś +pdfjs-editor-highlight-show-all-button = + .title = Wšykne pokazaś + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Alternatiwny tekst wobźěłaś (wobrazowe wopisanje) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Alternatiwny tekst pśidaś (wobrazowe wopisanje) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Pišćo how swójo wopisanje… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Krotke wopisanje za luźe, kótarež njamóžośo wobraz wiźeś abo gaž se wobraz njezacytajo. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Toś ten alternatiwny tekst jo se awtomatiski napórał a jo snaź njedokradny. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Dalšne informacije +pdfjs-editor-new-alt-text-create-automatically-button-label = Alternatiwny tekst awtomatiski napóraś +pdfjs-editor-new-alt-text-not-now-button = Nic něnto +pdfjs-editor-new-alt-text-error-title = Alternatiwny tekst njedajo se awtomatiski napóraś +pdfjs-editor-new-alt-text-error-description = Pšosym pišćo swój alternatiwny tekst abo wopytajśo pózdźej hyšći raz. +pdfjs-editor-new-alt-text-error-close-button = Zacyniś +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Model KI za alternatiwny tekst se ześěgujo ({ $downloadedSize } z { $totalSize } MB) + .aria-valuetext = Model KI za alternatiwny tekst se ześěgujo ({ $downloadedSize } z { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternatiwny tekst jo se pśidał +pdfjs-editor-new-alt-text-added-button-label = Alternatiwny tekst jo se pśidał +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Alternatiwny tekst felujo +pdfjs-editor-new-alt-text-missing-button-label = Alternatiwny tekst felujo +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternatiwny tekst pśeglědowaś +pdfjs-editor-new-alt-text-to-review-button-label = Alternatiwny tekst pśeglědowaś +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Awtomatiski napórany: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Nastajenja alternatiwnego wobrazowego teksta +pdfjs-image-alt-text-settings-button-label = Nastajenja alternatiwnego wobrazowego teksta +pdfjs-editor-alt-text-settings-dialog-label = Nastajenja alternatiwnego wobrazowego teksta +pdfjs-editor-alt-text-settings-automatic-title = Awtomatiski alternatiwny tekst +pdfjs-editor-alt-text-settings-create-model-button-label = Alternatiwny tekst awtomatiski napóraś +pdfjs-editor-alt-text-settings-create-model-description = Naraźujo wopisanja, aby pomagał ludam, kótarež njamóžośo wobraz wiźeś abo gaž se wobraz njezacytajo. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Model KI alternatiwnego teksta ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Běžy lokalnje na wašom rěźe, aby waše daty priwatne wóstali. Za awtomatiski alternatiwny tekst trjebny. +pdfjs-editor-alt-text-settings-delete-model-button = Lašowaś +pdfjs-editor-alt-text-settings-download-model-button = Ześěgnuś +pdfjs-editor-alt-text-settings-downloading-model-button = Ześěgujo se… +pdfjs-editor-alt-text-settings-editor-title = Editor za alternatiwny tekst +pdfjs-editor-alt-text-settings-show-dialog-button-label = Editor alternatiwnego teksta ned pokazaś, gaž se wobraz pśidawa +pdfjs-editor-alt-text-settings-show-dialog-description = Pomaga, wam wšym swójim wobrazam alternatiwny tekst pśidaś. +pdfjs-editor-alt-text-settings-close-button = Zacyniś + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Wótwónoźone wuzwignuś +pdfjs-editor-undo-bar-message-freetext = Tekst jo se wótwónoźeł +pdfjs-editor-undo-bar-message-ink = Kreslanka jo se wótwónoźeła +pdfjs-editor-undo-bar-message-stamp = Wobraz jo se wótwónoźeł +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } pśipisk jo se wótwónoźeł + [two] { $count } pśipiska stej se wótwónoźełej + [few] { $count } pśipiski su se wótwónoźeli + *[other] { $count } pśipiskow jo se wótwónoźeło + } +pdfjs-editor-undo-bar-undo-button = + .title = Anulěrowaś +pdfjs-editor-undo-bar-undo-button-label = Anulěrowaś +pdfjs-editor-undo-bar-close-button = + .title = Zacyniś +pdfjs-editor-undo-bar-close-button-label = Zacyniś diff --git a/public/pdfjs/web/locale/el/viewer.ftl b/public/pdfjs/web/locale/el/viewer.ftl new file mode 100644 index 0000000..5a04bd8 --- /dev/null +++ b/public/pdfjs/web/locale/el/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Προηγούμενη σελίδα +pdfjs-previous-button-label = Προηγούμενη +pdfjs-next-button = + .title = Επόμενη σελίδα +pdfjs-next-button-label = Επόμενη +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Σελίδα +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = από { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } από { $pagesCount }) +pdfjs-zoom-out-button = + .title = Σμίκρυνση +pdfjs-zoom-out-button-label = Σμίκρυνση +pdfjs-zoom-in-button = + .title = Μεγέθυνση +pdfjs-zoom-in-button-label = Μεγέθυνση +pdfjs-zoom-select = + .title = Ζουμ +pdfjs-presentation-mode-button = + .title = Εναλλαγή σε λειτουργία παρουσίασης +pdfjs-presentation-mode-button-label = Λειτουργία παρουσίασης +pdfjs-open-file-button = + .title = Άνοιγμα αρχείου +pdfjs-open-file-button-label = Άνοιγμα +pdfjs-print-button = + .title = Εκτύπωση +pdfjs-print-button-label = Εκτύπωση +pdfjs-save-button = + .title = Αποθήκευση +pdfjs-save-button-label = Αποθήκευση +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Λήψη +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Λήψη +pdfjs-bookmark-button = + .title = Τρέχουσα σελίδα (Προβολή URL από τρέχουσα σελίδα) +pdfjs-bookmark-button-label = Τρέχουσα σελίδα + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Εργαλεία +pdfjs-tools-button-label = Εργαλεία +pdfjs-first-page-button = + .title = Μετάβαση στην πρώτη σελίδα +pdfjs-first-page-button-label = Μετάβαση στην πρώτη σελίδα +pdfjs-last-page-button = + .title = Μετάβαση στην τελευταία σελίδα +pdfjs-last-page-button-label = Μετάβαση στην τελευταία σελίδα +pdfjs-page-rotate-cw-button = + .title = Δεξιόστροφη περιστροφή +pdfjs-page-rotate-cw-button-label = Δεξιόστροφη περιστροφή +pdfjs-page-rotate-ccw-button = + .title = Αριστερόστροφη περιστροφή +pdfjs-page-rotate-ccw-button-label = Αριστερόστροφη περιστροφή +pdfjs-cursor-text-select-tool-button = + .title = Ενεργοποίηση εργαλείου επιλογής κειμένου +pdfjs-cursor-text-select-tool-button-label = Εργαλείο επιλογής κειμένου +pdfjs-cursor-hand-tool-button = + .title = Ενεργοποίηση εργαλείου χεριού +pdfjs-cursor-hand-tool-button-label = Εργαλείο χεριού +pdfjs-scroll-page-button = + .title = Χρήση κύλισης σελίδας +pdfjs-scroll-page-button-label = Κύλιση σελίδας +pdfjs-scroll-vertical-button = + .title = Χρήση κάθετης κύλισης +pdfjs-scroll-vertical-button-label = Κάθετη κύλιση +pdfjs-scroll-horizontal-button = + .title = Χρήση οριζόντιας κύλισης +pdfjs-scroll-horizontal-button-label = Οριζόντια κύλιση +pdfjs-scroll-wrapped-button = + .title = Χρήση κυκλικής κύλισης +pdfjs-scroll-wrapped-button-label = Κυκλική κύλιση +pdfjs-spread-none-button = + .title = Να μη γίνει σύνδεση επεκτάσεων σελίδων +pdfjs-spread-none-button-label = Χωρίς επεκτάσεις +pdfjs-spread-odd-button = + .title = Σύνδεση επεκτάσεων σελίδων ξεκινώντας από τις μονές σελίδες +pdfjs-spread-odd-button-label = Μονές επεκτάσεις +pdfjs-spread-even-button = + .title = Σύνδεση επεκτάσεων σελίδων ξεκινώντας από τις ζυγές σελίδες +pdfjs-spread-even-button-label = Ζυγές επεκτάσεις + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Ιδιότητες εγγράφου… +pdfjs-document-properties-button-label = Ιδιότητες εγγράφου… +pdfjs-document-properties-file-name = Όνομα αρχείου: +pdfjs-document-properties-file-size = Μέγεθος αρχείου: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Τίτλος: +pdfjs-document-properties-author = Συγγραφέας: +pdfjs-document-properties-subject = Θέμα: +pdfjs-document-properties-keywords = Λέξεις-κλειδιά: +pdfjs-document-properties-creation-date = Ημερομηνία δημιουργίας: +pdfjs-document-properties-modification-date = Ημερομηνία τροποποίησης: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Δημιουργός: +pdfjs-document-properties-producer = Παραγωγός PDF: +pdfjs-document-properties-version = Έκδοση PDF: +pdfjs-document-properties-page-count = Αριθμός σελίδων: +pdfjs-document-properties-page-size = Μέγεθος σελίδας: +pdfjs-document-properties-page-size-unit-inches = ίντσες +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = κατακόρυφα +pdfjs-document-properties-page-size-orientation-landscape = οριζόντια +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Επιστολή +pdfjs-document-properties-page-size-name-legal = Τύπου Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Ταχεία προβολή ιστού: +pdfjs-document-properties-linearized-yes = Ναι +pdfjs-document-properties-linearized-no = Όχι +pdfjs-document-properties-close-button = Κλείσιμο + +## Print + +pdfjs-print-progress-message = Προετοιμασία του εγγράφου για εκτύπωση… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Ακύρωση +pdfjs-printing-not-supported = Προειδοποίηση: Η εκτύπωση δεν υποστηρίζεται πλήρως από το πρόγραμμα περιήγησης. +pdfjs-printing-not-ready = Προειδοποίηση: Το PDF δεν φορτώθηκε πλήρως για εκτύπωση. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = (Απ)ενεργοποίηση πλαϊνής γραμμής +pdfjs-toggle-sidebar-notification-button = + .title = (Απ)ενεργοποίηση πλαϊνής γραμμής (το έγγραφο περιέχει περίγραμμα/συνημμένα/επίπεδα) +pdfjs-toggle-sidebar-button-label = (Απ)ενεργοποίηση πλαϊνής γραμμής +pdfjs-document-outline-button = + .title = Εμφάνιση διάρθρωσης εγγράφου (διπλό κλικ για ανάπτυξη/σύμπτυξη όλων των στοιχείων) +pdfjs-document-outline-button-label = Διάρθρωση εγγράφου +pdfjs-attachments-button = + .title = Εμφάνιση συνημμένων +pdfjs-attachments-button-label = Συνημμένα +pdfjs-layers-button = + .title = Εμφάνιση επιπέδων (διπλό κλικ για επαναφορά όλων των επιπέδων στην προεπιλεγμένη κατάσταση) +pdfjs-layers-button-label = Επίπεδα +pdfjs-thumbs-button = + .title = Εμφάνιση μικρογραφιών +pdfjs-thumbs-button-label = Μικρογραφίες +pdfjs-current-outline-item-button = + .title = Εύρεση τρέχοντος στοιχείου διάρθρωσης +pdfjs-current-outline-item-button-label = Τρέχον στοιχείο διάρθρωσης +pdfjs-findbar-button = + .title = Εύρεση στο έγγραφο +pdfjs-findbar-button-label = Εύρεση +pdfjs-additional-layers = Επιπρόσθετα επίπεδα + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Σελίδα { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Μικρογραφία σελίδας { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Εύρεση + .placeholder = Εύρεση στο έγγραφο… +pdfjs-find-previous-button = + .title = Εύρεση της προηγούμενης εμφάνισης της φράσης +pdfjs-find-previous-button-label = Προηγούμενο +pdfjs-find-next-button = + .title = Εύρεση της επόμενης εμφάνισης της φράσης +pdfjs-find-next-button-label = Επόμενο +pdfjs-find-highlight-checkbox = Επισήμανση όλων +pdfjs-find-match-case-checkbox-label = Συμφωνία πεζών/κεφαλαίων +pdfjs-find-match-diacritics-checkbox-label = Αντιστοίχιση διακριτικών +pdfjs-find-entire-word-checkbox-label = Ολόκληρες λέξεις +pdfjs-find-reached-top = Φτάσατε στην αρχή του εγγράφου, συνέχεια από το τέλος +pdfjs-find-reached-bottom = Φτάσατε στο τέλος του εγγράφου, συνέχεια από την αρχή +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } από { $total } αντιστοιχία + *[other] { $current } από { $total } αντιστοιχίες + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Περισσότερες από { $limit } αντιστοιχία + *[other] Περισσότερες από { $limit } αντιστοιχίες + } +pdfjs-find-not-found = Η φράση δεν βρέθηκε + +## Predefined zoom values + +pdfjs-page-scale-width = Πλάτος σελίδας +pdfjs-page-scale-fit = Μέγεθος σελίδας +pdfjs-page-scale-auto = Αυτόματο ζουμ +pdfjs-page-scale-actual = Πραγματικό μέγεθος +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Σελίδα { $page } + +## Loading indicator messages + +pdfjs-loading-error = Προέκυψε σφάλμα κατά τη φόρτωση του PDF. +pdfjs-invalid-file-error = Μη έγκυρο ή κατεστραμμένο αρχείο PDF. +pdfjs-missing-file-error = Λείπει αρχείο PDF. +pdfjs-unexpected-response-error = Μη αναμενόμενη απόκριση από το διακομιστή. +pdfjs-rendering-error = Προέκυψε σφάλμα κατά την εμφάνιση της σελίδας. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Σχόλιο «{ $type }»] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Εισαγάγετε τον κωδικό πρόσβασης για να ανοίξετε αυτό το αρχείο PDF. +pdfjs-password-invalid = Μη έγκυρος κωδικός πρόσβασης. Παρακαλώ δοκιμάστε ξανά. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Ακύρωση +pdfjs-web-fonts-disabled = Οι γραμματοσειρές ιστού είναι ανενεργές: δεν είναι δυνατή η χρήση των ενσωματωμένων γραμματοσειρών PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Κείμενο +pdfjs-editor-free-text-button-label = Κείμενο +pdfjs-editor-ink-button = + .title = Σχέδιο +pdfjs-editor-ink-button-label = Σχέδιο +pdfjs-editor-stamp-button = + .title = Προσθήκη ή επεξεργασία εικόνων +pdfjs-editor-stamp-button-label = Προσθήκη ή επεξεργασία εικόνων +pdfjs-editor-highlight-button = + .title = Επισήμανση +pdfjs-editor-highlight-button-label = Επισήμανση +pdfjs-highlight-floating-button1 = + .title = Επισήμανση + .aria-label = Επισήμανση +pdfjs-highlight-floating-button-label = Επισήμανση + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Αφαίρεση σχεδίου +pdfjs-editor-remove-freetext-button = + .title = Αφαίρεση κειμένου +pdfjs-editor-remove-stamp-button = + .title = Αφαίρεση εικόνας +pdfjs-editor-remove-highlight-button = + .title = Αφαίρεση επισήμανσης + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Χρώμα +pdfjs-editor-free-text-size-input = Μέγεθος +pdfjs-editor-ink-color-input = Χρώμα +pdfjs-editor-ink-thickness-input = Πάχος +pdfjs-editor-ink-opacity-input = Αδιαφάνεια +pdfjs-editor-stamp-add-image-button = + .title = Προσθήκη εικόνας +pdfjs-editor-stamp-add-image-button-label = Προσθήκη εικόνας +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Πάχος +pdfjs-editor-free-highlight-thickness-title = + .title = Αλλαγή πάχους κατά την επισήμανση στοιχείων εκτός κειμένου +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Επεξεργασία κειμένου + .default-content = Ξεκινήστε να πληκτρολογείτε… +pdfjs-free-text = + .aria-label = Επεξεργασία κειμένου +pdfjs-free-text-default-content = Ξεκινήστε να πληκτρολογείτε… +pdfjs-ink = + .aria-label = Επεξεργασία σχεδίων +pdfjs-ink-canvas = + .aria-label = Εικόνα από τον χρήστη + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Εναλλακτικό κείμενο +pdfjs-editor-alt-text-edit-button = + .aria-label = Επεξεργασία εναλλακτικού κειμένου +pdfjs-editor-alt-text-edit-button-label = Επεξεργασία εναλλακτικού κειμένου +pdfjs-editor-alt-text-dialog-label = Διαλέξτε μια επιλογή +pdfjs-editor-alt-text-dialog-description = Το εναλλακτικό κείμενο είναι χρήσιμο όταν οι άνθρωποι δεν μπορούν να δουν την εικόνα ή όταν αυτή δεν φορτώνεται. +pdfjs-editor-alt-text-add-description-label = Προσθήκη περιγραφής +pdfjs-editor-alt-text-add-description-description = Στοχεύστε σε μία ή δύο προτάσεις που περιγράφουν το θέμα, τη ρύθμιση ή τις ενέργειες. +pdfjs-editor-alt-text-mark-decorative-label = Επισήμανση ως διακοσμητικό +pdfjs-editor-alt-text-mark-decorative-description = Χρησιμοποιείται για διακοσμητικές εικόνες, όπως περιγράμματα ή υδατογραφήματα. +pdfjs-editor-alt-text-cancel-button = Ακύρωση +pdfjs-editor-alt-text-save-button = Αποθήκευση +pdfjs-editor-alt-text-decorative-tooltip = Επισημασμένο ως διακοσμητικό +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Για παράδειγμα, «Ένας νεαρός άνδρας κάθεται σε ένα τραπέζι για να φάει ένα γεύμα» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Εναλλακτικό κείμενο + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Επάνω αριστερή γωνία — αλλαγή μεγέθους +pdfjs-editor-resizer-label-top-middle = Μέσο επάνω πλευράς — αλλαγή μεγέθους +pdfjs-editor-resizer-label-top-right = Επάνω δεξιά γωνία — αλλαγή μεγέθους +pdfjs-editor-resizer-label-middle-right = Μέσο δεξιάς πλευράς — αλλαγή μεγέθους +pdfjs-editor-resizer-label-bottom-right = Κάτω δεξιά γωνία — αλλαγή μεγέθους +pdfjs-editor-resizer-label-bottom-middle = Μέσο κάτω πλευράς — αλλαγή μεγέθους +pdfjs-editor-resizer-label-bottom-left = Κάτω αριστερή γωνία — αλλαγή μεγέθους +pdfjs-editor-resizer-label-middle-left = Μέσο αριστερής πλευράς — αλλαγή μεγέθους +pdfjs-editor-resizer-top-left = + .aria-label = Επάνω αριστερή γωνία — αλλαγή μεγέθους +pdfjs-editor-resizer-top-middle = + .aria-label = Μέσο επάνω πλευράς — αλλαγή μεγέθους +pdfjs-editor-resizer-top-right = + .aria-label = Επάνω δεξιά γωνία — αλλαγή μεγέθους +pdfjs-editor-resizer-middle-right = + .aria-label = Μέσο δεξιάς πλευράς — αλλαγή μεγέθους +pdfjs-editor-resizer-bottom-right = + .aria-label = Κάτω δεξιά γωνία — αλλαγή μεγέθους +pdfjs-editor-resizer-bottom-middle = + .aria-label = Μέσο κάτω πλευράς — αλλαγή μεγέθους +pdfjs-editor-resizer-bottom-left = + .aria-label = Κάτω αριστερή γωνία — αλλαγή μεγέθους +pdfjs-editor-resizer-middle-left = + .aria-label = Μέσο αριστερής πλευράς — αλλαγή μεγέθους + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Χρώμα επισήμανσης +pdfjs-editor-colorpicker-button = + .title = Αλλαγή χρώματος +pdfjs-editor-colorpicker-dropdown = + .aria-label = Επιλογές χρωμάτων +pdfjs-editor-colorpicker-yellow = + .title = Κίτρινο +pdfjs-editor-colorpicker-green = + .title = Πράσινο +pdfjs-editor-colorpicker-blue = + .title = Μπλε +pdfjs-editor-colorpicker-pink = + .title = Ροζ +pdfjs-editor-colorpicker-red = + .title = Κόκκινο + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Εμφάνιση όλων +pdfjs-editor-highlight-show-all-button = + .title = Εμφάνιση όλων + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Επεξεργασία εναλλακτικού κειμένου (περιγραφή εικόνας) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Προσθήκη εναλλακτικού κειμένου (περιγραφή εικόνας) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Γράψτε την περιγραφή σας εδώ… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Σύντομη περιγραφή για άτομα που δεν μπορούν να δουν την εικόνα ή όταν η εικόνα δεν φορτώνεται. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Αυτό το εναλλακτικό κείμενο δημιουργήθηκε αυτόματα και ενδέχεται να είναι ανακριβές. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Μάθετε περισσότερα +pdfjs-editor-new-alt-text-create-automatically-button-label = Αυτόματη δημιουργία εναλλακτικού κειμένου +pdfjs-editor-new-alt-text-not-now-button = Όχι τώρα +pdfjs-editor-new-alt-text-error-title = Δεν ήταν δυνατή η αυτόματη δημιουργία εναλλακτικού κειμένου +pdfjs-editor-new-alt-text-error-description = Γράψτε το δικό σας εναλλακτικό κείμενο ή δοκιμάστε ξανά αργότερα. +pdfjs-editor-new-alt-text-error-close-button = Κλείσιμο +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Λήψη μοντέλου AI εναλλακτικού κειμένου ({ $downloadedSize } από { $totalSize } MB) + .aria-valuetext = Λήψη μοντέλου AI εναλλακτικού κειμένου ({ $downloadedSize } από { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Προστέθηκε εναλλακτικό κείμενο +pdfjs-editor-new-alt-text-added-button-label = Προστέθηκε εναλλακτικό κείμενο +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Απουσία εναλλακτικού κειμένου +pdfjs-editor-new-alt-text-missing-button-label = Απουσία εναλλακτικού κειμένου +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Έλεγχος εναλλακτικού κειμένου +pdfjs-editor-new-alt-text-to-review-button-label = Έλεγχος εναλλακτικού κειμένου +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Αυτόματη δημιουργία: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Ρυθμίσεις εναλλακτικού κειμένου εικόνας +pdfjs-image-alt-text-settings-button-label = Ρυθμίσεις εναλλακτικού κειμένου εικόνας +pdfjs-editor-alt-text-settings-dialog-label = Ρυθμίσεις εναλλακτικού κειμένου εικόνας +pdfjs-editor-alt-text-settings-automatic-title = Αυτόματο εναλλακτικό κείμενο +pdfjs-editor-alt-text-settings-create-model-button-label = Αυτόματη δημιουργία εναλλακτικού κειμένου +pdfjs-editor-alt-text-settings-create-model-description = Προτείνει περιγραφές για άτομα που δεν μπορούν να δουν την εικόνα ή όταν η εικόνα δεν φορτώνεται. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Μοντέλο AI εναλλακτικού κειμένου ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Εκτελείται τοπικά στη συσκευή σας, ώστε τα δεδομένα σας να παραμένουν ιδιωτικά. Απαιτείται για τη δημιουργία του αυτόματου εναλλακτικού κειμένου. +pdfjs-editor-alt-text-settings-delete-model-button = Διαγραφή +pdfjs-editor-alt-text-settings-download-model-button = Λήψη +pdfjs-editor-alt-text-settings-downloading-model-button = Λήψη… +pdfjs-editor-alt-text-settings-editor-title = Επεξεργασία εναλλακτικού κειμένου +pdfjs-editor-alt-text-settings-show-dialog-button-label = Άμεση εμφάνιση της επεξεργασίας εναλλακτικού κειμένου κατά την προσθήκη εικόνας +pdfjs-editor-alt-text-settings-show-dialog-description = Σας βοηθά να βεβαιωθείτε ότι όλες οι εικόνες σας έχουν εναλλακτικό κείμενο. +pdfjs-editor-alt-text-settings-close-button = Κλείσιμο + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Η επισήμανση αφαιρέθηκε +pdfjs-editor-undo-bar-message-freetext = Το κείμενο αφαιρέθηκε +pdfjs-editor-undo-bar-message-ink = Το σχέδιο αφαιρέθηκε +pdfjs-editor-undo-bar-message-stamp = Η εικόνα αφαιρέθηκε +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] Αφαιρέθηκε { $count } σχολιασμός + *[other] Αφαιρέθηκαν { $count } σχολιασμοί + } +pdfjs-editor-undo-bar-undo-button = + .title = Αναίρεση +pdfjs-editor-undo-bar-undo-button-label = Αναίρεση +pdfjs-editor-undo-bar-close-button = + .title = Κλείσιμο +pdfjs-editor-undo-bar-close-button-label = Κλείσιμο diff --git a/public/pdfjs/web/locale/en-CA/viewer.ftl b/public/pdfjs/web/locale/en-CA/viewer.ftl new file mode 100644 index 0000000..346e6e8 --- /dev/null +++ b/public/pdfjs/web/locale/en-CA/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Previous Page +pdfjs-previous-button-label = Previous +pdfjs-next-button = + .title = Next Page +pdfjs-next-button-label = Next +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Page +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = of { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zoom Out +pdfjs-zoom-out-button-label = Zoom Out +pdfjs-zoom-in-button = + .title = Zoom In +pdfjs-zoom-in-button-label = Zoom In +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Switch to Presentation Mode +pdfjs-presentation-mode-button-label = Presentation Mode +pdfjs-open-file-button = + .title = Open File +pdfjs-open-file-button-label = Open +pdfjs-print-button = + .title = Print +pdfjs-print-button-label = Print +pdfjs-save-button = + .title = Save +pdfjs-save-button-label = Save +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Download +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Download +pdfjs-bookmark-button = + .title = Current Page (View URL from Current Page) +pdfjs-bookmark-button-label = Current Page + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Tools +pdfjs-tools-button-label = Tools +pdfjs-first-page-button = + .title = Go to First Page +pdfjs-first-page-button-label = Go to First Page +pdfjs-last-page-button = + .title = Go to Last Page +pdfjs-last-page-button-label = Go to Last Page +pdfjs-page-rotate-cw-button = + .title = Rotate Clockwise +pdfjs-page-rotate-cw-button-label = Rotate Clockwise +pdfjs-page-rotate-ccw-button = + .title = Rotate Counterclockwise +pdfjs-page-rotate-ccw-button-label = Rotate Counterclockwise +pdfjs-cursor-text-select-tool-button = + .title = Enable Text Selection Tool +pdfjs-cursor-text-select-tool-button-label = Text Selection Tool +pdfjs-cursor-hand-tool-button = + .title = Enable Hand Tool +pdfjs-cursor-hand-tool-button-label = Hand Tool +pdfjs-scroll-page-button = + .title = Use Page Scrolling +pdfjs-scroll-page-button-label = Page Scrolling +pdfjs-scroll-vertical-button = + .title = Use Vertical Scrolling +pdfjs-scroll-vertical-button-label = Vertical Scrolling +pdfjs-scroll-horizontal-button = + .title = Use Horizontal Scrolling +pdfjs-scroll-horizontal-button-label = Horizontal Scrolling +pdfjs-scroll-wrapped-button = + .title = Use Wrapped Scrolling +pdfjs-scroll-wrapped-button-label = Wrapped Scrolling +pdfjs-spread-none-button = + .title = Do not join page spreads +pdfjs-spread-none-button-label = No Spreads +pdfjs-spread-odd-button = + .title = Join page spreads starting with odd-numbered pages +pdfjs-spread-odd-button-label = Odd Spreads +pdfjs-spread-even-button = + .title = Join page spreads starting with even-numbered pages +pdfjs-spread-even-button-label = Even Spreads + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Document Properties… +pdfjs-document-properties-button-label = Document Properties… +pdfjs-document-properties-file-name = File name: +pdfjs-document-properties-file-size = File size: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Title: +pdfjs-document-properties-author = Author: +pdfjs-document-properties-subject = Subject: +pdfjs-document-properties-keywords = Keywords: +pdfjs-document-properties-creation-date = Creation Date: +pdfjs-document-properties-modification-date = Modification Date: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Creator: +pdfjs-document-properties-producer = PDF Producer: +pdfjs-document-properties-version = PDF Version: +pdfjs-document-properties-page-count = Page Count: +pdfjs-document-properties-page-size = Page Size: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = portrait +pdfjs-document-properties-page-size-orientation-landscape = landscape +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Fast Web View: +pdfjs-document-properties-linearized-yes = Yes +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Close + +## Print + +pdfjs-print-progress-message = Preparing document for printing… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cancel +pdfjs-printing-not-supported = Warning: Printing is not fully supported by this browser. +pdfjs-printing-not-ready = Warning: The PDF is not fully loaded for printing. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Toggle Sidebar +pdfjs-toggle-sidebar-notification-button = + .title = Toggle Sidebar (document contains outline/attachments/layers) +pdfjs-toggle-sidebar-button-label = Toggle Sidebar +pdfjs-document-outline-button = + .title = Show Document Outline (double-click to expand/collapse all items) +pdfjs-document-outline-button-label = Document Outline +pdfjs-attachments-button = + .title = Show Attachments +pdfjs-attachments-button-label = Attachments +pdfjs-layers-button = + .title = Show Layers (double-click to reset all layers to the default state) +pdfjs-layers-button-label = Layers +pdfjs-thumbs-button = + .title = Show Thumbnails +pdfjs-thumbs-button-label = Thumbnails +pdfjs-current-outline-item-button = + .title = Find Current Outline Item +pdfjs-current-outline-item-button-label = Current Outline Item +pdfjs-findbar-button = + .title = Find in Document +pdfjs-findbar-button-label = Find +pdfjs-additional-layers = Additional Layers + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Page { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Thumbnail of Page { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Find + .placeholder = Find in document… +pdfjs-find-previous-button = + .title = Find the previous occurrence of the phrase +pdfjs-find-previous-button-label = Previous +pdfjs-find-next-button = + .title = Find the next occurrence of the phrase +pdfjs-find-next-button-label = Next +pdfjs-find-highlight-checkbox = Highlight All +pdfjs-find-match-case-checkbox-label = Match Case +pdfjs-find-match-diacritics-checkbox-label = Match Diacritics +pdfjs-find-entire-word-checkbox-label = Whole Words +pdfjs-find-reached-top = Reached top of document, continued from bottom +pdfjs-find-reached-bottom = Reached end of document, continued from top +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } of { $total } match + *[other] { $current } of { $total } matches + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] More than { $limit } match + *[other] More than { $limit } matches + } +pdfjs-find-not-found = Phrase not found + +## Predefined zoom values + +pdfjs-page-scale-width = Page Width +pdfjs-page-scale-fit = Page Fit +pdfjs-page-scale-auto = Automatic Zoom +pdfjs-page-scale-actual = Actual Size +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Page { $page } + +## Loading indicator messages + +pdfjs-loading-error = An error occurred while loading the PDF. +pdfjs-invalid-file-error = Invalid or corrupted PDF file. +pdfjs-missing-file-error = Missing PDF file. +pdfjs-unexpected-response-error = Unexpected server response. +pdfjs-rendering-error = An error occurred while rendering the page. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Enter the password to open this PDF file. +pdfjs-password-invalid = Invalid password. Please try again. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Cancel +pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts. + +## Editing + +pdfjs-editor-free-text-button = + .title = Text +pdfjs-editor-free-text-button-label = Text +pdfjs-editor-ink-button = + .title = Draw +pdfjs-editor-ink-button-label = Draw +pdfjs-editor-stamp-button = + .title = Add or edit images +pdfjs-editor-stamp-button-label = Add or edit images +pdfjs-editor-highlight-button = + .title = Highlight +pdfjs-editor-highlight-button-label = Highlight +pdfjs-highlight-floating-button1 = + .title = Highlight + .aria-label = Highlight +pdfjs-highlight-floating-button-label = Highlight + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Remove drawing +pdfjs-editor-remove-freetext-button = + .title = Remove text +pdfjs-editor-remove-stamp-button = + .title = Remove image +pdfjs-editor-remove-highlight-button = + .title = Remove highlight + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Colour +pdfjs-editor-free-text-size-input = Size +pdfjs-editor-ink-color-input = Colour +pdfjs-editor-ink-thickness-input = Thickness +pdfjs-editor-ink-opacity-input = Opacity +pdfjs-editor-stamp-add-image-button = + .title = Add image +pdfjs-editor-stamp-add-image-button-label = Add image +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Thickness +pdfjs-editor-free-highlight-thickness-title = + .title = Change thickness when highlighting items other than text +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Text Editor + .default-content = Start typing… +pdfjs-free-text = + .aria-label = Text Editor +pdfjs-free-text-default-content = Start typing… +pdfjs-ink = + .aria-label = Draw Editor +pdfjs-ink-canvas = + .aria-label = User-created image + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alt text +pdfjs-editor-alt-text-edit-button = + .aria-label = Edit alt text +pdfjs-editor-alt-text-edit-button-label = Edit alt text +pdfjs-editor-alt-text-dialog-label = Choose an option +pdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people can’t see the image or when it doesn’t load. +pdfjs-editor-alt-text-add-description-label = Add a description +pdfjs-editor-alt-text-add-description-description = Aim for 1-2 sentences that describe the subject, setting, or actions. +pdfjs-editor-alt-text-mark-decorative-label = Mark as decorative +pdfjs-editor-alt-text-mark-decorative-description = This is used for ornamental images, like borders or watermarks. +pdfjs-editor-alt-text-cancel-button = Cancel +pdfjs-editor-alt-text-save-button = Save +pdfjs-editor-alt-text-decorative-tooltip = Marked as decorative +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = For example, “A young man sits down at a table to eat a meal” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt text + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Top left corner — resize +pdfjs-editor-resizer-label-top-middle = Top middle — resize +pdfjs-editor-resizer-label-top-right = Top right corner — resize +pdfjs-editor-resizer-label-middle-right = Middle right — resize +pdfjs-editor-resizer-label-bottom-right = Bottom right corner — resize +pdfjs-editor-resizer-label-bottom-middle = Bottom middle — resize +pdfjs-editor-resizer-label-bottom-left = Bottom left corner — resize +pdfjs-editor-resizer-label-middle-left = Middle left — resize +pdfjs-editor-resizer-top-left = + .aria-label = Top left corner — resize +pdfjs-editor-resizer-top-middle = + .aria-label = Top middle — resize +pdfjs-editor-resizer-top-right = + .aria-label = Top right corner — resize +pdfjs-editor-resizer-middle-right = + .aria-label = Middle right — resize +pdfjs-editor-resizer-bottom-right = + .aria-label = Bottom right corner — resize +pdfjs-editor-resizer-bottom-middle = + .aria-label = Bottom middle — resize +pdfjs-editor-resizer-bottom-left = + .aria-label = Bottom left corner — resize +pdfjs-editor-resizer-middle-left = + .aria-label = Middle left — resize + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Highlight colour +pdfjs-editor-colorpicker-button = + .title = Change colour +pdfjs-editor-colorpicker-dropdown = + .aria-label = Colour choices +pdfjs-editor-colorpicker-yellow = + .title = Yellow +pdfjs-editor-colorpicker-green = + .title = Green +pdfjs-editor-colorpicker-blue = + .title = Blue +pdfjs-editor-colorpicker-pink = + .title = Pink +pdfjs-editor-colorpicker-red = + .title = Red + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Show all +pdfjs-editor-highlight-show-all-button = + .title = Show all + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Edit alt text (image description) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Add alt text (image description) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Write your description here… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Short description for people who can’t see the image or when the image doesn’t load. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = This alt text was created automatically and may be inaccurate. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Learn more +pdfjs-editor-new-alt-text-create-automatically-button-label = Create alt text automatically +pdfjs-editor-new-alt-text-not-now-button = Not now +pdfjs-editor-new-alt-text-error-title = Couldn’t create alt text automatically +pdfjs-editor-new-alt-text-error-description = Please write your own alt text or try again later. +pdfjs-editor-new-alt-text-error-close-button = Close +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB) + .aria-valuetext = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alt text added +pdfjs-editor-new-alt-text-added-button-label = Alt text added +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Missing alt text +pdfjs-editor-new-alt-text-missing-button-label = Missing alt text +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Review alt text +pdfjs-editor-new-alt-text-to-review-button-label = Review alt text +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Created automatically: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Image alt text settings +pdfjs-image-alt-text-settings-button-label = Image alt text settings +pdfjs-editor-alt-text-settings-dialog-label = Image alt text settings +pdfjs-editor-alt-text-settings-automatic-title = Automatic alt text +pdfjs-editor-alt-text-settings-create-model-button-label = Create alt text automatically +pdfjs-editor-alt-text-settings-create-model-description = Suggests descriptions to help people who can’t see the image or when the image doesn’t load. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Alt text AI model ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Runs locally on your device so your data stays private. Required for automatic alt text. +pdfjs-editor-alt-text-settings-delete-model-button = Delete +pdfjs-editor-alt-text-settings-download-model-button = Download +pdfjs-editor-alt-text-settings-downloading-model-button = Downloading… +pdfjs-editor-alt-text-settings-editor-title = Alt text editor +pdfjs-editor-alt-text-settings-show-dialog-button-label = Show alt text editor right away when adding an image +pdfjs-editor-alt-text-settings-show-dialog-description = Helps you make sure all your images have alt text. +pdfjs-editor-alt-text-settings-close-button = Close + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Highlight removed +pdfjs-editor-undo-bar-message-freetext = Text removed +pdfjs-editor-undo-bar-message-ink = Drawing removed +pdfjs-editor-undo-bar-message-stamp = Image removed +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotation removed + *[other] { $count } annotations removed + } +pdfjs-editor-undo-bar-undo-button = + .title = Undo +pdfjs-editor-undo-bar-undo-button-label = Undo +pdfjs-editor-undo-bar-close-button = + .title = Close +pdfjs-editor-undo-bar-close-button-label = Close diff --git a/public/pdfjs/web/locale/en-GB/viewer.ftl b/public/pdfjs/web/locale/en-GB/viewer.ftl new file mode 100644 index 0000000..4222f6f --- /dev/null +++ b/public/pdfjs/web/locale/en-GB/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Previous Page +pdfjs-previous-button-label = Previous +pdfjs-next-button = + .title = Next Page +pdfjs-next-button-label = Next +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Page +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = of { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zoom Out +pdfjs-zoom-out-button-label = Zoom Out +pdfjs-zoom-in-button = + .title = Zoom In +pdfjs-zoom-in-button-label = Zoom In +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Switch to Presentation Mode +pdfjs-presentation-mode-button-label = Presentation Mode +pdfjs-open-file-button = + .title = Open File +pdfjs-open-file-button-label = Open +pdfjs-print-button = + .title = Print +pdfjs-print-button-label = Print +pdfjs-save-button = + .title = Save +pdfjs-save-button-label = Save +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Download +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Download +pdfjs-bookmark-button = + .title = Current Page (View URL from Current Page) +pdfjs-bookmark-button-label = Current Page + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Tools +pdfjs-tools-button-label = Tools +pdfjs-first-page-button = + .title = Go to First Page +pdfjs-first-page-button-label = Go to First Page +pdfjs-last-page-button = + .title = Go to Last Page +pdfjs-last-page-button-label = Go to Last Page +pdfjs-page-rotate-cw-button = + .title = Rotate Clockwise +pdfjs-page-rotate-cw-button-label = Rotate Clockwise +pdfjs-page-rotate-ccw-button = + .title = Rotate Anti-Clockwise +pdfjs-page-rotate-ccw-button-label = Rotate Anti-Clockwise +pdfjs-cursor-text-select-tool-button = + .title = Enable Text Selection Tool +pdfjs-cursor-text-select-tool-button-label = Text Selection Tool +pdfjs-cursor-hand-tool-button = + .title = Enable Hand Tool +pdfjs-cursor-hand-tool-button-label = Hand Tool +pdfjs-scroll-page-button = + .title = Use Page Scrolling +pdfjs-scroll-page-button-label = Page Scrolling +pdfjs-scroll-vertical-button = + .title = Use Vertical Scrolling +pdfjs-scroll-vertical-button-label = Vertical Scrolling +pdfjs-scroll-horizontal-button = + .title = Use Horizontal Scrolling +pdfjs-scroll-horizontal-button-label = Horizontal Scrolling +pdfjs-scroll-wrapped-button = + .title = Use Wrapped Scrolling +pdfjs-scroll-wrapped-button-label = Wrapped Scrolling +pdfjs-spread-none-button = + .title = Do not join page spreads +pdfjs-spread-none-button-label = No Spreads +pdfjs-spread-odd-button = + .title = Join page spreads starting with odd-numbered pages +pdfjs-spread-odd-button-label = Odd Spreads +pdfjs-spread-even-button = + .title = Join page spreads starting with even-numbered pages +pdfjs-spread-even-button-label = Even Spreads + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Document Properties… +pdfjs-document-properties-button-label = Document Properties… +pdfjs-document-properties-file-name = File name: +pdfjs-document-properties-file-size = File size: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } kB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Title: +pdfjs-document-properties-author = Author: +pdfjs-document-properties-subject = Subject: +pdfjs-document-properties-keywords = Keywords: +pdfjs-document-properties-creation-date = Creation Date: +pdfjs-document-properties-modification-date = Modification Date: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Creator: +pdfjs-document-properties-producer = PDF Producer: +pdfjs-document-properties-version = PDF Version: +pdfjs-document-properties-page-count = Page Count: +pdfjs-document-properties-page-size = Page Size: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = portrait +pdfjs-document-properties-page-size-orientation-landscape = landscape +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Fast Web View: +pdfjs-document-properties-linearized-yes = Yes +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Close + +## Print + +pdfjs-print-progress-message = Preparing document for printing… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cancel +pdfjs-printing-not-supported = Warning: Printing is not fully supported by this browser. +pdfjs-printing-not-ready = Warning: The PDF is not fully loaded for printing. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Toggle Sidebar +pdfjs-toggle-sidebar-notification-button = + .title = Toggle Sidebar (document contains outline/attachments/layers) +pdfjs-toggle-sidebar-button-label = Toggle Sidebar +pdfjs-document-outline-button = + .title = Show Document Outline (double-click to expand/collapse all items) +pdfjs-document-outline-button-label = Document Outline +pdfjs-attachments-button = + .title = Show Attachments +pdfjs-attachments-button-label = Attachments +pdfjs-layers-button = + .title = Show Layers (double-click to reset all layers to the default state) +pdfjs-layers-button-label = Layers +pdfjs-thumbs-button = + .title = Show Thumbnails +pdfjs-thumbs-button-label = Thumbnails +pdfjs-current-outline-item-button = + .title = Find Current Outline Item +pdfjs-current-outline-item-button-label = Current Outline Item +pdfjs-findbar-button = + .title = Find in Document +pdfjs-findbar-button-label = Find +pdfjs-additional-layers = Additional Layers + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Page { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Thumbnail of Page { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Find + .placeholder = Find in document… +pdfjs-find-previous-button = + .title = Find the previous occurrence of the phrase +pdfjs-find-previous-button-label = Previous +pdfjs-find-next-button = + .title = Find the next occurrence of the phrase +pdfjs-find-next-button-label = Next +pdfjs-find-highlight-checkbox = Highlight All +pdfjs-find-match-case-checkbox-label = Match Case +pdfjs-find-match-diacritics-checkbox-label = Match Diacritics +pdfjs-find-entire-word-checkbox-label = Whole Words +pdfjs-find-reached-top = Reached top of document, continued from bottom +pdfjs-find-reached-bottom = Reached end of document, continued from top +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } of { $total } match + *[other] { $current } of { $total } matches + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] More than { $limit } match + *[other] More than { $limit } matches + } +pdfjs-find-not-found = Phrase not found + +## Predefined zoom values + +pdfjs-page-scale-width = Page Width +pdfjs-page-scale-fit = Page Fit +pdfjs-page-scale-auto = Automatic Zoom +pdfjs-page-scale-actual = Actual Size +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Page { $page } + +## Loading indicator messages + +pdfjs-loading-error = An error occurred while loading the PDF. +pdfjs-invalid-file-error = Invalid or corrupted PDF file. +pdfjs-missing-file-error = Missing PDF file. +pdfjs-unexpected-response-error = Unexpected server response. +pdfjs-rendering-error = An error occurred while rendering the page. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Enter the password to open this PDF file. +pdfjs-password-invalid = Invalid password. Please try again. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Cancel +pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts. + +## Editing + +pdfjs-editor-free-text-button = + .title = Text +pdfjs-editor-free-text-button-label = Text +pdfjs-editor-ink-button = + .title = Draw +pdfjs-editor-ink-button-label = Draw +pdfjs-editor-stamp-button = + .title = Add or edit images +pdfjs-editor-stamp-button-label = Add or edit images +pdfjs-editor-highlight-button = + .title = Highlight +pdfjs-editor-highlight-button-label = Highlight +pdfjs-highlight-floating-button1 = + .title = Highlight + .aria-label = Highlight +pdfjs-highlight-floating-button-label = Highlight + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Remove drawing +pdfjs-editor-remove-freetext-button = + .title = Remove text +pdfjs-editor-remove-stamp-button = + .title = Remove image +pdfjs-editor-remove-highlight-button = + .title = Remove highlight + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Colour +pdfjs-editor-free-text-size-input = Size +pdfjs-editor-ink-color-input = Colour +pdfjs-editor-ink-thickness-input = Thickness +pdfjs-editor-ink-opacity-input = Opacity +pdfjs-editor-stamp-add-image-button = + .title = Add image +pdfjs-editor-stamp-add-image-button-label = Add image +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Thickness +pdfjs-editor-free-highlight-thickness-title = + .title = Change thickness when highlighting items other than text +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Text Editor + .default-content = Start typing… +pdfjs-free-text = + .aria-label = Text Editor +pdfjs-free-text-default-content = Start typing… +pdfjs-ink = + .aria-label = Draw Editor +pdfjs-ink-canvas = + .aria-label = User-created image + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alt text +pdfjs-editor-alt-text-edit-button = + .aria-label = Edit alt text +pdfjs-editor-alt-text-edit-button-label = Edit alt text +pdfjs-editor-alt-text-dialog-label = Choose an option +pdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people can’t see the image or when it doesn’t load. +pdfjs-editor-alt-text-add-description-label = Add a description +pdfjs-editor-alt-text-add-description-description = Aim for 1-2 sentences that describe the subject, setting, or actions. +pdfjs-editor-alt-text-mark-decorative-label = Mark as decorative +pdfjs-editor-alt-text-mark-decorative-description = This is used for ornamental images, like borders or watermarks. +pdfjs-editor-alt-text-cancel-button = Cancel +pdfjs-editor-alt-text-save-button = Save +pdfjs-editor-alt-text-decorative-tooltip = Marked as decorative +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = For example, “A young man sits down at a table to eat a meal” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt text + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Top left corner — resize +pdfjs-editor-resizer-label-top-middle = Top middle — resize +pdfjs-editor-resizer-label-top-right = Top right corner — resize +pdfjs-editor-resizer-label-middle-right = Middle right — resize +pdfjs-editor-resizer-label-bottom-right = Bottom right corner — resize +pdfjs-editor-resizer-label-bottom-middle = Bottom middle — resize +pdfjs-editor-resizer-label-bottom-left = Bottom left corner — resize +pdfjs-editor-resizer-label-middle-left = Middle left — resize +pdfjs-editor-resizer-top-left = + .aria-label = Top left corner — resize +pdfjs-editor-resizer-top-middle = + .aria-label = Top middle — resize +pdfjs-editor-resizer-top-right = + .aria-label = Top right corner — resize +pdfjs-editor-resizer-middle-right = + .aria-label = Middle right — resize +pdfjs-editor-resizer-bottom-right = + .aria-label = Bottom right corner — resize +pdfjs-editor-resizer-bottom-middle = + .aria-label = Bottom middle — resize +pdfjs-editor-resizer-bottom-left = + .aria-label = Bottom left corner — resize +pdfjs-editor-resizer-middle-left = + .aria-label = Middle left — resize + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Highlight colour +pdfjs-editor-colorpicker-button = + .title = Change colour +pdfjs-editor-colorpicker-dropdown = + .aria-label = Colour choices +pdfjs-editor-colorpicker-yellow = + .title = Yellow +pdfjs-editor-colorpicker-green = + .title = Green +pdfjs-editor-colorpicker-blue = + .title = Blue +pdfjs-editor-colorpicker-pink = + .title = Pink +pdfjs-editor-colorpicker-red = + .title = Red + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Show all +pdfjs-editor-highlight-show-all-button = + .title = Show all + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Edit alt text (image description) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Add alt text (image description) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Write your description here… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Short description for people who can’t see the image or when the image doesn’t load. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = This alt text was created automatically and may be inaccurate. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Learn more +pdfjs-editor-new-alt-text-create-automatically-button-label = Create alt text automatically +pdfjs-editor-new-alt-text-not-now-button = Not now +pdfjs-editor-new-alt-text-error-title = Couldn’t create alt text automatically +pdfjs-editor-new-alt-text-error-description = Please write your own alt text or try again later. +pdfjs-editor-new-alt-text-error-close-button = Close +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB) + .aria-valuetext = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alt text added +pdfjs-editor-new-alt-text-added-button-label = Alt text added +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Missing alt text +pdfjs-editor-new-alt-text-missing-button-label = Missing alt text +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Review alt text +pdfjs-editor-new-alt-text-to-review-button-label = Review alt text +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Created automatically: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Image alt text settings +pdfjs-image-alt-text-settings-button-label = Image alt text settings +pdfjs-editor-alt-text-settings-dialog-label = Image alt text settings +pdfjs-editor-alt-text-settings-automatic-title = Automatic alt text +pdfjs-editor-alt-text-settings-create-model-button-label = Create alt text automatically +pdfjs-editor-alt-text-settings-create-model-description = Suggests descriptions to help people who can’t see the image or when the image doesn’t load. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Alt text AI model ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Runs locally on your device so your data stays private. Required for automatic alt text. +pdfjs-editor-alt-text-settings-delete-model-button = Delete +pdfjs-editor-alt-text-settings-download-model-button = Download +pdfjs-editor-alt-text-settings-downloading-model-button = Downloading… +pdfjs-editor-alt-text-settings-editor-title = Alt text editor +pdfjs-editor-alt-text-settings-show-dialog-button-label = Show alt text editor right away when adding an image +pdfjs-editor-alt-text-settings-show-dialog-description = Helps you make sure all your images have alt text. +pdfjs-editor-alt-text-settings-close-button = Close + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Highlight removed +pdfjs-editor-undo-bar-message-freetext = Text removed +pdfjs-editor-undo-bar-message-ink = Drawing removed +pdfjs-editor-undo-bar-message-stamp = Image removed +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotation removed + *[other] { $count } annotations removed + } +pdfjs-editor-undo-bar-undo-button = + .title = Undo +pdfjs-editor-undo-bar-undo-button-label = Undo +pdfjs-editor-undo-bar-close-button = + .title = Close +pdfjs-editor-undo-bar-close-button-label = Close diff --git a/public/pdfjs/web/locale/en-US/viewer.ftl b/public/pdfjs/web/locale/en-US/viewer.ftl new file mode 100644 index 0000000..3e4a351 --- /dev/null +++ b/public/pdfjs/web/locale/en-US/viewer.ftl @@ -0,0 +1,526 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Previous Page +pdfjs-previous-button-label = Previous +pdfjs-next-button = + .title = Next Page +pdfjs-next-button-label = Next + +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Page + +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = of { $pagesCount } + +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount }) + +pdfjs-zoom-out-button = + .title = Zoom Out +pdfjs-zoom-out-button-label = Zoom Out +pdfjs-zoom-in-button = + .title = Zoom In +pdfjs-zoom-in-button-label = Zoom In +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Switch to Presentation Mode +pdfjs-presentation-mode-button-label = Presentation Mode +pdfjs-open-file-button = + .title = Open File +pdfjs-open-file-button-label = Open +pdfjs-print-button = + .title = Print +pdfjs-print-button-label = Print +pdfjs-save-button = + .title = Save +pdfjs-save-button-label = Save + +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Download + +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Download + +pdfjs-bookmark-button = + .title = Current Page (View URL from Current Page) +pdfjs-bookmark-button-label = Current Page + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Tools + +pdfjs-tools-button-label = Tools +pdfjs-first-page-button = + .title = Go to First Page +pdfjs-first-page-button-label = Go to First Page +pdfjs-last-page-button = + .title = Go to Last Page +pdfjs-last-page-button-label = Go to Last Page +pdfjs-page-rotate-cw-button = + .title = Rotate Clockwise +pdfjs-page-rotate-cw-button-label = Rotate Clockwise +pdfjs-page-rotate-ccw-button = + .title = Rotate Counterclockwise +pdfjs-page-rotate-ccw-button-label = Rotate Counterclockwise +pdfjs-cursor-text-select-tool-button = + .title = Enable Text Selection Tool +pdfjs-cursor-text-select-tool-button-label = Text Selection Tool +pdfjs-cursor-hand-tool-button = + .title = Enable Hand Tool +pdfjs-cursor-hand-tool-button-label = Hand Tool +pdfjs-scroll-page-button = + .title = Use Page Scrolling +pdfjs-scroll-page-button-label = Page Scrolling +pdfjs-scroll-vertical-button = + .title = Use Vertical Scrolling +pdfjs-scroll-vertical-button-label = Vertical Scrolling +pdfjs-scroll-horizontal-button = + .title = Use Horizontal Scrolling +pdfjs-scroll-horizontal-button-label = Horizontal Scrolling +pdfjs-scroll-wrapped-button = + .title = Use Wrapped Scrolling +pdfjs-scroll-wrapped-button-label = Wrapped Scrolling +pdfjs-spread-none-button = + .title = Do not join page spreads +pdfjs-spread-none-button-label = No Spreads +pdfjs-spread-odd-button = + .title = Join page spreads starting with odd-numbered pages +pdfjs-spread-odd-button-label = Odd Spreads +pdfjs-spread-even-button = + .title = Join page spreads starting with even-numbered pages +pdfjs-spread-even-button-label = Even Spreads + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Document Properties… +pdfjs-document-properties-button-label = Document Properties… +pdfjs-document-properties-file-name = File name: +pdfjs-document-properties-file-size = File size: + +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) + +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) + +pdfjs-document-properties-title = Title: +pdfjs-document-properties-author = Author: +pdfjs-document-properties-subject = Subject: +pdfjs-document-properties-keywords = Keywords: +pdfjs-document-properties-creation-date = Creation Date: +pdfjs-document-properties-modification-date = Modification Date: + +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +pdfjs-document-properties-creator = Creator: +pdfjs-document-properties-producer = PDF Producer: +pdfjs-document-properties-version = PDF Version: +pdfjs-document-properties-page-count = Page Count: +pdfjs-document-properties-page-size = Page Size: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = portrait +pdfjs-document-properties-page-size-orientation-landscape = landscape +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Fast Web View: +pdfjs-document-properties-linearized-yes = Yes +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Close + +## Print + +pdfjs-print-progress-message = Preparing document for printing… + +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% + +pdfjs-print-progress-close-button = Cancel +pdfjs-printing-not-supported = Warning: Printing is not fully supported by this browser. +pdfjs-printing-not-ready = Warning: The PDF is not fully loaded for printing. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Toggle Sidebar +pdfjs-toggle-sidebar-notification-button = + .title = Toggle Sidebar (document contains outline/attachments/layers) +pdfjs-toggle-sidebar-button-label = Toggle Sidebar +pdfjs-document-outline-button = + .title = Show Document Outline (double-click to expand/collapse all items) +pdfjs-document-outline-button-label = Document Outline +pdfjs-attachments-button = + .title = Show Attachments +pdfjs-attachments-button-label = Attachments +pdfjs-layers-button = + .title = Show Layers (double-click to reset all layers to the default state) +pdfjs-layers-button-label = Layers +pdfjs-thumbs-button = + .title = Show Thumbnails +pdfjs-thumbs-button-label = Thumbnails +pdfjs-current-outline-item-button = + .title = Find Current Outline Item +pdfjs-current-outline-item-button-label = Current Outline Item +pdfjs-findbar-button = + .title = Find in Document +pdfjs-findbar-button-label = Find +pdfjs-additional-layers = Additional Layers + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Page { $page } + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Thumbnail of Page { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Find + .placeholder = Find in document… +pdfjs-find-previous-button = + .title = Find the previous occurrence of the phrase +pdfjs-find-previous-button-label = Previous +pdfjs-find-next-button = + .title = Find the next occurrence of the phrase +pdfjs-find-next-button-label = Next +pdfjs-find-highlight-checkbox = Highlight All +pdfjs-find-match-case-checkbox-label = Match Case +pdfjs-find-match-diacritics-checkbox-label = Match Diacritics +pdfjs-find-entire-word-checkbox-label = Whole Words +pdfjs-find-reached-top = Reached top of document, continued from bottom +pdfjs-find-reached-bottom = Reached end of document, continued from top + +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } of { $total } match + *[other] { $current } of { $total } matches + } + +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] More than { $limit } match + *[other] More than { $limit } matches + } + +pdfjs-find-not-found = Phrase not found + +## Predefined zoom values + +pdfjs-page-scale-width = Page Width +pdfjs-page-scale-fit = Page Fit +pdfjs-page-scale-auto = Automatic Zoom +pdfjs-page-scale-actual = Actual Size + +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Page { $page } + +## Loading indicator messages + +pdfjs-loading-error = An error occurred while loading the PDF. +pdfjs-invalid-file-error = Invalid or corrupted PDF file. +pdfjs-missing-file-error = Missing PDF file. +pdfjs-unexpected-response-error = Unexpected server response. +pdfjs-rendering-error = An error occurred while rendering the page. + +## Annotations + +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] + +## Password + +pdfjs-password-label = Enter the password to open this PDF file. +pdfjs-password-invalid = Invalid password. Please try again. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Cancel +pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts. + +## Editing + +pdfjs-editor-free-text-button = + .title = Text +pdfjs-editor-free-text-button-label = Text +pdfjs-editor-ink-button = + .title = Draw +pdfjs-editor-ink-button-label = Draw +pdfjs-editor-stamp-button = + .title = Add or edit images +pdfjs-editor-stamp-button-label = Add or edit images +pdfjs-editor-highlight-button = + .title = Highlight +pdfjs-editor-highlight-button-label = Highlight +pdfjs-highlight-floating-button1 = + .title = Highlight + .aria-label = Highlight +pdfjs-highlight-floating-button-label = Highlight + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Remove drawing +pdfjs-editor-remove-freetext-button = + .title = Remove text +pdfjs-editor-remove-stamp-button = + .title = Remove image +pdfjs-editor-remove-highlight-button = + .title = Remove highlight + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Color +pdfjs-editor-free-text-size-input = Size +pdfjs-editor-ink-color-input = Color +pdfjs-editor-ink-thickness-input = Thickness +pdfjs-editor-ink-opacity-input = Opacity +pdfjs-editor-stamp-add-image-button = + .title = Add image +pdfjs-editor-stamp-add-image-button-label = Add image +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Thickness +pdfjs-editor-free-highlight-thickness-title = + .title = Change thickness when highlighting items other than text + +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Text Editor + .default-content = Start typing… +pdfjs-ink = + .aria-label = Draw Editor +pdfjs-ink-canvas = + .aria-label = User-created image + +## Alt-text dialog + +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt text +pdfjs-editor-alt-text-button-label = Alt text + +pdfjs-editor-alt-text-edit-button = + .aria-label = Edit alt text +pdfjs-editor-alt-text-dialog-label = Choose an option +pdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people can’t see the image or when it doesn’t load. +pdfjs-editor-alt-text-add-description-label = Add a description +pdfjs-editor-alt-text-add-description-description = Aim for 1-2 sentences that describe the subject, setting, or actions. +pdfjs-editor-alt-text-mark-decorative-label = Mark as decorative +pdfjs-editor-alt-text-mark-decorative-description = This is used for ornamental images, like borders or watermarks. +pdfjs-editor-alt-text-cancel-button = Cancel +pdfjs-editor-alt-text-save-button = Save +pdfjs-editor-alt-text-decorative-tooltip = Marked as decorative + +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = For example, “A young man sits down at a table to eat a meal” + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-top-left = + .aria-label = Top left corner — resize +pdfjs-editor-resizer-top-middle = + .aria-label = Top middle — resize +pdfjs-editor-resizer-top-right = + .aria-label = Top right corner — resize +pdfjs-editor-resizer-middle-right = + .aria-label = Middle right — resize +pdfjs-editor-resizer-bottom-right = + .aria-label = Bottom right corner — resize +pdfjs-editor-resizer-bottom-middle = + .aria-label = Bottom middle — resize +pdfjs-editor-resizer-bottom-left = + .aria-label = Bottom left corner — resize +pdfjs-editor-resizer-middle-left = + .aria-label = Middle left — resize + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Highlight color + +pdfjs-editor-colorpicker-button = + .title = Change color +pdfjs-editor-colorpicker-dropdown = + .aria-label = Color choices +pdfjs-editor-colorpicker-yellow = + .title = Yellow +pdfjs-editor-colorpicker-green = + .title = Green +pdfjs-editor-colorpicker-blue = + .title = Blue +pdfjs-editor-colorpicker-pink = + .title = Pink +pdfjs-editor-colorpicker-red = + .title = Red + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Show all +pdfjs-editor-highlight-show-all-button = + .title = Show all + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Edit alt text (image description) + +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Add alt text (image description) + +pdfjs-editor-new-alt-text-textarea = + .placeholder = Write your description here… + +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Short description for people who can’t see the image or when the image doesn’t load. + +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = This alt text was created automatically and may be inaccurate. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Learn more + +pdfjs-editor-new-alt-text-create-automatically-button-label = Create alt text automatically +pdfjs-editor-new-alt-text-not-now-button = Not now +pdfjs-editor-new-alt-text-error-title = Couldn’t create alt text automatically +pdfjs-editor-new-alt-text-error-description = Please write your own alt text or try again later. +pdfjs-editor-new-alt-text-error-close-button = Close + +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB) + .aria-valuetext = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB) + +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alt text added +pdfjs-editor-new-alt-text-added-button-label = Alt text added + +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Missing alt text +pdfjs-editor-new-alt-text-missing-button-label = Missing alt text + +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Review alt text +pdfjs-editor-new-alt-text-to-review-button-label = Review alt text + +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Created automatically: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Image alt text settings +pdfjs-image-alt-text-settings-button-label = Image alt text settings + +pdfjs-editor-alt-text-settings-dialog-label = Image alt text settings +pdfjs-editor-alt-text-settings-automatic-title = Automatic alt text +pdfjs-editor-alt-text-settings-create-model-button-label = Create alt text automatically +pdfjs-editor-alt-text-settings-create-model-description = Suggests descriptions to help people who can’t see the image or when the image doesn’t load. + +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Alt text AI model ({ $totalSize } MB) + +pdfjs-editor-alt-text-settings-ai-model-description = Runs locally on your device so your data stays private. Required for automatic alt text. +pdfjs-editor-alt-text-settings-delete-model-button = Delete +pdfjs-editor-alt-text-settings-download-model-button = Download +pdfjs-editor-alt-text-settings-downloading-model-button = Downloading… + +pdfjs-editor-alt-text-settings-editor-title = Alt text editor +pdfjs-editor-alt-text-settings-show-dialog-button-label = Show alt text editor right away when adding an image +pdfjs-editor-alt-text-settings-show-dialog-description = Helps you make sure all your images have alt text. +pdfjs-editor-alt-text-settings-close-button = Close + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Highlight removed +pdfjs-editor-undo-bar-message-freetext = Text removed +pdfjs-editor-undo-bar-message-ink = Drawing removed +pdfjs-editor-undo-bar-message-stamp = Image removed +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotation removed + *[other] { $count } annotations removed + } + +pdfjs-editor-undo-bar-undo-button = + .title = Undo +pdfjs-editor-undo-bar-undo-button-label = Undo +pdfjs-editor-undo-bar-close-button = + .title = Close +pdfjs-editor-undo-bar-close-button-label = Close diff --git a/public/pdfjs/web/locale/eo/viewer.ftl b/public/pdfjs/web/locale/eo/viewer.ftl new file mode 100644 index 0000000..ce45ebf --- /dev/null +++ b/public/pdfjs/web/locale/eo/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Antaŭa paĝo +pdfjs-previous-button-label = Malantaŭen +pdfjs-next-button = + .title = Venonta paĝo +pdfjs-next-button-label = Antaŭen +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Paĝo +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = el { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } el { $pagesCount }) +pdfjs-zoom-out-button = + .title = Malpligrandigi +pdfjs-zoom-out-button-label = Malpligrandigi +pdfjs-zoom-in-button = + .title = Pligrandigi +pdfjs-zoom-in-button-label = Pligrandigi +pdfjs-zoom-select = + .title = Pligrandigilo +pdfjs-presentation-mode-button = + .title = Iri al prezenta reĝimo +pdfjs-presentation-mode-button-label = Prezenta reĝimo +pdfjs-open-file-button = + .title = Malfermi dosieron +pdfjs-open-file-button-label = Malfermi +pdfjs-print-button = + .title = Presi +pdfjs-print-button-label = Presi +pdfjs-save-button = + .title = Konservi +pdfjs-save-button-label = Konservi +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Elŝuti +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Elŝuti +pdfjs-bookmark-button = + .title = Nuna paĝo (Montri adreson de la nuna paĝo) +pdfjs-bookmark-button-label = Nuna paĝo + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Iloj +pdfjs-tools-button-label = Iloj +pdfjs-first-page-button = + .title = Iri al la unua paĝo +pdfjs-first-page-button-label = Iri al la unua paĝo +pdfjs-last-page-button = + .title = Iri al la lasta paĝo +pdfjs-last-page-button-label = Iri al la lasta paĝo +pdfjs-page-rotate-cw-button = + .title = Rotaciigi dekstrume +pdfjs-page-rotate-cw-button-label = Rotaciigi dekstrume +pdfjs-page-rotate-ccw-button = + .title = Rotaciigi maldekstrume +pdfjs-page-rotate-ccw-button-label = Rotaciigi maldekstrume +pdfjs-cursor-text-select-tool-button = + .title = Aktivigi tekstan elektilon +pdfjs-cursor-text-select-tool-button-label = Teksta elektilo +pdfjs-cursor-hand-tool-button = + .title = Aktivigi ilon de mano +pdfjs-cursor-hand-tool-button-label = Ilo de mano +pdfjs-scroll-page-button = + .title = Uzi rulumon de paĝo +pdfjs-scroll-page-button-label = Rulumo de paĝo +pdfjs-scroll-vertical-button = + .title = Uzi vertikalan rulumon +pdfjs-scroll-vertical-button-label = Vertikala rulumo +pdfjs-scroll-horizontal-button = + .title = Uzi horizontalan rulumon +pdfjs-scroll-horizontal-button-label = Horizontala rulumo +pdfjs-scroll-wrapped-button = + .title = Uzi ambaŭdirektan rulumon +pdfjs-scroll-wrapped-button-label = Ambaŭdirekta rulumo +pdfjs-spread-none-button = + .title = Ne montri paĝojn po du +pdfjs-spread-none-button-label = Unupaĝa vido +pdfjs-spread-odd-button = + .title = Kunigi paĝojn komencante per nepara paĝo +pdfjs-spread-odd-button-label = Po du paĝoj, neparaj maldekstre +pdfjs-spread-even-button = + .title = Kunigi paĝojn komencante per para paĝo +pdfjs-spread-even-button-label = Po du paĝoj, paraj maldekstre + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Atributoj de dokumento… +pdfjs-document-properties-button-label = Atributoj de dokumento… +pdfjs-document-properties-file-name = Nomo de dosiero: +pdfjs-document-properties-file-size = Grando de dosiero: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KO ({ $b } oktetoj) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } Mo ({ $b } oktetoj) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KO ({ $size_b } oktetoj) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MO ({ $size_b } oktetoj) +pdfjs-document-properties-title = Titolo: +pdfjs-document-properties-author = Aŭtoro: +pdfjs-document-properties-subject = Temo: +pdfjs-document-properties-keywords = Ŝlosilvorto: +pdfjs-document-properties-creation-date = Dato de kreado: +pdfjs-document-properties-modification-date = Dato de modifo: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Kreinto: +pdfjs-document-properties-producer = Produktinto de PDF: +pdfjs-document-properties-version = Versio de PDF: +pdfjs-document-properties-page-count = Nombro de paĝoj: +pdfjs-document-properties-page-size = Grando de paĝo: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = vertikala +pdfjs-document-properties-page-size-orientation-landscape = horizontala +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letera +pdfjs-document-properties-page-size-name-legal = Jura + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Rapida tekstaĵa vido: +pdfjs-document-properties-linearized-yes = Jes +pdfjs-document-properties-linearized-no = Ne +pdfjs-document-properties-close-button = Fermi + +## Print + +pdfjs-print-progress-message = Preparo de dokumento por presi ĝin … +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Nuligi +pdfjs-printing-not-supported = Averto: tiu ĉi retumilo ne plene subtenas presadon. +pdfjs-printing-not-ready = Averto: la PDF dosiero ne estas plene ŝargita por presado. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Montri/kaŝi flankan strion +pdfjs-toggle-sidebar-notification-button = + .title = Montri/kaŝi flankan strion (la dokumento enhavas konturon/kunsendaĵojn/tavolojn) +pdfjs-toggle-sidebar-button-label = Montri/kaŝi flankan strion +pdfjs-document-outline-button = + .title = Montri la konturon de dokumento (alklaku duoble por faldi/malfaldi ĉiujn elementojn) +pdfjs-document-outline-button-label = Konturo de dokumento +pdfjs-attachments-button = + .title = Montri kunsendaĵojn +pdfjs-attachments-button-label = Kunsendaĵojn +pdfjs-layers-button = + .title = Montri tavolojn (duoble alklaku por remeti ĉiujn tavolojn en la norman staton) +pdfjs-layers-button-label = Tavoloj +pdfjs-thumbs-button = + .title = Montri miniaturojn +pdfjs-thumbs-button-label = Miniaturoj +pdfjs-current-outline-item-button = + .title = Trovi nunan konturan elementon +pdfjs-current-outline-item-button-label = Nuna kontura elemento +pdfjs-findbar-button = + .title = Serĉi en dokumento +pdfjs-findbar-button-label = Serĉi +pdfjs-additional-layers = Aldonaj tavoloj + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Paĝo { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniaturo de paĝo { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Serĉi + .placeholder = Serĉi en dokumento… +pdfjs-find-previous-button = + .title = Serĉi la antaŭan aperon de la frazo +pdfjs-find-previous-button-label = Malantaŭen +pdfjs-find-next-button = + .title = Serĉi la venontan aperon de la frazo +pdfjs-find-next-button-label = Antaŭen +pdfjs-find-highlight-checkbox = Elstarigi ĉiujn +pdfjs-find-match-case-checkbox-label = Distingi inter majuskloj kaj minuskloj +pdfjs-find-match-diacritics-checkbox-label = Respekti supersignojn +pdfjs-find-entire-word-checkbox-label = Tutaj vortoj +pdfjs-find-reached-top = Komenco de la dokumento atingita, daŭrigado ekde la fino +pdfjs-find-reached-bottom = Fino de la dokumento atingita, daŭrigado ekde la komenco +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } el { $total } kongruo + *[other] { $current } el { $total } kongruoj + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Pli ol { $limit } kongruo + *[other] Pli ol { $limit } kongruoj + } +pdfjs-find-not-found = Frazo ne trovita + +## Predefined zoom values + +pdfjs-page-scale-width = Larĝo de paĝo +pdfjs-page-scale-fit = Adapti paĝon +pdfjs-page-scale-auto = Aŭtomata skalo +pdfjs-page-scale-actual = Reala grando +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Paĝo { $page } + +## Loading indicator messages + +pdfjs-loading-error = Okazis eraro dum la ŝargado de la PDF dosiero. +pdfjs-invalid-file-error = Nevalida aŭ difektita PDF dosiero. +pdfjs-missing-file-error = Mankas dosiero PDF. +pdfjs-unexpected-response-error = Neatendita respondo de servilo. +pdfjs-rendering-error = Okazis eraro dum la montro de la paĝo. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Prinoto: { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Tajpu pasvorton por malfermi tiun ĉi dosieron PDF. +pdfjs-password-invalid = Nevalida pasvorto. Bonvolu provi denove. +pdfjs-password-ok-button = Akcepti +pdfjs-password-cancel-button = Nuligi +pdfjs-web-fonts-disabled = Neaktivaj teksaĵaj tiparoj: ne elbas uzi enmetitajn tiparojn de PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Teksto +pdfjs-editor-free-text-button-label = Teksto +pdfjs-editor-ink-button = + .title = Desegni +pdfjs-editor-ink-button-label = Desegni +pdfjs-editor-stamp-button = + .title = Aldoni aŭ modifi bildojn +pdfjs-editor-stamp-button-label = Aldoni aŭ modifi bildojn +pdfjs-editor-highlight-button = + .title = Elstarigi +pdfjs-editor-highlight-button-label = Elstarigi +pdfjs-highlight-floating-button1 = + .title = Elstarigi + .aria-label = Elstarigi +pdfjs-highlight-floating-button-label = Elstarigi + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Forigi desegnon +pdfjs-editor-remove-freetext-button = + .title = Forigi tekston +pdfjs-editor-remove-stamp-button = + .title = Forigi bildon +pdfjs-editor-remove-highlight-button = + .title = Forigi elstaraĵon + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Koloro +pdfjs-editor-free-text-size-input = Grando +pdfjs-editor-ink-color-input = Koloro +pdfjs-editor-ink-thickness-input = Dikeco +pdfjs-editor-ink-opacity-input = Maldiafaneco +pdfjs-editor-stamp-add-image-button = + .title = Aldoni bildon +pdfjs-editor-stamp-add-image-button-label = Aldoni bildon +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Dikeco +pdfjs-editor-free-highlight-thickness-title = + .title = Ŝanĝi dikecon dum elstarigo de netekstaj elementoj +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Teksta redaktilo + .default-content = Komencu tajpi… +pdfjs-free-text = + .aria-label = Teksta redaktilo +pdfjs-free-text-default-content = Ektajpi… +pdfjs-ink = + .aria-label = Desegnan redaktilon +pdfjs-ink-canvas = + .aria-label = Bildo kreita de uzanto + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alternativa teksto +pdfjs-editor-alt-text-edit-button = + .aria-label = Redakti alternativan tekston +pdfjs-editor-alt-text-edit-button-label = Redakti alternativan tekston +pdfjs-editor-alt-text-dialog-label = Elektu eblon +pdfjs-editor-alt-text-dialog-description = Alternativa teksto helpas personojn, en la okazoj kiam ili ne povas vidi aŭ ŝargi la bildon. +pdfjs-editor-alt-text-add-description-label = Aldoni priskribon +pdfjs-editor-alt-text-add-description-description = La celo estas unu aŭ du frazoj, kiuj priskribas la temon, etoson aŭ agojn. +pdfjs-editor-alt-text-mark-decorative-label = Marki kiel ornaman +pdfjs-editor-alt-text-mark-decorative-description = Tio ĉi estas uzita por ornamaj bildoj, kiel randoj aŭ fonaj bildoj. +pdfjs-editor-alt-text-cancel-button = Nuligi +pdfjs-editor-alt-text-save-button = Konservi +pdfjs-editor-alt-text-decorative-tooltip = Markita kiel ornama +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Ekzemple: “Juna persono sidiĝas ĉetable por ekmanĝi” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternativa teksto + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Supra maldekstra angulo — ŝangi grandon +pdfjs-editor-resizer-label-top-middle = Supra mezo — ŝanĝi grandon +pdfjs-editor-resizer-label-top-right = Supran dekstran angulon — ŝanĝi grandon +pdfjs-editor-resizer-label-middle-right = Dekstra mezo — ŝanĝi grandon +pdfjs-editor-resizer-label-bottom-right = Malsupra deksta angulo — ŝanĝi grandon +pdfjs-editor-resizer-label-bottom-middle = Malsupra mezo — ŝanĝi grandon +pdfjs-editor-resizer-label-bottom-left = Malsupra maldekstra angulo — ŝanĝi grandon +pdfjs-editor-resizer-label-middle-left = Maldekstra mezo — ŝanĝi grandon +pdfjs-editor-resizer-top-left = + .aria-label = Supra maldekstra angulo — ŝangi grandon +pdfjs-editor-resizer-top-middle = + .aria-label = Supra mezo — ŝanĝi grandon +pdfjs-editor-resizer-top-right = + .aria-label = Supran dekstran angulon — ŝanĝi grandon +pdfjs-editor-resizer-middle-right = + .aria-label = Dekstra mezo — ŝanĝi grandon +pdfjs-editor-resizer-bottom-right = + .aria-label = Malsupra deksta angulo — ŝanĝi grandon +pdfjs-editor-resizer-bottom-middle = + .aria-label = Malsupra mezo — ŝanĝi grandon +pdfjs-editor-resizer-bottom-left = + .aria-label = Malsupra maldekstra angulo — ŝanĝi grandon +pdfjs-editor-resizer-middle-left = + .aria-label = Maldekstra mezo — ŝanĝi grandon + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Elstarigi koloron +pdfjs-editor-colorpicker-button = + .title = Ŝanĝi koloron +pdfjs-editor-colorpicker-dropdown = + .aria-label = Elekto de koloroj +pdfjs-editor-colorpicker-yellow = + .title = Flava +pdfjs-editor-colorpicker-green = + .title = Verda +pdfjs-editor-colorpicker-blue = + .title = Blua +pdfjs-editor-colorpicker-pink = + .title = Roza +pdfjs-editor-colorpicker-red = + .title = Ruĝa + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Montri ĉiujn +pdfjs-editor-highlight-show-all-button = + .title = Montri ĉiujn + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Modifi alternativan tekston (priskribo de bildo) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Aldoni alternativan tekston (priskribo de bildo) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Skribu vian priskribon ĉi tie… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Mallonga priskribo por personoj kiuj ne povas vidi la bildon kaj por montri kiam la bildo ne ŝargeblas. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Tiu ĉi alternativa teksto estis aŭtomate kreita kaj povus esti malĝusta. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Pli da informo +pdfjs-editor-new-alt-text-create-automatically-button-label = Aŭtomate krei alternativan tekston +pdfjs-editor-new-alt-text-not-now-button = Ne nun +pdfjs-editor-new-alt-text-error-title = Ne eblis aŭtomate krei alternativan tekston +pdfjs-editor-new-alt-text-error-description = Bonvolu skribi vian propran alternativan tekston aŭ provi denove poste. +pdfjs-editor-new-alt-text-error-close-button = Fermi +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Elŝuto de modelo de artefarita intelekto por alternativa teksto ({ $downloadedSize } el { $totalSize } MO) + .aria-valuetext = Elŝuto de modelo de artefarita intelekto por alternativa teksto ({ $downloadedSize } el { $totalSize } MO) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternativa teksto aldonita +pdfjs-editor-new-alt-text-added-button-label = Alternativa teksto aldonita +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Mankas alternativa teksto +pdfjs-editor-new-alt-text-missing-button-label = Mankas alternativa teksto +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Kontroli alternativan tekston +pdfjs-editor-new-alt-text-to-review-button-label = Kontroli alternativan tekston +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Aŭtomate kreita: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Agordoj por alternativa teksto de bildoj +pdfjs-image-alt-text-settings-button-label = Agordoj por alternativa teksto de bildoj +pdfjs-editor-alt-text-settings-dialog-label = Agordoj por alternativa teksto de bildoj +pdfjs-editor-alt-text-settings-automatic-title = Aŭtomata alternativa teksto +pdfjs-editor-alt-text-settings-create-model-button-label = Aŭtomate krei alternativan tekston +pdfjs-editor-alt-text-settings-create-model-description = Tio ĉi sugestas priskribojn por helpi personojn kiuj ne povas vidi aŭ ŝargi la bildon. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Modelo de artefarita intelekto por alternativa teksto ({ $totalSize } MO) +pdfjs-editor-alt-text-settings-ai-model-description = Ĝi funkcias en via aparato, do viaj datumoj restas privataj. Ĝi estas postulata por aŭtomata kreado de alternativa teksto. +pdfjs-editor-alt-text-settings-delete-model-button = Forigi +pdfjs-editor-alt-text-settings-download-model-button = Elŝuti +pdfjs-editor-alt-text-settings-downloading-model-button = Elŝuto… +pdfjs-editor-alt-text-settings-editor-title = Redaktilo de alternativa teksto +pdfjs-editor-alt-text-settings-show-dialog-button-label = Montri redaktilon de alternativa teksto tuj post aldono de bildo +pdfjs-editor-alt-text-settings-show-dialog-description = Tio ĉi helpas vin kontroli ĉu ĉiuj bildoj havas alternativan tekston. +pdfjs-editor-alt-text-settings-close-button = Fermi + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Elstaraĵo forigita +pdfjs-editor-undo-bar-message-freetext = Teksto forigita +pdfjs-editor-undo-bar-message-ink = Desegno forigita +pdfjs-editor-undo-bar-message-stamp = Bildo forigita +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] unu prinoto forigita + *[other] { $count } prinotoj forigitaj + } +pdfjs-editor-undo-bar-undo-button = + .title = Malfari +pdfjs-editor-undo-bar-undo-button-label = Malfari +pdfjs-editor-undo-bar-close-button = + .title = Fermi +pdfjs-editor-undo-bar-close-button-label = Fermi diff --git a/public/pdfjs/web/locale/es-AR/viewer.ftl b/public/pdfjs/web/locale/es-AR/viewer.ftl new file mode 100644 index 0000000..ca73dc7 --- /dev/null +++ b/public/pdfjs/web/locale/es-AR/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Página anterior +pdfjs-previous-button-label = Anterior +pdfjs-next-button = + .title = Página siguiente +pdfjs-next-button-label = Siguiente +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Página +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = de { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ( { $pageNumber } de { $pagesCount } ) +pdfjs-zoom-out-button = + .title = Alejar +pdfjs-zoom-out-button-label = Alejar +pdfjs-zoom-in-button = + .title = Acercar +pdfjs-zoom-in-button-label = Acercar +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Cambiar a modo presentación +pdfjs-presentation-mode-button-label = Modo presentación +pdfjs-open-file-button = + .title = Abrir archivo +pdfjs-open-file-button-label = Abrir +pdfjs-print-button = + .title = Imprimir +pdfjs-print-button-label = Imprimir +pdfjs-save-button = + .title = Guardar +pdfjs-save-button-label = Guardar +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Descargar +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Descargar +pdfjs-bookmark-button = + .title = Página actual (Ver URL de la página actual) +pdfjs-bookmark-button-label = Página actual + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Herramientas +pdfjs-tools-button-label = Herramientas +pdfjs-first-page-button = + .title = Ir a primera página +pdfjs-first-page-button-label = Ir a primera página +pdfjs-last-page-button = + .title = Ir a última página +pdfjs-last-page-button-label = Ir a última página +pdfjs-page-rotate-cw-button = + .title = Rotar horario +pdfjs-page-rotate-cw-button-label = Rotar horario +pdfjs-page-rotate-ccw-button = + .title = Rotar antihorario +pdfjs-page-rotate-ccw-button-label = Rotar antihorario +pdfjs-cursor-text-select-tool-button = + .title = Habilitar herramienta de selección de texto +pdfjs-cursor-text-select-tool-button-label = Herramienta de selección de texto +pdfjs-cursor-hand-tool-button = + .title = Habilitar herramienta mano +pdfjs-cursor-hand-tool-button-label = Herramienta mano +pdfjs-scroll-page-button = + .title = Usar desplazamiento de página +pdfjs-scroll-page-button-label = Desplazamiento de página +pdfjs-scroll-vertical-button = + .title = Usar desplazamiento vertical +pdfjs-scroll-vertical-button-label = Desplazamiento vertical +pdfjs-scroll-horizontal-button = + .title = Usar desplazamiento vertical +pdfjs-scroll-horizontal-button-label = Desplazamiento horizontal +pdfjs-scroll-wrapped-button = + .title = Usar desplazamiento encapsulado +pdfjs-scroll-wrapped-button-label = Desplazamiento encapsulado +pdfjs-spread-none-button = + .title = No unir páginas dobles +pdfjs-spread-none-button-label = Sin dobles +pdfjs-spread-odd-button = + .title = Unir páginas dobles comenzando con las impares +pdfjs-spread-odd-button-label = Dobles impares +pdfjs-spread-even-button = + .title = Unir páginas dobles comenzando con las pares +pdfjs-spread-even-button-label = Dobles pares + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Propiedades del documento… +pdfjs-document-properties-button-label = Propiedades del documento… +pdfjs-document-properties-file-name = Nombre de archivo: +pdfjs-document-properties-file-size = Tamaño de archovo: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Título: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Asunto: +pdfjs-document-properties-keywords = Palabras clave: +pdfjs-document-properties-creation-date = Fecha de creación: +pdfjs-document-properties-modification-date = Fecha de modificación: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Creador: +pdfjs-document-properties-producer = PDF Productor: +pdfjs-document-properties-version = Versión de PDF: +pdfjs-document-properties-page-count = Cantidad de páginas: +pdfjs-document-properties-page-size = Tamaño de página: +pdfjs-document-properties-page-size-unit-inches = en +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = normal +pdfjs-document-properties-page-size-orientation-landscape = apaisado +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Carta +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Vista rápida de la Web: +pdfjs-document-properties-linearized-yes = Sí +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Cerrar + +## Print + +pdfjs-print-progress-message = Preparando documento para imprimir… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cancelar +pdfjs-printing-not-supported = Advertencia: La impresión no está totalmente soportada por este navegador. +pdfjs-printing-not-ready = Advertencia: El PDF no está completamente cargado para impresión. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Alternar barra lateral +pdfjs-toggle-sidebar-notification-button = + .title = Alternar barra lateral (el documento contiene esquemas/adjuntos/capas) +pdfjs-toggle-sidebar-button-label = Alternar barra lateral +pdfjs-document-outline-button = + .title = Mostrar esquema del documento (doble clic para expandir/colapsar todos los ítems) +pdfjs-document-outline-button-label = Esquema del documento +pdfjs-attachments-button = + .title = Mostrar adjuntos +pdfjs-attachments-button-label = Adjuntos +pdfjs-layers-button = + .title = Mostrar capas (doble clic para restablecer todas las capas al estado predeterminado) +pdfjs-layers-button-label = Capas +pdfjs-thumbs-button = + .title = Mostrar miniaturas +pdfjs-thumbs-button-label = Miniaturas +pdfjs-current-outline-item-button = + .title = Buscar elemento de esquema actual +pdfjs-current-outline-item-button-label = Elemento de esquema actual +pdfjs-findbar-button = + .title = Buscar en documento +pdfjs-findbar-button-label = Buscar +pdfjs-additional-layers = Capas adicionales + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Página { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura de página { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Buscar + .placeholder = Buscar en documento… +pdfjs-find-previous-button = + .title = Buscar la aparición anterior de la frase +pdfjs-find-previous-button-label = Anterior +pdfjs-find-next-button = + .title = Buscar la siguiente aparición de la frase +pdfjs-find-next-button-label = Siguiente +pdfjs-find-highlight-checkbox = Resaltar todo +pdfjs-find-match-case-checkbox-label = Coincidir mayúsculas +pdfjs-find-match-diacritics-checkbox-label = Coincidir diacríticos +pdfjs-find-entire-word-checkbox-label = Palabras completas +pdfjs-find-reached-top = Inicio de documento alcanzado, continuando desde abajo +pdfjs-find-reached-bottom = Fin de documento alcanzando, continuando desde arriba +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } de { $total } coincidencia + *[other] { $current } de { $total } coincidencias + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Más de { $limit } coincidencia + *[other] Más de { $limit } coincidencias + } +pdfjs-find-not-found = Frase no encontrada + +## Predefined zoom values + +pdfjs-page-scale-width = Ancho de página +pdfjs-page-scale-fit = Ajustar página +pdfjs-page-scale-auto = Zoom automático +pdfjs-page-scale-actual = Tamaño real +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Página { $page } + +## Loading indicator messages + +pdfjs-loading-error = Ocurrió un error al cargar el PDF. +pdfjs-invalid-file-error = Archivo PDF no válido o cocrrupto. +pdfjs-missing-file-error = Archivo PDF faltante. +pdfjs-unexpected-response-error = Respuesta del servidor inesperada. +pdfjs-rendering-error = Ocurrió un error al dibujar la página. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Anotación] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Ingrese la contraseña para abrir este archivo PDF +pdfjs-password-invalid = Contraseña inválida. Intente nuevamente. +pdfjs-password-ok-button = Aceptar +pdfjs-password-cancel-button = Cancelar +pdfjs-web-fonts-disabled = Tipografía web deshabilitada: no se pueden usar tipos incrustados en PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Texto +pdfjs-editor-free-text-button-label = Texto +pdfjs-editor-ink-button = + .title = Dibujar +pdfjs-editor-ink-button-label = Dibujar +pdfjs-editor-stamp-button = + .title = Agregar o editar imágenes +pdfjs-editor-stamp-button-label = Agregar o editar imágenes +pdfjs-editor-highlight-button = + .title = Resaltar +pdfjs-editor-highlight-button-label = Resaltar +pdfjs-highlight-floating-button1 = + .title = Resaltar + .aria-label = Resaltar +pdfjs-highlight-floating-button-label = Resaltar + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Eliminar dibujo +pdfjs-editor-remove-freetext-button = + .title = Eliminar texto +pdfjs-editor-remove-stamp-button = + .title = Eliminar imagen +pdfjs-editor-remove-highlight-button = + .title = Eliminar resaltado + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Color +pdfjs-editor-free-text-size-input = Tamaño +pdfjs-editor-ink-color-input = Color +pdfjs-editor-ink-thickness-input = Espesor +pdfjs-editor-ink-opacity-input = Opacidad +pdfjs-editor-stamp-add-image-button = + .title = Agregar una imagen +pdfjs-editor-stamp-add-image-button-label = Agregar una imagen +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Grosor +pdfjs-editor-free-highlight-thickness-title = + .title = Cambiar el grosor al resaltar elementos que no sean texto +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Comenzar a tipear… +pdfjs-free-text = + .aria-label = Editor de texto +pdfjs-free-text-default-content = Empezar a tipear… +pdfjs-ink = + .aria-label = Editor de dibujos +pdfjs-ink-canvas = + .aria-label = Imagen creada por el usuario + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Texto alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Editar texto alternativo +pdfjs-editor-alt-text-edit-button-label = Editar el texto alternativo +pdfjs-editor-alt-text-dialog-label = Eligir una opción +pdfjs-editor-alt-text-dialog-description = El texto alternativo (texto alternativo) ayuda cuando las personas no pueden ver la imagen o cuando no se carga. +pdfjs-editor-alt-text-add-description-label = Agregar una descripción +pdfjs-editor-alt-text-add-description-description = Intente escribir 1 o 2 oraciones que describan el tema, el entorno o las acciones. +pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorativo +pdfjs-editor-alt-text-mark-decorative-description = Esto se usa para imágenes ornamentales, como bordes o marcas de agua. +pdfjs-editor-alt-text-cancel-button = Cancelar +pdfjs-editor-alt-text-save-button = Guardar +pdfjs-editor-alt-text-decorative-tooltip = Marcado como decorativo +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Por ejemplo: “Un joven se sienta a la mesa a comer” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternativo + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Esquina superior izquierda — cambiar el tamaño +pdfjs-editor-resizer-label-top-middle = Arriba en el medio — cambiar el tamaño +pdfjs-editor-resizer-label-top-right = Esquina superior derecha — cambiar el tamaño +pdfjs-editor-resizer-label-middle-right = Al centro a la derecha — cambiar el tamaño +pdfjs-editor-resizer-label-bottom-right = Esquina inferior derecha — cambiar el tamaño +pdfjs-editor-resizer-label-bottom-middle = Abajo en el medio — cambiar el tamaño +pdfjs-editor-resizer-label-bottom-left = Esquina inferior izquierda — cambiar el tamaño +pdfjs-editor-resizer-label-middle-left = Al centro a la izquierda — cambiar el tamaño +pdfjs-editor-resizer-top-left = + .aria-label = Esquina superior izquierda — cambiar el tamaño +pdfjs-editor-resizer-top-middle = + .aria-label = Arriba en el medio — cambiar el tamaño +pdfjs-editor-resizer-top-right = + .aria-label = Esquina superior derecha — cambiar el tamaño +pdfjs-editor-resizer-middle-right = + .aria-label = Al centro a la derecha — cambiar el tamaño +pdfjs-editor-resizer-bottom-right = + .aria-label = Esquina inferior derecha — cambiar el tamaño +pdfjs-editor-resizer-bottom-middle = + .aria-label = Abajo en el medio — cambiar el tamaño +pdfjs-editor-resizer-bottom-left = + .aria-label = Esquina inferior izquierda — cambiar el tamaño +pdfjs-editor-resizer-middle-left = + .aria-label = Al centro a la izquierda — cambiar el tamaño + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Color de resaltado +pdfjs-editor-colorpicker-button = + .title = Cambiar el color +pdfjs-editor-colorpicker-dropdown = + .aria-label = Opciones de color +pdfjs-editor-colorpicker-yellow = + .title = Amarillo +pdfjs-editor-colorpicker-green = + .title = Verde +pdfjs-editor-colorpicker-blue = + .title = Azul +pdfjs-editor-colorpicker-pink = + .title = Rosado +pdfjs-editor-colorpicker-red = + .title = Rojo + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Mostrar todo +pdfjs-editor-highlight-show-all-button = + .title = Mostrar todo + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Editar texto alternativo (descripción de la imagen) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Agregar texto alternativo (descripción de la imagen) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Escribir la descripción aquí… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Descripción corta para las personas que no pueden ver la imagen o cuando la imagen no se carga. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Este texto alternativo fue creado automáticamente y puede ser incorrecto. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Conocer más +pdfjs-editor-new-alt-text-create-automatically-button-label = Crear texto alternativo automáticamente +pdfjs-editor-new-alt-text-not-now-button = No ahora +pdfjs-editor-new-alt-text-error-title = No se pudo crear el texto alternativo automáticamente +pdfjs-editor-new-alt-text-error-description = Escriba su propio texto alternativo o pruebe nuevamente más tarde. +pdfjs-editor-new-alt-text-error-close-button = Cerrar +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Descargando modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) + .aria-valuetext = Descargando modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Texto alternativo agregado +pdfjs-editor-new-alt-text-added-button-label = Texto alternativo agregado +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Falta el texto alternativo +pdfjs-editor-new-alt-text-missing-button-label = Falta el texto alternativo +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Calificar el texto alternativo +pdfjs-editor-new-alt-text-to-review-button-label = Revisar el texto alternativo +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Creado automáticamente: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Configuración de texto alternativo de la imagen +pdfjs-image-alt-text-settings-button-label = Configuración de texto alternativo de la imagen +pdfjs-editor-alt-text-settings-dialog-label = Configuración de texto alternativo de la imagen +pdfjs-editor-alt-text-settings-automatic-title = Texto alternativo automático +pdfjs-editor-alt-text-settings-create-model-button-label = Crear texto alternativo automáticamente +pdfjs-editor-alt-text-settings-create-model-description = Sugiere descripciones para ayudar a las personas que no pueden ver la imagen o cuando la imagen no se carga. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Modelo de IA de texto alternativo ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Se ejecuta localmente en el dispositivo para que los datos se mantengan privados. Requerido para texto alternativo automático. +pdfjs-editor-alt-text-settings-delete-model-button = Borrar +pdfjs-editor-alt-text-settings-download-model-button = Descargar +pdfjs-editor-alt-text-settings-downloading-model-button = Descargando… +pdfjs-editor-alt-text-settings-editor-title = Editor de texto alternativo +pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostrar el editor de texto alternativo inmediatamente al agregar una imagen +pdfjs-editor-alt-text-settings-show-dialog-description = Te ayuda a asegurarse de que todas las imágenes tengan texto alternativo. +pdfjs-editor-alt-text-settings-close-button = Cerrar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Resaltado eliminado +pdfjs-editor-undo-bar-message-freetext = Texto eliminado +pdfjs-editor-undo-bar-message-ink = Dibujo eliminado +pdfjs-editor-undo-bar-message-stamp = Imagen eliminado +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotación eliminada + *[other] { $count } anotaciones eliminadas + } +pdfjs-editor-undo-bar-undo-button = + .title = Deshacer +pdfjs-editor-undo-bar-undo-button-label = Deshacer +pdfjs-editor-undo-bar-close-button = + .title = Cerrar +pdfjs-editor-undo-bar-close-button-label = Cerrar diff --git a/public/pdfjs/web/locale/es-CL/viewer.ftl b/public/pdfjs/web/locale/es-CL/viewer.ftl new file mode 100644 index 0000000..74389e4 --- /dev/null +++ b/public/pdfjs/web/locale/es-CL/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Página anterior +pdfjs-previous-button-label = Anterior +pdfjs-next-button = + .title = Página siguiente +pdfjs-next-button-label = Siguiente +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Página +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = de { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount }) +pdfjs-zoom-out-button = + .title = Alejar +pdfjs-zoom-out-button-label = Alejar +pdfjs-zoom-in-button = + .title = Acercar +pdfjs-zoom-in-button-label = Acercar +pdfjs-zoom-select = + .title = Ampliación +pdfjs-presentation-mode-button = + .title = Cambiar al modo de presentación +pdfjs-presentation-mode-button-label = Modo de presentación +pdfjs-open-file-button = + .title = Abrir archivo +pdfjs-open-file-button-label = Abrir +pdfjs-print-button = + .title = Imprimir +pdfjs-print-button-label = Imprimir +pdfjs-save-button = + .title = Guardar +pdfjs-save-button-label = Guardar +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Descargar +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Descargar +pdfjs-bookmark-button = + .title = Página actual (Ver URL de la página actual) +pdfjs-bookmark-button-label = Página actual + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Herramientas +pdfjs-tools-button-label = Herramientas +pdfjs-first-page-button = + .title = Ir a la primera página +pdfjs-first-page-button-label = Ir a la primera página +pdfjs-last-page-button = + .title = Ir a la última página +pdfjs-last-page-button-label = Ir a la última página +pdfjs-page-rotate-cw-button = + .title = Girar a la derecha +pdfjs-page-rotate-cw-button-label = Girar a la derecha +pdfjs-page-rotate-ccw-button = + .title = Girar a la izquierda +pdfjs-page-rotate-ccw-button-label = Girar a la izquierda +pdfjs-cursor-text-select-tool-button = + .title = Activar la herramienta de selección de texto +pdfjs-cursor-text-select-tool-button-label = Herramienta de selección de texto +pdfjs-cursor-hand-tool-button = + .title = Activar la herramienta de mano +pdfjs-cursor-hand-tool-button-label = Herramienta de mano +pdfjs-scroll-page-button = + .title = Usar desplazamiento de página +pdfjs-scroll-page-button-label = Desplazamiento de página +pdfjs-scroll-vertical-button = + .title = Usar desplazamiento vertical +pdfjs-scroll-vertical-button-label = Desplazamiento vertical +pdfjs-scroll-horizontal-button = + .title = Usar desplazamiento horizontal +pdfjs-scroll-horizontal-button-label = Desplazamiento horizontal +pdfjs-scroll-wrapped-button = + .title = Usar desplazamiento en bloque +pdfjs-scroll-wrapped-button-label = Desplazamiento en bloque +pdfjs-spread-none-button = + .title = No juntar páginas a modo de libro +pdfjs-spread-none-button-label = Vista de una página +pdfjs-spread-odd-button = + .title = Junta las páginas partiendo con una de número impar +pdfjs-spread-odd-button-label = Vista de libro impar +pdfjs-spread-even-button = + .title = Junta las páginas partiendo con una de número par +pdfjs-spread-even-button-label = Vista de libro par + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Propiedades del documento… +pdfjs-document-properties-button-label = Propiedades del documento… +pdfjs-document-properties-file-name = Nombre de archivo: +pdfjs-document-properties-file-size = Tamaño del archivo: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Título: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Asunto: +pdfjs-document-properties-keywords = Palabras clave: +pdfjs-document-properties-creation-date = Fecha de creación: +pdfjs-document-properties-modification-date = Fecha de modificación: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Creador: +pdfjs-document-properties-producer = Productor del PDF: +pdfjs-document-properties-version = Versión de PDF: +pdfjs-document-properties-page-count = Cantidad de páginas: +pdfjs-document-properties-page-size = Tamaño de la página: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = vertical +pdfjs-document-properties-page-size-orientation-landscape = horizontal +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Carta +pdfjs-document-properties-page-size-name-legal = Oficio + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Vista rápida en Web: +pdfjs-document-properties-linearized-yes = Sí +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Cerrar + +## Print + +pdfjs-print-progress-message = Preparando documento para impresión… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cancelar +pdfjs-printing-not-supported = Advertencia: Imprimir no está soportado completamente por este navegador. +pdfjs-printing-not-ready = Advertencia: El PDF no está completamente cargado para ser impreso. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Barra lateral +pdfjs-toggle-sidebar-notification-button = + .title = Cambiar barra lateral (índice de contenidos del documento/adjuntos/capas) +pdfjs-toggle-sidebar-button-label = Mostrar u ocultar la barra lateral +pdfjs-document-outline-button = + .title = Mostrar esquema del documento (doble clic para expandir/contraer todos los elementos) +pdfjs-document-outline-button-label = Esquema del documento +pdfjs-attachments-button = + .title = Mostrar adjuntos +pdfjs-attachments-button-label = Adjuntos +pdfjs-layers-button = + .title = Mostrar capas (doble clic para restablecer todas las capas al estado predeterminado) +pdfjs-layers-button-label = Capas +pdfjs-thumbs-button = + .title = Mostrar miniaturas +pdfjs-thumbs-button-label = Miniaturas +pdfjs-current-outline-item-button = + .title = Buscar elemento de esquema actual +pdfjs-current-outline-item-button-label = Elemento de esquema actual +pdfjs-findbar-button = + .title = Buscar en el documento +pdfjs-findbar-button-label = Buscar +pdfjs-additional-layers = Capas adicionales + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Página { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura de la página { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Encontrar + .placeholder = Encontrar en el documento… +pdfjs-find-previous-button = + .title = Buscar la aparición anterior de la frase +pdfjs-find-previous-button-label = Previo +pdfjs-find-next-button = + .title = Buscar la siguiente aparición de la frase +pdfjs-find-next-button-label = Siguiente +pdfjs-find-highlight-checkbox = Destacar todos +pdfjs-find-match-case-checkbox-label = Coincidir mayús./minús. +pdfjs-find-match-diacritics-checkbox-label = Coincidir diacríticos +pdfjs-find-entire-word-checkbox-label = Palabras completas +pdfjs-find-reached-top = Se alcanzó el inicio del documento, continuando desde el final +pdfjs-find-reached-bottom = Se alcanzó el final del documento, continuando desde el inicio +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] Coincidencia { $current } de { $total } + *[other] Coincidencia { $current } de { $total } + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Más de { $limit } coincidencia + *[other] Más de { $limit } coincidencias + } +pdfjs-find-not-found = Frase no encontrada + +## Predefined zoom values + +pdfjs-page-scale-width = Ancho de página +pdfjs-page-scale-fit = Ajuste de página +pdfjs-page-scale-auto = Aumento automático +pdfjs-page-scale-actual = Tamaño actual +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Página { $page } + +## Loading indicator messages + +pdfjs-loading-error = Ocurrió un error al cargar el PDF. +pdfjs-invalid-file-error = Archivo PDF inválido o corrupto. +pdfjs-missing-file-error = Falta el archivo PDF. +pdfjs-unexpected-response-error = Respuesta del servidor inesperada. +pdfjs-rendering-error = Ocurrió un error al renderizar la página. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Anotación] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Ingresa la contraseña para abrir este archivo PDF. +pdfjs-password-invalid = Contraseña inválida. Por favor, vuelve a intentarlo. +pdfjs-password-ok-button = Aceptar +pdfjs-password-cancel-button = Cancelar +pdfjs-web-fonts-disabled = Las tipografías web están desactivadas: imposible usar las fuentes PDF embebidas. + +## Editing + +pdfjs-editor-free-text-button = + .title = Texto +pdfjs-editor-free-text-button-label = Texto +pdfjs-editor-ink-button = + .title = Dibujar +pdfjs-editor-ink-button-label = Dibujar +pdfjs-editor-stamp-button = + .title = Añadir o editar imágenes +pdfjs-editor-stamp-button-label = Añadir o editar imágenes +pdfjs-editor-highlight-button = + .title = Destacar +pdfjs-editor-highlight-button-label = Destacar +pdfjs-highlight-floating-button1 = + .title = Destacar + .aria-label = Destacar +pdfjs-highlight-floating-button-label = Destacar + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Eliminar dibujo +pdfjs-editor-remove-freetext-button = + .title = Eliminar texto +pdfjs-editor-remove-stamp-button = + .title = Eliminar imagen +pdfjs-editor-remove-highlight-button = + .title = Quitar resaltado + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Color +pdfjs-editor-free-text-size-input = Tamaño +pdfjs-editor-ink-color-input = Color +pdfjs-editor-ink-thickness-input = Grosor +pdfjs-editor-ink-opacity-input = Opacidad +pdfjs-editor-stamp-add-image-button = + .title = Añadir imagen +pdfjs-editor-stamp-add-image-button-label = Añadir imagen +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Grosor +pdfjs-editor-free-highlight-thickness-title = + .title = Cambia el grosor al resaltar elementos que no sean texto +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Empieza a escribir… +pdfjs-free-text = + .aria-label = Editor de texto +pdfjs-free-text-default-content = Empieza a escribir… +pdfjs-ink = + .aria-label = Editor de dibujos +pdfjs-ink-canvas = + .aria-label = Imagen creada por el usuario + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Texto alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Editar texto alternativo +pdfjs-editor-alt-text-edit-button-label = Editar texto alternativo +pdfjs-editor-alt-text-dialog-label = Elige una opción +pdfjs-editor-alt-text-dialog-description = El texto alternativo (alt text) ayuda cuando las personas no pueden ver la imagen o cuando no se carga. +pdfjs-editor-alt-text-add-description-label = Añade una descripción +pdfjs-editor-alt-text-add-description-description = Intenta escribir 1 o 2 oraciones que describan el tema, el ambiente o las acciones. +pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorativa +pdfjs-editor-alt-text-mark-decorative-description = Se utiliza para imágenes ornamentales, como bordes o marcas de agua. +pdfjs-editor-alt-text-cancel-button = Cancelar +pdfjs-editor-alt-text-save-button = Guardar +pdfjs-editor-alt-text-decorative-tooltip = Marcada como decorativa +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Por ejemplo: “Un joven se sienta a la mesa a comer” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternativo + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Esquina superior izquierda — cambiar el tamaño +pdfjs-editor-resizer-label-top-middle = Borde superior en el medio — cambiar el tamaño +pdfjs-editor-resizer-label-top-right = Esquina superior derecha — cambiar el tamaño +pdfjs-editor-resizer-label-middle-right = Borde derecho en el medio — cambiar el tamaño +pdfjs-editor-resizer-label-bottom-right = Esquina inferior derecha — cambiar el tamaño +pdfjs-editor-resizer-label-bottom-middle = Borde inferior en el medio — cambiar el tamaño +pdfjs-editor-resizer-label-bottom-left = Esquina inferior izquierda — cambiar el tamaño +pdfjs-editor-resizer-label-middle-left = Borde izquierdo en el medio — cambiar el tamaño +pdfjs-editor-resizer-top-left = + .aria-label = Esquina superior izquierda — cambiar el tamaño +pdfjs-editor-resizer-top-middle = + .aria-label = Borde superior en el medio — cambiar el tamaño +pdfjs-editor-resizer-top-right = + .aria-label = Esquina superior derecha — cambiar el tamaño +pdfjs-editor-resizer-middle-right = + .aria-label = Borde derecho en el medio — cambiar el tamaño +pdfjs-editor-resizer-bottom-right = + .aria-label = Esquina inferior derecha — cambiar el tamaño +pdfjs-editor-resizer-bottom-middle = + .aria-label = Borde inferior en el medio — cambiar el tamaño +pdfjs-editor-resizer-bottom-left = + .aria-label = Esquina inferior izquierda — cambiar el tamaño +pdfjs-editor-resizer-middle-left = + .aria-label = Borde izquierdo en el medio — cambiar el tamaño + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Color de resaltado +pdfjs-editor-colorpicker-button = + .title = Cambiar color +pdfjs-editor-colorpicker-dropdown = + .aria-label = Opciones de color +pdfjs-editor-colorpicker-yellow = + .title = Amarillo +pdfjs-editor-colorpicker-green = + .title = Verde +pdfjs-editor-colorpicker-blue = + .title = Azul +pdfjs-editor-colorpicker-pink = + .title = Rosa +pdfjs-editor-colorpicker-red = + .title = Rojo + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Mostrar todo +pdfjs-editor-highlight-show-all-button = + .title = Mostrar todo + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Editar texto alternativo (descripción de la imagen) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Añadir texto alternativo (descripción de la imagen) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Escribe tu descripción aquí… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Breve descripción para las personas que no pueden ver la imagen o cuando la imagen no se carga. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Este texto alternativo fue creado automáticamente y puede ser incorrecto. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Aprender más +pdfjs-editor-new-alt-text-create-automatically-button-label = Crear texto alternativo automáticamente +pdfjs-editor-new-alt-text-not-now-button = Ahora no +pdfjs-editor-new-alt-text-error-title = No se pudo crear el texto alternativo automáticamente +pdfjs-editor-new-alt-text-error-description = Escribe tu propio texto alternativo o vuelve a intentarlo más tarde. +pdfjs-editor-new-alt-text-error-close-button = Cerrar +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Descargando el modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) + .aria-valuetext = Descargando el modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Se añadió el texto alternativo +pdfjs-editor-new-alt-text-added-button-label = Se añadió el texto alternativo +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Falta el texto alternativo +pdfjs-editor-new-alt-text-missing-button-label = Falta el texto alternativo +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Revisar el texto alternativo +pdfjs-editor-new-alt-text-to-review-button-label = Revisar el texto alternativo +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Creado automáticamente: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Ajustes del texto alternativo de la imagen +pdfjs-image-alt-text-settings-button-label = Ajustes del texto alternativo de la imagen +pdfjs-editor-alt-text-settings-dialog-label = Ajustes del texto alternativo de la imagen +pdfjs-editor-alt-text-settings-automatic-title = Texto alternativo automático +pdfjs-editor-alt-text-settings-create-model-button-label = Crear texto alternativo automáticamente +pdfjs-editor-alt-text-settings-create-model-description = Sugiere descripciones para ayudar a las personas que no pueden ver la imagen o cuando la imagen no se carga. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Modelo de IA de texto alternativo ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Se ejecuta localmente en tu dispositivo para que tus datos permanezcan privados. Necesario para el texto alternativo automático. +pdfjs-editor-alt-text-settings-delete-model-button = Eliminar +pdfjs-editor-alt-text-settings-download-model-button = Descargar +pdfjs-editor-alt-text-settings-downloading-model-button = Bajando… +pdfjs-editor-alt-text-settings-editor-title = Editor de texto alternativo +pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostrar el editor de texto alternativo inmediatamente al añadir una imagen +pdfjs-editor-alt-text-settings-show-dialog-description = Te ayuda a asegurarte de que todas tus imágenes tengan texto alternativo. +pdfjs-editor-alt-text-settings-close-button = Cerrar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Resaltado eliminado +pdfjs-editor-undo-bar-message-freetext = Texto eliminado +pdfjs-editor-undo-bar-message-ink = Dibujo eliminado +pdfjs-editor-undo-bar-message-stamp = Imagen eliminada +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotación eliminada + *[other] { $count } anotaciones eliminadas + } +pdfjs-editor-undo-bar-undo-button = + .title = Deshacer +pdfjs-editor-undo-bar-undo-button-label = Deshacer +pdfjs-editor-undo-bar-close-button = + .title = Cerrar +pdfjs-editor-undo-bar-close-button-label = Cerrar diff --git a/public/pdfjs/web/locale/es-ES/viewer.ftl b/public/pdfjs/web/locale/es-ES/viewer.ftl new file mode 100644 index 0000000..f610a17 --- /dev/null +++ b/public/pdfjs/web/locale/es-ES/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Página anterior +pdfjs-previous-button-label = Anterior +pdfjs-next-button = + .title = Página siguiente +pdfjs-next-button-label = Siguiente +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Página +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = de { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount }) +pdfjs-zoom-out-button = + .title = Reducir +pdfjs-zoom-out-button-label = Reducir +pdfjs-zoom-in-button = + .title = Aumentar +pdfjs-zoom-in-button-label = Aumentar +pdfjs-zoom-select = + .title = Tamaño +pdfjs-presentation-mode-button = + .title = Cambiar al modo presentación +pdfjs-presentation-mode-button-label = Modo presentación +pdfjs-open-file-button = + .title = Abrir archivo +pdfjs-open-file-button-label = Abrir +pdfjs-print-button = + .title = Imprimir +pdfjs-print-button-label = Imprimir +pdfjs-save-button = + .title = Guardar +pdfjs-save-button-label = Guardar +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Descargar +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Descargar +pdfjs-bookmark-button = + .title = Página actual (Ver URL de la página actual) +pdfjs-bookmark-button-label = Página actual + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Herramientas +pdfjs-tools-button-label = Herramientas +pdfjs-first-page-button = + .title = Ir a la primera página +pdfjs-first-page-button-label = Ir a la primera página +pdfjs-last-page-button = + .title = Ir a la última página +pdfjs-last-page-button-label = Ir a la última página +pdfjs-page-rotate-cw-button = + .title = Rotar en sentido horario +pdfjs-page-rotate-cw-button-label = Rotar en sentido horario +pdfjs-page-rotate-ccw-button = + .title = Rotar en sentido antihorario +pdfjs-page-rotate-ccw-button-label = Rotar en sentido antihorario +pdfjs-cursor-text-select-tool-button = + .title = Activar herramienta de selección de texto +pdfjs-cursor-text-select-tool-button-label = Herramienta de selección de texto +pdfjs-cursor-hand-tool-button = + .title = Activar herramienta de mano +pdfjs-cursor-hand-tool-button-label = Herramienta de mano +pdfjs-scroll-page-button = + .title = Usar desplazamiento de página +pdfjs-scroll-page-button-label = Desplazamiento de página +pdfjs-scroll-vertical-button = + .title = Usar desplazamiento vertical +pdfjs-scroll-vertical-button-label = Desplazamiento vertical +pdfjs-scroll-horizontal-button = + .title = Usar desplazamiento horizontal +pdfjs-scroll-horizontal-button-label = Desplazamiento horizontal +pdfjs-scroll-wrapped-button = + .title = Usar desplazamiento en bloque +pdfjs-scroll-wrapped-button-label = Desplazamiento en bloque +pdfjs-spread-none-button = + .title = No juntar páginas en vista de libro +pdfjs-spread-none-button-label = Vista de libro +pdfjs-spread-odd-button = + .title = Juntar las páginas partiendo de una con número impar +pdfjs-spread-odd-button-label = Vista de libro impar +pdfjs-spread-even-button = + .title = Juntar las páginas partiendo de una con número par +pdfjs-spread-even-button-label = Vista de libro par + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Propiedades del documento… +pdfjs-document-properties-button-label = Propiedades del documento… +pdfjs-document-properties-file-name = Nombre de archivo: +pdfjs-document-properties-file-size = Tamaño de archivo: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Título: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Asunto: +pdfjs-document-properties-keywords = Palabras clave: +pdfjs-document-properties-creation-date = Fecha de creación: +pdfjs-document-properties-modification-date = Fecha de modificación: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Creador: +pdfjs-document-properties-producer = Productor PDF: +pdfjs-document-properties-version = Versión PDF: +pdfjs-document-properties-page-count = Número de páginas: +pdfjs-document-properties-page-size = Tamaño de la página: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = vertical +pdfjs-document-properties-page-size-orientation-landscape = horizontal +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Carta +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Vista rápida de la web: +pdfjs-document-properties-linearized-yes = Sí +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Cerrar + +## Print + +pdfjs-print-progress-message = Preparando documento para impresión… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cancelar +pdfjs-printing-not-supported = Advertencia: Imprimir no está totalmente soportado por este navegador. +pdfjs-printing-not-ready = Advertencia: Este PDF no se ha cargado completamente para poder imprimirse. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Cambiar barra lateral +pdfjs-toggle-sidebar-notification-button = + .title = Alternar barra lateral (el documento contiene esquemas/adjuntos/capas) +pdfjs-toggle-sidebar-button-label = Cambiar barra lateral +pdfjs-document-outline-button = + .title = Mostrar resumen del documento (doble clic para expandir/contraer todos los elementos) +pdfjs-document-outline-button-label = Resumen de documento +pdfjs-attachments-button = + .title = Mostrar adjuntos +pdfjs-attachments-button-label = Adjuntos +pdfjs-layers-button = + .title = Mostrar capas (doble clic para restablecer todas las capas al estado predeterminado) +pdfjs-layers-button-label = Capas +pdfjs-thumbs-button = + .title = Mostrar miniaturas +pdfjs-thumbs-button-label = Miniaturas +pdfjs-current-outline-item-button = + .title = Encontrar elemento de esquema actual +pdfjs-current-outline-item-button-label = Elemento de esquema actual +pdfjs-findbar-button = + .title = Buscar en el documento +pdfjs-findbar-button-label = Buscar +pdfjs-additional-layers = Capas adicionales + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Página { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura de la página { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Buscar + .placeholder = Buscar en el documento… +pdfjs-find-previous-button = + .title = Encontrar la anterior aparición de la frase +pdfjs-find-previous-button-label = Anterior +pdfjs-find-next-button = + .title = Encontrar la siguiente aparición de esta frase +pdfjs-find-next-button-label = Siguiente +pdfjs-find-highlight-checkbox = Resaltar todos +pdfjs-find-match-case-checkbox-label = Coincidencia de mayús./minús. +pdfjs-find-match-diacritics-checkbox-label = Coincidir diacríticos +pdfjs-find-entire-word-checkbox-label = Palabras completas +pdfjs-find-reached-top = Se alcanzó el inicio del documento, se continúa desde el final +pdfjs-find-reached-bottom = Se alcanzó el final del documento, se continúa desde el inicio +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } de { $total } coincidencia + *[other] { $current } de { $total } coincidencias + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Más de { $limit } coincidencia + *[other] Más de { $limit } coincidencias + } +pdfjs-find-not-found = Frase no encontrada + +## Predefined zoom values + +pdfjs-page-scale-width = Anchura de la página +pdfjs-page-scale-fit = Ajuste de la página +pdfjs-page-scale-auto = Tamaño automático +pdfjs-page-scale-actual = Tamaño real +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Página { $page } + +## Loading indicator messages + +pdfjs-loading-error = Ocurrió un error al cargar el PDF. +pdfjs-invalid-file-error = Fichero PDF no válido o corrupto. +pdfjs-missing-file-error = No hay fichero PDF. +pdfjs-unexpected-response-error = Respuesta inesperada del servidor. +pdfjs-rendering-error = Ocurrió un error al renderizar la página. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anotación { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Introduzca la contraseña para abrir este archivo PDF. +pdfjs-password-invalid = Contraseña no válida. Vuelva a intentarlo. +pdfjs-password-ok-button = Aceptar +pdfjs-password-cancel-button = Cancelar +pdfjs-web-fonts-disabled = Las tipografías web están desactivadas: es imposible usar las tipografías PDF embebidas. + +## Editing + +pdfjs-editor-free-text-button = + .title = Texto +pdfjs-editor-free-text-button-label = Texto +pdfjs-editor-ink-button = + .title = Dibujar +pdfjs-editor-ink-button-label = Dibujar +pdfjs-editor-stamp-button = + .title = Añadir o editar imágenes +pdfjs-editor-stamp-button-label = Añadir o editar imágenes +pdfjs-editor-highlight-button = + .title = Resaltar +pdfjs-editor-highlight-button-label = Resaltar +pdfjs-highlight-floating-button1 = + .title = Resaltar + .aria-label = Resaltar +pdfjs-highlight-floating-button-label = Resaltar + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Eliminar dibujo +pdfjs-editor-remove-freetext-button = + .title = Eliminar texto +pdfjs-editor-remove-stamp-button = + .title = Eliminar imagen +pdfjs-editor-remove-highlight-button = + .title = Quitar resaltado + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Color +pdfjs-editor-free-text-size-input = Tamaño +pdfjs-editor-ink-color-input = Color +pdfjs-editor-ink-thickness-input = Grosor +pdfjs-editor-ink-opacity-input = Opacidad +pdfjs-editor-stamp-add-image-button = + .title = Añadir imagen +pdfjs-editor-stamp-add-image-button-label = Añadir imagen +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Grosor +pdfjs-editor-free-highlight-thickness-title = + .title = Cambiar el grosor al resaltar elementos que no sean texto +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Empiece a escribir… +pdfjs-free-text = + .aria-label = Editor de texto +pdfjs-free-text-default-content = Empezar a escribir… +pdfjs-ink = + .aria-label = Editor de dibujos +pdfjs-ink-canvas = + .aria-label = Imagen creada por el usuario + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Texto alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Editar el texto alternativo +pdfjs-editor-alt-text-edit-button-label = Editar el texto alternativo +pdfjs-editor-alt-text-dialog-label = Eligir una opción +pdfjs-editor-alt-text-dialog-description = El texto alternativo (texto alternativo) ayuda cuando las personas no pueden ver la imagen o cuando no se carga. +pdfjs-editor-alt-text-add-description-label = Añadir una descripción +pdfjs-editor-alt-text-add-description-description = Intente escribir 1 o 2 frases que describan el tema, el entorno o las acciones. +pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorativa +pdfjs-editor-alt-text-mark-decorative-description = Se utiliza para imágenes ornamentales, como bordes o marcas de agua. +pdfjs-editor-alt-text-cancel-button = Cancelar +pdfjs-editor-alt-text-save-button = Guardar +pdfjs-editor-alt-text-decorative-tooltip = Marcada como decorativa +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Por ejemplo: “Un joven se sienta a la mesa a comer” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternativo + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Esquina superior izquierda — redimensionar +pdfjs-editor-resizer-label-top-middle = Borde superior en el medio — redimensionar +pdfjs-editor-resizer-label-top-right = Esquina superior derecha — redimensionar +pdfjs-editor-resizer-label-middle-right = Borde derecho en el medio — redimensionar +pdfjs-editor-resizer-label-bottom-right = Esquina inferior derecha — redimensionar +pdfjs-editor-resizer-label-bottom-middle = Borde inferior en el medio — redimensionar +pdfjs-editor-resizer-label-bottom-left = Esquina inferior izquierda — redimensionar +pdfjs-editor-resizer-label-middle-left = Borde izquierdo en el medio — redimensionar +pdfjs-editor-resizer-top-left = + .aria-label = Esquina superior izquierda — redimensionar +pdfjs-editor-resizer-top-middle = + .aria-label = Borde superior en el medio — redimensionar +pdfjs-editor-resizer-top-right = + .aria-label = Esquina superior derecha — redimensionar +pdfjs-editor-resizer-middle-right = + .aria-label = Borde derecho en el medio — redimensionar +pdfjs-editor-resizer-bottom-right = + .aria-label = Esquina inferior derecha — redimensionar +pdfjs-editor-resizer-bottom-middle = + .aria-label = Borde inferior en el medio — redimensionar +pdfjs-editor-resizer-bottom-left = + .aria-label = Esquina inferior izquierda — redimensionar +pdfjs-editor-resizer-middle-left = + .aria-label = Borde izquierdo en el medio — redimensionar + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Color de resaltado +pdfjs-editor-colorpicker-button = + .title = Cambiar color +pdfjs-editor-colorpicker-dropdown = + .aria-label = Opciones de color +pdfjs-editor-colorpicker-yellow = + .title = Amarillo +pdfjs-editor-colorpicker-green = + .title = Verde +pdfjs-editor-colorpicker-blue = + .title = Azul +pdfjs-editor-colorpicker-pink = + .title = Rosa +pdfjs-editor-colorpicker-red = + .title = Rojo + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Mostrar todo +pdfjs-editor-highlight-show-all-button = + .title = Mostrar todo + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Editar texto alternativo (descripción de la imagen) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Añadir texto alternativo (descripción de la imagen) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Escribir la descripción aquí… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Breve descripción para las personas que no pueden ver la imagen o cuando la imagen no se carga. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Este texto alternativo fue creado automáticamente y puede ser inexacto. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Saber más +pdfjs-editor-new-alt-text-create-automatically-button-label = Crear texto alternativo automáticamente +pdfjs-editor-new-alt-text-not-now-button = Ahora no +pdfjs-editor-new-alt-text-error-title = No se ha podido crear el texto alternativo automáticamente +pdfjs-editor-new-alt-text-error-description = Escriba su propio texto alternativo o inténtelo de nuevo más tarde. +pdfjs-editor-new-alt-text-error-close-button = Cerrar +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Descargando el modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) + .aria-valuetext = Descargando el modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Se añadió el texto alternativo +pdfjs-editor-new-alt-text-added-button-label = Se añadió el texto alternativo +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Falta el texto alternativo +pdfjs-editor-new-alt-text-missing-button-label = Falta el texto alternativo +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Revisar el texto alternativo +pdfjs-editor-new-alt-text-to-review-button-label = Revisar el texto alternativo +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Creado automáticamente: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Ajustes del texto alternativo de la imagen +pdfjs-image-alt-text-settings-button-label = Ajustes del texto alternativo de la imagen +pdfjs-editor-alt-text-settings-dialog-label = Ajustes del texto alternativo de la imagen +pdfjs-editor-alt-text-settings-automatic-title = Texto alternativo automático +pdfjs-editor-alt-text-settings-create-model-button-label = Crear texto alternativo automáticamente +pdfjs-editor-alt-text-settings-create-model-description = Sugiere descripciones para ayudar a las personas que no pueden ver la imagen o cuando la imagen no se carga. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Modelo de IA de texto alternativo ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Se ejecuta localmente en el dispositivo para que los datos se mantengan privados. Requerido para texto alternativo automático. +pdfjs-editor-alt-text-settings-delete-model-button = Eliminar +pdfjs-editor-alt-text-settings-download-model-button = Descargar +pdfjs-editor-alt-text-settings-downloading-model-button = Descargando… +pdfjs-editor-alt-text-settings-editor-title = Editor de texto alternativo +pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostrar el editor de texto alternativo inmediatamente al añadir una imagen +pdfjs-editor-alt-text-settings-show-dialog-description = Le ayuda a asegurarse de que todas sus imágenes tengan texto alternativo. +pdfjs-editor-alt-text-settings-close-button = Cerrar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Resaltado eliminado +pdfjs-editor-undo-bar-message-freetext = Texto eliminado +pdfjs-editor-undo-bar-message-ink = Dibujo eliminado +pdfjs-editor-undo-bar-message-stamp = Imagen eliminada +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotación eliminada + *[other] { $count } anotaciones eliminadas + } +pdfjs-editor-undo-bar-undo-button = + .title = Deshacer +pdfjs-editor-undo-bar-undo-button-label = Deshacer +pdfjs-editor-undo-bar-close-button = + .title = Cerrar +pdfjs-editor-undo-bar-close-button-label = Cerrar diff --git a/public/pdfjs/web/locale/es-MX/viewer.ftl b/public/pdfjs/web/locale/es-MX/viewer.ftl new file mode 100644 index 0000000..03afe7c --- /dev/null +++ b/public/pdfjs/web/locale/es-MX/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Página anterior +pdfjs-previous-button-label = Anterior +pdfjs-next-button = + .title = Página siguiente +pdfjs-next-button-label = Siguiente +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Página +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = de { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount }) +pdfjs-zoom-out-button = + .title = Reducir +pdfjs-zoom-out-button-label = Reducir +pdfjs-zoom-in-button = + .title = Aumentar +pdfjs-zoom-in-button-label = Aumentar +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Cambiar al modo presentación +pdfjs-presentation-mode-button-label = Modo presentación +pdfjs-open-file-button = + .title = Abrir archivo +pdfjs-open-file-button-label = Abrir +pdfjs-print-button = + .title = Imprimir +pdfjs-print-button-label = Imprimir +pdfjs-save-button = + .title = Guardar +pdfjs-save-button-label = Guardar +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Descargar +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Descargar +pdfjs-bookmark-button = + .title = Página actual (Ver URL de la página actual) +pdfjs-bookmark-button-label = Página actual + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Herramientas +pdfjs-tools-button-label = Herramientas +pdfjs-first-page-button = + .title = Ir a la primera página +pdfjs-first-page-button-label = Ir a la primera página +pdfjs-last-page-button = + .title = Ir a la última página +pdfjs-last-page-button-label = Ir a la última página +pdfjs-page-rotate-cw-button = + .title = Girar a la derecha +pdfjs-page-rotate-cw-button-label = Girar a la derecha +pdfjs-page-rotate-ccw-button = + .title = Girar a la izquierda +pdfjs-page-rotate-ccw-button-label = Girar a la izquierda +pdfjs-cursor-text-select-tool-button = + .title = Activar la herramienta de selección de texto +pdfjs-cursor-text-select-tool-button-label = Herramienta de selección de texto +pdfjs-cursor-hand-tool-button = + .title = Activar la herramienta de mano +pdfjs-cursor-hand-tool-button-label = Herramienta de mano +pdfjs-scroll-page-button = + .title = Usar desplazamiento de página +pdfjs-scroll-page-button-label = Desplazamiento de página +pdfjs-scroll-vertical-button = + .title = Usar desplazamiento vertical +pdfjs-scroll-vertical-button-label = Desplazamiento vertical +pdfjs-scroll-horizontal-button = + .title = Usar desplazamiento horizontal +pdfjs-scroll-horizontal-button-label = Desplazamiento horizontal +pdfjs-scroll-wrapped-button = + .title = Usar desplazamiento encapsulado +pdfjs-scroll-wrapped-button-label = Desplazamiento encapsulado +pdfjs-spread-none-button = + .title = No unir páginas separadas +pdfjs-spread-none-button-label = Vista de una página +pdfjs-spread-odd-button = + .title = Unir las páginas partiendo con una de número impar +pdfjs-spread-odd-button-label = Vista de libro impar +pdfjs-spread-even-button = + .title = Juntar las páginas partiendo con una de número par +pdfjs-spread-even-button-label = Vista de libro par + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Propiedades del documento… +pdfjs-document-properties-button-label = Propiedades del documento… +pdfjs-document-properties-file-name = Nombre del archivo: +pdfjs-document-properties-file-size = Tamaño del archivo: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Título: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Asunto: +pdfjs-document-properties-keywords = Palabras claves: +pdfjs-document-properties-creation-date = Fecha de creación: +pdfjs-document-properties-modification-date = Fecha de modificación: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Creador: +pdfjs-document-properties-producer = Productor PDF: +pdfjs-document-properties-version = Versión PDF: +pdfjs-document-properties-page-count = Número de páginas: +pdfjs-document-properties-page-size = Tamaño de la página: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = vertical +pdfjs-document-properties-page-size-orientation-landscape = horizontal +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Carta +pdfjs-document-properties-page-size-name-legal = Oficio + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Vista rápida de la web: +pdfjs-document-properties-linearized-yes = Sí +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Cerrar + +## Print + +pdfjs-print-progress-message = Preparando documento para impresión… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cancelar +pdfjs-printing-not-supported = Advertencia: La impresión no esta completamente soportada por este navegador. +pdfjs-printing-not-ready = Advertencia: El PDF no cargo completamente para impresión. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Cambiar barra lateral +pdfjs-toggle-sidebar-notification-button = + .title = Alternar barra lateral (el documento contiene esquemas/adjuntos/capas) +pdfjs-toggle-sidebar-button-label = Cambiar barra lateral +pdfjs-document-outline-button = + .title = Mostrar esquema del documento (doble clic para expandir/contraer todos los elementos) +pdfjs-document-outline-button-label = Esquema del documento +pdfjs-attachments-button = + .title = Mostrar adjuntos +pdfjs-attachments-button-label = Adjuntos +pdfjs-layers-button = + .title = Mostrar capas (doble clic para restablecer todas las capas al estado predeterminado) +pdfjs-layers-button-label = Capas +pdfjs-thumbs-button = + .title = Mostrar miniaturas +pdfjs-thumbs-button-label = Miniaturas +pdfjs-current-outline-item-button = + .title = Buscar elemento de esquema actual +pdfjs-current-outline-item-button-label = Elemento de esquema actual +pdfjs-findbar-button = + .title = Buscar en el documento +pdfjs-findbar-button-label = Buscar +pdfjs-additional-layers = Capas adicionales + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Página { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura de la página { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Buscar + .placeholder = Buscar en el documento… +pdfjs-find-previous-button = + .title = Ir a la anterior frase encontrada +pdfjs-find-previous-button-label = Anterior +pdfjs-find-next-button = + .title = Ir a la siguiente frase encontrada +pdfjs-find-next-button-label = Siguiente +pdfjs-find-highlight-checkbox = Resaltar todo +pdfjs-find-match-case-checkbox-label = Coincidir con mayúsculas y minúsculas +pdfjs-find-match-diacritics-checkbox-label = Coincidir diacríticos +pdfjs-find-entire-word-checkbox-label = Palabras completas +pdfjs-find-reached-top = Se alcanzó el inicio del documento, se buscará al final +pdfjs-find-reached-bottom = Se alcanzó el final del documento, se buscará al inicio +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } de { $total } coincidencia + *[other] { $current } de { $total } coincidencias + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Más de { $limit } coincidencia + *[other] Más de { $limit } coincidencias + } +pdfjs-find-not-found = No se encontró la frase + +## Predefined zoom values + +pdfjs-page-scale-width = Ancho de página +pdfjs-page-scale-fit = Ajustar página +pdfjs-page-scale-auto = Zoom automático +pdfjs-page-scale-actual = Tamaño real +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Página { $page } + +## Loading indicator messages + +pdfjs-loading-error = Un error ocurrió al cargar el PDF. +pdfjs-invalid-file-error = Archivo PDF invalido o dañado. +pdfjs-missing-file-error = Archivo PDF no encontrado. +pdfjs-unexpected-response-error = Respuesta inesperada del servidor. +pdfjs-rendering-error = Un error ocurrió al renderizar la página. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } anotación] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Ingresa la contraseña para abrir este archivo PDF. +pdfjs-password-invalid = Contraseña inválida. Por favor intenta de nuevo. +pdfjs-password-ok-button = Aceptar +pdfjs-password-cancel-button = Cancelar +pdfjs-web-fonts-disabled = Las fuentes web están desactivadas: es imposible usar las fuentes PDF embebidas. + +## Editing + +pdfjs-editor-free-text-button = + .title = Texto +pdfjs-editor-free-text-button-label = Texto +pdfjs-editor-ink-button = + .title = Dibujar +pdfjs-editor-ink-button-label = Dibujar +pdfjs-editor-stamp-button = + .title = Agregar o editar imágenes +pdfjs-editor-stamp-button-label = Agregar o editar imágenes +pdfjs-editor-highlight-button = + .title = Destacar +pdfjs-editor-highlight-button-label = Destacar +pdfjs-highlight-floating-button1 = + .title = Destacados + .aria-label = Destacados +pdfjs-highlight-floating-button-label = Destacados + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Eliminar dibujo +pdfjs-editor-remove-freetext-button = + .title = Eliminar texto +pdfjs-editor-remove-stamp-button = + .title = Eliminar imagen +pdfjs-editor-remove-highlight-button = + .title = Eliminar destacado + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Color +pdfjs-editor-free-text-size-input = Tamaño +pdfjs-editor-ink-color-input = Color +pdfjs-editor-ink-thickness-input = Grossor +pdfjs-editor-ink-opacity-input = Opacidad +pdfjs-editor-stamp-add-image-button = + .title = Agregar imagen +pdfjs-editor-stamp-add-image-button-label = Agregar imagen +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Espesor +pdfjs-editor-free-highlight-thickness-title = + .title = Cambiar el grosor al resaltar elementos que no sean texto +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Comenzar a escribir… +pdfjs-free-text = + .aria-label = Editor de texto +pdfjs-free-text-default-content = Empieza a escribir… +pdfjs-ink = + .aria-label = Editor de dibujo +pdfjs-ink-canvas = + .aria-label = Imagen creada por el usuario + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Texto alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Editar texto alternativo +pdfjs-editor-alt-text-edit-button-label = Editar texto alternativo +pdfjs-editor-alt-text-dialog-label = Elige una opción +pdfjs-editor-alt-text-dialog-description = El texto alternativo (texto alternativo) ayuda cuando las personas no pueden ver la imagen o cuando no se carga. +pdfjs-editor-alt-text-add-description-label = Añadir una descripción +pdfjs-editor-alt-text-add-description-description = Intente escribir 1 o 2 oraciones que describan el tema, el entorno o las acciones. +pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorativo +pdfjs-editor-alt-text-mark-decorative-description = Se utiliza para imágenes ornamentales, como bordes o marcas de agua. +pdfjs-editor-alt-text-cancel-button = Cancelar +pdfjs-editor-alt-text-save-button = Guardar +pdfjs-editor-alt-text-decorative-tooltip = Marcado como decorativo +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Por ejemplo: “Un joven se sienta a la mesa a comer” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternativo + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Esquina superior izquierda: cambiar el tamaño +pdfjs-editor-resizer-label-top-middle = Arriba en el medio: cambiar el tamaño +pdfjs-editor-resizer-label-top-right = Esquina superior derecha: cambiar el tamaño +pdfjs-editor-resizer-label-middle-right = Centro derecha: cambiar el tamaño +pdfjs-editor-resizer-label-bottom-right = Esquina inferior derecha: cambiar el tamaño +pdfjs-editor-resizer-label-bottom-middle = Abajo en el medio: cambiar el tamaño +pdfjs-editor-resizer-label-bottom-left = Esquina inferior izquierda: cambiar el tamaño +pdfjs-editor-resizer-label-middle-left = Centro izquierda: cambiar el tamaño +pdfjs-editor-resizer-top-left = + .aria-label = Esquina superior izquierda — redimensionar +pdfjs-editor-resizer-top-middle = + .aria-label = Borde superior en el medio — redimensionar +pdfjs-editor-resizer-top-right = + .aria-label = Esquina superior derecha — redimensionar +pdfjs-editor-resizer-middle-right = + .aria-label = Borde derecho en el medio — redimensionar +pdfjs-editor-resizer-bottom-right = + .aria-label = Esquina inferior derecha — redimensionar +pdfjs-editor-resizer-bottom-middle = + .aria-label = Borde inferior en el medio — redimensionar +pdfjs-editor-resizer-bottom-left = + .aria-label = Esquina inferior izquierda — redimensionar +pdfjs-editor-resizer-middle-left = + .aria-label = Borde izquierdo en el medio — redimensionar + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Color de resaltado +pdfjs-editor-colorpicker-button = + .title = Cambiar color +pdfjs-editor-colorpicker-dropdown = + .aria-label = Opciones de color +pdfjs-editor-colorpicker-yellow = + .title = Amarillo +pdfjs-editor-colorpicker-green = + .title = Verde +pdfjs-editor-colorpicker-blue = + .title = Azul +pdfjs-editor-colorpicker-pink = + .title = Rosa +pdfjs-editor-colorpicker-red = + .title = Rojo + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Mostrar todo +pdfjs-editor-highlight-show-all-button = + .title = Mostrar todo + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Editar texto alternativo (descripción de la imagen) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Agregar texto alternativo (descripción de la imagen) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Escribe tu descripción aquí… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Breve descripción para las personas que no pueden ver la imagen o cuando la imagen no se carga. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Este texto alternativo fue creado automáticamente y puede ser inexacto. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Saber más +pdfjs-editor-new-alt-text-create-automatically-button-label = Crear texto alternativo automáticamente +pdfjs-editor-new-alt-text-not-now-button = Ahora no +pdfjs-editor-new-alt-text-error-title = No se pudo crear el texto alternativo automáticamente +pdfjs-editor-new-alt-text-error-description = Escribe tu propio texto alternativo o inténtalo de nuevo más tarde. +pdfjs-editor-new-alt-text-error-close-button = Cerrar +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Descargando el modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) + .aria-valuetext = Descargando el modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Se agregó el texto alternativo +pdfjs-editor-new-alt-text-added-button-label = Se agregó el texto alternativo +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Falta el texto alternativo +pdfjs-editor-new-alt-text-missing-button-label = Falta texto alternativo +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Revisar el texto alternativo +pdfjs-editor-new-alt-text-to-review-button-label = Revisar el texto alternativo +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Creado automáticamente: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Ajustes del texto alternativo de la imagen +pdfjs-image-alt-text-settings-button-label = Ajustes del texto alternativo de la imagen +pdfjs-editor-alt-text-settings-dialog-label = Ajustes del texto alternativo de la imagen +pdfjs-editor-alt-text-settings-automatic-title = Texto alternativo automático +pdfjs-editor-alt-text-settings-create-model-button-label = Crear texto alternativo automáticamente +pdfjs-editor-alt-text-settings-create-model-description = Sugiere descripciones para ayudar a las personas que no pueden ver la imagen o cuando la imagen no se carga. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Modelo de IA de texto alternativo ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Se ejecuta localmente en el dispositivo para que los datos se mantengan privados. Requerido para texto alternativo automático. +pdfjs-editor-alt-text-settings-delete-model-button = Eliminar +pdfjs-editor-alt-text-settings-download-model-button = Descargar +pdfjs-editor-alt-text-settings-downloading-model-button = Descargando… +pdfjs-editor-alt-text-settings-editor-title = Editor de texto alternativo +pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostrar el editor de texto alternativo inmediatamente al añadir una imagen +pdfjs-editor-alt-text-settings-show-dialog-description = Te ayuda a asegurarte de que todas tus imágenes tengan texto alternativo. +pdfjs-editor-alt-text-settings-close-button = Cerrar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Resaltado eliminado +pdfjs-editor-undo-bar-message-freetext = Texto eliminado +pdfjs-editor-undo-bar-message-ink = Dibujo eliminado +pdfjs-editor-undo-bar-message-stamp = Imagen eliminada +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotación eliminada + *[other] { $count } anotaciones eliminadas + } +pdfjs-editor-undo-bar-undo-button = + .title = Deshacer +pdfjs-editor-undo-bar-undo-button-label = Deshacer +pdfjs-editor-undo-bar-close-button = + .title = Cerrar +pdfjs-editor-undo-bar-close-button-label = Cerrar diff --git a/public/pdfjs/web/locale/et/viewer.ftl b/public/pdfjs/web/locale/et/viewer.ftl new file mode 100644 index 0000000..b28c6d5 --- /dev/null +++ b/public/pdfjs/web/locale/et/viewer.ftl @@ -0,0 +1,268 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Eelmine lehekülg +pdfjs-previous-button-label = Eelmine +pdfjs-next-button = + .title = Järgmine lehekülg +pdfjs-next-button-label = Järgmine +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Leht +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = / { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber }/{ $pagesCount }) +pdfjs-zoom-out-button = + .title = Vähenda +pdfjs-zoom-out-button-label = Vähenda +pdfjs-zoom-in-button = + .title = Suurenda +pdfjs-zoom-in-button-label = Suurenda +pdfjs-zoom-select = + .title = Suurendamine +pdfjs-presentation-mode-button = + .title = Lülitu esitlusrežiimi +pdfjs-presentation-mode-button-label = Esitlusrežiim +pdfjs-open-file-button = + .title = Ava fail +pdfjs-open-file-button-label = Ava +pdfjs-print-button = + .title = Prindi +pdfjs-print-button-label = Prindi + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Tööriistad +pdfjs-tools-button-label = Tööriistad +pdfjs-first-page-button = + .title = Mine esimesele leheküljele +pdfjs-first-page-button-label = Mine esimesele leheküljele +pdfjs-last-page-button = + .title = Mine viimasele leheküljele +pdfjs-last-page-button-label = Mine viimasele leheküljele +pdfjs-page-rotate-cw-button = + .title = Pööra päripäeva +pdfjs-page-rotate-cw-button-label = Pööra päripäeva +pdfjs-page-rotate-ccw-button = + .title = Pööra vastupäeva +pdfjs-page-rotate-ccw-button-label = Pööra vastupäeva +pdfjs-cursor-text-select-tool-button = + .title = Luba teksti valimise tööriist +pdfjs-cursor-text-select-tool-button-label = Teksti valimise tööriist +pdfjs-cursor-hand-tool-button = + .title = Luba sirvimistööriist +pdfjs-cursor-hand-tool-button-label = Sirvimistööriist +pdfjs-scroll-page-button = + .title = Kasutatakse lehe kaupa kerimist +pdfjs-scroll-page-button-label = Lehe kaupa kerimine +pdfjs-scroll-vertical-button = + .title = Kasuta vertikaalset kerimist +pdfjs-scroll-vertical-button-label = Vertikaalne kerimine +pdfjs-scroll-horizontal-button = + .title = Kasuta horisontaalset kerimist +pdfjs-scroll-horizontal-button-label = Horisontaalne kerimine +pdfjs-scroll-wrapped-button = + .title = Kasuta rohkem mahutavat kerimist +pdfjs-scroll-wrapped-button-label = Rohkem mahutav kerimine +pdfjs-spread-none-button = + .title = Ära kõrvuta lehekülgi +pdfjs-spread-none-button-label = Lehtede kõrvutamine puudub +pdfjs-spread-odd-button = + .title = Kõrvuta leheküljed, alustades paaritute numbritega lehekülgedega +pdfjs-spread-odd-button-label = Kõrvutamine paaritute numbritega alustades +pdfjs-spread-even-button = + .title = Kõrvuta leheküljed, alustades paarisnumbritega lehekülgedega +pdfjs-spread-even-button-label = Kõrvutamine paarisnumbritega alustades + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumendi omadused… +pdfjs-document-properties-button-label = Dokumendi omadused… +pdfjs-document-properties-file-name = Faili nimi: +pdfjs-document-properties-file-size = Faili suurus: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KiB ({ $size_b } baiti) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MiB ({ $size_b } baiti) +pdfjs-document-properties-title = Pealkiri: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Teema: +pdfjs-document-properties-keywords = Märksõnad: +pdfjs-document-properties-creation-date = Loodud: +pdfjs-document-properties-modification-date = Muudetud: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date } { $time } +pdfjs-document-properties-creator = Looja: +pdfjs-document-properties-producer = Generaator: +pdfjs-document-properties-version = Generaatori versioon: +pdfjs-document-properties-page-count = Lehekülgi: +pdfjs-document-properties-page-size = Lehe suurus: +pdfjs-document-properties-page-size-unit-inches = tolli +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = vertikaalpaigutus +pdfjs-document-properties-page-size-orientation-landscape = rõhtpaigutus +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = "Fast Web View" tugi: +pdfjs-document-properties-linearized-yes = Jah +pdfjs-document-properties-linearized-no = Ei +pdfjs-document-properties-close-button = Sulge + +## Print + +pdfjs-print-progress-message = Dokumendi ettevalmistamine printimiseks… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Loobu +pdfjs-printing-not-supported = Hoiatus: printimine pole selle brauseri poolt täielikult toetatud. +pdfjs-printing-not-ready = Hoiatus: PDF pole printimiseks täielikult laaditud. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Näita külgriba +pdfjs-toggle-sidebar-notification-button = + .title = Näita külgriba (dokument sisaldab sisukorda/manuseid/kihte) +pdfjs-toggle-sidebar-button-label = Näita külgriba +pdfjs-document-outline-button = + .title = Näita sisukorda (kõigi punktide laiendamiseks/ahendamiseks topeltklõpsa) +pdfjs-document-outline-button-label = Näita sisukorda +pdfjs-attachments-button = + .title = Näita manuseid +pdfjs-attachments-button-label = Manused +pdfjs-layers-button = + .title = Näita kihte (kõikide kihtide vaikeolekusse lähtestamiseks topeltklõpsa) +pdfjs-layers-button-label = Kihid +pdfjs-thumbs-button = + .title = Näita pisipilte +pdfjs-thumbs-button-label = Pisipildid +pdfjs-current-outline-item-button = + .title = Otsi üles praegune kontuuriüksus +pdfjs-current-outline-item-button-label = Praegune kontuuriüksus +pdfjs-findbar-button = + .title = Otsi dokumendist +pdfjs-findbar-button-label = Otsi +pdfjs-additional-layers = Täiendavad kihid + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = { $page }. lehekülg +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page }. lehekülje pisipilt + +## Find panel button title and messages + +pdfjs-find-input = + .title = Otsi + .placeholder = Otsi dokumendist… +pdfjs-find-previous-button = + .title = Otsi fraasi eelmine esinemiskoht +pdfjs-find-previous-button-label = Eelmine +pdfjs-find-next-button = + .title = Otsi fraasi järgmine esinemiskoht +pdfjs-find-next-button-label = Järgmine +pdfjs-find-highlight-checkbox = Too kõik esile +pdfjs-find-match-case-checkbox-label = Tõstutundlik +pdfjs-find-match-diacritics-checkbox-label = Otsitakse diakriitiliselt +pdfjs-find-entire-word-checkbox-label = Täissõnad +pdfjs-find-reached-top = Jõuti dokumendi algusesse, jätkati lõpust +pdfjs-find-reached-bottom = Jõuti dokumendi lõppu, jätkati algusest +pdfjs-find-not-found = Fraasi ei leitud + +## Predefined zoom values + +pdfjs-page-scale-width = Mahuta laiusele +pdfjs-page-scale-fit = Mahuta leheküljele +pdfjs-page-scale-auto = Automaatne suurendamine +pdfjs-page-scale-actual = Tegelik suurus +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Lehekülg { $page } + +## Loading indicator messages + +pdfjs-loading-error = PDFi laadimisel esines viga. +pdfjs-invalid-file-error = Vigane või rikutud PDF-fail. +pdfjs-missing-file-error = PDF-fail puudub. +pdfjs-unexpected-response-error = Ootamatu vastus serverilt. +pdfjs-rendering-error = Lehe renderdamisel esines viga. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date } { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] + +## Password + +pdfjs-password-label = PDF-faili avamiseks sisesta parool. +pdfjs-password-invalid = Vigane parool. Palun proovi uuesti. +pdfjs-password-ok-button = Sobib +pdfjs-password-cancel-button = Loobu +pdfjs-web-fonts-disabled = Veebifondid on keelatud: PDFiga kaasatud fonte pole võimalik kasutada. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/eu/viewer.ftl b/public/pdfjs/web/locale/eu/viewer.ftl new file mode 100644 index 0000000..2b2660b --- /dev/null +++ b/public/pdfjs/web/locale/eu/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Aurreko orria +pdfjs-previous-button-label = Aurrekoa +pdfjs-next-button = + .title = Hurrengo orria +pdfjs-next-button-label = Hurrengoa +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Orria +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = / { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = { $pagesCount }/{ $pageNumber } +pdfjs-zoom-out-button = + .title = Urrundu zooma +pdfjs-zoom-out-button-label = Urrundu zooma +pdfjs-zoom-in-button = + .title = Gerturatu zooma +pdfjs-zoom-in-button-label = Gerturatu zooma +pdfjs-zoom-select = + .title = Zooma +pdfjs-presentation-mode-button = + .title = Aldatu aurkezpen modura +pdfjs-presentation-mode-button-label = Arkezpen modua +pdfjs-open-file-button = + .title = Ireki fitxategia +pdfjs-open-file-button-label = Ireki +pdfjs-print-button = + .title = Inprimatu +pdfjs-print-button-label = Inprimatu +pdfjs-save-button = + .title = Gorde +pdfjs-save-button-label = Gorde +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Deskargatu +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Deskargatu +pdfjs-bookmark-button = + .title = Uneko orria (ikusi uneko orriaren URLa) +pdfjs-bookmark-button-label = Uneko orria + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Tresnak +pdfjs-tools-button-label = Tresnak +pdfjs-first-page-button = + .title = Joan lehen orrira +pdfjs-first-page-button-label = Joan lehen orrira +pdfjs-last-page-button = + .title = Joan azken orrira +pdfjs-last-page-button-label = Joan azken orrira +pdfjs-page-rotate-cw-button = + .title = Biratu erlojuaren norantzan +pdfjs-page-rotate-cw-button-label = Biratu erlojuaren norantzan +pdfjs-page-rotate-ccw-button = + .title = Biratu erlojuaren aurkako norantzan +pdfjs-page-rotate-ccw-button-label = Biratu erlojuaren aurkako norantzan +pdfjs-cursor-text-select-tool-button = + .title = Gaitu testuaren hautapen tresna +pdfjs-cursor-text-select-tool-button-label = Testuaren hautapen tresna +pdfjs-cursor-hand-tool-button = + .title = Gaitu eskuaren tresna +pdfjs-cursor-hand-tool-button-label = Eskuaren tresna +pdfjs-scroll-page-button = + .title = Erabili orriaren korritzea +pdfjs-scroll-page-button-label = Orriaren korritzea +pdfjs-scroll-vertical-button = + .title = Erabili korritze bertikala +pdfjs-scroll-vertical-button-label = Korritze bertikala +pdfjs-scroll-horizontal-button = + .title = Erabili korritze horizontala +pdfjs-scroll-horizontal-button-label = Korritze horizontala +pdfjs-scroll-wrapped-button = + .title = Erabili korritze egokitua +pdfjs-scroll-wrapped-button-label = Korritze egokitua +pdfjs-spread-none-button = + .title = Ez elkartu barreiatutako orriak +pdfjs-spread-none-button-label = Barreiatzerik ez +pdfjs-spread-odd-button = + .title = Elkartu barreiatutako orriak bakoiti zenbakidunekin hasita +pdfjs-spread-odd-button-label = Barreiatze bakoitia +pdfjs-spread-even-button = + .title = Elkartu barreiatutako orriak bikoiti zenbakidunekin hasita +pdfjs-spread-even-button-label = Barreiatze bikoitia + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumentuaren propietateak… +pdfjs-document-properties-button-label = Dokumentuaren propietateak… +pdfjs-document-properties-file-name = Fitxategi-izena: +pdfjs-document-properties-file-size = Fitxategiaren tamaina: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } byte) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } byte) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } byte) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } byte) +pdfjs-document-properties-title = Izenburua: +pdfjs-document-properties-author = Egilea: +pdfjs-document-properties-subject = Gaia: +pdfjs-document-properties-keywords = Gako-hitzak: +pdfjs-document-properties-creation-date = Sortze-data: +pdfjs-document-properties-modification-date = Aldatze-data: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Sortzailea: +pdfjs-document-properties-producer = PDFaren ekoizlea: +pdfjs-document-properties-version = PDF bertsioa: +pdfjs-document-properties-page-count = Orrialde kopurua: +pdfjs-document-properties-page-size = Orriaren tamaina: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = bertikala +pdfjs-document-properties-page-size-orientation-landscape = horizontala +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Gutuna +pdfjs-document-properties-page-size-name-legal = Legala + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Webeko ikuspegi bizkorra: +pdfjs-document-properties-linearized-yes = Bai +pdfjs-document-properties-linearized-no = Ez +pdfjs-document-properties-close-button = Itxi + +## Print + +pdfjs-print-progress-message = Dokumentua inprimatzeko prestatzen… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = %{ $progress } +pdfjs-print-progress-close-button = Utzi +pdfjs-printing-not-supported = Abisua: inprimatzeko euskarria ez da erabatekoa nabigatzaile honetan. +pdfjs-printing-not-ready = Abisua: PDFa ez dago erabat kargatuta inprimatzeko. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Txandakatu alboko barra +pdfjs-toggle-sidebar-notification-button = + .title = Txandakatu alboko barra (dokumentuak eskema/eranskinak/geruzak ditu) +pdfjs-toggle-sidebar-button-label = Txandakatu alboko barra +pdfjs-document-outline-button = + .title = Erakutsi dokumentuaren eskema (klik bikoitza elementu guztiak zabaltzeko/tolesteko) +pdfjs-document-outline-button-label = Dokumentuaren eskema +pdfjs-attachments-button = + .title = Erakutsi eranskinak +pdfjs-attachments-button-label = Eranskinak +pdfjs-layers-button = + .title = Erakutsi geruzak (klik bikoitza geruza guztiak egoera lehenetsira berrezartzeko) +pdfjs-layers-button-label = Geruzak +pdfjs-thumbs-button = + .title = Erakutsi koadro txikiak +pdfjs-thumbs-button-label = Koadro txikiak +pdfjs-current-outline-item-button = + .title = Bilatu uneko eskemaren elementua +pdfjs-current-outline-item-button-label = Uneko eskemaren elementua +pdfjs-findbar-button = + .title = Bilatu dokumentuan +pdfjs-findbar-button-label = Bilatu +pdfjs-additional-layers = Geruza gehigarriak + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = { $page }. orria +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page }. orriaren koadro txikia + +## Find panel button title and messages + +pdfjs-find-input = + .title = Bilatu + .placeholder = Bilatu dokumentuan… +pdfjs-find-previous-button = + .title = Bilatu esaldiaren aurreko parekatzea +pdfjs-find-previous-button-label = Aurrekoa +pdfjs-find-next-button = + .title = Bilatu esaldiaren hurrengo parekatzea +pdfjs-find-next-button-label = Hurrengoa +pdfjs-find-highlight-checkbox = Nabarmendu guztia +pdfjs-find-match-case-checkbox-label = Bat etorri maiuskulekin/minuskulekin +pdfjs-find-match-diacritics-checkbox-label = Bereizi diakritikoak +pdfjs-find-entire-word-checkbox-label = Hitz osoak +pdfjs-find-reached-top = Dokumentuaren hasierara heldu da, bukaeratik jarraitzen +pdfjs-find-reached-bottom = Dokumentuaren bukaerara heldu da, hasieratik jarraitzen +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $total }/{ $current }. bat-etortzea + *[other] { $total }/{ $current }. bat-etortzea + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Bat datorren { $limit } baino gehiago + *[other] Bat datozen { $limit } baino gehiago + } +pdfjs-find-not-found = Esaldia ez da aurkitu + +## Predefined zoom values + +pdfjs-page-scale-width = Orriaren zabalera +pdfjs-page-scale-fit = Doitu orrira +pdfjs-page-scale-auto = Zoom automatikoa +pdfjs-page-scale-actual = Benetako tamaina +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = %{ $scale } + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = { $page }. orria + +## Loading indicator messages + +pdfjs-loading-error = Errorea gertatu da PDFa kargatzean. +pdfjs-invalid-file-error = PDF fitxategi baliogabe edo hondatua. +pdfjs-missing-file-error = PDF fitxategia falta da. +pdfjs-unexpected-response-error = Espero gabeko zerbitzariaren erantzuna. +pdfjs-rendering-error = Errorea gertatu da orria errendatzean. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } ohartarazpena] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Idatzi PDF fitxategi hau irekitzeko pasahitza. +pdfjs-password-invalid = Pasahitz baliogabea. Saiatu berriro mesedez. +pdfjs-password-ok-button = Ados +pdfjs-password-cancel-button = Utzi +pdfjs-web-fonts-disabled = Webeko letra-tipoak desgaituta daude: ezin dira kapsulatutako PDF letra-tipoak erabili. + +## Editing + +pdfjs-editor-free-text-button = + .title = Testua +pdfjs-editor-free-text-button-label = Testua +pdfjs-editor-ink-button = + .title = Marrazkia +pdfjs-editor-ink-button-label = Marrazkia +pdfjs-editor-stamp-button = + .title = Gehitu edo editatu irudiak +pdfjs-editor-stamp-button-label = Gehitu edo editatu irudiak +pdfjs-editor-highlight-button = + .title = Nabarmendu +pdfjs-editor-highlight-button-label = Nabarmendu +pdfjs-highlight-floating-button1 = + .title = Nabarmendu + .aria-label = Nabarmendu +pdfjs-highlight-floating-button-label = Nabarmendu + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Kendu marrazkia +pdfjs-editor-remove-freetext-button = + .title = Kendu testua +pdfjs-editor-remove-stamp-button = + .title = Kendu irudia +pdfjs-editor-remove-highlight-button = + .title = Kendu nabarmentzea + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Kolorea +pdfjs-editor-free-text-size-input = Tamaina +pdfjs-editor-ink-color-input = Kolorea +pdfjs-editor-ink-thickness-input = Loditasuna +pdfjs-editor-ink-opacity-input = Opakutasuna +pdfjs-editor-stamp-add-image-button = + .title = Gehitu irudia +pdfjs-editor-stamp-add-image-button-label = Gehitu irudia +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Loditasuna +pdfjs-editor-free-highlight-thickness-title = + .title = Aldatu loditasuna testua ez beste elementuak nabarmentzean +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Testu-editorea + .default-content = Hasi idazten… +pdfjs-free-text = + .aria-label = Testu-editorea +pdfjs-free-text-default-content = Hasi idazten… +pdfjs-ink = + .aria-label = Marrazki-editorea +pdfjs-ink-canvas = + .aria-label = Erabiltzaileak sortutako irudia + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Testu alternatiboa +pdfjs-editor-alt-text-edit-button = + .aria-label = Editatu testu alternatiboa +pdfjs-editor-alt-text-edit-button-label = Editatu testu alternatiboa +pdfjs-editor-alt-text-dialog-label = Aukeratu aukera +pdfjs-editor-alt-text-dialog-description = Testu alternatiboak laguntzen du jendeak ezin duenean irudia ikusi edo ez denean kargatzen. +pdfjs-editor-alt-text-add-description-label = Gehitu azalpena +pdfjs-editor-alt-text-add-description-description = Saiatu idazten gaia, ezarpena edo ekintzak deskribatzen dituen esaldi 1 edo 2. +pdfjs-editor-alt-text-mark-decorative-label = Markatu apaingarri gisa +pdfjs-editor-alt-text-mark-decorative-description = Irudiak apaingarrientzat erabiltzen da, adibidez ertz edo ur-marketarako. +pdfjs-editor-alt-text-cancel-button = Utzi +pdfjs-editor-alt-text-save-button = Gorde +pdfjs-editor-alt-text-decorative-tooltip = Apaingarri gisa markatuta +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Adibidez, "gizon gaztea mahaian eserita dago bazkaltzeko" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Testu alternatiboa + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Goiko ezkerreko izkina — aldatu tamaina +pdfjs-editor-resizer-label-top-middle = Goian erdian — aldatu tamaina +pdfjs-editor-resizer-label-top-right = Goiko eskuineko izkina — aldatu tamaina +pdfjs-editor-resizer-label-middle-right = Erdian eskuinean — aldatu tamaina +pdfjs-editor-resizer-label-bottom-right = Beheko eskuineko izkina — aldatu tamaina +pdfjs-editor-resizer-label-bottom-middle = Behean erdian — aldatu tamaina +pdfjs-editor-resizer-label-bottom-left = Beheko ezkerreko izkina — aldatu tamaina +pdfjs-editor-resizer-label-middle-left = Erdian ezkerrean — aldatu tamaina +pdfjs-editor-resizer-top-left = + .aria-label = Goiko ezkerreko izkina — aldatu tamaina +pdfjs-editor-resizer-top-middle = + .aria-label = Goian erdian — aldatu tamaina +pdfjs-editor-resizer-top-right = + .aria-label = Goiko eskuineko izkina — aldatu tamaina +pdfjs-editor-resizer-middle-right = + .aria-label = Erdian eskuinean — aldatu tamaina +pdfjs-editor-resizer-bottom-right = + .aria-label = Beheko eskuineko izkina — aldatu tamaina +pdfjs-editor-resizer-bottom-middle = + .aria-label = Behean erdian — aldatu tamaina +pdfjs-editor-resizer-bottom-left = + .aria-label = Beheko ezkerreko izkina — aldatu tamaina +pdfjs-editor-resizer-middle-left = + .aria-label = Erdian ezkerrean — aldatu tamaina + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Nabarmentze kolorea +pdfjs-editor-colorpicker-button = + .title = Aldatu kolorea +pdfjs-editor-colorpicker-dropdown = + .aria-label = Kolore-aukerak +pdfjs-editor-colorpicker-yellow = + .title = Horia +pdfjs-editor-colorpicker-green = + .title = Berdea +pdfjs-editor-colorpicker-blue = + .title = Urdina +pdfjs-editor-colorpicker-pink = + .title = Arrosa +pdfjs-editor-colorpicker-red = + .title = Gorria + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Erakutsi denak +pdfjs-editor-highlight-show-all-button = + .title = Erakutsi denak + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Editatu testu alternatiboa (irudiaren azalpena) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Gehitu testu alternatiboa (irudiaren azalpena) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Idatzi zure azalpena hemen… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Azalpen laburra irudia ikusi ezin duen jendearentzat edo irudia kargatu ezin denerako. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Testu alternatibo hau automatikoki sortu da eta okerra izan liteke. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Argibide gehiago +pdfjs-editor-new-alt-text-create-automatically-button-label = Sortu testu alternatiboa automatikoki +pdfjs-editor-new-alt-text-not-now-button = Une honetan ez +pdfjs-editor-new-alt-text-error-title = Ezin da testu alternatiboa automatikoki sortu +pdfjs-editor-new-alt-text-error-description = Idatzi zure testu alternatibo propioa edo saiatu berriro geroago. +pdfjs-editor-new-alt-text-error-close-button = Itxi +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Testu alternatiboaren AA modeloa deskargatzen ({ $totalSize }/{ $downloadedSize } MB) + .aria-valuetext = Testu alternatiboaren AA modeloa deskargatzen ({ $totalSize }/{ $downloadedSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Testu alternatiboa gehituta +pdfjs-editor-new-alt-text-added-button-label = Testu alternatiboa gehituta +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Testu alternatiboa falta da +pdfjs-editor-new-alt-text-missing-button-label = Testu alternatiboa falta da +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Berrikusi testu alternatiboa +pdfjs-editor-new-alt-text-to-review-button-label = Berrikusi testu alternatiboa +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Automatikoki sortua: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Irudiaren testu alternatiboaren ezarpenak +pdfjs-image-alt-text-settings-button-label = Irudiaren testu alternatiboaren ezarpenak +pdfjs-editor-alt-text-settings-dialog-label = Irudiaren testu alternatiboaren ezarpenak +pdfjs-editor-alt-text-settings-automatic-title = Testu alternatibo automatikoa +pdfjs-editor-alt-text-settings-create-model-button-label = Sortu testu alternatiboa automatikoki +pdfjs-editor-alt-text-settings-create-model-description = Azalpenak iradokitzen ditu irudia ikusi ezin duen jendearentzat edo irudia kargatu ezin denerako. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Testu alternatiboaren AA modeloa ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Zure gailuan modu lokalean exekutatzen da eta zure datuak pribatu mantentzen dira. Testu alternatibo automatikorako beharrezkoa. +pdfjs-editor-alt-text-settings-delete-model-button = Ezabatu +pdfjs-editor-alt-text-settings-download-model-button = Deskargatu +pdfjs-editor-alt-text-settings-downloading-model-button = Deskargatzen… +pdfjs-editor-alt-text-settings-editor-title = Testu alternatiboaren editorea +pdfjs-editor-alt-text-settings-show-dialog-button-label = Erakutsi testu alternatiboa irudi bat gehitzean berehala +pdfjs-editor-alt-text-settings-show-dialog-description = Zure irudiek testu alternatiboa duela ziurtatzen laguntzen dizu. +pdfjs-editor-alt-text-settings-close-button = Itxi + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Nabarmentzea kenduta +pdfjs-editor-undo-bar-message-freetext = Testua kenduta +pdfjs-editor-undo-bar-message-ink = Marrazkia kenduta +pdfjs-editor-undo-bar-message-stamp = Irudia kenduta +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] Esku-ohar bat kenduta + *[other] { $count } esku-ohar kenduta + } +pdfjs-editor-undo-bar-undo-button = + .title = Desegin +pdfjs-editor-undo-bar-undo-button-label = Desegin +pdfjs-editor-undo-bar-close-button = + .title = Itxi +pdfjs-editor-undo-bar-close-button-label = Itxi diff --git a/public/pdfjs/web/locale/fa/viewer.ftl b/public/pdfjs/web/locale/fa/viewer.ftl new file mode 100644 index 0000000..4969209 --- /dev/null +++ b/public/pdfjs/web/locale/fa/viewer.ftl @@ -0,0 +1,348 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = صفحهٔ قبلی +pdfjs-previous-button-label = قبلی +pdfjs-next-button = + .title = صفحهٔ بعدی +pdfjs-next-button-label = بعدی +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = صفحه +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = از { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber }از { $pagesCount }) +pdfjs-zoom-out-button = + .title = کوچک‌نمایی +pdfjs-zoom-out-button-label = کوچک‌نمایی +pdfjs-zoom-in-button = + .title = بزرگ‌نمایی +pdfjs-zoom-in-button-label = بزرگ‌نمایی +pdfjs-zoom-select = + .title = زوم +pdfjs-presentation-mode-button = + .title = تغییر به حالت ارائه +pdfjs-presentation-mode-button-label = حالت ارائه +pdfjs-open-file-button = + .title = باز کردن پرونده +pdfjs-open-file-button-label = باز کردن +pdfjs-print-button = + .title = چاپ +pdfjs-print-button-label = چاپ +pdfjs-save-button = + .title = ذخیره +pdfjs-save-button-label = ذخیره +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = دریافت +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = دریافت +pdfjs-bookmark-button = + .title = صفحه فعلی (مشاهده نشانی اینترنتی از صفحه فعلی) +pdfjs-bookmark-button-label = صفحه فعلی + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = ابزارها +pdfjs-tools-button-label = ابزارها +pdfjs-first-page-button = + .title = برو به اولین صفحه +pdfjs-first-page-button-label = برو به اولین صفحه +pdfjs-last-page-button = + .title = برو به آخرین صفحه +pdfjs-last-page-button-label = برو به آخرین صفحه +pdfjs-page-rotate-cw-button = + .title = چرخش ساعتگرد +pdfjs-page-rotate-cw-button-label = چرخش ساعتگرد +pdfjs-page-rotate-ccw-button = + .title = چرخش پاد ساعتگرد +pdfjs-page-rotate-ccw-button-label = چرخش پاد ساعتگرد +pdfjs-cursor-text-select-tool-button = + .title = فعال کردن ابزارِ انتخابِ متن +pdfjs-cursor-text-select-tool-button-label = ابزارِ انتخابِ متن +pdfjs-cursor-hand-tool-button = + .title = فعال کردن ابزارِ دست +pdfjs-cursor-hand-tool-button-label = ابزار دست +pdfjs-scroll-page-button = + .title = استفاده از پیمایش صفحه +pdfjs-scroll-page-button-label = پیمایش صفحه +pdfjs-scroll-vertical-button = + .title = استفاده از پیمایش عمودی +pdfjs-scroll-vertical-button-label = پیمایش عمودی +pdfjs-scroll-horizontal-button = + .title = استفاده از پیمایش افقی +pdfjs-scroll-horizontal-button-label = پیمایش افقی +pdfjs-spread-none-button = + .title = صفحات پیوسته را یکی نکنید +pdfjs-spread-none-button-label = بدون صفحات پیوسته + +## Document properties dialog + +pdfjs-document-properties-button = + .title = خصوصیات سند... +pdfjs-document-properties-button-label = خصوصیات سند... +pdfjs-document-properties-file-name = نام پرونده: +pdfjs-document-properties-file-size = حجم پرونده: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } کیلوبایت ({ $b } بایت) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } مگابایت ({ $b } بایت) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } کیلوبایت ({ $size_b } بایت) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } مگابایت ({ $size_b } بایت) +pdfjs-document-properties-title = عنوان: +pdfjs-document-properties-author = نویسنده: +pdfjs-document-properties-subject = موضوع: +pdfjs-document-properties-keywords = کلیدواژه‌ها: +pdfjs-document-properties-creation-date = تاریخ ایجاد: +pdfjs-document-properties-modification-date = تاریخ ویرایش: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }، { $time } +pdfjs-document-properties-creator = ایجاد کننده: +pdfjs-document-properties-producer = ایجاد کننده PDF: +pdfjs-document-properties-version = نسخه PDF: +pdfjs-document-properties-page-count = تعداد صفحات: +pdfjs-document-properties-page-size = اندازه صفحه: +pdfjs-document-properties-page-size-unit-inches = اینچ +pdfjs-document-properties-page-size-unit-millimeters = میلی‌متر +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = نامه +pdfjs-document-properties-page-size-name-legal = حقوقی + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +pdfjs-document-properties-linearized-yes = بله +pdfjs-document-properties-linearized-no = خیر +pdfjs-document-properties-close-button = بستن + +## Print + +pdfjs-print-progress-message = آماده سازی مدارک برای چاپ کردن… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = لغو +pdfjs-printing-not-supported = هشدار: قابلیت چاپ به‌طور کامل در این مرورگر پشتیبانی نمی‌شود. +pdfjs-printing-not-ready = اخطار: پرونده PDF بطور کامل بارگیری نشده و امکان چاپ وجود ندارد. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = باز و بسته کردن نوار کناری +pdfjs-toggle-sidebar-button-label = تغییرحالت نوارکناری +pdfjs-document-outline-button = + .title = نمایش رئوس مطالب مدارک(برای بازشدن/جمع شدن همه موارد دوبار کلیک کنید) +pdfjs-document-outline-button-label = طرح نوشتار +pdfjs-attachments-button = + .title = نمایش پیوست‌ها +pdfjs-attachments-button-label = پیوست‌ها +pdfjs-layers-button-label = لایه‌ها +pdfjs-thumbs-button = + .title = نمایش تصاویر بندانگشتی +pdfjs-thumbs-button-label = تصاویر بندانگشتی +pdfjs-findbar-button = + .title = جستجو در سند +pdfjs-findbar-button-label = پیدا کردن + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = صفحه { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = تصویر بند‌ انگشتی صفحه { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = پیدا کردن + .placeholder = پیدا کردن در سند… +pdfjs-find-previous-button = + .title = پیدا کردن رخداد قبلی عبارت +pdfjs-find-previous-button-label = قبلی +pdfjs-find-next-button = + .title = پیدا کردن رخداد بعدی عبارت +pdfjs-find-next-button-label = بعدی +pdfjs-find-highlight-checkbox = برجسته و هایلایت کردن همه موارد +pdfjs-find-match-case-checkbox-label = تطبیق کوچکی و بزرگی حروف +pdfjs-find-entire-word-checkbox-label = تمام کلمه‌ها +pdfjs-find-reached-top = به بالای صفحه رسیدیم، از پایین ادامه می‌دهیم +pdfjs-find-reached-bottom = به آخر صفحه رسیدیم، از بالا ادامه می‌دهیم +pdfjs-find-not-found = عبارت پیدا نشد + +## Predefined zoom values + +pdfjs-page-scale-width = عرض صفحه +pdfjs-page-scale-fit = اندازه کردن صفحه +pdfjs-page-scale-auto = بزرگنمایی خودکار +pdfjs-page-scale-actual = اندازه واقعی‌ +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = صفحهٔ { $page } + +## Loading indicator messages + +pdfjs-loading-error = هنگام بارگیری پرونده PDF خطایی رخ داد. +pdfjs-invalid-file-error = پرونده PDF نامعتبر یامعیوب می‌باشد. +pdfjs-missing-file-error = پرونده PDF یافت نشد. +pdfjs-unexpected-response-error = پاسخ پیش بینی نشده سرور +pdfjs-rendering-error = هنگام بارگیری صفحه خطایی رخ داد. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }، { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = جهت باز کردن پرونده PDF گذرواژه را وارد نمائید. +pdfjs-password-invalid = گذرواژه نامعتبر. لطفا مجددا تلاش کنید. +pdfjs-password-ok-button = تأیید +pdfjs-password-cancel-button = لغو +pdfjs-web-fonts-disabled = فونت های تحت وب غیر فعال شده اند: امکان استفاده از نمایش دهنده داخلی PDF وجود ندارد. + +## Editing + +pdfjs-editor-free-text-button = + .title = متن +pdfjs-editor-free-text-button-label = متن +pdfjs-editor-ink-button = + .title = کشیدن +pdfjs-editor-ink-button-label = کشیدن +pdfjs-editor-stamp-button = + .title = افزودن یا ویرایش تصاویر +pdfjs-editor-stamp-button-label = افزودن یا ویرایش تصاویر +pdfjs-editor-highlight-button = + .title = برجسته کردن +pdfjs-editor-highlight-button-label = برجسته کردن +pdfjs-highlight-floating-button1 = + .title = برجسته کردن + .aria-label = برجسته کردن +pdfjs-highlight-floating-button-label = برجسته کردن + +## Remove button for the various kind of editor. + + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = رنگ +pdfjs-editor-free-text-size-input = اندازه +pdfjs-editor-ink-color-input = رنگ +pdfjs-editor-stamp-add-image-button = + .title = افزودن تصویر +pdfjs-editor-stamp-add-image-button-label = افزودن تصویر +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = ویرایشگر متن + .default-content = شروع به نوشتن کنید… +pdfjs-free-text = + .aria-label = ویرایشگر متن +pdfjs-free-text-default-content = شروع به نوشتن کنید… + +## Alt-text dialog + +pdfjs-editor-alt-text-add-description-label = افزودن توضیحات +pdfjs-editor-alt-text-cancel-button = انصراف +pdfjs-editor-alt-text-save-button = ذخیره + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + + +## Color picker + +pdfjs-editor-colorpicker-button = + .title = تغییر رنگ +pdfjs-editor-colorpicker-dropdown = + .aria-label = انتخاب رنگ +pdfjs-editor-colorpicker-yellow = + .title = زرد +pdfjs-editor-colorpicker-green = + .title = سبز +pdfjs-editor-colorpicker-blue = + .title = آبی +pdfjs-editor-colorpicker-pink = + .title = صورتی +pdfjs-editor-colorpicker-red = + .title = قرمز + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = نمایش همه +pdfjs-editor-highlight-show-all-button = + .title = نمایش همه + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = بیشتر بدانید +pdfjs-editor-new-alt-text-not-now-button = اکنون نه +pdfjs-editor-new-alt-text-error-close-button = بستن + +## Image alt-text settings + +pdfjs-editor-alt-text-settings-delete-model-button = حذف +pdfjs-editor-alt-text-settings-download-model-button = دریافت +pdfjs-editor-alt-text-settings-downloading-model-button = در حال دریافت… +pdfjs-editor-alt-text-settings-close-button = بستن diff --git a/public/pdfjs/web/locale/ff/viewer.ftl b/public/pdfjs/web/locale/ff/viewer.ftl new file mode 100644 index 0000000..d1419f5 --- /dev/null +++ b/public/pdfjs/web/locale/ff/viewer.ftl @@ -0,0 +1,247 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Hello Ɓennungo +pdfjs-previous-button-label = Ɓennuɗo +pdfjs-next-button = + .title = Hello faango +pdfjs-next-button-label = Yeeso +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Hello +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = e nder { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount }) +pdfjs-zoom-out-button = + .title = Lonngo Woɗɗa +pdfjs-zoom-out-button-label = Lonngo Woɗɗa +pdfjs-zoom-in-button = + .title = Lonngo Ara +pdfjs-zoom-in-button-label = Lonngo Ara +pdfjs-zoom-select = + .title = Lonngo +pdfjs-presentation-mode-button = + .title = Faytu to Presentation Mode +pdfjs-presentation-mode-button-label = Presentation Mode +pdfjs-open-file-button = + .title = Uddit Fiilde +pdfjs-open-file-button-label = Uddit +pdfjs-print-button = + .title = Winndito +pdfjs-print-button-label = Winndito + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Kuutorɗe +pdfjs-tools-button-label = Kuutorɗe +pdfjs-first-page-button = + .title = Yah to hello adanngo +pdfjs-first-page-button-label = Yah to hello adanngo +pdfjs-last-page-button = + .title = Yah to hello wattindiingo +pdfjs-last-page-button-label = Yah to hello wattindiingo +pdfjs-page-rotate-cw-button = + .title = Yiiltu Faya Ñaamo +pdfjs-page-rotate-cw-button-label = Yiiltu Faya Ñaamo +pdfjs-page-rotate-ccw-button = + .title = Yiiltu Faya Nano +pdfjs-page-rotate-ccw-button-label = Yiiltu Faya Nano +pdfjs-cursor-text-select-tool-button = + .title = Gollin kaɓirgel cuɓirgel binndi +pdfjs-cursor-text-select-tool-button-label = Kaɓirgel cuɓirgel binndi +pdfjs-cursor-hand-tool-button = + .title = Hurmin kuutorgal junngo +pdfjs-cursor-hand-tool-button-label = Kaɓirgel junngo +pdfjs-scroll-vertical-button = + .title = Huutoro gorwitol daringol +pdfjs-scroll-vertical-button-label = Gorwitol daringol +pdfjs-scroll-horizontal-button = + .title = Huutoro gorwitol lelingol +pdfjs-scroll-horizontal-button-label = Gorwitol daringol +pdfjs-scroll-wrapped-button = + .title = Huutoro gorwitol coomingol +pdfjs-scroll-wrapped-button-label = Gorwitol coomingol +pdfjs-spread-none-button = + .title = Hoto tawtu kelle kelle +pdfjs-spread-none-button-label = Alaa Spreads +pdfjs-spread-odd-button = + .title = Tawtu kelle puɗɗortooɗe kelle teelɗe +pdfjs-spread-odd-button-label = Kelle teelɗe +pdfjs-spread-even-button = + .title = Tawtu ɗereeji kelle puɗɗoriiɗi kelle teeltuɗe +pdfjs-spread-even-button-label = Kelle teeltuɗe + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Keeroraaɗi Winndannde… +pdfjs-document-properties-button-label = Keeroraaɗi Winndannde… +pdfjs-document-properties-file-name = Innde fiilde: +pdfjs-document-properties-file-size = Ɓetol fiilde: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bite) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bite) +pdfjs-document-properties-title = Tiitoonde: +pdfjs-document-properties-author = Binnduɗo: +pdfjs-document-properties-subject = Toɓɓere: +pdfjs-document-properties-keywords = Kelmekele jiytirɗe: +pdfjs-document-properties-creation-date = Ñalnde Sosaa: +pdfjs-document-properties-modification-date = Ñalnde Waylaa: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Cosɗo: +pdfjs-document-properties-producer = Paggiiɗo PDF: +pdfjs-document-properties-version = Yamre PDF: +pdfjs-document-properties-page-count = Limoore Kelle: +pdfjs-document-properties-page-size = Ɓeto Hello: +pdfjs-document-properties-page-size-unit-inches = nder +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = dariingo +pdfjs-document-properties-page-size-orientation-landscape = wertiingo +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Ɓataake +pdfjs-document-properties-page-size-name-legal = Laawol + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Ɗisngo geese yaawngo: +pdfjs-document-properties-linearized-yes = Eey +pdfjs-document-properties-linearized-no = Alaa +pdfjs-document-properties-close-button = Uddu + +## Print + +pdfjs-print-progress-message = Nana heboo winnditaade fiilannde… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Haaytu +pdfjs-printing-not-supported = Reentino: Winnditagol tammbitaaka no feewi e ndee wanngorde. +pdfjs-printing-not-ready = Reentino: PDF oo loowaaki haa timmi ngam winnditagol. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Toggilo Palal Sawndo +pdfjs-toggle-sidebar-button-label = Toggilo Palal Sawndo +pdfjs-document-outline-button = + .title = Hollu Ƴiyal Fiilannde (dobdobo ngam wertude/taggude teme fof) +pdfjs-document-outline-button-label = Toɓɓe Fiilannde +pdfjs-attachments-button = + .title = Hollu Ɗisanɗe +pdfjs-attachments-button-label = Ɗisanɗe +pdfjs-thumbs-button = + .title = Hollu Dooɓe +pdfjs-thumbs-button-label = Dooɓe +pdfjs-findbar-button = + .title = Yiylo e fiilannde +pdfjs-findbar-button-label = Yiytu + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Hello { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Dooɓre Hello { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Yiytu + .placeholder = Yiylo nder dokimaa +pdfjs-find-previous-button = + .title = Yiylo cilol ɓennugol konngol ngol +pdfjs-find-previous-button-label = Ɓennuɗo +pdfjs-find-next-button = + .title = Yiylo cilol garowol konngol ngol +pdfjs-find-next-button-label = Yeeso +pdfjs-find-highlight-checkbox = Jalbin fof +pdfjs-find-match-case-checkbox-label = Jaaɓnu darnde +pdfjs-find-entire-word-checkbox-label = Kelme timmuɗe tan +pdfjs-find-reached-top = Heɓii fuɗɗorde fiilannde, jokku faya les +pdfjs-find-reached-bottom = Heɓii hoore fiilannde, jokku faya les +pdfjs-find-not-found = Konngi njiyataa + +## Predefined zoom values + +pdfjs-page-scale-width = Njaajeendi Hello +pdfjs-page-scale-fit = Keƴeendi Hello +pdfjs-page-scale-auto = Loongorde Jaajol +pdfjs-page-scale-actual = Ɓetol Jaati +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Juumre waɗii tuma nde loowata PDF oo. +pdfjs-invalid-file-error = Fiilde PDF moƴƴaani walla jiibii. +pdfjs-missing-file-error = Fiilde PDF ena ŋakki. +pdfjs-unexpected-response-error = Jaabtol sarworde tijjinooka. +pdfjs-rendering-error = Juumre waɗii tuma nde yoŋkittoo hello. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Siiftannde] + +## Password + +pdfjs-password-label = Naatu finnde ngam uddite ndee fiilde PDF. +pdfjs-password-invalid = Finnde moƴƴaani. Tiiɗno eto kadi. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Haaytu +pdfjs-web-fonts-disabled = Ponte geese ko daaƴaaɗe: horiima huutoraade ponte PDF coomtoraaɗe. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/fi/viewer.ftl b/public/pdfjs/web/locale/fi/viewer.ftl new file mode 100644 index 0000000..0819d0e --- /dev/null +++ b/public/pdfjs/web/locale/fi/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Edellinen sivu +pdfjs-previous-button-label = Edellinen +pdfjs-next-button = + .title = Seuraava sivu +pdfjs-next-button-label = Seuraava +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Sivu +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = / { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount }) +pdfjs-zoom-out-button = + .title = Loitonna +pdfjs-zoom-out-button-label = Loitonna +pdfjs-zoom-in-button = + .title = Lähennä +pdfjs-zoom-in-button-label = Lähennä +pdfjs-zoom-select = + .title = Suurennus +pdfjs-presentation-mode-button = + .title = Siirry esitystilaan +pdfjs-presentation-mode-button-label = Esitystila +pdfjs-open-file-button = + .title = Avaa tiedosto +pdfjs-open-file-button-label = Avaa +pdfjs-print-button = + .title = Tulosta +pdfjs-print-button-label = Tulosta +pdfjs-save-button = + .title = Tallenna +pdfjs-save-button-label = Tallenna +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Lataa +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Lataa +pdfjs-bookmark-button = + .title = Nykyinen sivu (Näytä URL-osoite nykyiseltä sivulta) +pdfjs-bookmark-button-label = Nykyinen sivu + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Tools +pdfjs-tools-button-label = Tools +pdfjs-first-page-button = + .title = Siirry ensimmäiselle sivulle +pdfjs-first-page-button-label = Siirry ensimmäiselle sivulle +pdfjs-last-page-button = + .title = Siirry viimeiselle sivulle +pdfjs-last-page-button-label = Siirry viimeiselle sivulle +pdfjs-page-rotate-cw-button = + .title = Kierrä oikealle +pdfjs-page-rotate-cw-button-label = Kierrä oikealle +pdfjs-page-rotate-ccw-button = + .title = Kierrä vasemmalle +pdfjs-page-rotate-ccw-button-label = Kierrä vasemmalle +pdfjs-cursor-text-select-tool-button = + .title = Käytä tekstinvalintatyökalua +pdfjs-cursor-text-select-tool-button-label = Tekstinvalintatyökalu +pdfjs-cursor-hand-tool-button = + .title = Käytä käsityökalua +pdfjs-cursor-hand-tool-button-label = Käsityökalu +pdfjs-scroll-page-button = + .title = Käytä sivun vieritystä +pdfjs-scroll-page-button-label = Sivun vieritys +pdfjs-scroll-vertical-button = + .title = Käytä pystysuuntaista vieritystä +pdfjs-scroll-vertical-button-label = Pystysuuntainen vieritys +pdfjs-scroll-horizontal-button = + .title = Käytä vaakasuuntaista vieritystä +pdfjs-scroll-horizontal-button-label = Vaakasuuntainen vieritys +pdfjs-scroll-wrapped-button = + .title = Käytä rivittyvää vieritystä +pdfjs-scroll-wrapped-button-label = Rivittyvä vieritys +pdfjs-spread-none-button = + .title = Älä yhdistä sivuja aukeamiksi +pdfjs-spread-none-button-label = Ei aukeamia +pdfjs-spread-odd-button = + .title = Yhdistä sivut aukeamiksi alkaen parittomalta sivulta +pdfjs-spread-odd-button-label = Parittomalta alkavat aukeamat +pdfjs-spread-even-button = + .title = Yhdistä sivut aukeamiksi alkaen parilliselta sivulta +pdfjs-spread-even-button-label = Parilliselta alkavat aukeamat + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumentin ominaisuudet… +pdfjs-document-properties-button-label = Dokumentin ominaisuudet… +pdfjs-document-properties-file-name = Tiedoston nimi: +pdfjs-document-properties-file-size = Tiedoston koko: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } kt ({ $b } tavua) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } Mt ({ $b } tavua) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } kt ({ $size_b } tavua) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } Mt ({ $size_b } tavua) +pdfjs-document-properties-title = Otsikko: +pdfjs-document-properties-author = Tekijä: +pdfjs-document-properties-subject = Aihe: +pdfjs-document-properties-keywords = Avainsanat: +pdfjs-document-properties-creation-date = Luomispäivämäärä: +pdfjs-document-properties-modification-date = Muokkauspäivämäärä: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Luoja: +pdfjs-document-properties-producer = PDF-tuottaja: +pdfjs-document-properties-version = PDF-versio: +pdfjs-document-properties-page-count = Sivujen määrä: +pdfjs-document-properties-page-size = Sivun koko: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = pysty +pdfjs-document-properties-page-size-orientation-landscape = vaaka +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Nopea web-katselu: +pdfjs-document-properties-linearized-yes = Kyllä +pdfjs-document-properties-linearized-no = Ei +pdfjs-document-properties-close-button = Sulje + +## Print + +pdfjs-print-progress-message = Valmistellaan dokumenttia tulostamista varten… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress } % +pdfjs-print-progress-close-button = Peruuta +pdfjs-printing-not-supported = Varoitus: Selain ei tue kaikkia tulostustapoja. +pdfjs-printing-not-ready = Varoitus: PDF-tiedosto ei ole vielä latautunut kokonaan, eikä sitä voi vielä tulostaa. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Näytä/piilota sivupaneeli +pdfjs-toggle-sidebar-notification-button = + .title = Näytä/piilota sivupaneeli (dokumentissa on sisällys/liitteitä/tasoja) +pdfjs-toggle-sidebar-button-label = Näytä/piilota sivupaneeli +pdfjs-document-outline-button = + .title = Näytä dokumentin sisällys (laajenna tai kutista kohdat kaksoisnapsauttamalla) +pdfjs-document-outline-button-label = Dokumentin sisällys +pdfjs-attachments-button = + .title = Näytä liitteet +pdfjs-attachments-button-label = Liitteet +pdfjs-layers-button = + .title = Näytä tasot (kaksoisnapsauta palauttaaksesi kaikki tasot oletustilaan) +pdfjs-layers-button-label = Tasot +pdfjs-thumbs-button = + .title = Näytä pienoiskuvat +pdfjs-thumbs-button-label = Pienoiskuvat +pdfjs-current-outline-item-button = + .title = Etsi nykyinen sisällyksen kohta +pdfjs-current-outline-item-button-label = Nykyinen sisällyksen kohta +pdfjs-findbar-button = + .title = Etsi dokumentista +pdfjs-findbar-button-label = Etsi +pdfjs-additional-layers = Lisätasot + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Sivu { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Pienoiskuva sivusta { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Etsi + .placeholder = Etsi dokumentista… +pdfjs-find-previous-button = + .title = Etsi hakusanan edellinen osuma +pdfjs-find-previous-button-label = Edellinen +pdfjs-find-next-button = + .title = Etsi hakusanan seuraava osuma +pdfjs-find-next-button-label = Seuraava +pdfjs-find-highlight-checkbox = Korosta kaikki +pdfjs-find-match-case-checkbox-label = Huomioi kirjainkoko +pdfjs-find-match-diacritics-checkbox-label = Erota tarkkeet +pdfjs-find-entire-word-checkbox-label = Kokonaiset sanat +pdfjs-find-reached-top = Päästiin dokumentin alkuun, jatketaan lopusta +pdfjs-find-reached-bottom = Päästiin dokumentin loppuun, jatketaan alusta +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } / { $total } osuma + *[other] { $current } / { $total } osumaa + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Yli { $limit } osuma + *[other] Yli { $limit } osumaa + } +pdfjs-find-not-found = Hakusanaa ei löytynyt + +## Predefined zoom values + +pdfjs-page-scale-width = Sivun leveys +pdfjs-page-scale-fit = Koko sivu +pdfjs-page-scale-auto = Automaattinen suurennus +pdfjs-page-scale-actual = Todellinen koko +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale } % + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Sivu { $page } + +## Loading indicator messages + +pdfjs-loading-error = Tapahtui virhe ladattaessa PDF-tiedostoa. +pdfjs-invalid-file-error = Virheellinen tai vioittunut PDF-tiedosto. +pdfjs-missing-file-error = Puuttuva PDF-tiedosto. +pdfjs-unexpected-response-error = Odottamaton vastaus palvelimelta. +pdfjs-rendering-error = Tapahtui virhe piirrettäessä sivua. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type }-merkintä] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Kirjoita PDF-tiedoston salasana. +pdfjs-password-invalid = Virheellinen salasana. Yritä uudestaan. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Peruuta +pdfjs-web-fonts-disabled = Verkkosivujen omat kirjasinlajit on estetty: ei voida käyttää upotettuja PDF-kirjasinlajeja. + +## Editing + +pdfjs-editor-free-text-button = + .title = Teksti +pdfjs-editor-free-text-button-label = Teksti +pdfjs-editor-ink-button = + .title = Piirros +pdfjs-editor-ink-button-label = Piirros +pdfjs-editor-stamp-button = + .title = Lisää tai muokkaa kuvia +pdfjs-editor-stamp-button-label = Lisää tai muokkaa kuvia +pdfjs-editor-highlight-button = + .title = Korostus +pdfjs-editor-highlight-button-label = Korostus +pdfjs-highlight-floating-button1 = + .title = Korostus + .aria-label = Korostus +pdfjs-highlight-floating-button-label = Korostus + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Poista piirros +pdfjs-editor-remove-freetext-button = + .title = Poista teksti +pdfjs-editor-remove-stamp-button = + .title = Poista kuva +pdfjs-editor-remove-highlight-button = + .title = Poista korostus + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Väri +pdfjs-editor-free-text-size-input = Koko +pdfjs-editor-ink-color-input = Väri +pdfjs-editor-ink-thickness-input = Paksuus +pdfjs-editor-ink-opacity-input = Peittävyys +pdfjs-editor-stamp-add-image-button = + .title = Lisää kuva +pdfjs-editor-stamp-add-image-button-label = Lisää kuva +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Paksuus +pdfjs-editor-free-highlight-thickness-title = + .title = Muuta paksuutta korostaessasi muita kohteita kuin tekstiä +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstimuokkain + .default-content = Aloita kirjoittaminen… +pdfjs-free-text = + .aria-label = Tekstimuokkain +pdfjs-free-text-default-content = Aloita kirjoittaminen… +pdfjs-ink = + .aria-label = Piirrustusmuokkain +pdfjs-ink-canvas = + .aria-label = Käyttäjän luoma kuva + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Vaihtoehtoinen teksti +pdfjs-editor-alt-text-edit-button = + .aria-label = Muokkaa vaihtoehtoista tekstiä +pdfjs-editor-alt-text-edit-button-label = Muokkaa vaihtoehtoista tekstiä +pdfjs-editor-alt-text-dialog-label = Valitse vaihtoehto +pdfjs-editor-alt-text-dialog-description = Vaihtoehtoinen teksti ("alt-teksti") auttaa ihmisiä, jotka eivät näe kuvaa tai kun kuva ei lataudu. +pdfjs-editor-alt-text-add-description-label = Lisää kuvaus +pdfjs-editor-alt-text-add-description-description = Pyri 1-2 lauseeseen, jotka kuvaavat aihetta, ympäristöä tai toimintaa. +pdfjs-editor-alt-text-mark-decorative-label = Merkitse koristeelliseksi +pdfjs-editor-alt-text-mark-decorative-description = Tätä käytetään koristekuville, kuten reunuksille tai vesileimoille. +pdfjs-editor-alt-text-cancel-button = Peruuta +pdfjs-editor-alt-text-save-button = Tallenna +pdfjs-editor-alt-text-decorative-tooltip = Merkitty koristeelliseksi +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Esimerkiksi "Nuori mies istuu pöytään syömään aterian" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Vaihtoehtoinen teksti + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Vasen yläkulma - muuta kokoa +pdfjs-editor-resizer-label-top-middle = Ylhäällä keskellä - muuta kokoa +pdfjs-editor-resizer-label-top-right = Oikea yläkulma - muuta kokoa +pdfjs-editor-resizer-label-middle-right = Keskellä oikealla - muuta kokoa +pdfjs-editor-resizer-label-bottom-right = Oikea alakulma - muuta kokoa +pdfjs-editor-resizer-label-bottom-middle = Alhaalla keskellä - muuta kokoa +pdfjs-editor-resizer-label-bottom-left = Vasen alakulma - muuta kokoa +pdfjs-editor-resizer-label-middle-left = Keskellä vasemmalla - muuta kokoa +pdfjs-editor-resizer-top-left = + .aria-label = Vasen yläkulma - muuta kokoa +pdfjs-editor-resizer-top-middle = + .aria-label = Ylhäällä keskellä - muuta kokoa +pdfjs-editor-resizer-top-right = + .aria-label = Oikea yläkulma - muuta kokoa +pdfjs-editor-resizer-middle-right = + .aria-label = Keskellä oikealla - muuta kokoa +pdfjs-editor-resizer-bottom-right = + .aria-label = Oikea alakulma - muuta kokoa +pdfjs-editor-resizer-bottom-middle = + .aria-label = Alhaalla keskellä - muuta kokoa +pdfjs-editor-resizer-bottom-left = + .aria-label = Vasen alakulma - muuta kokoa +pdfjs-editor-resizer-middle-left = + .aria-label = Keskellä vasemmalla - muuta kokoa + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Korostusväri +pdfjs-editor-colorpicker-button = + .title = Vaihda väri +pdfjs-editor-colorpicker-dropdown = + .aria-label = Värivalinnat +pdfjs-editor-colorpicker-yellow = + .title = Keltainen +pdfjs-editor-colorpicker-green = + .title = Vihreä +pdfjs-editor-colorpicker-blue = + .title = Sininen +pdfjs-editor-colorpicker-pink = + .title = Pinkki +pdfjs-editor-colorpicker-red = + .title = Punainen + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Näytä kaikki +pdfjs-editor-highlight-show-all-button = + .title = Näytä kaikki + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Muokkaa vaihtoehtoista tekstiä (kuvan kuvaus) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Lisää vaihtoehtoinen teksti (kuvan kuvaus) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Kirjoita kuvaus tähän… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Lyhyt kuvaus ihmisille, jotka eivät näe kuvaa tai kun kuva ei lataudu. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Tämä vaihtoehtoinen teksti luotiin automaattisesti, ja se voi olla epätarkka. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Lue lisää +pdfjs-editor-new-alt-text-create-automatically-button-label = Luo vaihtoehtoinen teksti automaattisesti +pdfjs-editor-new-alt-text-not-now-button = Ei nyt +pdfjs-editor-new-alt-text-error-title = Vaihtoehtotekstiä ei voitu luoda automaattisesti +pdfjs-editor-new-alt-text-error-description = Kirjoita oma vaihtoehtoinen teksti tai yritä myöhemmin uudelleen. +pdfjs-editor-new-alt-text-error-close-button = Sulje +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Ladataan vaihtoehtoisen tekstin tekoälymallia ({ $downloadedSize } / { $totalSize } Mt) + .aria-valuetext = Ladataan vaihtoehtoisen tekstin tekoälymallia ({ $downloadedSize } / { $totalSize } Mt) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Vaihtoehtoinen teksti lisätty +pdfjs-editor-new-alt-text-added-button-label = Vaihtoehtoinen teksti lisätty +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Vaihtoehtoinen teksti puuttuu +pdfjs-editor-new-alt-text-missing-button-label = Vaihtoehtoinen teksti puuttuu +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Tarkista vaihtoehtoinen teksti +pdfjs-editor-new-alt-text-to-review-button-label = Tarkista vaihtoehtoinen teksti +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Luotu automaattisesti: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Kuvan vaihtoehtoisen tekstin asetukset +pdfjs-image-alt-text-settings-button-label = Kuvan vaihtoehtoisen tekstin asetukset +pdfjs-editor-alt-text-settings-dialog-label = Kuvan vaihtoehtoisen tekstin asetukset +pdfjs-editor-alt-text-settings-automatic-title = Automaattinen vaihtoehtoinen teksti +pdfjs-editor-alt-text-settings-create-model-button-label = Luo vaihtoehtoinen teksti automaattisesti +pdfjs-editor-alt-text-settings-create-model-description = Ehdottaa kuvauksia, jotka auttavat ihmisiä, jotka eivät näe kuvaa tai kun kuva ei lataudu. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Vaihtoehtoisen tekstin tekoälymalli ({ $totalSize } Mt) +pdfjs-editor-alt-text-settings-ai-model-description = Toimii paikallisesti laitteellasi, joten tietosi pysyvät yksityisinä. Vaadittu automaattiselle vaihtoehtoiselle tekstille. +pdfjs-editor-alt-text-settings-delete-model-button = Poista +pdfjs-editor-alt-text-settings-download-model-button = Lataa +pdfjs-editor-alt-text-settings-downloading-model-button = Ladataan… +pdfjs-editor-alt-text-settings-editor-title = Vaihtoehtoisen tekstin muokkain +pdfjs-editor-alt-text-settings-show-dialog-button-label = Näytä vaihtoehtoisen tekstin muokkain heti, kun lisäät kuvan +pdfjs-editor-alt-text-settings-show-dialog-description = Auttaa varmistamaan, että kaikissa kuvissasi on vaihtoehtoinen teksti. +pdfjs-editor-alt-text-settings-close-button = Sulje + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Korostus poistettu +pdfjs-editor-undo-bar-message-freetext = Teksti poistettu +pdfjs-editor-undo-bar-message-ink = Piirustus poistettu +pdfjs-editor-undo-bar-message-stamp = Kuva poistettu +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } merkintä poistettu + *[other] { $count } merkintää poistettu + } +pdfjs-editor-undo-bar-undo-button = + .title = Kumoa +pdfjs-editor-undo-bar-undo-button-label = Kumoa +pdfjs-editor-undo-bar-close-button = + .title = Sulje +pdfjs-editor-undo-bar-close-button-label = Sulje diff --git a/public/pdfjs/web/locale/fr/viewer.ftl b/public/pdfjs/web/locale/fr/viewer.ftl new file mode 100644 index 0000000..d0a778f --- /dev/null +++ b/public/pdfjs/web/locale/fr/viewer.ftl @@ -0,0 +1,511 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Page précédente +pdfjs-previous-button-label = Précédent +pdfjs-next-button = + .title = Page suivante +pdfjs-next-button-label = Suivant +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Page +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = sur { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } sur { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zoom arrière +pdfjs-zoom-out-button-label = Zoom arrière +pdfjs-zoom-in-button = + .title = Zoom avant +pdfjs-zoom-in-button-label = Zoom avant +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Basculer en mode présentation +pdfjs-presentation-mode-button-label = Mode présentation +pdfjs-open-file-button = + .title = Ouvrir le fichier +pdfjs-open-file-button-label = Ouvrir le fichier +pdfjs-print-button = + .title = Imprimer +pdfjs-print-button-label = Imprimer +pdfjs-save-button = + .title = Enregistrer +pdfjs-save-button-label = Enregistrer +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Télécharger +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Télécharger +pdfjs-bookmark-button = + .title = Page courante (montrer l’adresse de la page courante) +pdfjs-bookmark-button-label = Page courante + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Outils +pdfjs-tools-button-label = Outils +pdfjs-first-page-button = + .title = Aller à la première page +pdfjs-first-page-button-label = Aller à la première page +pdfjs-last-page-button = + .title = Aller à la dernière page +pdfjs-last-page-button-label = Aller à la dernière page +pdfjs-page-rotate-cw-button = + .title = Rotation horaire +pdfjs-page-rotate-cw-button-label = Rotation horaire +pdfjs-page-rotate-ccw-button = + .title = Rotation antihoraire +pdfjs-page-rotate-ccw-button-label = Rotation antihoraire +pdfjs-cursor-text-select-tool-button = + .title = Activer l’outil de sélection de texte +pdfjs-cursor-text-select-tool-button-label = Outil de sélection de texte +pdfjs-cursor-hand-tool-button = + .title = Activer l’outil main +pdfjs-cursor-hand-tool-button-label = Outil main +pdfjs-scroll-page-button = + .title = Utiliser le défilement par page +pdfjs-scroll-page-button-label = Défilement par page +pdfjs-scroll-vertical-button = + .title = Utiliser le défilement vertical +pdfjs-scroll-vertical-button-label = Défilement vertical +pdfjs-scroll-horizontal-button = + .title = Utiliser le défilement horizontal +pdfjs-scroll-horizontal-button-label = Défilement horizontal +pdfjs-scroll-wrapped-button = + .title = Utiliser le défilement par bloc +pdfjs-scroll-wrapped-button-label = Défilement par bloc +pdfjs-spread-none-button = + .title = Ne pas afficher les pages deux à deux +pdfjs-spread-none-button-label = Pas de double affichage +pdfjs-spread-odd-button = + .title = Afficher les pages par deux, impaires à gauche +pdfjs-spread-odd-button-label = Doubles pages, impaires à gauche +pdfjs-spread-even-button = + .title = Afficher les pages par deux, paires à gauche +pdfjs-spread-even-button-label = Doubles pages, paires à gauche + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Propriétés du document… +pdfjs-document-properties-button-label = Propriétés du document… +pdfjs-document-properties-file-name = Nom du fichier : +pdfjs-document-properties-file-size = Taille du fichier : +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } Ko ({ $b } octets) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } Mo ({ $b } octets) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } Ko ({ $size_b } octets) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } Mo ({ $size_b } octets) +pdfjs-document-properties-title = Titre : +pdfjs-document-properties-author = Auteur : +pdfjs-document-properties-subject = Sujet : +pdfjs-document-properties-keywords = Mots-clés : +pdfjs-document-properties-creation-date = Date de création : +pdfjs-document-properties-modification-date = Modifié le : +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date } à { $time } +pdfjs-document-properties-creator = Créé par : +pdfjs-document-properties-producer = Outil de conversion PDF : +pdfjs-document-properties-version = Version PDF : +pdfjs-document-properties-page-count = Nombre de pages : +pdfjs-document-properties-page-size = Taille de la page : +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = portrait +pdfjs-document-properties-page-size-orientation-landscape = paysage +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = lettre +pdfjs-document-properties-page-size-name-legal = document juridique + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Affichage rapide des pages web : +pdfjs-document-properties-linearized-yes = Oui +pdfjs-document-properties-linearized-no = Non +pdfjs-document-properties-close-button = Fermer + +## Print + +pdfjs-print-progress-message = Préparation du document pour l’impression… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress } % +pdfjs-print-progress-close-button = Annuler +pdfjs-printing-not-supported = Attention : l’impression n’est pas totalement prise en charge par ce navigateur. +pdfjs-printing-not-ready = Attention : le PDF n’est pas entièrement chargé pour pouvoir l’imprimer. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Afficher/Masquer le panneau latéral +pdfjs-toggle-sidebar-notification-button = + .title = Afficher/Masquer le panneau latéral (le document contient des signets/pièces jointes/calques) +pdfjs-toggle-sidebar-button-label = Afficher/Masquer le panneau latéral +pdfjs-document-outline-button = + .title = Afficher les signets du document (double-cliquer pour développer/réduire tous les éléments) +pdfjs-document-outline-button-label = Signets du document +pdfjs-attachments-button = + .title = Afficher les pièces jointes +pdfjs-attachments-button-label = Pièces jointes +pdfjs-layers-button = + .title = Afficher les calques (double-cliquer pour réinitialiser tous les calques à l’état par défaut) +pdfjs-layers-button-label = Calques +pdfjs-thumbs-button = + .title = Afficher les vignettes +pdfjs-thumbs-button-label = Vignettes +pdfjs-current-outline-item-button = + .title = Trouver l’élément de plan actuel +pdfjs-current-outline-item-button-label = Élément de plan actuel +pdfjs-findbar-button = + .title = Rechercher dans le document +pdfjs-findbar-button-label = Rechercher +pdfjs-additional-layers = Calques additionnels + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Page { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Vignette de la page { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Rechercher + .placeholder = Rechercher dans le document… +pdfjs-find-previous-button = + .title = Trouver l’occurrence précédente de l’expression +pdfjs-find-previous-button-label = Précédent +pdfjs-find-next-button = + .title = Trouver la prochaine occurrence de l’expression +pdfjs-find-next-button-label = Suivant +pdfjs-find-highlight-checkbox = Tout surligner +pdfjs-find-match-case-checkbox-label = Respecter la casse +pdfjs-find-match-diacritics-checkbox-label = Respecter les accents et diacritiques +pdfjs-find-entire-word-checkbox-label = Mots entiers +pdfjs-find-reached-top = Haut de la page atteint, poursuite depuis la fin +pdfjs-find-reached-bottom = Bas de la page atteint, poursuite au début +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = Occurrence { $current } sur { $total } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Plus d’{ $limit } occurrence + *[other] Plus de { $limit } occurrences + } +pdfjs-find-not-found = Expression non trouvée + +## Predefined zoom values + +pdfjs-page-scale-width = Pleine largeur +pdfjs-page-scale-fit = Page entière +pdfjs-page-scale-auto = Zoom automatique +pdfjs-page-scale-actual = Taille réelle +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale } % + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Page { $page } + +## Loading indicator messages + +pdfjs-loading-error = Une erreur s’est produite lors du chargement du fichier PDF. +pdfjs-invalid-file-error = Fichier PDF invalide ou corrompu. +pdfjs-missing-file-error = Fichier PDF manquant. +pdfjs-unexpected-response-error = Réponse inattendue du serveur. +pdfjs-rendering-error = Une erreur s’est produite lors de l’affichage de la page. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date } à { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Annotation { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Veuillez saisir le mot de passe pour ouvrir ce fichier PDF. +pdfjs-password-invalid = Mot de passe incorrect. Veuillez réessayer. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Annuler +pdfjs-web-fonts-disabled = Les polices web sont désactivées : impossible d’utiliser les polices intégrées au PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Texte +pdfjs-editor-free-text-button-label = Texte +pdfjs-editor-ink-button = + .title = Dessiner +pdfjs-editor-ink-button-label = Dessiner +pdfjs-editor-stamp-button = + .title = Ajouter ou modifier des images +pdfjs-editor-stamp-button-label = Ajouter ou modifier des images +pdfjs-editor-highlight-button = + .title = Surligner +pdfjs-editor-highlight-button-label = Surligner +pdfjs-highlight-floating-button1 = + .title = Surligner + .aria-label = Surligner +pdfjs-highlight-floating-button-label = Surligner + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Supprimer le dessin +pdfjs-editor-remove-freetext-button = + .title = Supprimer le texte +pdfjs-editor-remove-stamp-button = + .title = Supprimer l’image +pdfjs-editor-remove-highlight-button = + .title = Supprimer le surlignage + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Couleur +pdfjs-editor-free-text-size-input = Taille +pdfjs-editor-ink-color-input = Couleur +pdfjs-editor-ink-thickness-input = Épaisseur +pdfjs-editor-ink-opacity-input = Opacité +pdfjs-editor-stamp-add-image-button = + .title = Ajouter une image +pdfjs-editor-stamp-add-image-button-label = Ajouter une image +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Épaisseur +pdfjs-editor-free-highlight-thickness-title = + .title = Modifier l’épaisseur pour le surlignage d’éléments non textuels +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Éditeur de texte + .default-content = Commencez à écrire… +pdfjs-free-text = + .aria-label = Éditeur de texte +pdfjs-free-text-default-content = Commencer à écrire… +pdfjs-ink = + .aria-label = Éditeur de dessin +pdfjs-ink-canvas = + .aria-label = Image créée par l’utilisateur·trice + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Texte alternatif +pdfjs-editor-alt-text-edit-button = + .aria-label = Modifier le texte alternatif +pdfjs-editor-alt-text-edit-button-label = Modifier le texte alternatif +pdfjs-editor-alt-text-dialog-label = Sélectionnez une option +pdfjs-editor-alt-text-dialog-description = Le texte alternatif est utile lorsque des personnes ne peuvent pas voir l’image ou que l’image ne se charge pas. +pdfjs-editor-alt-text-add-description-label = Ajouter une description +pdfjs-editor-alt-text-add-description-description = Il est conseillé de rédiger une ou deux phrases décrivant le sujet, le cadre ou les actions. +pdfjs-editor-alt-text-mark-decorative-label = Marquer comme décorative +pdfjs-editor-alt-text-mark-decorative-description = Cette option est utilisée pour les images décoratives, comme les bordures ou les filigranes. +pdfjs-editor-alt-text-cancel-button = Annuler +pdfjs-editor-alt-text-save-button = Enregistrer +pdfjs-editor-alt-text-decorative-tooltip = Marquée comme décorative +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Par exemple, « Un jeune homme est assis à une table pour prendre un repas » +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texte alternatif + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Coin supérieur gauche — redimensionner +pdfjs-editor-resizer-label-top-middle = Milieu haut — redimensionner +pdfjs-editor-resizer-label-top-right = Coin supérieur droit — redimensionner +pdfjs-editor-resizer-label-middle-right = Milieu droit — redimensionner +pdfjs-editor-resizer-label-bottom-right = Coin inférieur droit — redimensionner +pdfjs-editor-resizer-label-bottom-middle = Centre bas — redimensionner +pdfjs-editor-resizer-label-bottom-left = Coin inférieur gauche — redimensionner +pdfjs-editor-resizer-label-middle-left = Milieu gauche — redimensionner +pdfjs-editor-resizer-top-left = + .aria-label = Coin supérieur gauche — redimensionner +pdfjs-editor-resizer-top-middle = + .aria-label = Milieu haut — redimensionner +pdfjs-editor-resizer-top-right = + .aria-label = Coin supérieur droit — redimensionner +pdfjs-editor-resizer-middle-right = + .aria-label = Milieu droit — redimensionner +pdfjs-editor-resizer-bottom-right = + .aria-label = Coin inférieur droit — redimensionner +pdfjs-editor-resizer-bottom-middle = + .aria-label = Centre bas — redimensionner +pdfjs-editor-resizer-bottom-left = + .aria-label = Coin inférieur gauche — redimensionner +pdfjs-editor-resizer-middle-left = + .aria-label = Milieu gauche — redimensionner + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Couleur de surlignage +pdfjs-editor-colorpicker-button = + .title = Changer de couleur +pdfjs-editor-colorpicker-dropdown = + .aria-label = Choix de couleurs +pdfjs-editor-colorpicker-yellow = + .title = Jaune +pdfjs-editor-colorpicker-green = + .title = Vert +pdfjs-editor-colorpicker-blue = + .title = Bleu +pdfjs-editor-colorpicker-pink = + .title = Rose +pdfjs-editor-colorpicker-red = + .title = Rouge + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Tout afficher +pdfjs-editor-highlight-show-all-button = + .title = Tout afficher + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Modifier le texte alternatif (description de l’image) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Ajouter du texte alternatif (description de l’image) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Rédigez votre description ici… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Courte description pour les personnes qui ne peuvent pas voir l’image ou lorsque l’image ne se charge pas. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Ce texte alternatif a été créé automatiquement et peut être inexact. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = En savoir plus +pdfjs-editor-new-alt-text-create-automatically-button-label = Créer automatiquement le texte alternatif +pdfjs-editor-new-alt-text-not-now-button = Pas maintenant +pdfjs-editor-new-alt-text-error-title = Impossible de créer automatiquement le texte alternatif +pdfjs-editor-new-alt-text-error-description = Veuillez rédiger votre propre texte alternatif ou réessayer plus tard. +pdfjs-editor-new-alt-text-error-close-button = Fermer +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Téléchargement du modèle d’IA de texte alternatif ({ $downloadedSize } sur { $totalSize } Mo) + .aria-valuetext = Téléchargement du modèle d’IA de texte alternatif ({ $downloadedSize } sur { $totalSize } Mo) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Texte alternatif ajouté +pdfjs-editor-new-alt-text-added-button-label = Texte alternatif ajouté +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Texte alternatif manquant +pdfjs-editor-new-alt-text-missing-button-label = Texte alternatif manquant +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Réviser le texte alternatif +pdfjs-editor-new-alt-text-to-review-button-label = Réviser le texte alternatif +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Créé automatiquement : { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Paramètres du texte alternatif des images +pdfjs-image-alt-text-settings-button-label = Paramètres du texte alternatif des images +pdfjs-editor-alt-text-settings-dialog-label = Paramètres du texte alternatif des images +pdfjs-editor-alt-text-settings-automatic-title = Texte alternatif automatique +pdfjs-editor-alt-text-settings-create-model-button-label = Créer automatiquement le texte alternatif +pdfjs-editor-alt-text-settings-create-model-description = Suggère des descriptions pour aider les personnes qui ne peuvent pas voir l’image ou lorsque l’image ne se charge pas. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Modèle d’IA de texte alternatif ({ $totalSize } Mo) +pdfjs-editor-alt-text-settings-ai-model-description = Fonctionne localement sur votre appareil, vos données restent privées. Obligatoire pour la génération automatique de texte alternatif. +pdfjs-editor-alt-text-settings-delete-model-button = Supprimer +pdfjs-editor-alt-text-settings-download-model-button = Télécharger +pdfjs-editor-alt-text-settings-downloading-model-button = Téléchargement… +pdfjs-editor-alt-text-settings-editor-title = Éditeur de texte alternatif +pdfjs-editor-alt-text-settings-show-dialog-button-label = Afficher l’éditeur de texte alternatif immédiatement lors de l’ajout d’une image +pdfjs-editor-alt-text-settings-show-dialog-description = Vous aide à vous assurer que toutes vos images ont du texte alternatif. +pdfjs-editor-alt-text-settings-close-button = Fermer + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Surlignage supprimé +pdfjs-editor-undo-bar-message-freetext = Texte supprimé +pdfjs-editor-undo-bar-message-ink = Dessin supprimé +pdfjs-editor-undo-bar-message-stamp = Image supprimée +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotation supprimée + *[other] { $count } annotations supprimées + } +pdfjs-editor-undo-bar-undo-button = + .title = Annuler +pdfjs-editor-undo-bar-undo-button-label = Annuler +pdfjs-editor-undo-bar-close-button = + .title = Fermer +pdfjs-editor-undo-bar-close-button-label = Fermer diff --git a/public/pdfjs/web/locale/fur/viewer.ftl b/public/pdfjs/web/locale/fur/viewer.ftl new file mode 100644 index 0000000..370af3f --- /dev/null +++ b/public/pdfjs/web/locale/fur/viewer.ftl @@ -0,0 +1,485 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pagjine precedente +pdfjs-previous-button-label = Indaûr +pdfjs-next-button = + .title = Prossime pagjine +pdfjs-next-button-label = Indevant +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pagjine +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = di { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } di { $pagesCount }) +pdfjs-zoom-out-button = + .title = Impiçulìs +pdfjs-zoom-out-button-label = Impiçulìs +pdfjs-zoom-in-button = + .title = Ingrandìs +pdfjs-zoom-in-button-label = Ingrandìs +pdfjs-zoom-select = + .title = Ingrandiment +pdfjs-presentation-mode-button = + .title = Passe ae modalitât presentazion +pdfjs-presentation-mode-button-label = Modalitât presentazion +pdfjs-open-file-button = + .title = Vierç un file +pdfjs-open-file-button-label = Vierç +pdfjs-print-button = + .title = Stampe +pdfjs-print-button-label = Stampe +pdfjs-save-button = + .title = Salve +pdfjs-save-button-label = Salve +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Discjame +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Discjame +pdfjs-bookmark-button = + .title = Pagjine corinte (mostre URL de pagjine atuâl) +pdfjs-bookmark-button-label = Pagjine corinte + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Struments +pdfjs-tools-button-label = Struments +pdfjs-first-page-button = + .title = Va ae prime pagjine +pdfjs-first-page-button-label = Va ae prime pagjine +pdfjs-last-page-button = + .title = Va ae ultime pagjine +pdfjs-last-page-button-label = Va ae ultime pagjine +pdfjs-page-rotate-cw-button = + .title = Zire in sens orari +pdfjs-page-rotate-cw-button-label = Zire in sens orari +pdfjs-page-rotate-ccw-button = + .title = Zire in sens antiorari +pdfjs-page-rotate-ccw-button-label = Zire in sens antiorari +pdfjs-cursor-text-select-tool-button = + .title = Ative il strument di selezion dal test +pdfjs-cursor-text-select-tool-button-label = Strument di selezion dal test +pdfjs-cursor-hand-tool-button = + .title = Ative il strument manute +pdfjs-cursor-hand-tool-button-label = Strument manute +pdfjs-scroll-page-button = + .title = Dopre il scoriment des pagjinis +pdfjs-scroll-page-button-label = Scoriment pagjinis +pdfjs-scroll-vertical-button = + .title = Dopre scoriment verticâl +pdfjs-scroll-vertical-button-label = Scoriment verticâl +pdfjs-scroll-horizontal-button = + .title = Dopre scoriment orizontâl +pdfjs-scroll-horizontal-button-label = Scoriment orizontâl +pdfjs-scroll-wrapped-button = + .title = Dopre scoriment par blocs +pdfjs-scroll-wrapped-button-label = Scoriment par blocs +pdfjs-spread-none-button = + .title = No sta meti dongje pagjinis in cubie +pdfjs-spread-none-button-label = No cubiis di pagjinis +pdfjs-spread-odd-button = + .title = Met dongje cubiis di pagjinis scomençant des pagjinis dispar +pdfjs-spread-odd-button-label = Cubiis di pagjinis, dispar a çampe +pdfjs-spread-even-button = + .title = Met dongje cubiis di pagjinis scomençant des pagjinis pâr +pdfjs-spread-even-button-label = Cubiis di pagjinis, pâr a çampe + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Proprietâts dal document… +pdfjs-document-properties-button-label = Proprietâts dal document… +pdfjs-document-properties-file-name = Non dal file: +pdfjs-document-properties-file-size = Dimension dal file: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Titul: +pdfjs-document-properties-author = Autôr: +pdfjs-document-properties-subject = Ogjet: +pdfjs-document-properties-keywords = Peraulis clâf: +pdfjs-document-properties-creation-date = Date di creazion: +pdfjs-document-properties-modification-date = Date di modifiche: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Creatôr +pdfjs-document-properties-producer = Gjeneradôr PDF: +pdfjs-document-properties-version = Version PDF: +pdfjs-document-properties-page-count = Numar di pagjinis: +pdfjs-document-properties-page-size = Dimension de pagjine: +pdfjs-document-properties-page-size-unit-inches = oncis +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = verticâl +pdfjs-document-properties-page-size-orientation-landscape = orizontâl +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letare +pdfjs-document-properties-page-size-name-legal = Legâl + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Visualizazion web svelte: +pdfjs-document-properties-linearized-yes = Sì +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Siere + +## Print + +pdfjs-print-progress-message = Daûr a prontâ il document pe stampe… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Anule +pdfjs-printing-not-supported = Atenzion: la stampe no je supuartade ad implen di chest navigadôr. +pdfjs-printing-not-ready = Atenzion: il PDF nol è stât cjamât dal dut pe stampe. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Ative/Disative sbare laterâl +pdfjs-toggle-sidebar-notification-button = + .title = Ative/Disative sbare laterâl (il document al conten struture/zontis/strâts) +pdfjs-toggle-sidebar-button-label = Ative/Disative sbare laterâl +pdfjs-document-outline-button = + .title = Mostre la struture dal document (dopli clic par slargjâ/strenzi ducj i elements) +pdfjs-document-outline-button-label = Struture dal document +pdfjs-attachments-button = + .title = Mostre lis zontis +pdfjs-attachments-button-label = Zontis +pdfjs-layers-button = + .title = Mostre i strâts (dopli clic par ristabilî ducj i strâts al stât predefinît) +pdfjs-layers-button-label = Strâts +pdfjs-thumbs-button = + .title = Mostre miniaturis +pdfjs-thumbs-button-label = Miniaturis +pdfjs-current-outline-item-button = + .title = Cjate l'element de struture atuâl +pdfjs-current-outline-item-button-label = Element de struture atuâl +pdfjs-findbar-button = + .title = Cjate tal document +pdfjs-findbar-button-label = Cjate +pdfjs-additional-layers = Strâts adizionâi + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pagjine { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniature de pagjine { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Cjate + .placeholder = Cjate tal document… +pdfjs-find-previous-button = + .title = Cjate il câs precedent dal test +pdfjs-find-previous-button-label = Precedent +pdfjs-find-next-button = + .title = Cjate il câs sucessîf dal test +pdfjs-find-next-button-label = Sucessîf +pdfjs-find-highlight-checkbox = Evidenzie dut +pdfjs-find-match-case-checkbox-label = Fâs distinzion tra maiusculis e minusculis +pdfjs-find-match-diacritics-checkbox-label = Corispondence diacritiche +pdfjs-find-entire-word-checkbox-label = Peraulis interiis +pdfjs-find-reached-top = Si è rivâts al inizi dal document e si à continuât de fin +pdfjs-find-reached-bottom = Si è rivât ae fin dal document e si à continuât dal inizi +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } di { $total } corispondence + *[other] { $current } di { $total } corispondencis + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Plui di { $limit } corispondence + *[other] Plui di { $limit } corispondencis + } +pdfjs-find-not-found = Test no cjatât + +## Predefined zoom values + +pdfjs-page-scale-width = Largjece de pagjine +pdfjs-page-scale-fit = Pagjine interie +pdfjs-page-scale-auto = Ingrandiment automatic +pdfjs-page-scale-actual = Dimension reâl +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Pagjine { $page } + +## Loading indicator messages + +pdfjs-loading-error = Al è vignût fûr un erôr intant che si cjariave il PDF. +pdfjs-invalid-file-error = File PDF no valit o ruvinât. +pdfjs-missing-file-error = Al mancje il file PDF. +pdfjs-unexpected-response-error = Rispueste dal servidôr inspietade. +pdfjs-rendering-error = Al è vignût fûr un erôr tal realizâ la visualizazion de pagjine. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anotazion { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Inserìs la password par vierzi chest file PDF. +pdfjs-password-invalid = Password no valide. Par plasê torne prove. +pdfjs-password-ok-button = Va ben +pdfjs-password-cancel-button = Anule +pdfjs-web-fonts-disabled = I caratars dal Web a son disativâts: Impussibil doprâ i caratars PDF incorporâts. + +## Editing + +pdfjs-editor-free-text-button = + .title = Test +pdfjs-editor-free-text-button-label = Test +pdfjs-editor-ink-button = + .title = Dissen +pdfjs-editor-ink-button-label = Dissen +pdfjs-editor-stamp-button = + .title = Zonte o modifiche imagjins +pdfjs-editor-stamp-button-label = Zonte o modifiche imagjins +pdfjs-editor-highlight-button = + .title = Evidenzie +pdfjs-editor-highlight-button-label = Evidenzie +pdfjs-highlight-floating-button1 = + .title = Evidenzie + .aria-label = Evidenzie +pdfjs-highlight-floating-button-label = Evidenzie + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Gjave dissen +pdfjs-editor-remove-freetext-button = + .title = Gjave test +pdfjs-editor-remove-stamp-button = + .title = Gjave imagjin +pdfjs-editor-remove-highlight-button = + .title = Gjave evidenziazion + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Colôr +pdfjs-editor-free-text-size-input = Dimension +pdfjs-editor-ink-color-input = Colôr +pdfjs-editor-ink-thickness-input = Spessôr +pdfjs-editor-ink-opacity-input = Opacitât +pdfjs-editor-stamp-add-image-button = + .title = Zonte imagjin +pdfjs-editor-stamp-add-image-button-label = Zonte imagjin +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Spessôr +pdfjs-editor-free-highlight-thickness-title = + .title = Modifiche il spessôr de selezion pai elements che no son testuâi +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editôr di test + .default-content = Scomence a scrivi… +pdfjs-free-text = + .aria-label = Editôr di test +pdfjs-free-text-default-content = Scomence a scrivi… +pdfjs-ink = + .aria-label = Editôr dissens +pdfjs-ink-canvas = + .aria-label = Imagjin creade dal utent + +## Alt-text dialog + +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button-label = Test alternatîf +pdfjs-editor-alt-text-edit-button-label = Modifiche test alternatîf +pdfjs-editor-alt-text-dialog-label = Sielç une opzion +pdfjs-editor-alt-text-dialog-description = Il test alternatîf (“alt text”) al jude cuant che lis personis no puedin viodi la imagjin o cuant che la imagjine no ven cjariade. +pdfjs-editor-alt-text-add-description-label = Zonte une descrizion +pdfjs-editor-alt-text-add-description-description = Ponte a une o dôs frasis che a descrivin l’argoment, la ambientazion o lis azions. +pdfjs-editor-alt-text-mark-decorative-label = Segne come decorative +pdfjs-editor-alt-text-mark-decorative-description = Chest al ven doprât pes imagjins ornamentâls, come i ôrs o lis filigranis. +pdfjs-editor-alt-text-cancel-button = Anule +pdfjs-editor-alt-text-save-button = Salve +pdfjs-editor-alt-text-decorative-tooltip = Segnade come decorative +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Par esempli, “Un zovin si sente a taule par mangjâ” + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Cjanton in alt a çampe — ridimensione +pdfjs-editor-resizer-label-top-middle = Bande superiôr tal mieç — ridimensione +pdfjs-editor-resizer-label-top-right = Cjanton in alt a diestre — ridimensione +pdfjs-editor-resizer-label-middle-right = Bande diestre tal mieç — ridimensione +pdfjs-editor-resizer-label-bottom-right = Cjanton in bas a diestre — ridimensione +pdfjs-editor-resizer-label-bottom-middle = Bande inferiôr tal mieç — ridimensione +pdfjs-editor-resizer-label-bottom-left = Cjanton in bas a çampe — ridimensione +pdfjs-editor-resizer-label-middle-left = Bande di çampe tal mieç — ridimensione +pdfjs-editor-resizer-top-left = + .aria-label = Cjanton in alt a çampe — ridimensione +pdfjs-editor-resizer-top-middle = + .aria-label = Bande superiôr tal mieç — ridimensione +pdfjs-editor-resizer-top-right = + .aria-label = Cjanton in alt a diestre — ridimensione +pdfjs-editor-resizer-middle-right = + .aria-label = Bande diestre tal mieç — ridimensione +pdfjs-editor-resizer-bottom-right = + .aria-label = Cjanton in bas a diestre — ridimensione +pdfjs-editor-resizer-bottom-middle = + .aria-label = Bande inferiôr tal mieç — ridimensione +pdfjs-editor-resizer-bottom-left = + .aria-label = Cjanton in bas a çampe — ridimensione +pdfjs-editor-resizer-middle-left = + .aria-label = Bande di çampe tal mieç — ridimensione + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Colôr par evidenziâ +pdfjs-editor-colorpicker-button = + .title = Cambie colôr +pdfjs-editor-colorpicker-dropdown = + .aria-label = Sieltis di colôr +pdfjs-editor-colorpicker-yellow = + .title = Zâl +pdfjs-editor-colorpicker-green = + .title = Vert +pdfjs-editor-colorpicker-blue = + .title = Blu +pdfjs-editor-colorpicker-pink = + .title = Rose +pdfjs-editor-colorpicker-red = + .title = Ros + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Mostre dut +pdfjs-editor-highlight-show-all-button = + .title = Mostre dut + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Modifiche test alternatîf (descrizion de imagjin) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Zonte test alternatîf (descrizion de imagjin) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Scrîf achì la tô descrizion… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Curte descrizion par personis che no rivin a viodi la imagjin, o che e ven mostrade cuant che no si rive a cjariâle. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Chest test alternatîf al è stât creât in automatic e al è pussibil che nol sedi cret. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Plui informazions +pdfjs-editor-new-alt-text-create-automatically-button-label = Cree test alternatîf in automatic +pdfjs-editor-new-alt-text-not-now-button = No cumò +pdfjs-editor-new-alt-text-error-title = Impussibil creâ test alternatîf in automatic +pdfjs-editor-new-alt-text-error-description = Scrîf il to test alternatîf o prove plui tart. +pdfjs-editor-new-alt-text-error-close-button = Siere +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Daûr a discjariâil model IA pal test alternatîf ({ $downloadedSize } di { $totalSize } MB) + .aria-valuetext = Daûr a discjariâ il model IA pal test alternatîf ({ $downloadedSize } di { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button-label = Test alternatîf zontât +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button-label = Al mancje il test alternatîf +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button-label = Verifiche test alternatîf +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Creât in automatic: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Impostazions test alternatîf pes imagjins +pdfjs-image-alt-text-settings-button-label = Impostazions test alternatîf pes imagjins +pdfjs-editor-alt-text-settings-dialog-label = Impostazions test alternatîf pes imagjins +pdfjs-editor-alt-text-settings-automatic-title = Test alternatîf automatic +pdfjs-editor-alt-text-settings-create-model-button-label = Cree test alternatîf in automatic +pdfjs-editor-alt-text-settings-create-model-description = Al sugjerìs descrizions par judâ lis personis che no rivin a viodi la imagjin o cuant che la imagjin no ven cjariade. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Model IA pal test alternatîf ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Al ven eseguît in locâl sul to dispositîf, cussì che i tiei dâts a restin riservâts. Al è necessari pe gjenerazion automatiche dal test alternatîf. +pdfjs-editor-alt-text-settings-delete-model-button = Elimine +pdfjs-editor-alt-text-settings-download-model-button = Discjame +pdfjs-editor-alt-text-settings-downloading-model-button = Daûr a discjariâ… +pdfjs-editor-alt-text-settings-editor-title = Modifiche test alternatîf +pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostre l'editôr dal test alternatîf a pene che e ven zontade une imagjin +pdfjs-editor-alt-text-settings-show-dialog-description = Ti jude a sigurâti che dutis lis tôs imagjins a vedin il test alternatîf. +pdfjs-editor-alt-text-settings-close-button = Siere diff --git a/public/pdfjs/web/locale/fy-NL/viewer.ftl b/public/pdfjs/web/locale/fy-NL/viewer.ftl new file mode 100644 index 0000000..15850b4 --- /dev/null +++ b/public/pdfjs/web/locale/fy-NL/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Foarige side +pdfjs-previous-button-label = Foarige +pdfjs-next-button = + .title = Folgjende side +pdfjs-next-button-label = Folgjende +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Side +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = fan { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } fan { $pagesCount }) +pdfjs-zoom-out-button = + .title = Utzoome +pdfjs-zoom-out-button-label = Utzoome +pdfjs-zoom-in-button = + .title = Ynzoome +pdfjs-zoom-in-button-label = Ynzoome +pdfjs-zoom-select = + .title = Zoome +pdfjs-presentation-mode-button = + .title = Wikselje nei presintaasjemodus +pdfjs-presentation-mode-button-label = Presintaasjemodus +pdfjs-open-file-button = + .title = Bestân iepenje +pdfjs-open-file-button-label = Iepenje +pdfjs-print-button = + .title = Ofdrukke +pdfjs-print-button-label = Ofdrukke +pdfjs-save-button = + .title = Bewarje +pdfjs-save-button-label = Bewarje +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Downloade +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Downloade +pdfjs-bookmark-button = + .title = Aktuele side (URL fan aktuele side besjen) +pdfjs-bookmark-button-label = Aktuele side + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Ark +pdfjs-tools-button-label = Ark +pdfjs-first-page-button = + .title = Gean nei earste side +pdfjs-first-page-button-label = Gean nei earste side +pdfjs-last-page-button = + .title = Gean nei lêste side +pdfjs-last-page-button-label = Gean nei lêste side +pdfjs-page-rotate-cw-button = + .title = Rjochtsom draaie +pdfjs-page-rotate-cw-button-label = Rjochtsom draaie +pdfjs-page-rotate-ccw-button = + .title = Linksom draaie +pdfjs-page-rotate-ccw-button-label = Linksom draaie +pdfjs-cursor-text-select-tool-button = + .title = Tekstseleksjehelpmiddel ynskeakelje +pdfjs-cursor-text-select-tool-button-label = Tekstseleksjehelpmiddel +pdfjs-cursor-hand-tool-button = + .title = Hânhelpmiddel ynskeakelje +pdfjs-cursor-hand-tool-button-label = Hânhelpmiddel +pdfjs-scroll-page-button = + .title = Sideskowen brûke +pdfjs-scroll-page-button-label = Sideskowen +pdfjs-scroll-vertical-button = + .title = Fertikaal skowe brûke +pdfjs-scroll-vertical-button-label = Fertikaal skowe +pdfjs-scroll-horizontal-button = + .title = Horizontaal skowe brûke +pdfjs-scroll-horizontal-button-label = Horizontaal skowe +pdfjs-scroll-wrapped-button = + .title = Skowe mei oersjoch brûke +pdfjs-scroll-wrapped-button-label = Skowe mei oersjoch +pdfjs-spread-none-button = + .title = Sidesprieding net gearfetsje +pdfjs-spread-none-button-label = Gjin sprieding +pdfjs-spread-odd-button = + .title = Sidesprieding gearfetsje te starten mei ûneven nûmers +pdfjs-spread-odd-button-label = Uneven sprieding +pdfjs-spread-even-button = + .title = Sidesprieding gearfetsje te starten mei even nûmers +pdfjs-spread-even-button-label = Even sprieding + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokuminteigenskippen… +pdfjs-document-properties-button-label = Dokuminteigenskippen… +pdfjs-document-properties-file-name = Bestânsnamme: +pdfjs-document-properties-file-size = Bestânsgrutte: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Titel: +pdfjs-document-properties-author = Auteur: +pdfjs-document-properties-subject = Underwerp: +pdfjs-document-properties-keywords = Kaaiwurden: +pdfjs-document-properties-creation-date = Oanmaakdatum: +pdfjs-document-properties-modification-date = Bewurkingsdatum: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Makker: +pdfjs-document-properties-producer = PDF-makker: +pdfjs-document-properties-version = PDF-ferzje: +pdfjs-document-properties-page-count = Siden: +pdfjs-document-properties-page-size = Sideformaat: +pdfjs-document-properties-page-size-unit-inches = yn +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = steand +pdfjs-document-properties-page-size-orientation-landscape = lizzend +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Juridysk + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Flugge webwerjefte: +pdfjs-document-properties-linearized-yes = Ja +pdfjs-document-properties-linearized-no = Nee +pdfjs-document-properties-close-button = Slute + +## Print + +pdfjs-print-progress-message = Dokumint tariede oar ôfdrukken… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Annulearje +pdfjs-printing-not-supported = Warning: Printen is net folslein stipe troch dizze browser. +pdfjs-printing-not-ready = Warning: PDF is net folslein laden om ôf te drukken. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Sidebalke yn-/útskeakelje +pdfjs-toggle-sidebar-notification-button = + .title = Sidebalke yn-/útskeakelje (dokumint befettet oersjoch/bylagen/lagen) +pdfjs-toggle-sidebar-button-label = Sidebalke yn-/útskeakelje +pdfjs-document-outline-button = + .title = Dokumintoersjoch toane (dûbelklik om alle items út/yn te klappen) +pdfjs-document-outline-button-label = Dokumintoersjoch +pdfjs-attachments-button = + .title = Bylagen toane +pdfjs-attachments-button-label = Bylagen +pdfjs-layers-button = + .title = Lagen toane (dûbelklik om alle lagen nei de standertsteat werom te setten) +pdfjs-layers-button-label = Lagen +pdfjs-thumbs-button = + .title = Foarbylden toane +pdfjs-thumbs-button-label = Foarbylden +pdfjs-current-outline-item-button = + .title = Aktueel item yn ynhâldsopjefte sykje +pdfjs-current-outline-item-button-label = Aktueel item yn ynhâldsopjefte +pdfjs-findbar-button = + .title = Sykje yn dokumint +pdfjs-findbar-button-label = Sykje +pdfjs-additional-layers = Oanfoljende lagen + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Side { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Foarbyld fan side { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Sykje + .placeholder = Sykje yn dokumint… +pdfjs-find-previous-button = + .title = It foarige foarkommen fan de tekst sykje +pdfjs-find-previous-button-label = Foarige +pdfjs-find-next-button = + .title = It folgjende foarkommen fan de tekst sykje +pdfjs-find-next-button-label = Folgjende +pdfjs-find-highlight-checkbox = Alles markearje +pdfjs-find-match-case-checkbox-label = Haadlettergefoelich +pdfjs-find-match-diacritics-checkbox-label = Diakrityske tekens brûke +pdfjs-find-entire-word-checkbox-label = Hiele wurden +pdfjs-find-reached-top = Boppekant fan dokumint berikt, trochgien fan ûnder ôf +pdfjs-find-reached-bottom = Ein fan dokumint berikt, trochgien fan boppe ôf +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } fan { $total } oerienkomst + *[other] { $current } fan { $total } oerienkomsten + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Mear as { $limit } oerienkomst + *[other] Mear as { $limit } oerienkomsten + } +pdfjs-find-not-found = Tekst net fûn + +## Predefined zoom values + +pdfjs-page-scale-width = Sidebreedte +pdfjs-page-scale-fit = Hiele side +pdfjs-page-scale-auto = Automatysk zoome +pdfjs-page-scale-actual = Werklike grutte +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Side { $page } + +## Loading indicator messages + +pdfjs-loading-error = Der is in flater bard by it laden fan de PDF. +pdfjs-invalid-file-error = Ynfalide of korruptearre PDF-bestân. +pdfjs-missing-file-error = PDF-bestân ûntbrekt. +pdfjs-unexpected-response-error = Unferwacht serverantwurd. +pdfjs-rendering-error = Der is in flater bard by it renderjen fan de side. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type }-annotaasje] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Jou it wachtwurd om dit PDF-bestân te iepenjen. +pdfjs-password-invalid = Ferkeard wachtwurd. Probearje opnij. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Annulearje +pdfjs-web-fonts-disabled = Weblettertypen binne útskeakele: gebrûk fan ynsluten PDF-lettertypen is net mooglik. + +## Editing + +pdfjs-editor-free-text-button = + .title = Tekst +pdfjs-editor-free-text-button-label = Tekst +pdfjs-editor-ink-button = + .title = Tekenje +pdfjs-editor-ink-button-label = Tekenje +pdfjs-editor-stamp-button = + .title = Ofbyldingen tafoegje of bewurkje +pdfjs-editor-stamp-button-label = Ofbyldingen tafoegje of bewurkje +pdfjs-editor-highlight-button = + .title = Markearje +pdfjs-editor-highlight-button-label = Markearje +pdfjs-highlight-floating-button1 = + .title = Markearje + .aria-label = Markearje +pdfjs-highlight-floating-button-label = Markearje + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Tekening fuortsmite +pdfjs-editor-remove-freetext-button = + .title = Tekst fuortsmite +pdfjs-editor-remove-stamp-button = + .title = Ofbylding fuortsmite +pdfjs-editor-remove-highlight-button = + .title = Markearring fuortsmite + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Kleur +pdfjs-editor-free-text-size-input = Grutte +pdfjs-editor-ink-color-input = Kleur +pdfjs-editor-ink-thickness-input = Tsjokte +pdfjs-editor-ink-opacity-input = Transparânsje +pdfjs-editor-stamp-add-image-button = + .title = Ofbylding tafoegje +pdfjs-editor-stamp-add-image-button-label = Ofbylding tafoegje +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Tsjokte +pdfjs-editor-free-highlight-thickness-title = + .title = Tsjokte wizigje by aksintuearring fan oare items as tekst +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstbewurker + .default-content = Start mei typen… +pdfjs-free-text = + .aria-label = Tekstbewurker +pdfjs-free-text-default-content = Begjin mei typen… +pdfjs-ink = + .aria-label = Tekeningbewurker +pdfjs-ink-canvas = + .aria-label = Troch brûker makke ôfbylding + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alternative tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternative tekst bewurkje +pdfjs-editor-alt-text-edit-button-label = Alternative tekst bewurkje +pdfjs-editor-alt-text-dialog-label = Kies in opsje +pdfjs-editor-alt-text-dialog-description = Alternative tekst helpt wannear’t minsken de ôfbylding net sjen kinne of wannear’t dizze net laden wurdt. +pdfjs-editor-alt-text-add-description-label = Foegje in beskriuwing ta +pdfjs-editor-alt-text-add-description-description = Stribje nei 1-2 sinnen dy’t it ûnderwerp, de omjouwing of de aksjes beskriuwe. +pdfjs-editor-alt-text-mark-decorative-label = As dekoratyf markearje +pdfjs-editor-alt-text-mark-decorative-description = Dit wurdt brûkt foar sierlike ôfbyldingen, lykas rânen of wettermerken. +pdfjs-editor-alt-text-cancel-button = Annulearje +pdfjs-editor-alt-text-save-button = Bewarje +pdfjs-editor-alt-text-decorative-tooltip = As dekoratyf markearre +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Bygelyks, ‘In jonge man sit oan in tafel om te iten’ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternative tekst + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Linkerboppehoek – formaat wizigje +pdfjs-editor-resizer-label-top-middle = Midden boppe – formaat wizigje +pdfjs-editor-resizer-label-top-right = Rjochterboppehoek – formaat wizigje +pdfjs-editor-resizer-label-middle-right = Midden rjochts – formaat wizigje +pdfjs-editor-resizer-label-bottom-right = Rjochterûnderhoek – formaat wizigje +pdfjs-editor-resizer-label-bottom-middle = Midden ûnder – formaat wizigje +pdfjs-editor-resizer-label-bottom-left = Linkerûnderhoek – formaat wizigje +pdfjs-editor-resizer-label-middle-left = Links midden – formaat wizigje +pdfjs-editor-resizer-top-left = + .aria-label = Linkerboppehoek – formaat wizigje +pdfjs-editor-resizer-top-middle = + .aria-label = Midden boppe – formaat wizigje +pdfjs-editor-resizer-top-right = + .aria-label = Rjochterboppehoek – formaat wizigje +pdfjs-editor-resizer-middle-right = + .aria-label = Midden rjochts – formaat wizigje +pdfjs-editor-resizer-bottom-right = + .aria-label = Rjochterûnderhoek – formaat wizigje +pdfjs-editor-resizer-bottom-middle = + .aria-label = Midden ûnder – formaat wizigje +pdfjs-editor-resizer-bottom-left = + .aria-label = Linkerûnderhoek – formaat wizigje +pdfjs-editor-resizer-middle-left = + .aria-label = Links midden – formaat wizigje + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Markearringskleur +pdfjs-editor-colorpicker-button = + .title = Kleur wizigje +pdfjs-editor-colorpicker-dropdown = + .aria-label = Kleurkarren +pdfjs-editor-colorpicker-yellow = + .title = Giel +pdfjs-editor-colorpicker-green = + .title = Grien +pdfjs-editor-colorpicker-blue = + .title = Blau +pdfjs-editor-colorpicker-pink = + .title = Roze +pdfjs-editor-colorpicker-red = + .title = Read + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Alles toane +pdfjs-editor-highlight-show-all-button = + .title = Alles toane + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Alternative tekst (ôfbyldingsbeskriuwing) bewurkje +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Alternative tekst (ôfbyldingsbeskriuwing) tafoegje +pdfjs-editor-new-alt-text-textarea = + .placeholder = Skriuw hjir jo beskriuwing… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Koarte beskriuwing foar minsken dy’t de ôfbylding net sjen kinne of wannear’t de ôfbylding net laden wurdt. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Dizze alternative tekst is automatysk makke en is mooglik net korrekt. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Mear ynfo +pdfjs-editor-new-alt-text-create-automatically-button-label = Alternative tekst automatysk oanmeitsje +pdfjs-editor-new-alt-text-not-now-button = No net +pdfjs-editor-new-alt-text-error-title = Kin alternative tekst net automatysk oanmeitsje +pdfjs-editor-new-alt-text-error-description = Skriuw jo eigen alternative tekst of probearje it letter nochris. +pdfjs-editor-new-alt-text-error-close-button = Slute +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = AI-model foar alternative tekst downloade ({ $downloadedSize } fan { $totalSize } MB) + .aria-valuetext = AI-model foar alternative tekst downloade ({ $downloadedSize } fan { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternative tekst tafoege +pdfjs-editor-new-alt-text-added-button-label = Alternative tekst tafoege +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Alternative tekst ûntbrekt +pdfjs-editor-new-alt-text-missing-button-label = Alternative tekst ûntbrekt +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternative tekst beoardiele +pdfjs-editor-new-alt-text-to-review-button-label = Alternative tekst beoardiele +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Automatysk oanmakke: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Ynstellingen foar alternative tekst fan ôfbyldingen +pdfjs-image-alt-text-settings-button-label = Ynstellingen foar alternative tekst fan ôfbyldingen +pdfjs-editor-alt-text-settings-dialog-label = Ynstellingen foar alternative tekst fan ôfbyldingen +pdfjs-editor-alt-text-settings-automatic-title = Automatyske alternative tekst +pdfjs-editor-alt-text-settings-create-model-button-label = Alternative tekst automatysk oanmeitsje +pdfjs-editor-alt-text-settings-create-model-description = Stelt beskriuwingen foar om minsken te helpen dy’t de ôfbylding net sjen kinne of foar wa’t de ôfbylding net laden wurdt. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = AI-model foar alternative tekst ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Wurdt lokaal op jo apparaat útfierd, sadat jo gegevens privee bliuwe. Fereaske foar automatyske alternative tekst. +pdfjs-editor-alt-text-settings-delete-model-button = Fuortsmite +pdfjs-editor-alt-text-settings-download-model-button = Downloade +pdfjs-editor-alt-text-settings-downloading-model-button = Downloade… +pdfjs-editor-alt-text-settings-editor-title = Alternative-tekstbewurker +pdfjs-editor-alt-text-settings-show-dialog-button-label = Alternative-tekstbewurker daliks toane by tafoegjen fan in ôfbylding +pdfjs-editor-alt-text-settings-show-dialog-description = Helpt jo derfoar te soargjen dat al jo ôfbyldingen alternative tekst hawwe. +pdfjs-editor-alt-text-settings-close-button = Slute + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Markearring fuortsmiten +pdfjs-editor-undo-bar-message-freetext = Tekst fuortsmiten +pdfjs-editor-undo-bar-message-ink = Tekening fuortsmiten +pdfjs-editor-undo-bar-message-stamp = Ofbylding fuortsmiten +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotaasje fuortsmiten + *[other] { $count } annotaasjes fuortsmiten + } +pdfjs-editor-undo-bar-undo-button = + .title = Ungedien meitsje +pdfjs-editor-undo-bar-undo-button-label = Ungedien meitsje +pdfjs-editor-undo-bar-close-button = + .title = Slute +pdfjs-editor-undo-bar-close-button-label = Slute diff --git a/public/pdfjs/web/locale/ga-IE/viewer.ftl b/public/pdfjs/web/locale/ga-IE/viewer.ftl new file mode 100644 index 0000000..cb59308 --- /dev/null +++ b/public/pdfjs/web/locale/ga-IE/viewer.ftl @@ -0,0 +1,213 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = An Leathanach Roimhe Seo +pdfjs-previous-button-label = Roimhe Seo +pdfjs-next-button = + .title = An Chéad Leathanach Eile +pdfjs-next-button-label = Ar Aghaidh +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Leathanach +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = as { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } as { $pagesCount }) +pdfjs-zoom-out-button = + .title = Súmáil Amach +pdfjs-zoom-out-button-label = Súmáil Amach +pdfjs-zoom-in-button = + .title = Súmáil Isteach +pdfjs-zoom-in-button-label = Súmáil Isteach +pdfjs-zoom-select = + .title = Súmáil +pdfjs-presentation-mode-button = + .title = Úsáid an Mód Láithreoireachta +pdfjs-presentation-mode-button-label = Mód Láithreoireachta +pdfjs-open-file-button = + .title = Oscail Comhad +pdfjs-open-file-button-label = Oscail +pdfjs-print-button = + .title = Priontáil +pdfjs-print-button-label = Priontáil + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Uirlisí +pdfjs-tools-button-label = Uirlisí +pdfjs-first-page-button = + .title = Go dtí an chéad leathanach +pdfjs-first-page-button-label = Go dtí an chéad leathanach +pdfjs-last-page-button = + .title = Go dtí an leathanach deiridh +pdfjs-last-page-button-label = Go dtí an leathanach deiridh +pdfjs-page-rotate-cw-button = + .title = Rothlaigh ar deiseal +pdfjs-page-rotate-cw-button-label = Rothlaigh ar deiseal +pdfjs-page-rotate-ccw-button = + .title = Rothlaigh ar tuathal +pdfjs-page-rotate-ccw-button-label = Rothlaigh ar tuathal +pdfjs-cursor-text-select-tool-button = + .title = Cumasaigh an Uirlis Roghnaithe Téacs +pdfjs-cursor-text-select-tool-button-label = Uirlis Roghnaithe Téacs +pdfjs-cursor-hand-tool-button = + .title = Cumasaigh an Uirlis Láimhe +pdfjs-cursor-hand-tool-button-label = Uirlis Láimhe + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Airíonna na Cáipéise… +pdfjs-document-properties-button-label = Airíonna na Cáipéise… +pdfjs-document-properties-file-name = Ainm an chomhaid: +pdfjs-document-properties-file-size = Méid an chomhaid: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } beart) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } beart) +pdfjs-document-properties-title = Teideal: +pdfjs-document-properties-author = Údar: +pdfjs-document-properties-subject = Ábhar: +pdfjs-document-properties-keywords = Eochairfhocail: +pdfjs-document-properties-creation-date = Dáta Cruthaithe: +pdfjs-document-properties-modification-date = Dáta Athraithe: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Cruthaitheoir: +pdfjs-document-properties-producer = Cruthaitheoir an PDF: +pdfjs-document-properties-version = Leagan PDF: +pdfjs-document-properties-page-count = Líon Leathanach: + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + + +## + +pdfjs-document-properties-close-button = Dún + +## Print + +pdfjs-print-progress-message = Cáipéis á hullmhú le priontáil… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cealaigh +pdfjs-printing-not-supported = Rabhadh: Ní thacaíonn an brabhsálaí le priontáil go hiomlán. +pdfjs-printing-not-ready = Rabhadh: Ní féidir an PDF a phriontáil go dtí go mbeidh an cháipéis iomlán lódáilte. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Scoránaigh an Barra Taoibh +pdfjs-toggle-sidebar-button-label = Scoránaigh an Barra Taoibh +pdfjs-document-outline-button = + .title = Taispeáin Imlíne na Cáipéise (déchliceáil chun chuile rud a leathnú nó a laghdú) +pdfjs-document-outline-button-label = Creatlach na Cáipéise +pdfjs-attachments-button = + .title = Taispeáin Iatáin +pdfjs-attachments-button-label = Iatáin +pdfjs-thumbs-button = + .title = Taispeáin Mionsamhlacha +pdfjs-thumbs-button-label = Mionsamhlacha +pdfjs-findbar-button = + .title = Aimsigh sa Cháipéis +pdfjs-findbar-button-label = Aimsigh + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Leathanach { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Mionsamhail Leathanaigh { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Aimsigh + .placeholder = Aimsigh sa cháipéis… +pdfjs-find-previous-button = + .title = Aimsigh an sampla roimhe seo den nath seo +pdfjs-find-previous-button-label = Roimhe seo +pdfjs-find-next-button = + .title = Aimsigh an chéad sampla eile den nath sin +pdfjs-find-next-button-label = Ar aghaidh +pdfjs-find-highlight-checkbox = Aibhsigh uile +pdfjs-find-match-case-checkbox-label = Cásíogair +pdfjs-find-entire-word-checkbox-label = Focail iomlána +pdfjs-find-reached-top = Ag barr na cáipéise, ag leanúint ón mbun +pdfjs-find-reached-bottom = Ag bun na cáipéise, ag leanúint ón mbarr +pdfjs-find-not-found = Frása gan aimsiú + +## Predefined zoom values + +pdfjs-page-scale-width = Leithead Leathanaigh +pdfjs-page-scale-fit = Laghdaigh go dtí an Leathanach +pdfjs-page-scale-auto = Súmáil Uathoibríoch +pdfjs-page-scale-actual = Fíormhéid +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Tharla earráid agus an cháipéis PDF á lódáil. +pdfjs-invalid-file-error = Comhad neamhbhailí nó truaillithe PDF. +pdfjs-missing-file-error = Comhad PDF ar iarraidh. +pdfjs-unexpected-response-error = Freagra ón bhfreastalaí nach rabhthas ag súil leis. +pdfjs-rendering-error = Tharla earráid agus an leathanach á leagan amach. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anótáil { $type }] + +## Password + +pdfjs-password-label = Cuir an focal faire isteach chun an comhad PDF seo a oscailt. +pdfjs-password-invalid = Focal faire mícheart. Déan iarracht eile. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Cealaigh +pdfjs-web-fonts-disabled = Tá clófhoirne Gréasáin díchumasaithe: ní féidir clófhoirne leabaithe PDF a úsáid. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/gd/viewer.ftl b/public/pdfjs/web/locale/gd/viewer.ftl new file mode 100644 index 0000000..a3d62a0 --- /dev/null +++ b/public/pdfjs/web/locale/gd/viewer.ftl @@ -0,0 +1,313 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = An duilleag roimhe +pdfjs-previous-button-label = Air ais +pdfjs-next-button = + .title = An ath-dhuilleag +pdfjs-next-button-label = Air adhart +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Duilleag +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = à { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } à { $pagesCount }) +pdfjs-zoom-out-button = + .title = Sùm a-mach +pdfjs-zoom-out-button-label = Sùm a-mach +pdfjs-zoom-in-button = + .title = Sùm a-steach +pdfjs-zoom-in-button-label = Sùm a-steach +pdfjs-zoom-select = + .title = Sùm +pdfjs-presentation-mode-button = + .title = Gearr leum dhan mhodh taisbeanaidh +pdfjs-presentation-mode-button-label = Am modh taisbeanaidh +pdfjs-open-file-button = + .title = Fosgail faidhle +pdfjs-open-file-button-label = Fosgail +pdfjs-print-button = + .title = Clò-bhuail +pdfjs-print-button-label = Clò-bhuail +pdfjs-save-button = + .title = Sàbhail +pdfjs-save-button-label = Sàbhail +pdfjs-bookmark-button = + .title = An duilleag làithreach (Seall an URL on duilleag làithreach) +pdfjs-bookmark-button-label = An duilleag làithreach + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Innealan +pdfjs-tools-button-label = Innealan +pdfjs-first-page-button = + .title = Rach gun chiad duilleag +pdfjs-first-page-button-label = Rach gun chiad duilleag +pdfjs-last-page-button = + .title = Rach gun duilleag mu dheireadh +pdfjs-last-page-button-label = Rach gun duilleag mu dheireadh +pdfjs-page-rotate-cw-button = + .title = Cuairtich gu deiseil +pdfjs-page-rotate-cw-button-label = Cuairtich gu deiseil +pdfjs-page-rotate-ccw-button = + .title = Cuairtich gu tuathail +pdfjs-page-rotate-ccw-button-label = Cuairtich gu tuathail +pdfjs-cursor-text-select-tool-button = + .title = Cuir an comas inneal taghadh an teacsa +pdfjs-cursor-text-select-tool-button-label = Inneal taghadh an teacsa +pdfjs-cursor-hand-tool-button = + .title = Cuir inneal na làimhe an comas +pdfjs-cursor-hand-tool-button-label = Inneal na làimhe +pdfjs-scroll-page-button = + .title = Cleachd sgroladh duilleige +pdfjs-scroll-page-button-label = Sgroladh duilleige +pdfjs-scroll-vertical-button = + .title = Cleachd sgroladh inghearach +pdfjs-scroll-vertical-button-label = Sgroladh inghearach +pdfjs-scroll-horizontal-button = + .title = Cleachd sgroladh còmhnard +pdfjs-scroll-horizontal-button-label = Sgroladh còmhnard +pdfjs-scroll-wrapped-button = + .title = Cleachd sgroladh paisgte +pdfjs-scroll-wrapped-button-label = Sgroladh paisgte +pdfjs-spread-none-button = + .title = Na cuir còmhla sgoileadh dhuilleagan +pdfjs-spread-none-button-label = Gun sgaoileadh dhuilleagan +pdfjs-spread-odd-button = + .title = Cuir còmhla duilleagan sgaoilte a thòisicheas le duilleagan aig a bheil àireamh chorr +pdfjs-spread-odd-button-label = Sgaoileadh dhuilleagan corra +pdfjs-spread-even-button = + .title = Cuir còmhla duilleagan sgaoilte a thòisicheas le duilleagan aig a bheil àireamh chothrom +pdfjs-spread-even-button-label = Sgaoileadh dhuilleagan cothrom + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Roghainnean na sgrìobhainne… +pdfjs-document-properties-button-label = Roghainnean na sgrìobhainne… +pdfjs-document-properties-file-name = Ainm an fhaidhle: +pdfjs-document-properties-file-size = Meud an fhaidhle: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Tiotal: +pdfjs-document-properties-author = Ùghdar: +pdfjs-document-properties-subject = Cuspair: +pdfjs-document-properties-keywords = Faclan-luirg: +pdfjs-document-properties-creation-date = Latha a chruthachaidh: +pdfjs-document-properties-modification-date = Latha atharrachaidh: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Cruthadair: +pdfjs-document-properties-producer = Saothraiche a' PDF: +pdfjs-document-properties-version = Tionndadh a' PDF: +pdfjs-document-properties-page-count = Àireamh de dhuilleagan: +pdfjs-document-properties-page-size = Meud na duilleige: +pdfjs-document-properties-page-size-unit-inches = ann an +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = portraid +pdfjs-document-properties-page-size-orientation-landscape = dreach-tìre +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Litir +pdfjs-document-properties-page-size-name-legal = Laghail + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Grad shealladh-lìn: +pdfjs-document-properties-linearized-yes = Tha +pdfjs-document-properties-linearized-no = Chan eil +pdfjs-document-properties-close-button = Dùin + +## Print + +pdfjs-print-progress-message = Ag ullachadh na sgrìobhainn airson clò-bhualadh… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Sguir dheth +pdfjs-printing-not-supported = Rabhadh: Chan eil am brabhsair seo a' cur làn-taic ri clò-bhualadh. +pdfjs-printing-not-ready = Rabhadh: Cha deach am PDF a luchdadh gu tur airson clò-bhualadh. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Toglaich am bàr-taoibh +pdfjs-toggle-sidebar-notification-button = + .title = Toglaich am bàr-taoibh (tha oir-loidhne/ceanglachain/breathan aig an sgrìobhainn) +pdfjs-toggle-sidebar-button-label = Toglaich am bàr-taoibh +pdfjs-document-outline-button = + .title = Seall oir-loidhne na sgrìobhainn (dèan briogadh dùbailte airson a h-uile nì a leudachadh/a cho-theannadh) +pdfjs-document-outline-button-label = Oir-loidhne na sgrìobhainne +pdfjs-attachments-button = + .title = Seall na ceanglachain +pdfjs-attachments-button-label = Ceanglachain +pdfjs-layers-button = + .title = Seall na breathan (dèan briogadh dùbailte airson a h-uile breath ath-shuidheachadh dhan staid bhunaiteach) +pdfjs-layers-button-label = Breathan +pdfjs-thumbs-button = + .title = Seall na dealbhagan +pdfjs-thumbs-button-label = Dealbhagan +pdfjs-current-outline-item-button = + .title = Lorg nì làithreach na h-oir-loidhne +pdfjs-current-outline-item-button-label = Nì làithreach na h-oir-loidhne +pdfjs-findbar-button = + .title = Lorg san sgrìobhainn +pdfjs-findbar-button-label = Lorg +pdfjs-additional-layers = Barrachd breathan + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Duilleag a { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Dealbhag duilleag a { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Lorg + .placeholder = Lorg san sgrìobhainn... +pdfjs-find-previous-button = + .title = Lorg làthair roimhe na h-abairt seo +pdfjs-find-previous-button-label = Air ais +pdfjs-find-next-button = + .title = Lorg ath-làthair na h-abairt seo +pdfjs-find-next-button-label = Air adhart +pdfjs-find-highlight-checkbox = Soillsich a h-uile +pdfjs-find-match-case-checkbox-label = Aire do litrichean mòra is beaga +pdfjs-find-match-diacritics-checkbox-label = Aire do stràcan +pdfjs-find-entire-word-checkbox-label = Faclan-slàna +pdfjs-find-reached-top = Ràinig sinn barr na duilleige, a' leantainn air adhart o bhonn na duilleige +pdfjs-find-reached-bottom = Ràinig sinn bonn na duilleige, a' leantainn air adhart o bharr na duilleige +pdfjs-find-not-found = Cha deach an abairt a lorg + +## Predefined zoom values + +pdfjs-page-scale-width = Leud na duilleige +pdfjs-page-scale-fit = Freagair ri meud na duilleige +pdfjs-page-scale-auto = Sùm fèin-obrachail +pdfjs-page-scale-actual = Am fìor-mheud +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Duilleag { $page } + +## Loading indicator messages + +pdfjs-loading-error = Thachair mearachd rè luchdadh a' PDF. +pdfjs-invalid-file-error = Faidhle PDF a tha mì-dhligheach no coirbte. +pdfjs-missing-file-error = Faidhle PDF a tha a dhìth. +pdfjs-unexpected-response-error = Freagairt on fhrithealaiche ris nach robh dùil. +pdfjs-rendering-error = Thachair mearachd rè reandaradh na duilleige. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Nòtachadh { $type }] + +## Password + +pdfjs-password-label = Cuir a-steach am facal-faire gus am faidhle PDF seo fhosgladh. +pdfjs-password-invalid = Tha am facal-faire cearr. Nach fheuch thu ris a-rithist? +pdfjs-password-ok-button = Ceart ma-thà +pdfjs-password-cancel-button = Sguir dheth +pdfjs-web-fonts-disabled = Tha cruthan-clò lìn à comas: Chan urrainn dhuinn cruthan-clò PDF leabaichte a chleachdadh. + +## Editing + +pdfjs-editor-free-text-button = + .title = Teacsa +pdfjs-editor-free-text-button-label = Teacsa +pdfjs-editor-ink-button = + .title = Tarraing +pdfjs-editor-ink-button-label = Tarraing + +## Remove button for the various kind of editor. + + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Dath +pdfjs-editor-free-text-size-input = Meud +pdfjs-editor-ink-color-input = Dath +pdfjs-editor-ink-thickness-input = Tighead +pdfjs-editor-ink-opacity-input = Trìd-dhoilleireachd +pdfjs-free-text = + .aria-label = An deasaiche teacsa +pdfjs-free-text-default-content = Tòisich air sgrìobhadh… +pdfjs-ink = + .aria-label = An deasaiche tharraingean +pdfjs-ink-canvas = + .aria-label = Dealbh a chruthaich cleachdaiche + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + diff --git a/public/pdfjs/web/locale/gl/viewer.ftl b/public/pdfjs/web/locale/gl/viewer.ftl new file mode 100644 index 0000000..641a607 --- /dev/null +++ b/public/pdfjs/web/locale/gl/viewer.ftl @@ -0,0 +1,385 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Páxina anterior +pdfjs-previous-button-label = Anterior +pdfjs-next-button = + .title = Seguinte páxina +pdfjs-next-button-label = Seguinte +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Páxina +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = de { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount }) +pdfjs-zoom-out-button = + .title = Reducir +pdfjs-zoom-out-button-label = Reducir +pdfjs-zoom-in-button = + .title = Ampliar +pdfjs-zoom-in-button-label = Ampliar +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Cambiar ao modo presentación +pdfjs-presentation-mode-button-label = Modo presentación +pdfjs-open-file-button = + .title = Abrir ficheiro +pdfjs-open-file-button-label = Abrir +pdfjs-print-button = + .title = Imprimir +pdfjs-print-button-label = Imprimir +pdfjs-save-button = + .title = Gardar +pdfjs-save-button-label = Gardar +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Descargar +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Descargar +pdfjs-bookmark-button = + .title = Páxina actual (ver o URL da páxina actual) +pdfjs-bookmark-button-label = Páxina actual + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Ferramentas +pdfjs-tools-button-label = Ferramentas +pdfjs-first-page-button = + .title = Ir á primeira páxina +pdfjs-first-page-button-label = Ir á primeira páxina +pdfjs-last-page-button = + .title = Ir á última páxina +pdfjs-last-page-button-label = Ir á última páxina +pdfjs-page-rotate-cw-button = + .title = Rotar no sentido das agullas do reloxo +pdfjs-page-rotate-cw-button-label = Rotar no sentido das agullas do reloxo +pdfjs-page-rotate-ccw-button = + .title = Rotar no sentido contrario ás agullas do reloxo +pdfjs-page-rotate-ccw-button-label = Rotar no sentido contrario ás agullas do reloxo +pdfjs-cursor-text-select-tool-button = + .title = Activar a ferramenta de selección de texto +pdfjs-cursor-text-select-tool-button-label = Ferramenta de selección de texto +pdfjs-cursor-hand-tool-button = + .title = Activar a ferramenta de man +pdfjs-cursor-hand-tool-button-label = Ferramenta de man +pdfjs-scroll-page-button = + .title = Usar o desprazamento da páxina +pdfjs-scroll-page-button-label = Desprazamento da páxina +pdfjs-scroll-vertical-button = + .title = Usar o desprazamento vertical +pdfjs-scroll-vertical-button-label = Desprazamento vertical +pdfjs-scroll-horizontal-button = + .title = Usar o desprazamento horizontal +pdfjs-scroll-horizontal-button-label = Desprazamento horizontal +pdfjs-scroll-wrapped-button = + .title = Usar o desprazamento en bloque +pdfjs-scroll-wrapped-button-label = Desprazamento por bloque +pdfjs-spread-none-button = + .title = Non agrupar páxinas +pdfjs-spread-none-button-label = Ningún agrupamento +pdfjs-spread-odd-button = + .title = Crea grupo de páxinas que comezan con números de páxina impares +pdfjs-spread-odd-button-label = Agrupamento impar +pdfjs-spread-even-button = + .title = Crea grupo de páxinas que comezan con números de páxina pares +pdfjs-spread-even-button-label = Agrupamento par + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Propiedades do documento… +pdfjs-document-properties-button-label = Propiedades do documento… +pdfjs-document-properties-file-name = Nome do ficheiro: +pdfjs-document-properties-file-size = Tamaño do ficheiro: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Título: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Asunto: +pdfjs-document-properties-keywords = Palabras clave: +pdfjs-document-properties-creation-date = Data de creación: +pdfjs-document-properties-modification-date = Data de modificación: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Creado por: +pdfjs-document-properties-producer = Xenerador do PDF: +pdfjs-document-properties-version = Versión de PDF: +pdfjs-document-properties-page-count = Número de páxinas: +pdfjs-document-properties-page-size = Tamaño da páxina: +pdfjs-document-properties-page-size-unit-inches = pol +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = vertical +pdfjs-document-properties-page-size-orientation-landscape = horizontal +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Carta +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Visualización rápida das páxinas web: +pdfjs-document-properties-linearized-yes = Si +pdfjs-document-properties-linearized-no = Non +pdfjs-document-properties-close-button = Pechar + +## Print + +pdfjs-print-progress-message = Preparando o documento para imprimir… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cancelar +pdfjs-printing-not-supported = Aviso: A impresión non é compatíbel de todo con este navegador. +pdfjs-printing-not-ready = Aviso: O PDF non se cargou completamente para imprimirse. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Amosar/agochar a barra lateral +pdfjs-toggle-sidebar-notification-button = + .title = Alternar barra lateral (o documento contén esquema/anexos/capas) +pdfjs-toggle-sidebar-button-label = Amosar/agochar a barra lateral +pdfjs-document-outline-button = + .title = Amosar a estrutura do documento (dobre clic para expandir/contraer todos os elementos) +pdfjs-document-outline-button-label = Estrutura do documento +pdfjs-attachments-button = + .title = Amosar anexos +pdfjs-attachments-button-label = Anexos +pdfjs-layers-button = + .title = Mostrar capas (prema dúas veces para restaurar todas as capas o estado predeterminado) +pdfjs-layers-button-label = Capas +pdfjs-thumbs-button = + .title = Amosar miniaturas +pdfjs-thumbs-button-label = Miniaturas +pdfjs-current-outline-item-button = + .title = Atopar o elemento delimitado actualmente +pdfjs-current-outline-item-button-label = Elemento delimitado actualmente +pdfjs-findbar-button = + .title = Atopar no documento +pdfjs-findbar-button-label = Atopar +pdfjs-additional-layers = Capas adicionais + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Páxina { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura da páxina { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Atopar + .placeholder = Atopar no documento… +pdfjs-find-previous-button = + .title = Atopar a anterior aparición da frase +pdfjs-find-previous-button-label = Anterior +pdfjs-find-next-button = + .title = Atopar a seguinte aparición da frase +pdfjs-find-next-button-label = Seguinte +pdfjs-find-highlight-checkbox = Realzar todo +pdfjs-find-match-case-checkbox-label = Diferenciar maiúsculas de minúsculas +pdfjs-find-match-diacritics-checkbox-label = Distinguir os diacríticos +pdfjs-find-entire-word-checkbox-label = Palabras completas +pdfjs-find-reached-top = Chegouse ao inicio do documento, continuar desde o final +pdfjs-find-reached-bottom = Chegouse ao final do documento, continuar desde o inicio +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] Coincidencia { $current } de { $total } + *[other] Coincidencia { $current } de { $total } + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Máis de { $limit } coincidencia + *[other] Máis de { $limit } coincidencias + } +pdfjs-find-not-found = Non se atopou a frase + +## Predefined zoom values + +pdfjs-page-scale-width = Largura da páxina +pdfjs-page-scale-fit = Axuste de páxina +pdfjs-page-scale-auto = Zoom automático +pdfjs-page-scale-actual = Tamaño actual +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Páxina { $page } + +## Loading indicator messages + +pdfjs-loading-error = Produciuse un erro ao cargar o PDF. +pdfjs-invalid-file-error = Ficheiro PDF danado ou non válido. +pdfjs-missing-file-error = Falta o ficheiro PDF. +pdfjs-unexpected-response-error = Resposta inesperada do servidor. +pdfjs-rendering-error = Produciuse un erro ao representar a páxina. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anotación { $type }] + +## Password + +pdfjs-password-label = Escriba o contrasinal para abrir este ficheiro PDF. +pdfjs-password-invalid = Contrasinal incorrecto. Tente de novo. +pdfjs-password-ok-button = Aceptar +pdfjs-password-cancel-button = Cancelar +pdfjs-web-fonts-disabled = Desactiváronse as fontes web: foi imposíbel usar as fontes incrustadas no PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Texto +pdfjs-editor-free-text-button-label = Texto +pdfjs-editor-ink-button = + .title = Debuxo +pdfjs-editor-ink-button-label = Debuxo +pdfjs-editor-stamp-button = + .title = Engadir ou editar imaxes +pdfjs-editor-stamp-button-label = Engadir ou editar imaxes + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-freetext-button = + .title = Eliminar o texto +pdfjs-editor-remove-stamp-button = + .title = Eliminar a imaxe +pdfjs-editor-remove-highlight-button = + .title = Eliminar o resaltado + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Cor +pdfjs-editor-free-text-size-input = Tamaño +pdfjs-editor-ink-color-input = Cor +pdfjs-editor-ink-thickness-input = Grosor +pdfjs-editor-ink-opacity-input = Opacidade +pdfjs-editor-stamp-add-image-button = + .title = Engadir imaxe +pdfjs-editor-stamp-add-image-button-label = Engadir imaxe +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Grosor +pdfjs-free-text = + .aria-label = Editor de texto +pdfjs-free-text-default-content = Comezar a teclear… +pdfjs-ink = + .aria-label = Editor de debuxos +pdfjs-ink-canvas = + .aria-label = Imaxe creada por unha usuaria + +## Alt-text dialog + +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button-label = Texto alternativo +pdfjs-editor-alt-text-edit-button-label = Editar o texto alternativo +pdfjs-editor-alt-text-dialog-label = Escoller unha opción +pdfjs-editor-alt-text-add-description-label = Engadir unha descrición +pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorativo +pdfjs-editor-alt-text-mark-decorative-description = Utilízase para imaxes ornamentais, como bordos ou marcas de auga. +pdfjs-editor-alt-text-cancel-button = Cancelar +pdfjs-editor-alt-text-save-button = Gardar +pdfjs-editor-alt-text-decorative-tooltip = Marcado como decorativo +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Por exemplo, «Un mozo séntase á mesa para comer» + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Esquina superior esquerda: cambia o tamaño +pdfjs-editor-resizer-label-top-middle = Medio superior: cambia o tamaño +pdfjs-editor-resizer-label-top-right = Esquina superior dereita: cambia o tamaño +pdfjs-editor-resizer-label-middle-right = Medio dereito: cambia o tamaño +pdfjs-editor-resizer-label-bottom-right = Esquina inferior dereita: cambia o tamaño +pdfjs-editor-resizer-label-bottom-middle = Abaixo medio: cambia o tamaño +pdfjs-editor-resizer-label-bottom-left = Esquina inferior esquerda: cambia o tamaño +pdfjs-editor-resizer-label-middle-left = Medio esquerdo: cambia o tamaño +pdfjs-editor-resizer-top-left = + .aria-label = Esquina superior esquerda: cambia o tamaño +pdfjs-editor-resizer-top-middle = + .aria-label = Medio superior: cambia o tamaño +pdfjs-editor-resizer-top-right = + .aria-label = Esquina superior dereita: cambia o tamaño +pdfjs-editor-resizer-middle-right = + .aria-label = Medio dereito: cambia o tamaño +pdfjs-editor-resizer-bottom-right = + .aria-label = Esquina inferior dereita: cambia o tamaño +pdfjs-editor-resizer-bottom-middle = + .aria-label = Abaixo medio: cambia o tamaño +pdfjs-editor-resizer-bottom-left = + .aria-label = Esquina inferior esquerda: cambia o tamaño +pdfjs-editor-resizer-middle-left = + .aria-label = Medio esquerdo: cambia o tamaño + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + diff --git a/public/pdfjs/web/locale/gn/viewer.ftl b/public/pdfjs/web/locale/gn/viewer.ftl new file mode 100644 index 0000000..6402c6f --- /dev/null +++ b/public/pdfjs/web/locale/gn/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Kuatiarogue mboyvegua +pdfjs-previous-button-label = Mboyvegua +pdfjs-next-button = + .title = Kuatiarogue upeigua +pdfjs-next-button-label = Upeigua +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Kuatiarogue +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount } gui +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount }) +pdfjs-zoom-out-button = + .title = Momichĩ +pdfjs-zoom-out-button-label = Momichĩ +pdfjs-zoom-in-button = + .title = Mbotuicha +pdfjs-zoom-in-button-label = Mbotuicha +pdfjs-zoom-select = + .title = Tuichakue +pdfjs-presentation-mode-button = + .title = Jehechauka reko moambue +pdfjs-presentation-mode-button-label = Jehechauka reko +pdfjs-open-file-button = + .title = Marandurendápe jeike +pdfjs-open-file-button-label = Jeike +pdfjs-print-button = + .title = Monguatia +pdfjs-print-button-label = Monguatia +pdfjs-save-button = + .title = Ñongatu +pdfjs-save-button-label = Ñongatu +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Mboguejy +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Mboguejy +pdfjs-bookmark-button = + .title = Kuatiarogue ag̃agua (Ehecha URL kuatiarogue ag̃agua) +pdfjs-bookmark-button-label = Kuatiarogue Ag̃agua + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Tembiporu +pdfjs-tools-button-label = Tembiporu +pdfjs-first-page-button = + .title = Kuatiarogue ñepyrũme jeho +pdfjs-first-page-button-label = Kuatiarogue ñepyrũme jeho +pdfjs-last-page-button = + .title = Kuatiarogue pahápe jeho +pdfjs-last-page-button-label = Kuatiarogue pahápe jeho +pdfjs-page-rotate-cw-button = + .title = Aravóicha mbojere +pdfjs-page-rotate-cw-button-label = Aravóicha mbojere +pdfjs-page-rotate-ccw-button = + .title = Aravo rapykue gotyo mbojere +pdfjs-page-rotate-ccw-button-label = Aravo rapykue gotyo mbojere +pdfjs-cursor-text-select-tool-button = + .title = Emyandy moñe’ẽrã jeporavo rembiporu +pdfjs-cursor-text-select-tool-button-label = Moñe’ẽrã jeporavo rembiporu +pdfjs-cursor-hand-tool-button = + .title = Tembiporu po pegua myandy +pdfjs-cursor-hand-tool-button-label = Tembiporu po pegua +pdfjs-scroll-page-button = + .title = Eiporu kuatiarogue jeku’e +pdfjs-scroll-page-button-label = Kuatiarogue jeku’e +pdfjs-scroll-vertical-button = + .title = Eiporu jeku’e ykeguáva +pdfjs-scroll-vertical-button-label = Jeku’e ykeguáva +pdfjs-scroll-horizontal-button = + .title = Eiporu jeku’e yvate gotyo +pdfjs-scroll-horizontal-button-label = Jeku’e yvate gotyo +pdfjs-scroll-wrapped-button = + .title = Eiporu jeku’e mbohyrupyre +pdfjs-scroll-wrapped-button-label = Jeku’e mbohyrupyre +pdfjs-spread-none-button = + .title = Ani ejuaju spreads kuatiarogue ndive +pdfjs-spread-none-button-label = Spreads ỹre +pdfjs-spread-odd-button = + .title = Embojuaju kuatiarogue jepysokue eñepyrũvo kuatiarogue impar-vagui +pdfjs-spread-odd-button-label = Spreads impar +pdfjs-spread-even-button = + .title = Embojuaju kuatiarogue jepysokue eñepyrũvo kuatiarogue par-vagui +pdfjs-spread-even-button-label = Ipukuve uvei + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Kuatia mba’etee… +pdfjs-document-properties-button-label = Kuatia mba’etee… +pdfjs-document-properties-file-name = Marandurenda réra: +pdfjs-document-properties-file-size = Marandurenda tuichakue: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Teratee: +pdfjs-document-properties-author = Apohára: +pdfjs-document-properties-subject = Mba’egua: +pdfjs-document-properties-keywords = Jehero: +pdfjs-document-properties-creation-date = Teñoihague arange: +pdfjs-document-properties-modification-date = Iñambue hague arange: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Apo’ypyha: +pdfjs-document-properties-producer = PDF mbosako’iha: +pdfjs-document-properties-version = PDF mbojuehegua: +pdfjs-document-properties-page-count = Kuatiarogue papapy: +pdfjs-document-properties-page-size = Kuatiarogue tuichakue: +pdfjs-document-properties-page-size-unit-inches = Amo +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = Oĩháicha +pdfjs-document-properties-page-size-orientation-landscape = apaisado +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Kuatiañe’ẽ +pdfjs-document-properties-page-size-name-legal = Tee + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Ñanduti jahecha pya’e: +pdfjs-document-properties-linearized-yes = Añete +pdfjs-document-properties-linearized-no = Ahániri +pdfjs-document-properties-close-button = Mboty + +## Print + +pdfjs-print-progress-message = Embosako’i kuatia emonguatia hag̃ua… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Heja +pdfjs-printing-not-supported = Kyhyjerã: Ñembokuatia ndojokupytypái ko kundahára ndive. +pdfjs-printing-not-ready = Kyhyjerã: Ko PDF nahenyhẽmbái oñembokuatia hag̃uáicha. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Tenda yke moambue +pdfjs-toggle-sidebar-notification-button = + .title = Embojopyru tenda ykegua (kuatia oguereko kuaakaha/moirũha/ñuãha) +pdfjs-toggle-sidebar-button-label = Tenda yke moambue +pdfjs-document-outline-button = + .title = Ehechauka kuatia rape (eikutu mokõi jey embotuicha/emomichĩ hag̃ua opavavete mba’eporu) +pdfjs-document-outline-button-label = Kuatia apopyre +pdfjs-attachments-button = + .title = Moirũha jehechauka +pdfjs-attachments-button-label = Moirũha +pdfjs-layers-button = + .title = Ehechauka ñuãha (eikutu jo’a emomba’apo hag̃ua opaite ñuãha tekoypýpe) +pdfjs-layers-button-label = Ñuãha +pdfjs-thumbs-button = + .title = Mba’emirĩ jehechauka +pdfjs-thumbs-button-label = Mba’emirĩ +pdfjs-current-outline-item-button = + .title = Eheka mba’eporu ag̃aguaitéva +pdfjs-current-outline-item-button-label = Mba’eporu ag̃aguaitéva +pdfjs-findbar-button = + .title = Kuatiápe jeheka +pdfjs-findbar-button-label = Juhu +pdfjs-additional-layers = Ñuãha moirũguáva + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Kuatiarogue { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Kuatiarogue mba’emirĩ { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Juhu + .placeholder = Kuatiápe jejuhu… +pdfjs-find-previous-button = + .title = Ejuhu ñe’ẽrysýi osẽ’ypy hague +pdfjs-find-previous-button-label = Mboyvegua +pdfjs-find-next-button = + .title = Eho ñe’ẽ juhupyre upeiguávape +pdfjs-find-next-button-label = Upeigua +pdfjs-find-highlight-checkbox = Embojekuaavepa +pdfjs-find-match-case-checkbox-label = Ejesareko taiguasu/taimichĩre +pdfjs-find-match-diacritics-checkbox-label = Diacrítico moñondive +pdfjs-find-entire-word-checkbox-label = Ñe’ẽ oĩmbáva +pdfjs-find-reached-top = Ojehupyty kuatia ñepyrũ, oku’ejeýta kuatia paha guive +pdfjs-find-reached-bottom = Ojehupyty kuatia paha, oku’ejeýta kuatia ñepyrũ guive +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } ha { $total } ojueheguáva + *[other] { $current } ha { $total } ojueheguáva + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Hetave { $limit } ojueheguáva + *[other] Hetave { $limit } ojueheguáva + } +pdfjs-find-not-found = Ñe’ẽrysýi ojejuhu’ỹva + +## Predefined zoom values + +pdfjs-page-scale-width = Kuatiarogue pekue +pdfjs-page-scale-fit = Kuatiarogue ñemoĩporã +pdfjs-page-scale-auto = Tuichakue ijeheguíva +pdfjs-page-scale-actual = Tuichakue ag̃agua +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Kuatiarogue { $page } + +## Loading indicator messages + +pdfjs-loading-error = Oiko jejavy PDF oñemyeñyhẽnguévo. +pdfjs-invalid-file-error = PDF marandurenda ndoikóiva térã ivaipyréva. +pdfjs-missing-file-error = Ndaipóri PDF marandurenda +pdfjs-unexpected-response-error = Mohendahavusu mbohovái eha’ãrõ’ỹva. +pdfjs-rendering-error = Oiko jejavy ehechaukasévo kuatiarogue. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Jehaipy { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Emoinge ñe’ẽñemi eipe’a hag̃ua ko marandurenda PDF. +pdfjs-password-invalid = Ñe’ẽñemi ndoikóiva. Eha’ã jey. +pdfjs-password-ok-button = MONEĨ +pdfjs-password-cancel-button = Heja +pdfjs-web-fonts-disabled = Ñanduti taity oñemongéma: ndaikatumo’ãi eiporu PDF jehai’íva taity. + +## Editing + +pdfjs-editor-free-text-button = + .title = Moñe’ẽrã +pdfjs-editor-free-text-button-label = Moñe’ẽrã +pdfjs-editor-ink-button = + .title = Moha’ãnga +pdfjs-editor-ink-button-label = Moha’ãnga +pdfjs-editor-stamp-button = + .title = Embojuaju térã embosako’i ta’ãnga +pdfjs-editor-stamp-button-label = Embojuaju térã embosako’i ta’ãnga +pdfjs-editor-highlight-button = + .title = Mbosa’y +pdfjs-editor-highlight-button-label = Mbosa’y +pdfjs-highlight-floating-button1 = + .title = Mbosa’y + .aria-label = Mbosa’y +pdfjs-highlight-floating-button-label = Mbosa’y + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Emboguete ta’ãnga +pdfjs-editor-remove-freetext-button = + .title = Emboguete moñe’ẽrã +pdfjs-editor-remove-stamp-button = + .title = Emboguete ta’ãnga +pdfjs-editor-remove-highlight-button = + .title = Eipe’a jehechaveha + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Sa’y +pdfjs-editor-free-text-size-input = Tuichakue +pdfjs-editor-ink-color-input = Sa’y +pdfjs-editor-ink-thickness-input = Anambusu +pdfjs-editor-ink-opacity-input = Pytũngy +pdfjs-editor-stamp-add-image-button = + .title = Embojuaju ta’ãnga +pdfjs-editor-stamp-add-image-button-label = Embojuaju ta’ãnga +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Anambusu +pdfjs-editor-free-highlight-thickness-title = + .title = Emoambue anambusukue embosa’ývo mba’eporu ha’e’ỹva moñe’ẽrã +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Moñe’ẽrã moheñoiha + .default-content = Eñepyrũ ehai… +pdfjs-free-text = + .aria-label = Moñe’ẽrã moheñoiha +pdfjs-free-text-default-content = Ehai ñepyrũ… +pdfjs-ink = + .aria-label = Ta’ãnga moheñoiha +pdfjs-ink-canvas = + .aria-label = Ta’ãnga omoheñóiva poruhára + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Moñe’ẽrã mokõiháva +pdfjs-editor-alt-text-edit-button = + .aria-label = Embojuruja moñe’ẽrã mokõiháva +pdfjs-editor-alt-text-edit-button-label = Embojuruja moñe’ẽrã mokõiháva +pdfjs-editor-alt-text-dialog-label = Eiporavo poravorã +pdfjs-editor-alt-text-dialog-description = Moñe’ẽrã ykepegua (moñe’ẽrã ykepegua) nepytyvõ nderehecháiramo ta’ãnga térã nahenyhẽiramo. +pdfjs-editor-alt-text-add-description-label = Embojuaju ñemoha’ãnga +pdfjs-editor-alt-text-add-description-description = Ehaimi 1 térã 2 ñe’ẽjuaju oñe’ẽva pe téma rehe, ijere térã mba’eapóre. +pdfjs-editor-alt-text-mark-decorative-label = Emongurusu jeguakárõ +pdfjs-editor-alt-text-mark-decorative-description = Ojeporu ta’ãnga jeguakarã, tembe’y térã ta’ãnga ruguarãramo. +pdfjs-editor-alt-text-cancel-button = Heja +pdfjs-editor-alt-text-save-button = Ñongatu +pdfjs-editor-alt-text-decorative-tooltip = Jeguakárõ mongurusupyre +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Techapyrã: “Peteĩ mitãrusu oguapy mesápe okaru hag̃ua” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Moñe’ẽrã mokõiháva + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Yvate asu gotyo — emoambue tuichakue +pdfjs-editor-resizer-label-top-middle = Yvate mbytépe — emoambue tuichakue +pdfjs-editor-resizer-label-top-right = Yvate akatúape — emoambue tuichakue +pdfjs-editor-resizer-label-middle-right = Mbyte akatúape — emoambue tuichakue +pdfjs-editor-resizer-label-bottom-right = Yvy gotyo akatúape — emoambue tuichakue +pdfjs-editor-resizer-label-bottom-middle = Yvy gotyo mbytépe — emoambue tuichakue +pdfjs-editor-resizer-label-bottom-left = Iguýpe asu gotyo — emoambue tuichakue +pdfjs-editor-resizer-label-middle-left = Mbyte asu gotyo — emoambue tuichakue +pdfjs-editor-resizer-top-left = + .aria-label = Yvate asu gotyo — emoambue tuichakue +pdfjs-editor-resizer-top-middle = + .aria-label = Yvate mbytépe — emoambue tuichakue +pdfjs-editor-resizer-top-right = + .aria-label = Yvate akatúape — emoambue tuichakue +pdfjs-editor-resizer-middle-right = + .aria-label = Mbyte akatúape — emoambue tuichakue +pdfjs-editor-resizer-bottom-right = + .aria-label = Yvy gotyo akatúape — emoambue tuichakue +pdfjs-editor-resizer-bottom-middle = + .aria-label = Yvy gotyo mbytépe — emoambue tuichakue +pdfjs-editor-resizer-bottom-left = + .aria-label = Iguýpe asu gotyo — emoambue tuichakue +pdfjs-editor-resizer-middle-left = + .aria-label = Mbyte asu gotyo — emoambue tuichakue + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Jehechaveha sa’y +pdfjs-editor-colorpicker-button = + .title = Emoambue sa’y +pdfjs-editor-colorpicker-dropdown = + .aria-label = Sa’y poravopyrã +pdfjs-editor-colorpicker-yellow = + .title = Sa’yju +pdfjs-editor-colorpicker-green = + .title = Hovyũ +pdfjs-editor-colorpicker-blue = + .title = Hovy +pdfjs-editor-colorpicker-pink = + .title = Pytãngy +pdfjs-editor-colorpicker-red = + .title = Pyha + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Techaukapa +pdfjs-editor-highlight-show-all-button = + .title = Techaukapa + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Embosako’i moñe’ẽrã mokõiha (ta’ãngáre ñeñe’ẽ) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Embojuaju moñe’ẽrã mokõiha (ta’ãngáre ñeñe’ẽ) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Edescribi ko’ápe… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Ñemyesakã mbykymi opavave ohecha’ỹva upe ta’ãnga térã pe ta’ãnga nahenyhẽiramo. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Ko moñe’ẽrã mokõiha oñemoheñói ijehegui ha ikatu ndoikoporãi. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Eikuaave +pdfjs-editor-new-alt-text-create-automatically-button-label = Emoheñói moñe’ẽrã mokõiha ijeheguíva +pdfjs-editor-new-alt-text-not-now-button = Ani ko’ág̃a +pdfjs-editor-new-alt-text-error-title = Noñemoheñói moñe’ẽrã mokõiha ijeheguíva +pdfjs-editor-new-alt-text-error-description = Ehai ne moñe’ẽrã mokõiha térã eha’ã jey ag̃amieve. +pdfjs-editor-new-alt-text-error-close-button = Mboty +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Emboguejyhína IA moñe’ẽrã mokõiháva ({ $downloadedSize } { $totalSize } MB) mba’e + .aria-valuetext = Emboguejyhína IA moñe’ẽrã mokõiháva ({ $downloadedSize } { $totalSize } MB) mba’e +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Moñe’ẽrã mokõiha mbojuajupyre +pdfjs-editor-new-alt-text-added-button-label = Oñembojuaju moñe’ẽrã mokõiha +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Ndaipóri moñe’ẽrã mokõiha +pdfjs-editor-new-alt-text-missing-button-label = Ndaipóri moñe’ẽrã mokõiha +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Ehechajey moñe’ẽrã mokõiha +pdfjs-editor-new-alt-text-to-review-button-label = Ehechajey moñe’ẽrã mokõiha +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Heñóiva ijeheguiete: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Ta’ãnga moñe’ẽrã mokõiha ñemboheko +pdfjs-image-alt-text-settings-button-label = Ta’ãnga moñe’ẽrã mokõiha ñemboheko +pdfjs-editor-alt-text-settings-dialog-label = Ta’ãnga moñe’ẽrã mokõiha ñemboheko +pdfjs-editor-alt-text-settings-automatic-title = Moñe’ẽrã mokõiha ijeheguíva +pdfjs-editor-alt-text-settings-create-model-button-label = Emoheñói moñe’ẽrã mokõiha ijeheguíva +pdfjs-editor-alt-text-settings-create-model-description = Ñemyesakã mbykymi opavave tapicha ohecha’ỹva upe ta’ãnga térã pe ta’ãnga nahenyhẽiramo. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Peteĩva IA moñe’ẽrã mokõiha ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Oku’e mba’e’okaitépe umi mba’ekuaarã hekoñemi hag̃ua. Tekotevẽva moñe’ẽrã ykegua ijeheguívape. +pdfjs-editor-alt-text-settings-delete-model-button = Mboguete +pdfjs-editor-alt-text-settings-download-model-button = Mboguejy +pdfjs-editor-alt-text-settings-downloading-model-button = Emboguejyhína… +pdfjs-editor-alt-text-settings-editor-title = Moñe’ẽrã mokõiha mbosako’iha +pdfjs-editor-alt-text-settings-show-dialog-button-label = Ehechauka moñe’ẽrã mokõiha mbosako’iha embojuajúvo ta’ãnga +pdfjs-editor-alt-text-settings-show-dialog-description = Nepytyvõta ta’ãngakuéra orekotaha moñe’ẽrã mokõiha. +pdfjs-editor-alt-text-settings-close-button = Mboty + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Mbosa’ýva mboguete +pdfjs-editor-undo-bar-message-freetext = Moñe’ẽrã mboguepyre +pdfjs-editor-undo-bar-message-ink = Ta’ãnga mboguepyre +pdfjs-editor-undo-bar-message-stamp = Ta’ãnga mboguepyre +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } jehaikue mboguepyre + *[other] { $count } jehaikue mboguepyre + } +pdfjs-editor-undo-bar-undo-button = + .title = Mboguevi +pdfjs-editor-undo-bar-undo-button-label = Mboguevi +pdfjs-editor-undo-bar-close-button = + .title = Mboty +pdfjs-editor-undo-bar-close-button-label = Mboty diff --git a/public/pdfjs/web/locale/gu-IN/viewer.ftl b/public/pdfjs/web/locale/gu-IN/viewer.ftl new file mode 100644 index 0000000..5d8bb54 --- /dev/null +++ b/public/pdfjs/web/locale/gu-IN/viewer.ftl @@ -0,0 +1,247 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = પહેલાનુ પાનું +pdfjs-previous-button-label = પહેલાનુ +pdfjs-next-button = + .title = આગળનુ પાનું +pdfjs-next-button-label = આગળનું +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = પાનું +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = નો { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } નો { $pagesCount }) +pdfjs-zoom-out-button = + .title = મોટુ કરો +pdfjs-zoom-out-button-label = મોટુ કરો +pdfjs-zoom-in-button = + .title = નાનું કરો +pdfjs-zoom-in-button-label = નાનું કરો +pdfjs-zoom-select = + .title = નાનું મોટુ કરો +pdfjs-presentation-mode-button = + .title = રજૂઆત સ્થિતિમાં જાવ +pdfjs-presentation-mode-button-label = રજૂઆત સ્થિતિ +pdfjs-open-file-button = + .title = ફાઇલ ખોલો +pdfjs-open-file-button-label = ખોલો +pdfjs-print-button = + .title = છાપો +pdfjs-print-button-label = છારો + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = સાધનો +pdfjs-tools-button-label = સાધનો +pdfjs-first-page-button = + .title = પહેલાં પાનામાં જાવ +pdfjs-first-page-button-label = પ્રથમ પાનાં પર જાવ +pdfjs-last-page-button = + .title = છેલ્લા પાનાં પર જાવ +pdfjs-last-page-button-label = છેલ્લા પાનાં પર જાવ +pdfjs-page-rotate-cw-button = + .title = ઘડિયાળનાં કાંટા તરફ ફેરવો +pdfjs-page-rotate-cw-button-label = ઘડિયાળનાં કાંટા તરફ ફેરવો +pdfjs-page-rotate-ccw-button = + .title = ઘડિયાળનાં કાંટાની ઉલટી દિશામાં ફેરવો +pdfjs-page-rotate-ccw-button-label = ઘડિયાળનાં કાંટાની વિરુદ્દ ફેરવો +pdfjs-cursor-text-select-tool-button = + .title = ટેક્સ્ટ પસંદગી ટૂલ સક્ષમ કરો +pdfjs-cursor-text-select-tool-button-label = ટેક્સ્ટ પસંદગી ટૂલ +pdfjs-cursor-hand-tool-button = + .title = હાથનાં સાધનને સક્રિય કરો +pdfjs-cursor-hand-tool-button-label = હેન્ડ ટૂલ +pdfjs-scroll-vertical-button = + .title = ઊભી સ્ક્રોલિંગનો ઉપયોગ કરો +pdfjs-scroll-vertical-button-label = ઊભી સ્ક્રોલિંગ +pdfjs-scroll-horizontal-button = + .title = આડી સ્ક્રોલિંગનો ઉપયોગ કરો +pdfjs-scroll-horizontal-button-label = આડી સ્ક્રોલિંગ +pdfjs-scroll-wrapped-button = + .title = આવરિત સ્ક્રોલિંગનો ઉપયોગ કરો +pdfjs-scroll-wrapped-button-label = આવરિત સ્ક્રોલિંગ +pdfjs-spread-none-button = + .title = પૃષ્ઠ સ્પ્રેડમાં જોડાવશો નહીં +pdfjs-spread-none-button-label = કોઈ સ્પ્રેડ નથી +pdfjs-spread-odd-button = + .title = એકી-ક્રમાંકિત પૃષ્ઠો સાથે પ્રારંભ થતાં પૃષ્ઠ સ્પ્રેડમાં જોડાઓ +pdfjs-spread-odd-button-label = એકી સ્પ્રેડ્સ +pdfjs-spread-even-button = + .title = નંબર-ક્રમાંકિત પૃષ્ઠોથી શરૂ થતાં પૃષ્ઠ સ્પ્રેડમાં જોડાઓ +pdfjs-spread-even-button-label = સરખું ફેલાવવું + +## Document properties dialog + +pdfjs-document-properties-button = + .title = દસ્તાવેજ ગુણધર્મો… +pdfjs-document-properties-button-label = દસ્તાવેજ ગુણધર્મો… +pdfjs-document-properties-file-name = ફાઇલ નામ: +pdfjs-document-properties-file-size = ફાઇલ માપ: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } બાઇટ) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } બાઇટ) +pdfjs-document-properties-title = શીર્ષક: +pdfjs-document-properties-author = લેખક: +pdfjs-document-properties-subject = વિષય: +pdfjs-document-properties-keywords = કિવર્ડ: +pdfjs-document-properties-creation-date = નિર્માણ તારીખ: +pdfjs-document-properties-modification-date = ફેરફાર તારીખ: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = નિર્માતા: +pdfjs-document-properties-producer = PDF નિર્માતા: +pdfjs-document-properties-version = PDF આવૃત્તિ: +pdfjs-document-properties-page-count = પાનાં ગણતરી: +pdfjs-document-properties-page-size = પૃષ્ઠનું કદ: +pdfjs-document-properties-page-size-unit-inches = ઇંચ +pdfjs-document-properties-page-size-unit-millimeters = મીમી +pdfjs-document-properties-page-size-orientation-portrait = ઉભું +pdfjs-document-properties-page-size-orientation-landscape = આડુ +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = પત્ર +pdfjs-document-properties-page-size-name-legal = કાયદાકીય + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = ઝડપી વૅબ દૃશ્ય: +pdfjs-document-properties-linearized-yes = હા +pdfjs-document-properties-linearized-no = ના +pdfjs-document-properties-close-button = બંધ કરો + +## Print + +pdfjs-print-progress-message = છાપકામ માટે દસ્તાવેજ તૈયાર કરી રહ્યા છે… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = રદ કરો +pdfjs-printing-not-supported = ચેતવણી: છાપવાનું આ બ્રાઉઝર દ્દારા સંપૂર્ણપણે આધારભૂત નથી. +pdfjs-printing-not-ready = Warning: PDF એ છાપવા માટે સંપૂર્ણપણે લાવેલ છે. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = ટૉગલ બાજુપટ્ટી +pdfjs-toggle-sidebar-button-label = ટૉગલ બાજુપટ્ટી +pdfjs-document-outline-button = + .title = દસ્તાવેજની રૂપરેખા બતાવો(બધી આઇટમ્સને વિસ્તૃત/સંકુચિત કરવા માટે ડબલ-ક્લિક કરો) +pdfjs-document-outline-button-label = દસ્તાવેજ રૂપરેખા +pdfjs-attachments-button = + .title = જોડાણોને બતાવો +pdfjs-attachments-button-label = જોડાણો +pdfjs-thumbs-button = + .title = થંબનેલ્સ બતાવો +pdfjs-thumbs-button-label = થંબનેલ્સ +pdfjs-findbar-button = + .title = દસ્તાવેજમાં શોધો +pdfjs-findbar-button-label = શોધો + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = પાનું { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = પાનાં { $page } નું થંબનેલ્સ + +## Find panel button title and messages + +pdfjs-find-input = + .title = શોધો + .placeholder = દસ્તાવેજમાં શોધો… +pdfjs-find-previous-button = + .title = શબ્દસમૂહની પાછલી ઘટનાને શોધો +pdfjs-find-previous-button-label = પહેલાંનુ +pdfjs-find-next-button = + .title = શબ્દસમૂહની આગળની ઘટનાને શોધો +pdfjs-find-next-button-label = આગળનું +pdfjs-find-highlight-checkbox = બધુ પ્રકાશિત કરો +pdfjs-find-match-case-checkbox-label = કેસ બંધબેસાડો +pdfjs-find-entire-word-checkbox-label = સંપૂર્ણ શબ્દો +pdfjs-find-reached-top = દસ્તાવેજનાં ટોચે પહોંચી ગયા, તળિયેથી ચાલુ કરેલ હતુ +pdfjs-find-reached-bottom = દસ્તાવેજનાં અંતે પહોંચી ગયા, ઉપરથી ચાલુ કરેલ હતુ +pdfjs-find-not-found = શબ્દસમૂહ મળ્યુ નથી + +## Predefined zoom values + +pdfjs-page-scale-width = પાનાની પહોળાઇ +pdfjs-page-scale-fit = પાનું બંધબેસતુ +pdfjs-page-scale-auto = આપમેળે નાનુંમોટુ કરો +pdfjs-page-scale-actual = ચોક્કસ માપ +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = ભૂલ ઉદ્ભવી જ્યારે PDF ને લાવી રહ્યા હોય. +pdfjs-invalid-file-error = અયોગ્ય અથવા ભાંગેલ PDF ફાઇલ. +pdfjs-missing-file-error = ગુમ થયેલ PDF ફાઇલ. +pdfjs-unexpected-response-error = અનપેક્ષિત સર્વર પ્રતિસાદ. +pdfjs-rendering-error = ભૂલ ઉદ્ભવી જ્યારે પાનાંનુ રેન્ડ કરી રહ્યા હોય. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] + +## Password + +pdfjs-password-label = આ PDF ફાઇલને ખોલવા પાસવર્ડને દાખલ કરો. +pdfjs-password-invalid = અયોગ્ય પાસવર્ડ. મહેરબાની કરીને ફરી પ્રયત્ન કરો. +pdfjs-password-ok-button = બરાબર +pdfjs-password-cancel-button = રદ કરો +pdfjs-web-fonts-disabled = વેબ ફોન્ટ નિષ્ક્રિય થયેલ છે: ઍમ્બેડ થયેલ PDF ફોન્ટને વાપરવાનું અસમર્થ. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/he/viewer.ftl b/public/pdfjs/web/locale/he/viewer.ftl new file mode 100644 index 0000000..08308c0 --- /dev/null +++ b/public/pdfjs/web/locale/he/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = דף קודם +pdfjs-previous-button-label = קודם +pdfjs-next-button = + .title = דף הבא +pdfjs-next-button-label = הבא +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = דף +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = מתוך { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } מתוך { $pagesCount }) +pdfjs-zoom-out-button = + .title = התרחקות +pdfjs-zoom-out-button-label = התרחקות +pdfjs-zoom-in-button = + .title = התקרבות +pdfjs-zoom-in-button-label = התקרבות +pdfjs-zoom-select = + .title = מרחק מתצוגה +pdfjs-presentation-mode-button = + .title = מעבר למצב מצגת +pdfjs-presentation-mode-button-label = מצב מצגת +pdfjs-open-file-button = + .title = פתיחת קובץ +pdfjs-open-file-button-label = פתיחה +pdfjs-print-button = + .title = הדפסה +pdfjs-print-button-label = הדפסה +pdfjs-save-button = + .title = שמירה +pdfjs-save-button-label = שמירה +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = הורדה +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = הורדה +pdfjs-bookmark-button = + .title = עמוד נוכחי (הצגת כתובת האתר מהעמוד הנוכחי) +pdfjs-bookmark-button-label = עמוד נוכחי + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = כלים +pdfjs-tools-button-label = כלים +pdfjs-first-page-button = + .title = מעבר לעמוד הראשון +pdfjs-first-page-button-label = מעבר לעמוד הראשון +pdfjs-last-page-button = + .title = מעבר לעמוד האחרון +pdfjs-last-page-button-label = מעבר לעמוד האחרון +pdfjs-page-rotate-cw-button = + .title = הטיה עם כיוון השעון +pdfjs-page-rotate-cw-button-label = הטיה עם כיוון השעון +pdfjs-page-rotate-ccw-button = + .title = הטיה כנגד כיוון השעון +pdfjs-page-rotate-ccw-button-label = הטיה כנגד כיוון השעון +pdfjs-cursor-text-select-tool-button = + .title = הפעלת כלי בחירת טקסט +pdfjs-cursor-text-select-tool-button-label = כלי בחירת טקסט +pdfjs-cursor-hand-tool-button = + .title = הפעלת כלי היד +pdfjs-cursor-hand-tool-button-label = כלי יד +pdfjs-scroll-page-button = + .title = שימוש בגלילת עמוד +pdfjs-scroll-page-button-label = גלילת עמוד +pdfjs-scroll-vertical-button = + .title = שימוש בגלילה אנכית +pdfjs-scroll-vertical-button-label = גלילה אנכית +pdfjs-scroll-horizontal-button = + .title = שימוש בגלילה אופקית +pdfjs-scroll-horizontal-button-label = גלילה אופקית +pdfjs-scroll-wrapped-button = + .title = שימוש בגלילה רציפה +pdfjs-scroll-wrapped-button-label = גלילה רציפה +pdfjs-spread-none-button = + .title = לא לצרף מפתחי עמודים +pdfjs-spread-none-button-label = ללא מפתחים +pdfjs-spread-odd-button = + .title = צירוף מפתחי עמודים שמתחילים בדפים עם מספרים אי־זוגיים +pdfjs-spread-odd-button-label = מפתחים אי־זוגיים +pdfjs-spread-even-button = + .title = צירוף מפתחי עמודים שמתחילים בדפים עם מספרים זוגיים +pdfjs-spread-even-button-label = מפתחים זוגיים + +## Document properties dialog + +pdfjs-document-properties-button = + .title = מאפייני מסמך… +pdfjs-document-properties-button-label = מאפייני מסמך… +pdfjs-document-properties-file-name = שם קובץ: +pdfjs-document-properties-file-size = גודל הקובץ: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } ק״ב ({ $b } בתים) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } מ״ב ({ $b } בתים) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } ק״ב ({ $size_b } בתים) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } מ״ב ({ $size_b } בתים) +pdfjs-document-properties-title = כותרת: +pdfjs-document-properties-author = מחבר: +pdfjs-document-properties-subject = נושא: +pdfjs-document-properties-keywords = מילות מפתח: +pdfjs-document-properties-creation-date = תאריך יצירה: +pdfjs-document-properties-modification-date = תאריך שינוי: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = יוצר: +pdfjs-document-properties-producer = יצרן PDF: +pdfjs-document-properties-version = גרסת PDF: +pdfjs-document-properties-page-count = מספר דפים: +pdfjs-document-properties-page-size = גודל העמוד: +pdfjs-document-properties-page-size-unit-inches = אינ׳ +pdfjs-document-properties-page-size-unit-millimeters = מ״מ +pdfjs-document-properties-page-size-orientation-portrait = לאורך +pdfjs-document-properties-page-size-orientation-landscape = לרוחב +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = מכתב +pdfjs-document-properties-page-size-name-legal = דף משפטי + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = תצוגת דף מהירה: +pdfjs-document-properties-linearized-yes = כן +pdfjs-document-properties-linearized-no = לא +pdfjs-document-properties-close-button = סגירה + +## Print + +pdfjs-print-progress-message = מסמך בהכנה להדפסה… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = ביטול +pdfjs-printing-not-supported = אזהרה: הדפסה אינה נתמכת במלואה בדפדפן זה. +pdfjs-printing-not-ready = אזהרה: מסמך ה־PDF לא נטען לחלוטין עד מצב שמאפשר הדפסה. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = הצגה/הסתרה של סרגל הצד +pdfjs-toggle-sidebar-notification-button = + .title = החלפת תצוגת סרגל צד (מסמך שמכיל תוכן עניינים/קבצים מצורפים/שכבות) +pdfjs-toggle-sidebar-button-label = הצגה/הסתרה של סרגל הצד +pdfjs-document-outline-button = + .title = הצגת תוכן העניינים של המסמך (לחיצה כפולה כדי להרחיב או לצמצם את כל הפריטים) +pdfjs-document-outline-button-label = תוכן העניינים של המסמך +pdfjs-attachments-button = + .title = הצגת צרופות +pdfjs-attachments-button-label = צרופות +pdfjs-layers-button = + .title = הצגת שכבות (יש ללחוץ לחיצה כפולה כדי לאפס את כל השכבות למצב ברירת המחדל) +pdfjs-layers-button-label = שכבות +pdfjs-thumbs-button = + .title = הצגת תצוגה מקדימה +pdfjs-thumbs-button-label = תצוגה מקדימה +pdfjs-current-outline-item-button = + .title = מציאת פריט תוכן העניינים הנוכחי +pdfjs-current-outline-item-button-label = פריט תוכן העניינים הנוכחי +pdfjs-findbar-button = + .title = חיפוש במסמך +pdfjs-findbar-button-label = חיפוש +pdfjs-additional-layers = שכבות נוספות + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = עמוד { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = תצוגה מקדימה של עמוד { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = חיפוש + .placeholder = חיפוש במסמך… +pdfjs-find-previous-button = + .title = מציאת המופע הקודם של הביטוי +pdfjs-find-previous-button-label = קודם +pdfjs-find-next-button = + .title = מציאת המופע הבא של הביטוי +pdfjs-find-next-button-label = הבא +pdfjs-find-highlight-checkbox = הדגשת הכול +pdfjs-find-match-case-checkbox-label = התאמת אותיות +pdfjs-find-match-diacritics-checkbox-label = התאמה דיאקריטית +pdfjs-find-entire-word-checkbox-label = מילים שלמות +pdfjs-find-reached-top = הגיע לראש הדף, ממשיך מלמטה +pdfjs-find-reached-bottom = הגיע לסוף הדף, ממשיך מלמעלה +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } מתוך { $total } תוצאות + *[other] { $current } מתוך { $total } תוצאות + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] יותר מתוצאה אחת + *[other] יותר מ־{ $limit } תוצאות + } +pdfjs-find-not-found = הביטוי לא נמצא + +## Predefined zoom values + +pdfjs-page-scale-width = רוחב העמוד +pdfjs-page-scale-fit = התאמה לעמוד +pdfjs-page-scale-auto = מרחק מתצוגה אוטומטי +pdfjs-page-scale-actual = גודל אמיתי +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = עמוד { $page } + +## Loading indicator messages + +pdfjs-loading-error = אירעה שגיאה בעת טעינת ה־PDF. +pdfjs-invalid-file-error = קובץ PDF פגום או לא תקין. +pdfjs-missing-file-error = קובץ PDF חסר. +pdfjs-unexpected-response-error = תגובת שרת לא צפויה. +pdfjs-rendering-error = אירעה שגיאה בעת עיבוד הדף. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [הערת { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = נא להכניס את הססמה לפתיחת קובץ PDF זה. +pdfjs-password-invalid = ססמה שגויה. נא לנסות שנית. +pdfjs-password-ok-button = אישור +pdfjs-password-cancel-button = ביטול +pdfjs-web-fonts-disabled = גופני רשת מנוטרלים: לא ניתן להשתמש בגופני PDF מוטבעים. + +## Editing + +pdfjs-editor-free-text-button = + .title = טקסט +pdfjs-editor-free-text-button-label = טקסט +pdfjs-editor-ink-button = + .title = ציור +pdfjs-editor-ink-button-label = ציור +pdfjs-editor-stamp-button = + .title = הוספה או עריכת תמונות +pdfjs-editor-stamp-button-label = הוספה או עריכת תמונות +pdfjs-editor-highlight-button = + .title = סימון +pdfjs-editor-highlight-button-label = סימון +pdfjs-highlight-floating-button1 = + .title = סימון + .aria-label = סימון +pdfjs-highlight-floating-button-label = סימון + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = הסרת ציור +pdfjs-editor-remove-freetext-button = + .title = הסרת טקסט +pdfjs-editor-remove-stamp-button = + .title = הסרת תמונה +pdfjs-editor-remove-highlight-button = + .title = הסרת סימון + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = צבע +pdfjs-editor-free-text-size-input = גודל +pdfjs-editor-ink-color-input = צבע +pdfjs-editor-ink-thickness-input = עובי +pdfjs-editor-ink-opacity-input = אטימות +pdfjs-editor-stamp-add-image-button = + .title = הוספת תמונה +pdfjs-editor-stamp-add-image-button-label = הוספת תמונה +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = עובי +pdfjs-editor-free-highlight-thickness-title = + .title = שינוי עובי בעת סימון פריטים שאינם טקסט +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = עורך טקסט + .default-content = נא להתחיל להקליד… +pdfjs-free-text = + .aria-label = עורך טקסט +pdfjs-free-text-default-content = להתחיל להקליד… +pdfjs-ink = + .aria-label = עורך ציור +pdfjs-ink-canvas = + .aria-label = תמונה שנוצרה על־ידי משתמש + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = טקסט חלופי +pdfjs-editor-alt-text-edit-button = + .aria-label = עריכת טקסט חלופי +pdfjs-editor-alt-text-edit-button-label = עריכת טקסט חלופי +pdfjs-editor-alt-text-dialog-label = בחירת אפשרות +pdfjs-editor-alt-text-dialog-description = טקסט חלופי עוזר כשאנשים לא יכולים לראות את התמונה או כשהיא לא נטענת. +pdfjs-editor-alt-text-add-description-label = הוספת תיאור +pdfjs-editor-alt-text-add-description-description = כדאי לתאר במשפט אחד או שניים את הנושא, התפאורה או הפעולות. +pdfjs-editor-alt-text-mark-decorative-label = סימון כדקורטיבי +pdfjs-editor-alt-text-mark-decorative-description = זה משמש לתמונות נוי, כמו גבולות או סימני מים. +pdfjs-editor-alt-text-cancel-button = ביטול +pdfjs-editor-alt-text-save-button = שמירה +pdfjs-editor-alt-text-decorative-tooltip = מסומן כדקורטיבי +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = לדוגמה, ״גבר צעיר מתיישב ליד שולחן לאכול ארוחה״ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = טקסט חלופי + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = פינה שמאלית עליונה - שינוי גודל +pdfjs-editor-resizer-label-top-middle = למעלה באמצע - שינוי גודל +pdfjs-editor-resizer-label-top-right = פינה ימנית עליונה - שינוי גודל +pdfjs-editor-resizer-label-middle-right = ימינה באמצע - שינוי גודל +pdfjs-editor-resizer-label-bottom-right = פינה ימנית תחתונה - שינוי גודל +pdfjs-editor-resizer-label-bottom-middle = למטה באמצע - שינוי גודל +pdfjs-editor-resizer-label-bottom-left = פינה שמאלית תחתונה - שינוי גודל +pdfjs-editor-resizer-label-middle-left = שמאלה באמצע - שינוי גודל +pdfjs-editor-resizer-top-left = + .aria-label = פינה שמאלית עליונה - שינוי גודל +pdfjs-editor-resizer-top-middle = + .aria-label = למעלה באמצע - שינוי גודל +pdfjs-editor-resizer-top-right = + .aria-label = פינה ימנית עליונה - שינוי גודל +pdfjs-editor-resizer-middle-right = + .aria-label = ימינה באמצע - שינוי גודל +pdfjs-editor-resizer-bottom-right = + .aria-label = פינה ימנית תחתונה - שינוי גודל +pdfjs-editor-resizer-bottom-middle = + .aria-label = למטה באמצע - שינוי גודל +pdfjs-editor-resizer-bottom-left = + .aria-label = פינה שמאלית תחתונה - שינוי גודל +pdfjs-editor-resizer-middle-left = + .aria-label = שמאלה באמצע - שינוי גודל + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = צבע סימון +pdfjs-editor-colorpicker-button = + .title = שינוי צבע +pdfjs-editor-colorpicker-dropdown = + .aria-label = בחירת צבע +pdfjs-editor-colorpicker-yellow = + .title = צהוב +pdfjs-editor-colorpicker-green = + .title = ירוק +pdfjs-editor-colorpicker-blue = + .title = כחול +pdfjs-editor-colorpicker-pink = + .title = ורוד +pdfjs-editor-colorpicker-red = + .title = אדום + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = הצגת הכול +pdfjs-editor-highlight-show-all-button = + .title = הצגת הכול + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = עריכת טקסט חלופי (תיאור תמונה) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = הוספת טקסט חלופי (תיאור תמונה) +pdfjs-editor-new-alt-text-textarea = + .placeholder = נא לכתוב את התיאור שלך כאן… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = תיאור קצר לאנשים שאינם יכולים לראות את התמונה או כאשר התמונה אינה נטענת. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = טקסט חלופי זה נוצר באופן אוטומטי ועשוי להיות לא מדויק. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = מידע נוסף +pdfjs-editor-new-alt-text-create-automatically-button-label = יצירת טקסט חלופי באופן אוטומטי +pdfjs-editor-new-alt-text-not-now-button = לא כעת +pdfjs-editor-new-alt-text-error-title = לא ניתן היה ליצור טקסט חלופי באופן אוטומטי +pdfjs-editor-new-alt-text-error-description = נא לכתוב טקסט חלופי משלך או לנסות שוב מאוחר יותר. +pdfjs-editor-new-alt-text-error-close-button = סגירה +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = בתהליך הורדת מודל AI של טקסט חלופי ({ $downloadedSize } מתוך { $totalSize } מ״ב) + .aria-valuetext = בתהליך הורדת מודל AI של טקסט חלופי ({ $downloadedSize } מתוך { $totalSize } מ״ב) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = נוסף טקסט חלופי +pdfjs-editor-new-alt-text-added-button-label = נוסף טקסט חלופי +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = חסר טקסט חלופי +pdfjs-editor-new-alt-text-missing-button-label = חסר טקסט חלופי +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = סקירת טקסט חלופי +pdfjs-editor-new-alt-text-to-review-button-label = סקירת טקסט חלופי +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = נוצר באופן אוטומטי: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = הגדרות טקסט חלופי של תמונה +pdfjs-image-alt-text-settings-button-label = הגדרות טקסט חלופי של תמונה +pdfjs-editor-alt-text-settings-dialog-label = הגדרות טקסט חלופי של תמונה +pdfjs-editor-alt-text-settings-automatic-title = טקסט חלופי אוטומטי +pdfjs-editor-alt-text-settings-create-model-button-label = יצירת טקסט חלופי באופן אוטומטי +pdfjs-editor-alt-text-settings-create-model-description = הצעת תיאורים כדי לסייע לאנשים שאינם יכולים לראות את התמונה או כאשר התמונה אינה נטענת. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = מודל AI לטקסט חלופי ({ $totalSize } מ״ב) +pdfjs-editor-alt-text-settings-ai-model-description = פועל באופן מקומי במכשיר שלך כך שהנתונים שלך נשארים פרטיים. נדרש עבור טקסט חלופי אוטומטי. +pdfjs-editor-alt-text-settings-delete-model-button = מחיקה +pdfjs-editor-alt-text-settings-download-model-button = הורדה +pdfjs-editor-alt-text-settings-downloading-model-button = בהורדה… +pdfjs-editor-alt-text-settings-editor-title = עורך טקסט חלופי +pdfjs-editor-alt-text-settings-show-dialog-button-label = הצגת עורך טקסט חלופי מיד בעת הוספת תמונה +pdfjs-editor-alt-text-settings-show-dialog-description = מסייע לך לוודא שלכל התמונות שלך יש טקסט חלופי. +pdfjs-editor-alt-text-settings-close-button = סגירה + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = הסימון הוסר +pdfjs-editor-undo-bar-message-freetext = הטקסט הוסר +pdfjs-editor-undo-bar-message-ink = הציור הוסר +pdfjs-editor-undo-bar-message-stamp = התמונה הוסרה +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] הערה אחת הוסרה + *[other] { $count } הערות הוסרו + } +pdfjs-editor-undo-bar-undo-button = + .title = ביטול פעולה +pdfjs-editor-undo-bar-undo-button-label = ביטול פעלה +pdfjs-editor-undo-bar-close-button = + .title = סגירה +pdfjs-editor-undo-bar-close-button-label = סגירה diff --git a/public/pdfjs/web/locale/hi-IN/viewer.ftl b/public/pdfjs/web/locale/hi-IN/viewer.ftl new file mode 100644 index 0000000..b6f378f --- /dev/null +++ b/public/pdfjs/web/locale/hi-IN/viewer.ftl @@ -0,0 +1,267 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = पिछला पृष्ठ +pdfjs-previous-button-label = पिछला +pdfjs-next-button = + .title = अगला पृष्ठ +pdfjs-next-button-label = आगे +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = पृष्ठ: +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount } का +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount }) +pdfjs-zoom-out-button = + .title = छोटा करें +pdfjs-zoom-out-button-label = छोटा करें +pdfjs-zoom-in-button = + .title = बड़ा करें +pdfjs-zoom-in-button-label = बड़ा करें +pdfjs-zoom-select = + .title = बड़ा-छोटा करें +pdfjs-presentation-mode-button = + .title = प्रस्तुति अवस्था में जाएँ +pdfjs-presentation-mode-button-label = प्रस्तुति अवस्था +pdfjs-open-file-button = + .title = फ़ाइल खोलें +pdfjs-open-file-button-label = खोलें +pdfjs-print-button = + .title = छापें +pdfjs-print-button-label = छापें + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = औज़ार +pdfjs-tools-button-label = औज़ार +pdfjs-first-page-button = + .title = प्रथम पृष्ठ पर जाएँ +pdfjs-first-page-button-label = प्रथम पृष्ठ पर जाएँ +pdfjs-last-page-button = + .title = अंतिम पृष्ठ पर जाएँ +pdfjs-last-page-button-label = अंतिम पृष्ठ पर जाएँ +pdfjs-page-rotate-cw-button = + .title = घड़ी की दिशा में घुमाएँ +pdfjs-page-rotate-cw-button-label = घड़ी की दिशा में घुमाएँ +pdfjs-page-rotate-ccw-button = + .title = घड़ी की दिशा से उल्टा घुमाएँ +pdfjs-page-rotate-ccw-button-label = घड़ी की दिशा से उल्टा घुमाएँ +pdfjs-cursor-text-select-tool-button = + .title = पाठ चयन उपकरण सक्षम करें +pdfjs-cursor-text-select-tool-button-label = पाठ चयन उपकरण +pdfjs-cursor-hand-tool-button = + .title = हस्त उपकरण सक्षम करें +pdfjs-cursor-hand-tool-button-label = हस्त उपकरण +pdfjs-scroll-vertical-button = + .title = लंबवत स्क्रॉलिंग का उपयोग करें +pdfjs-scroll-vertical-button-label = लंबवत स्क्रॉलिंग +pdfjs-scroll-horizontal-button = + .title = क्षितिजिय स्क्रॉलिंग का उपयोग करें +pdfjs-scroll-horizontal-button-label = क्षितिजिय स्क्रॉलिंग +pdfjs-scroll-wrapped-button = + .title = व्राप्पेड स्क्रॉलिंग का उपयोग करें +pdfjs-spread-none-button-label = कोई स्प्रेड उपलब्ध नहीं +pdfjs-spread-odd-button = + .title = विषम-क्रमांकित पृष्ठों से प्रारंभ होने वाले पृष्ठ स्प्रेड में शामिल हों +pdfjs-spread-odd-button-label = विषम फैलाव + +## Document properties dialog + +pdfjs-document-properties-button = + .title = दस्तावेज़ विशेषता... +pdfjs-document-properties-button-label = दस्तावेज़ विशेषता... +pdfjs-document-properties-file-name = फ़ाइल नाम: +pdfjs-document-properties-file-size = फाइल आकारः +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = शीर्षक: +pdfjs-document-properties-author = लेखकः +pdfjs-document-properties-subject = विषय: +pdfjs-document-properties-keywords = कुंजी-शब्द: +pdfjs-document-properties-creation-date = निर्माण दिनांक: +pdfjs-document-properties-modification-date = संशोधन दिनांक: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = निर्माता: +pdfjs-document-properties-producer = PDF उत्पादक: +pdfjs-document-properties-version = PDF संस्करण: +pdfjs-document-properties-page-count = पृष्ठ गिनती: +pdfjs-document-properties-page-size = पृष्ठ आकार: +pdfjs-document-properties-page-size-unit-inches = इंच +pdfjs-document-properties-page-size-unit-millimeters = मिमी +pdfjs-document-properties-page-size-orientation-portrait = पोर्ट्रेट +pdfjs-document-properties-page-size-orientation-landscape = लैंडस्केप +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = पत्र +pdfjs-document-properties-page-size-name-legal = क़ानूनी + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = तीव्र वेब व्यू: +pdfjs-document-properties-linearized-yes = हाँ +pdfjs-document-properties-linearized-no = नहीं +pdfjs-document-properties-close-button = बंद करें + +## Print + +pdfjs-print-progress-message = छपाई के लिए दस्तावेज़ को तैयार किया जा रहा है... +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = रद्द करें +pdfjs-printing-not-supported = चेतावनी: इस ब्राउज़र पर छपाई पूरी तरह से समर्थित नहीं है. +pdfjs-printing-not-ready = चेतावनी: PDF छपाई के लिए पूरी तरह से लोड नहीं है. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = स्लाइडर टॉगल करें +pdfjs-toggle-sidebar-button-label = स्लाइडर टॉगल करें +pdfjs-document-outline-button = + .title = दस्तावेज़ की रूपरेखा दिखाइए (सारी वस्तुओं को फलने अथवा समेटने के लिए दो बार क्लिक करें) +pdfjs-document-outline-button-label = दस्तावेज़ आउटलाइन +pdfjs-attachments-button = + .title = संलग्नक दिखायें +pdfjs-attachments-button-label = संलग्नक +pdfjs-thumbs-button = + .title = लघुछवियाँ दिखाएँ +pdfjs-thumbs-button-label = लघु छवि +pdfjs-findbar-button = + .title = दस्तावेज़ में ढूँढ़ें +pdfjs-findbar-button-label = ढूँढें + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = पृष्ठ { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = पृष्ठ { $page } की लघु-छवि + +## Find panel button title and messages + +pdfjs-find-input = + .title = ढूँढें + .placeholder = दस्तावेज़ में खोजें... +pdfjs-find-previous-button = + .title = वाक्यांश की पिछली उपस्थिति ढूँढ़ें +pdfjs-find-previous-button-label = पिछला +pdfjs-find-next-button = + .title = वाक्यांश की अगली उपस्थिति ढूँढ़ें +pdfjs-find-next-button-label = अगला +pdfjs-find-highlight-checkbox = सभी आलोकित करें +pdfjs-find-match-case-checkbox-label = मिलान स्थिति +pdfjs-find-entire-word-checkbox-label = संपूर्ण शब्द +pdfjs-find-reached-top = पृष्ठ के ऊपर पहुंच गया, नीचे से जारी रखें +pdfjs-find-reached-bottom = पृष्ठ के नीचे में जा पहुँचा, ऊपर से जारी +pdfjs-find-not-found = वाक्यांश नहीं मिला + +## Predefined zoom values + +pdfjs-page-scale-width = पृष्ठ चौड़ाई +pdfjs-page-scale-fit = पृष्ठ फिट +pdfjs-page-scale-auto = स्वचालित जूम +pdfjs-page-scale-actual = वास्तविक आकार +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = PDF लोड करते समय एक त्रुटि हुई. +pdfjs-invalid-file-error = अमान्य या भ्रष्ट PDF फ़ाइल. +pdfjs-missing-file-error = अनुपस्थित PDF फ़ाइल. +pdfjs-unexpected-response-error = अप्रत्याशित सर्वर प्रतिक्रिया. +pdfjs-rendering-error = पृष्ठ रेंडरिंग के दौरान त्रुटि आई. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] + +## Password + +pdfjs-password-label = इस PDF फ़ाइल को खोलने के लिए कृपया कूटशब्द भरें. +pdfjs-password-invalid = अवैध कूटशब्द, कृपया फिर कोशिश करें. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = रद्द करें +pdfjs-web-fonts-disabled = वेब फॉन्ट्स निष्क्रिय हैं: अंतःस्थापित PDF फॉन्टस के उपयोग में असमर्थ. + +## Editing + + +## Remove button for the various kind of editor. + + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = रंग + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + diff --git a/public/pdfjs/web/locale/hr/viewer.ftl b/public/pdfjs/web/locale/hr/viewer.ftl new file mode 100644 index 0000000..c081c6f --- /dev/null +++ b/public/pdfjs/web/locale/hr/viewer.ftl @@ -0,0 +1,473 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Prethodna stranica +pdfjs-previous-button-label = Prethodna +pdfjs-next-button = + .title = Sljedeća stranica +pdfjs-next-button-label = Sljedeća +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Stranica +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = od { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } od { $pagesCount }) +pdfjs-zoom-out-button = + .title = Umanji +pdfjs-zoom-out-button-label = Umanji +pdfjs-zoom-in-button = + .title = Uvećaj +pdfjs-zoom-in-button-label = Uvećaj +pdfjs-zoom-select = + .title = Zumiranje +pdfjs-presentation-mode-button = + .title = Prebaci u modus prezentacija +pdfjs-presentation-mode-button-label = Modus prezentacija +pdfjs-open-file-button = + .title = Otvori datoteku +pdfjs-open-file-button-label = Otvori +pdfjs-print-button = + .title = Ispiši +pdfjs-print-button-label = Ispiši +pdfjs-save-button = + .title = Spremi +pdfjs-save-button-label = Spremi +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Preuzimanja +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Preuzimanja +pdfjs-bookmark-button = + .title = Trenutna stranica (pogledajte URL s trenutne stranice) +pdfjs-bookmark-button-label = Trenutna stranica + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Alati +pdfjs-tools-button-label = Alati +pdfjs-first-page-button = + .title = Idi na prvu stranicu +pdfjs-first-page-button-label = Idi na prvu stranicu +pdfjs-last-page-button = + .title = Idi na posljednju stranicu +pdfjs-last-page-button-label = Idi na posljednju stranicu +pdfjs-page-rotate-cw-button = + .title = Rotiraj u smjeru kazaljke na satu +pdfjs-page-rotate-cw-button-label = Rotiraj u smjeru kazaljke na satu +pdfjs-page-rotate-ccw-button = + .title = Rotiraj obrnutno od smjera kazaljke na satu +pdfjs-page-rotate-ccw-button-label = Rotiraj obrnutno od smjera kazaljke na satu +pdfjs-cursor-text-select-tool-button = + .title = Aktiviraj alat za biranje teksta +pdfjs-cursor-text-select-tool-button-label = Alat za označavanje teksta +pdfjs-cursor-hand-tool-button = + .title = Aktiviraj ručni alat +pdfjs-cursor-hand-tool-button-label = Ručni alat +pdfjs-scroll-page-button = + .title = Koristi klizanje stranice +pdfjs-scroll-page-button-label = Klizanje stranice +pdfjs-scroll-vertical-button = + .title = Koristi okomito pomicanje +pdfjs-scroll-vertical-button-label = Okomito pomicanje +pdfjs-scroll-horizontal-button = + .title = Koristi vodoravno pomicanje +pdfjs-scroll-horizontal-button-label = Vodoravno pomicanje +pdfjs-scroll-wrapped-button = + .title = Koristi kontinuirani raspored stranica +pdfjs-scroll-wrapped-button-label = Kontinuirani raspored stranica +pdfjs-spread-none-button = + .title = Ne izrađuj duplerice +pdfjs-spread-none-button-label = Pojedinačne stranice +pdfjs-spread-odd-button = + .title = Izradi duplerice koje počinju s neparnim stranicama +pdfjs-spread-odd-button-label = Neparne duplerice +pdfjs-spread-even-button = + .title = Izradi duplerice koje počinju s parnim stranicama +pdfjs-spread-even-button-label = Parne duplerice + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Svojstva dokumenta … +pdfjs-document-properties-button-label = Svojstva dokumenta … +pdfjs-document-properties-file-name = Ime datoteke: +pdfjs-document-properties-file-size = Veličina datoteke: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bajtova) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bajtova) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajtova) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajtova) +pdfjs-document-properties-title = Naslov: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Predmet: +pdfjs-document-properties-keywords = Ključne riječi: +pdfjs-document-properties-creation-date = Datum stvaranja: +pdfjs-document-properties-modification-date = Datum promjene: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Stvaratelj: +pdfjs-document-properties-producer = PDF stvaratelj: +pdfjs-document-properties-version = PDF verzija: +pdfjs-document-properties-page-count = Broj stranica: +pdfjs-document-properties-page-size = Dimenzije stranice: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = uspravno +pdfjs-document-properties-page-size-orientation-landscape = položeno +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Brzi web pregled: +pdfjs-document-properties-linearized-yes = Da +pdfjs-document-properties-linearized-no = Ne +pdfjs-document-properties-close-button = Zatvori + +## Print + +pdfjs-print-progress-message = Pripremanje dokumenta za ispis… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Odustani +pdfjs-printing-not-supported = Upozorenje: Ovaj preglednik ne podržava u potpunosti ispisivanje. +pdfjs-printing-not-ready = Upozorenje: PDF nije u potpunosti učitan za ispis. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Prikaži/sakrij bočnu traku +pdfjs-toggle-sidebar-notification-button = + .title = Prikazivanje i sklanjanje bočne trake (dokument sadrži strukturu/privitke/slojeve) +pdfjs-toggle-sidebar-button-label = Prikaži/sakrij bočnu traku +pdfjs-document-outline-button = + .title = Prikaži strukturu dokumenta (dvostruki klik za rasklapanje/sklapanje svih stavki) +pdfjs-document-outline-button-label = Struktura dokumenta +pdfjs-attachments-button = + .title = Prikaži privitke +pdfjs-attachments-button-label = Privitci +pdfjs-layers-button = + .title = Prikaži slojeve (dvoklik za vraćanje svih slojeva u standardno stanje) +pdfjs-layers-button-label = Slojevi +pdfjs-thumbs-button = + .title = Prikaži minijature +pdfjs-thumbs-button-label = Minijature +pdfjs-current-outline-item-button = + .title = Pronađi trenutačni element strukture +pdfjs-current-outline-item-button-label = Trenutačni element strukture +pdfjs-findbar-button = + .title = Pronađi u dokumentu +pdfjs-findbar-button-label = Pronađi +pdfjs-additional-layers = Dodatni slojevi + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Stranica { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Minijatura stranice { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Pronađi + .placeholder = Pronađi u dokumentu … +pdfjs-find-previous-button = + .title = Pronađi prethodno pojavljivanje ovog izraza +pdfjs-find-previous-button-label = Prethodno +pdfjs-find-next-button = + .title = Pronađi sljedeće pojavljivanje ovog izraza +pdfjs-find-next-button-label = Dalje +pdfjs-find-highlight-checkbox = Istankni sve +pdfjs-find-match-case-checkbox-label = Razlikovanje velikih i malih slova +pdfjs-find-match-diacritics-checkbox-label = Razlikuj dijakritičke znakove +pdfjs-find-entire-word-checkbox-label = Cijele riječi +pdfjs-find-reached-top = Dosegnut početak dokumenta, nastavak s kraja +pdfjs-find-reached-bottom = Dosegnut kraj dokumenta, nastavak s početka +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } od { $total } rezultata + [few] { $current } od { $total } rezultata + *[other] { $current } od { $total } rezultata + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Više od { $limit } rezultat + [few] Više od { $limit } rezultata + *[other] Više od { $limit } rezultata + } +pdfjs-find-not-found = Izraz nije pronađen + +## Predefined zoom values + +pdfjs-page-scale-width = Prilagodi širini prozora +pdfjs-page-scale-fit = Prilagodi veličini prozora +pdfjs-page-scale-auto = Automatsko zumiranje +pdfjs-page-scale-actual = Stvarna veličina +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale } % + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Stranica { $page } + +## Loading indicator messages + +pdfjs-loading-error = Došlo je do greške pri učitavanju PDF-a. +pdfjs-invalid-file-error = Neispravna ili oštećena PDF datoteka. +pdfjs-missing-file-error = Nedostaje PDF datoteka. +pdfjs-unexpected-response-error = Neočekivani odgovor servera. +pdfjs-rendering-error = Došlo je do greške prilikom iscrtavanja stranice. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Bilješka] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Za otvoranje ove PDF datoteku upiši lozinku. +pdfjs-password-invalid = Neispravna lozinka. Pokušaj ponovo. +pdfjs-password-ok-button = U redu +pdfjs-password-cancel-button = Odustani +pdfjs-web-fonts-disabled = Web fontovi su deaktivirani: nije moguće koristiti ugrađene PDF fontove. + +## Editing + +pdfjs-editor-free-text-button = + .title = Tekst +pdfjs-editor-free-text-button-label = Tekst +pdfjs-editor-ink-button = + .title = Crtanje +pdfjs-editor-ink-button-label = Crtanje +pdfjs-editor-stamp-button = + .title = Dodajte ili uredite slike +pdfjs-editor-stamp-button-label = Dodajte ili uredite slike +pdfjs-editor-highlight-button = + .title = Istakni +pdfjs-editor-highlight-button-label = Istakni +pdfjs-highlight-floating-button1 = + .title = Istakni + .aria-label = Istakni +pdfjs-highlight-floating-button-label = Istakni + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Ukloni crtež +pdfjs-editor-remove-freetext-button = + .title = Ukloni tekst +pdfjs-editor-remove-stamp-button = + .title = Ukloni sliku +pdfjs-editor-remove-highlight-button = + .title = Ukloni isticanje + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Boja +pdfjs-editor-free-text-size-input = Veličina +pdfjs-editor-ink-color-input = Boja +pdfjs-editor-ink-thickness-input = Debljina +pdfjs-editor-ink-opacity-input = Neprozirnost +pdfjs-editor-stamp-add-image-button = + .title = Dodaj sliku +pdfjs-editor-stamp-add-image-button-label = Dodaj sliku +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Debljina +pdfjs-editor-free-highlight-thickness-title = + .title = Promjeni debljinu pri isticanju drugih stavki osim teksta +pdfjs-free-text = + .aria-label = Uređivač teksta +pdfjs-free-text-default-content = Počni tipkati … +pdfjs-ink = + .aria-label = Uređivač crteža +pdfjs-ink-canvas = + .aria-label = Slika koju je izradio korisnik + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alternativni tekst +pdfjs-editor-alt-text-edit-button-label = Uredi alternativni tekst +pdfjs-editor-alt-text-dialog-label = Odaberi jednu opciju +pdfjs-editor-alt-text-dialog-description = Alternativni tekst pomaže slijepim osobama ili kada se slika ne učita. +pdfjs-editor-alt-text-add-description-label = Dodaj opis +pdfjs-editor-alt-text-add-description-description = Sažmi sadržaj predmeta, okruženje ili radnje u jednoj ili dvije rečenice. +pdfjs-editor-alt-text-mark-decorative-label = Označi kao ukrasno +pdfjs-editor-alt-text-mark-decorative-description = Ovo se koristi za ukrasne slike, poput rubova ili vodenih žigova. +pdfjs-editor-alt-text-cancel-button = Odustani +pdfjs-editor-alt-text-save-button = Spremi +pdfjs-editor-alt-text-decorative-tooltip = Označeno kao ukrasno +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Na primjer, „Mladić sjeda za stol kako bi jeo” + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Gornji lijevi kut – promijeni veličinu +pdfjs-editor-resizer-label-top-middle = Sredina gore – promijeni veličinu +pdfjs-editor-resizer-label-top-right = Gornji desni kut – promijeni veličinu +pdfjs-editor-resizer-label-middle-right = Sredina desno – promijeni veličinu +pdfjs-editor-resizer-label-bottom-right = Donji desni kut – promijeni veličinu +pdfjs-editor-resizer-label-bottom-middle = Sredina dolje – promjeni veličinu +pdfjs-editor-resizer-label-bottom-left = Donji lijevi kut – promijeni veličinu +pdfjs-editor-resizer-label-middle-left = Sredina lijevo – promijeni veličinu +pdfjs-editor-resizer-top-left = + .aria-label = Gornji lijevi kut – promijeni veličinu +pdfjs-editor-resizer-top-middle = + .aria-label = Sredina gore – promijeni veličinu +pdfjs-editor-resizer-top-right = + .aria-label = Gornji desni kut – promijeni veličinu +pdfjs-editor-resizer-middle-right = + .aria-label = Sredina desno – promijeni veličinu +pdfjs-editor-resizer-bottom-right = + .aria-label = Donji desni kut – promijeni veličinu +pdfjs-editor-resizer-bottom-middle = + .aria-label = Sredina dolje – promjeni veličinu +pdfjs-editor-resizer-bottom-left = + .aria-label = Donji lijevi kut – promijeni veličinu +pdfjs-editor-resizer-middle-left = + .aria-label = Sredina lijevo – promijeni veličinu + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Boja isticanja +pdfjs-editor-colorpicker-button = + .title = Promjeni boju +pdfjs-editor-colorpicker-dropdown = + .aria-label = Izbor boja +pdfjs-editor-colorpicker-yellow = + .title = Žuta +pdfjs-editor-colorpicker-green = + .title = Zelena +pdfjs-editor-colorpicker-blue = + .title = Plava +pdfjs-editor-colorpicker-pink = + .title = Ružičasta +pdfjs-editor-colorpicker-red = + .title = Crvena + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Prikaži sve +pdfjs-editor-highlight-show-all-button = + .title = Prikaži sve + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +pdfjs-editor-new-alt-text-textarea = + .placeholder = Ovdje upiši tvoj opis … +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Ovaj je alternativni tekst stvoren automatski i može biti netočan. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Saznaj više +pdfjs-editor-new-alt-text-create-automatically-button-label = Automatski stvori alternativni tekst +pdfjs-editor-new-alt-text-error-title = Nije bilo moguće automatski izraditi alternativni tekst +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Preuzimanje alternativnog teksta UI modela ({ $downloadedSize } od { $totalSize } MB) + .aria-valuetext = Preuzimanje alternativnog teksta UI modela ({ $downloadedSize } od { $totalSize } MB) +pdfjs-editor-new-alt-text-added-button-label = Alternativni tekst je dodan +pdfjs-editor-new-alt-text-missing-button-label = Nedostaje alternativni tekst +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Pregledaj alternativni tekst +pdfjs-editor-new-alt-text-to-review-button-label = Pregledaj alternativni tekst +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Stvoreno automatski: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Postavke alternativnog teksta slike +pdfjs-image-alt-text-settings-button-label = Postavke alternativnog teksta slike +pdfjs-editor-alt-text-settings-dialog-label = Postavke alternativnog teksta slike +pdfjs-editor-alt-text-settings-automatic-title = Automatski alternativni tekst +pdfjs-editor-alt-text-settings-create-model-button-label = Stvori alternativni tekst automatski +pdfjs-editor-alt-text-settings-create-model-description = Predlaže opise koji pomažu osobama koji ne mogu vidjeti sliku ili kada se slika ne učita. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Alternativni tekst UI modela ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Radi lokalno na tvom uređaju kako bi tvoji podaci ostali privatni. Potrebno za automatski alternativni tekst. +pdfjs-editor-alt-text-settings-delete-model-button = Izbriši +pdfjs-editor-alt-text-settings-download-model-button = Preuzmi +pdfjs-editor-alt-text-settings-downloading-model-button = Preuzimanje … +pdfjs-editor-alt-text-settings-editor-title = Uređivač alternativnog teksta +pdfjs-editor-alt-text-settings-show-dialog-button-label = Prikaži uređivač alternativnog teksta odmah pri dodavanju slike +pdfjs-editor-alt-text-settings-show-dialog-description = Pomaže osigurati da sve tvoje slike imaju alternativni tekst. +pdfjs-editor-alt-text-settings-close-button = Zatvori diff --git a/public/pdfjs/web/locale/hsb/viewer.ftl b/public/pdfjs/web/locale/hsb/viewer.ftl new file mode 100644 index 0000000..065ab8d --- /dev/null +++ b/public/pdfjs/web/locale/hsb/viewer.ftl @@ -0,0 +1,521 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Předchadna strona +pdfjs-previous-button-label = Wróćo +pdfjs-next-button = + .title = Přichodna strona +pdfjs-next-button-label = Dale +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Strona +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = z { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } z { $pagesCount }) +pdfjs-zoom-out-button = + .title = Pomjeńšić +pdfjs-zoom-out-button-label = Pomjeńšić +pdfjs-zoom-in-button = + .title = Powjetšić +pdfjs-zoom-in-button-label = Powjetšić +pdfjs-zoom-select = + .title = Skalowanje +pdfjs-presentation-mode-button = + .title = Do prezentaciskeho modusa přeńć +pdfjs-presentation-mode-button-label = Prezentaciski modus +pdfjs-open-file-button = + .title = Dataju wočinić +pdfjs-open-file-button-label = Wočinić +pdfjs-print-button = + .title = Ćišćeć +pdfjs-print-button-label = Ćišćeć +pdfjs-save-button = + .title = Składować +pdfjs-save-button-label = Składować +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Sćahnyć +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Sćahnyć +pdfjs-bookmark-button = + .title = Aktualna strona (URL z aktualneje strony pokazać) +pdfjs-bookmark-button-label = Aktualna strona + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Nastroje +pdfjs-tools-button-label = Nastroje +pdfjs-first-page-button = + .title = K prěnjej stronje +pdfjs-first-page-button-label = K prěnjej stronje +pdfjs-last-page-button = + .title = K poslednjej stronje +pdfjs-last-page-button-label = K poslednjej stronje +pdfjs-page-rotate-cw-button = + .title = K směrej časnika wjerćeć +pdfjs-page-rotate-cw-button-label = K směrej časnika wjerćeć +pdfjs-page-rotate-ccw-button = + .title = Přećiwo směrej časnika wjerćeć +pdfjs-page-rotate-ccw-button-label = Přećiwo směrej časnika wjerćeć +pdfjs-cursor-text-select-tool-button = + .title = Nastroj za wuběranje teksta zmóžnić +pdfjs-cursor-text-select-tool-button-label = Nastroj za wuběranje teksta +pdfjs-cursor-hand-tool-button = + .title = Ručny nastroj zmóžnić +pdfjs-cursor-hand-tool-button-label = Ručny nastroj +pdfjs-scroll-page-button = + .title = Kulenje strony wužiwać +pdfjs-scroll-page-button-label = Kulenje strony +pdfjs-scroll-vertical-button = + .title = Wertikalne suwanje wužiwać +pdfjs-scroll-vertical-button-label = Wertikalne suwanje +pdfjs-scroll-horizontal-button = + .title = Horicontalne suwanje wužiwać +pdfjs-scroll-horizontal-button-label = Horicontalne suwanje +pdfjs-scroll-wrapped-button = + .title = Postupne suwanje wužiwać +pdfjs-scroll-wrapped-button-label = Postupne suwanje +pdfjs-spread-none-button = + .title = Strony njezwjazać +pdfjs-spread-none-button-label = Žana dwójna strona +pdfjs-spread-odd-button = + .title = Strony započinajo z njerunymi stronami zwjazać +pdfjs-spread-odd-button-label = Njerune strony +pdfjs-spread-even-button = + .title = Strony započinajo z runymi stronami zwjazać +pdfjs-spread-even-button-label = Rune strony + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumentowe kajkosće… +pdfjs-document-properties-button-label = Dokumentowe kajkosće… +pdfjs-document-properties-file-name = Mjeno dataje: +pdfjs-document-properties-file-size = Wulkosć dataje: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bajtow) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bajtow) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajtow) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajtow) +pdfjs-document-properties-title = Titul: +pdfjs-document-properties-author = Awtor: +pdfjs-document-properties-subject = Předmjet: +pdfjs-document-properties-keywords = Klučowe słowa: +pdfjs-document-properties-creation-date = Datum wutworjenja: +pdfjs-document-properties-modification-date = Datum změny: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Awtor: +pdfjs-document-properties-producer = PDF-zhotowjer: +pdfjs-document-properties-version = PDF-wersija: +pdfjs-document-properties-page-count = Ličba stronow: +pdfjs-document-properties-page-size = Wulkosć strony: +pdfjs-document-properties-page-size-unit-inches = cól +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = wysoki format +pdfjs-document-properties-page-size-orientation-landscape = prěčny format +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Fast Web View: +pdfjs-document-properties-linearized-yes = Haj +pdfjs-document-properties-linearized-no = Ně +pdfjs-document-properties-close-button = Začinić + +## Print + +pdfjs-print-progress-message = Dokument so za ćišćenje přihotuje… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Přetorhnyć +pdfjs-printing-not-supported = Warnowanje: Ćišćenje so přez tutón wobhladowak połnje njepodpěruje. +pdfjs-printing-not-ready = Warnowanje: PDF njeje so za ćišćenje dospołnje začitał. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Bóčnicu pokazać/schować +pdfjs-toggle-sidebar-notification-button = + .title = Bóčnicu přepinać (dokument rozrjad/přiwěški/woršty wobsahuje) +pdfjs-toggle-sidebar-button-label = Bóčnicu pokazać/schować +pdfjs-document-outline-button = + .title = Dokumentowy naćisk pokazać (dwójne kliknjenje, zo bychu so wšě zapiski pokazali/schowali) +pdfjs-document-outline-button-label = Dokumentowa struktura +pdfjs-attachments-button = + .title = Přiwěški pokazać +pdfjs-attachments-button-label = Přiwěški +pdfjs-layers-button = + .title = Woršty pokazać (klikńće dwójce, zo byšće wšě woršty na standardny staw wróćo stajił) +pdfjs-layers-button-label = Woršty +pdfjs-thumbs-button = + .title = Miniatury pokazać +pdfjs-thumbs-button-label = Miniatury +pdfjs-current-outline-item-button = + .title = Aktualny rozrjadowy zapisk pytać +pdfjs-current-outline-item-button-label = Aktualny rozrjadowy zapisk +pdfjs-findbar-button = + .title = W dokumenće pytać +pdfjs-findbar-button-label = Pytać +pdfjs-additional-layers = Dalše woršty + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Strona { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura strony { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Pytać + .placeholder = W dokumenće pytać… +pdfjs-find-previous-button = + .title = Předchadne wustupowanje pytanskeho wuraza pytać +pdfjs-find-previous-button-label = Wróćo +pdfjs-find-next-button = + .title = Přichodne wustupowanje pytanskeho wuraza pytać +pdfjs-find-next-button-label = Dale +pdfjs-find-highlight-checkbox = Wšě wuzběhnyć +pdfjs-find-match-case-checkbox-label = Wulkopisanje wobkedźbować +pdfjs-find-match-diacritics-checkbox-label = Diakritiske znamješka wužiwać +pdfjs-find-entire-word-checkbox-label = Cyłe słowa +pdfjs-find-reached-top = Spočatk dokumenta docpěty, pokročuje so z kóncom +pdfjs-find-reached-bottom = Kónc dokument docpěty, pokročuje so ze spočatkom +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } z { $total } wotpowědnika + [two] { $current } z { $total } wotpowědnikow + [few] { $current } z { $total } wotpowědnikow + *[other] { $current } z { $total } wotpowědnikow + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Wyše { $limit } wotpowědnik + [two] Wyše { $limit } wotpowědnikaj + [few] Wyše { $limit } wotpowědniki + *[other] Wyše { $limit } wotpowědnikow + } +pdfjs-find-not-found = Pytanski wuraz njeje so namakał + +## Predefined zoom values + +pdfjs-page-scale-width = Šěrokosć strony +pdfjs-page-scale-fit = Wulkosć strony +pdfjs-page-scale-auto = Awtomatiske skalowanje +pdfjs-page-scale-actual = Aktualna wulkosć +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Strona { $page } + +## Loading indicator messages + +pdfjs-loading-error = Při začitowanju PDF je zmylk wustupił. +pdfjs-invalid-file-error = Njepłaćiwa abo wobškodźena PDF-dataja. +pdfjs-missing-file-error = Falowaca PDF-dataja. +pdfjs-unexpected-response-error = Njewočakowana serwerowa wotmołwa. +pdfjs-rendering-error = Při zwobraznjenju strony je zmylk wustupił. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Typ přispomnjenki: { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Zapodajće hesło, zo byšće PDF-dataju wočinił. +pdfjs-password-invalid = Njepłaćiwe hesło. Prošu spytajće hišće raz. +pdfjs-password-ok-button = W porjadku +pdfjs-password-cancel-button = Přetorhnyć +pdfjs-web-fonts-disabled = Webpisma su znjemóžnjene: njeje móžno, zasadźene PDF-pisma wužiwać. + +## Editing + +pdfjs-editor-free-text-button = + .title = Tekst +pdfjs-editor-free-text-button-label = Tekst +pdfjs-editor-ink-button = + .title = Rysować +pdfjs-editor-ink-button-label = Rysować +pdfjs-editor-stamp-button = + .title = Wobrazy přidać abo wobdźěłać +pdfjs-editor-stamp-button-label = Wobrazy přidać abo wobdźěłać +pdfjs-editor-highlight-button = + .title = Wuzběhnyć +pdfjs-editor-highlight-button-label = Wuzběhnyć +pdfjs-highlight-floating-button1 = + .title = Wuzběhnjenje + .aria-label = Wuzběhnjenje +pdfjs-highlight-floating-button-label = Wuzběhnjenje + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Rysowanku wotstronić +pdfjs-editor-remove-freetext-button = + .title = Tekst wotstronić +pdfjs-editor-remove-stamp-button = + .title = Wobraz wotstronić +pdfjs-editor-remove-highlight-button = + .title = Wuzběhnjenje wotstronić + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Barba +pdfjs-editor-free-text-size-input = Wulkosć +pdfjs-editor-ink-color-input = Barba +pdfjs-editor-ink-thickness-input = Tołstosć +pdfjs-editor-ink-opacity-input = Opacita +pdfjs-editor-stamp-add-image-button = + .title = Wobraz přidać +pdfjs-editor-stamp-add-image-button-label = Wobraz přidać +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Tołstosć +pdfjs-editor-free-highlight-thickness-title = + .title = Tołstosć změnić, hdyž so zapiski wuzběhuja, kotrež tekst njejsu +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstowy editor + .default-content = Započńće pisać … +pdfjs-free-text = + .aria-label = Tekstowy editor +pdfjs-free-text-default-content = Započńće pisać… +pdfjs-ink = + .aria-label = Rysowanski editor +pdfjs-ink-canvas = + .aria-label = Wobraz wutworjeny wot wužiwarja + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alternatiwny tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternatiwny tekst wobdźěłać +pdfjs-editor-alt-text-edit-button-label = Alternatiwny tekst wobdźěłać +pdfjs-editor-alt-text-dialog-label = Nastajenje wubrać +pdfjs-editor-alt-text-dialog-description = Alternatiwny tekst pomha, hdyž ludźo njemóža wobraz widźeć abo hdyž so wobraz njezačita. +pdfjs-editor-alt-text-add-description-label = Wopisanje přidać +pdfjs-editor-alt-text-add-description-description = Pisajće 1 sadu abo 2 sadźe, kotrejž temu, nastajenje abo akcije wopisujetej. +pdfjs-editor-alt-text-mark-decorative-label = Jako dekoratiwny markěrować +pdfjs-editor-alt-text-mark-decorative-description = To so za pyšace wobrazy wužiwa, na přikład ramiki abo wodowe znamjenja. +pdfjs-editor-alt-text-cancel-button = Přetorhnyć +pdfjs-editor-alt-text-save-button = Składować +pdfjs-editor-alt-text-decorative-tooltip = Jako dekoratiwny markěrowany +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Na přikład, „Młody muž za blidom sedźi, zo by jědź jědł“ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternatiwny tekst + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Horjeka nalěwo – wulkosć změnić +pdfjs-editor-resizer-label-top-middle = Horjeka wosrjedź – wulkosć změnić +pdfjs-editor-resizer-label-top-right = Horjeka naprawo – wulkosć změnić +pdfjs-editor-resizer-label-middle-right = Wosrjedź naprawo – wulkosć změnić +pdfjs-editor-resizer-label-bottom-right = Deleka naprawo – wulkosć změnić +pdfjs-editor-resizer-label-bottom-middle = Deleka wosrjedź – wulkosć změnić +pdfjs-editor-resizer-label-bottom-left = Deleka nalěwo – wulkosć změnić +pdfjs-editor-resizer-label-middle-left = Wosrjedź nalěwo – wulkosć změnić +pdfjs-editor-resizer-top-left = + .aria-label = Horjeka nalěwo – wulkosć změnić +pdfjs-editor-resizer-top-middle = + .aria-label = Horjeka wosrjedź – wulkosć změnić +pdfjs-editor-resizer-top-right = + .aria-label = Horjeka naprawo – wulkosć změnić +pdfjs-editor-resizer-middle-right = + .aria-label = Wosrjedź naprawo – wulkosć změnić +pdfjs-editor-resizer-bottom-right = + .aria-label = Deleka naprawo – wulkosć změnić +pdfjs-editor-resizer-bottom-middle = + .aria-label = Deleka wosrjedź – wulkosć změnić +pdfjs-editor-resizer-bottom-left = + .aria-label = Deleka nalěwo – wulkosć změnić +pdfjs-editor-resizer-middle-left = + .aria-label = Wosrjedź nalěwo – wulkosć změnić + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Barba wuzběhnjenja +pdfjs-editor-colorpicker-button = + .title = Barbu změnić +pdfjs-editor-colorpicker-dropdown = + .aria-label = Wuběr barbow +pdfjs-editor-colorpicker-yellow = + .title = Žołty +pdfjs-editor-colorpicker-green = + .title = Zeleny +pdfjs-editor-colorpicker-blue = + .title = Módry +pdfjs-editor-colorpicker-pink = + .title = Pink +pdfjs-editor-colorpicker-red = + .title = Čerwjeny + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Wšě pokazać +pdfjs-editor-highlight-show-all-button = + .title = Wšě pokazać + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Alternatiwny tekst wobdźěłać (wobrazowe wopisanje) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Alternatiwny tekst přidać (wobrazowe wopisanje) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Pisajće tu swoje wopisanje… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Krótke wopisanje za ludźi, kotřiž njemóžeće wobraz widźeć abo hdyž so wobraz njezačita. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Tutón alternatiwny tekst je so awtomatisce wutworił a je snano njedokładny. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Dalše informacije +pdfjs-editor-new-alt-text-create-automatically-button-label = Alternatiwny tekst awtomatisce wutworić +pdfjs-editor-new-alt-text-not-now-button = Nic nětko +pdfjs-editor-new-alt-text-error-title = Alternatiwny tekst njeda so awtomatisce wutworić +pdfjs-editor-new-alt-text-error-description = Prošu pisajće swój alternatiwny tekst abo spytajće pozdźišo hišće raz. +pdfjs-editor-new-alt-text-error-close-button = Začinić +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Model KI za alternatiwny tekst so sćahuje ({ $downloadedSize } z { $totalSize } MB) + .aria-valuetext = Model KI za alternatiwny tekst so sćahuje ({ $downloadedSize } z { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternatiwny tekst je so přidał +pdfjs-editor-new-alt-text-added-button-label = Alternatiwny tekst je so přidał +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Alternatiwny tekst faluje +pdfjs-editor-new-alt-text-missing-button-label = Alternatiwny tekst faluje +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternatiwny tekst přepruwować +pdfjs-editor-new-alt-text-to-review-button-label = Alternatiwny tekst přepruwować +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Awtomatisce wutworjeny: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Nastajenja alternatiwneho wobrazoweho teksta +pdfjs-image-alt-text-settings-button-label = Nastajenja alternatiwneho wobrazoweho teksta +pdfjs-editor-alt-text-settings-dialog-label = Nastajenja alternatiwneho wobrazoweho teksta +pdfjs-editor-alt-text-settings-automatic-title = Awtomatiski alternatiwny tekst +pdfjs-editor-alt-text-settings-create-model-button-label = Alternatiwny tekst awtomatisce wutworić +pdfjs-editor-alt-text-settings-create-model-description = Namjetuje wopisanja, zo by ludźom pomhał, kotřiž njemóžeće wobraz widźeć abo hdyž so wobraz njezačita. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Model KI alternatiwneho teksta ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Běži lokalnje na wašim graće, zo bychu waše daty priwatne wostali. Za awtomatiski alternatiwny tekst trěbny. +pdfjs-editor-alt-text-settings-delete-model-button = Zhašeć +pdfjs-editor-alt-text-settings-download-model-button = Sćahnyć +pdfjs-editor-alt-text-settings-downloading-model-button = Sćahuje so… +pdfjs-editor-alt-text-settings-editor-title = Editor za alternatiwny tekst +pdfjs-editor-alt-text-settings-show-dialog-button-label = Editor alternatiwneho teksta hnydom pokazać, hdyž so wobraz přidawa +pdfjs-editor-alt-text-settings-show-dialog-description = Pomha, wam wšěm swojim wobrazam alternatiwny tekst přidać. +pdfjs-editor-alt-text-settings-close-button = Začinić + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Wotstronjene wuzběhnyć +pdfjs-editor-undo-bar-message-freetext = Tekst je so wotstronił +pdfjs-editor-undo-bar-message-ink = Rysowanka je so wotstroniła +pdfjs-editor-undo-bar-message-stamp = Wobraz je so wotstronił +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } přispomnjenka je so wotstroniła + [two] { $count } přispomnjence stej so wotstroniłoj + [few] { $count } přispomnjenki su so wotstronili + *[other] { $count } přispomnjenkow je so wotstroniło + } +pdfjs-editor-undo-bar-undo-button = + .title = Cofnyć +pdfjs-editor-undo-bar-undo-button-label = Cofnyć +pdfjs-editor-undo-bar-close-button = + .title = Začinić +pdfjs-editor-undo-bar-close-button-label = Začinić diff --git a/public/pdfjs/web/locale/hu/viewer.ftl b/public/pdfjs/web/locale/hu/viewer.ftl new file mode 100644 index 0000000..76307a2 --- /dev/null +++ b/public/pdfjs/web/locale/hu/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Előző oldal +pdfjs-previous-button-label = Előző +pdfjs-next-button = + .title = Következő oldal +pdfjs-next-button-label = Tovább +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Oldal +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = összesen: { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount }) +pdfjs-zoom-out-button = + .title = Kicsinyítés +pdfjs-zoom-out-button-label = Kicsinyítés +pdfjs-zoom-in-button = + .title = Nagyítás +pdfjs-zoom-in-button-label = Nagyítás +pdfjs-zoom-select = + .title = Nagyítás +pdfjs-presentation-mode-button = + .title = Váltás bemutató módba +pdfjs-presentation-mode-button-label = Bemutató mód +pdfjs-open-file-button = + .title = Fájl megnyitása +pdfjs-open-file-button-label = Megnyitás +pdfjs-print-button = + .title = Nyomtatás +pdfjs-print-button-label = Nyomtatás +pdfjs-save-button = + .title = Mentés +pdfjs-save-button-label = Mentés +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Letöltés +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Letöltés +pdfjs-bookmark-button = + .title = Jelenlegi oldal (webcím megtekintése a jelenlegi oldalról) +pdfjs-bookmark-button-label = Jelenlegi oldal + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Eszközök +pdfjs-tools-button-label = Eszközök +pdfjs-first-page-button = + .title = Ugrás az első oldalra +pdfjs-first-page-button-label = Ugrás az első oldalra +pdfjs-last-page-button = + .title = Ugrás az utolsó oldalra +pdfjs-last-page-button-label = Ugrás az utolsó oldalra +pdfjs-page-rotate-cw-button = + .title = Forgatás az óramutató járásával egyezően +pdfjs-page-rotate-cw-button-label = Forgatás az óramutató járásával egyezően +pdfjs-page-rotate-ccw-button = + .title = Forgatás az óramutató járásával ellentétesen +pdfjs-page-rotate-ccw-button-label = Forgatás az óramutató járásával ellentétesen +pdfjs-cursor-text-select-tool-button = + .title = Szövegkijelölő eszköz bekapcsolása +pdfjs-cursor-text-select-tool-button-label = Szövegkijelölő eszköz +pdfjs-cursor-hand-tool-button = + .title = Kéz eszköz bekapcsolása +pdfjs-cursor-hand-tool-button-label = Kéz eszköz +pdfjs-scroll-page-button = + .title = Oldalgörgetés használata +pdfjs-scroll-page-button-label = Oldalgörgetés +pdfjs-scroll-vertical-button = + .title = Függőleges görgetés használata +pdfjs-scroll-vertical-button-label = Függőleges görgetés +pdfjs-scroll-horizontal-button = + .title = Vízszintes görgetés használata +pdfjs-scroll-horizontal-button-label = Vízszintes görgetés +pdfjs-scroll-wrapped-button = + .title = Rácsos elrendezés használata +pdfjs-scroll-wrapped-button-label = Rácsos elrendezés +pdfjs-spread-none-button = + .title = Ne tapassza össze az oldalakat +pdfjs-spread-none-button-label = Nincs összetapasztás +pdfjs-spread-odd-button = + .title = Lapok összetapasztása, a páratlan számú oldalakkal kezdve +pdfjs-spread-odd-button-label = Összetapasztás: páratlan +pdfjs-spread-even-button = + .title = Lapok összetapasztása, a páros számú oldalakkal kezdve +pdfjs-spread-even-button-label = Összetapasztás: páros + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumentum tulajdonságai… +pdfjs-document-properties-button-label = Dokumentum tulajdonságai… +pdfjs-document-properties-file-name = Fájlnév: +pdfjs-document-properties-file-size = Fájlméret: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } kB ({ $b } bájt) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bájt) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bájt) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bájt) +pdfjs-document-properties-title = Cím: +pdfjs-document-properties-author = Szerző: +pdfjs-document-properties-subject = Tárgy: +pdfjs-document-properties-keywords = Kulcsszavak: +pdfjs-document-properties-creation-date = Létrehozás dátuma: +pdfjs-document-properties-modification-date = Módosítás dátuma: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Létrehozta: +pdfjs-document-properties-producer = PDF előállító: +pdfjs-document-properties-version = PDF verzió: +pdfjs-document-properties-page-count = Oldalszám: +pdfjs-document-properties-page-size = Lapméret: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = álló +pdfjs-document-properties-page-size-orientation-landscape = fekvő +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Jogi információk + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Gyors webes nézet: +pdfjs-document-properties-linearized-yes = Igen +pdfjs-document-properties-linearized-no = Nem +pdfjs-document-properties-close-button = Bezárás + +## Print + +pdfjs-print-progress-message = Dokumentum előkészítése nyomtatáshoz… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Mégse +pdfjs-printing-not-supported = Figyelmeztetés: Ez a böngésző nem teljesen támogatja a nyomtatást. +pdfjs-printing-not-ready = Figyelmeztetés: A PDF nincs teljesen betöltve a nyomtatáshoz. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Oldalsáv be/ki +pdfjs-toggle-sidebar-notification-button = + .title = Oldalsáv be/ki (a dokumentum vázlatot/mellékleteket/rétegeket tartalmaz) +pdfjs-toggle-sidebar-button-label = Oldalsáv be/ki +pdfjs-document-outline-button = + .title = Dokumentum megjelenítése online (dupla kattintás minden elem kinyitásához/összecsukásához) +pdfjs-document-outline-button-label = Dokumentumvázlat +pdfjs-attachments-button = + .title = Mellékletek megjelenítése +pdfjs-attachments-button-label = Van melléklet +pdfjs-layers-button = + .title = Rétegek megjelenítése (dupla kattintás az összes réteg alapértelmezett állapotra visszaállításához) +pdfjs-layers-button-label = Rétegek +pdfjs-thumbs-button = + .title = Bélyegképek megjelenítése +pdfjs-thumbs-button-label = Bélyegképek +pdfjs-current-outline-item-button = + .title = Jelenlegi vázlatelem megkeresése +pdfjs-current-outline-item-button-label = Jelenlegi vázlatelem +pdfjs-findbar-button = + .title = Keresés a dokumentumban +pdfjs-findbar-button-label = Keresés +pdfjs-additional-layers = További rétegek + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = { $page }. oldal +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page }. oldal bélyegképe + +## Find panel button title and messages + +pdfjs-find-input = + .title = Keresés + .placeholder = Keresés a dokumentumban… +pdfjs-find-previous-button = + .title = A kifejezés előző előfordulásának keresése +pdfjs-find-previous-button-label = Előző +pdfjs-find-next-button = + .title = A kifejezés következő előfordulásának keresése +pdfjs-find-next-button-label = Tovább +pdfjs-find-highlight-checkbox = Összes kiemelése +pdfjs-find-match-case-checkbox-label = Kis- és nagybetűk megkülönböztetése +pdfjs-find-match-diacritics-checkbox-label = Diakritikus jelek +pdfjs-find-entire-word-checkbox-label = Teljes szavak +pdfjs-find-reached-top = A dokumentum eleje elérve, folytatás a végétől +pdfjs-find-reached-bottom = A dokumentum vége elérve, folytatás az elejétől +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } / { $total } találat + *[other] { $current } / { $total } találat + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Több mint { $limit } találat + *[other] Több mint { $limit } találat + } +pdfjs-find-not-found = A kifejezés nem található + +## Predefined zoom values + +pdfjs-page-scale-width = Oldalszélesség +pdfjs-page-scale-fit = Teljes oldal +pdfjs-page-scale-auto = Automatikus nagyítás +pdfjs-page-scale-actual = Valódi méret +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = { $page }. oldal + +## Loading indicator messages + +pdfjs-loading-error = Hiba történt a PDF betöltésekor. +pdfjs-invalid-file-error = Érvénytelen vagy sérült PDF fájl. +pdfjs-missing-file-error = Hiányzó PDF fájl. +pdfjs-unexpected-response-error = Váratlan kiszolgálóválasz. +pdfjs-rendering-error = Hiba történt az oldal feldolgozása közben. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } megjegyzés] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Adja meg a jelszót a PDF fájl megnyitásához. +pdfjs-password-invalid = Helytelen jelszó. Próbálja újra. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Mégse +pdfjs-web-fonts-disabled = Webes betűkészletek letiltva: nem használhatók a beágyazott PDF betűkészletek. + +## Editing + +pdfjs-editor-free-text-button = + .title = Szöveg +pdfjs-editor-free-text-button-label = Szöveg +pdfjs-editor-ink-button = + .title = Rajzolás +pdfjs-editor-ink-button-label = Rajzolás +pdfjs-editor-stamp-button = + .title = Képek hozzáadása vagy szerkesztése +pdfjs-editor-stamp-button-label = Képek hozzáadása vagy szerkesztése +pdfjs-editor-highlight-button = + .title = Kiemelés +pdfjs-editor-highlight-button-label = Kiemelés +pdfjs-highlight-floating-button1 = + .title = Kiemelés + .aria-label = Kiemelés +pdfjs-highlight-floating-button-label = Kiemelés + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Rajz eltávolítása +pdfjs-editor-remove-freetext-button = + .title = Szöveg eltávolítása +pdfjs-editor-remove-stamp-button = + .title = Kép eltávolítása +pdfjs-editor-remove-highlight-button = + .title = Kiemelés eltávolítása + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Szín +pdfjs-editor-free-text-size-input = Méret +pdfjs-editor-ink-color-input = Szín +pdfjs-editor-ink-thickness-input = Vastagság +pdfjs-editor-ink-opacity-input = Átlátszatlanság +pdfjs-editor-stamp-add-image-button = + .title = Kép hozzáadása +pdfjs-editor-stamp-add-image-button-label = Kép hozzáadása +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Vastagság +pdfjs-editor-free-highlight-thickness-title = + .title = Vastagság módosítása, ha nem szöveges elemeket emel ki +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Szövegszerkesztő + .default-content = Kezdjen gépelni… +pdfjs-free-text = + .aria-label = Szövegszerkesztő +pdfjs-free-text-default-content = Kezdjen el gépelni… +pdfjs-ink = + .aria-label = Rajzszerkesztő +pdfjs-ink-canvas = + .aria-label = Felhasználó által készített kép + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alternatív szöveg +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternatív szöveg szerkesztése +pdfjs-editor-alt-text-edit-button-label = Alternatív szöveg szerkesztése +pdfjs-editor-alt-text-dialog-label = Válasszon egy lehetőséget +pdfjs-editor-alt-text-dialog-description = Az alternatív szöveg segít, ha az emberek nem látják a képet, vagy ha az nem töltődik be. +pdfjs-editor-alt-text-add-description-label = Leírás hozzáadása +pdfjs-editor-alt-text-add-description-description = Törekedjen 1-2 mondatra, amely jellemzi a témát, környezetet vagy cselekvést. +pdfjs-editor-alt-text-mark-decorative-label = Megjelölés dekoratívként +pdfjs-editor-alt-text-mark-decorative-description = Ez a díszítőképeknél használatos, mint a szegélyek vagy a vízjelek. +pdfjs-editor-alt-text-cancel-button = Mégse +pdfjs-editor-alt-text-save-button = Mentés +pdfjs-editor-alt-text-decorative-tooltip = Megjelölve dekoratívként +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Például: „Egy fiatal férfi leül enni egy asztalhoz” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternatív szöveg + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Bal felső sarok – átméretezés +pdfjs-editor-resizer-label-top-middle = Felül középen – átméretezés +pdfjs-editor-resizer-label-top-right = Jobb felső sarok – átméretezés +pdfjs-editor-resizer-label-middle-right = Jobbra középen – átméretezés +pdfjs-editor-resizer-label-bottom-right = Jobb alsó sarok – átméretezés +pdfjs-editor-resizer-label-bottom-middle = Alul középen – átméretezés +pdfjs-editor-resizer-label-bottom-left = Bal alsó sarok – átméretezés +pdfjs-editor-resizer-label-middle-left = Balra középen – átméretezés +pdfjs-editor-resizer-top-left = + .aria-label = Bal felső sarok – átméretezés +pdfjs-editor-resizer-top-middle = + .aria-label = Felül középen – átméretezés +pdfjs-editor-resizer-top-right = + .aria-label = Jobb felső sarok – átméretezés +pdfjs-editor-resizer-middle-right = + .aria-label = Jobbra középen – átméretezés +pdfjs-editor-resizer-bottom-right = + .aria-label = Jobb alsó sarok – átméretezés +pdfjs-editor-resizer-bottom-middle = + .aria-label = Alul középen – átméretezés +pdfjs-editor-resizer-bottom-left = + .aria-label = Bal alsó sarok – átméretezés +pdfjs-editor-resizer-middle-left = + .aria-label = Balra középen – átméretezés + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Kiemelés színe +pdfjs-editor-colorpicker-button = + .title = Szín módosítása +pdfjs-editor-colorpicker-dropdown = + .aria-label = Színválasztások +pdfjs-editor-colorpicker-yellow = + .title = Sárga +pdfjs-editor-colorpicker-green = + .title = Zöld +pdfjs-editor-colorpicker-blue = + .title = Kék +pdfjs-editor-colorpicker-pink = + .title = Rózsaszín +pdfjs-editor-colorpicker-red = + .title = Vörös + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Összes megjelenítése +pdfjs-editor-highlight-show-all-button = + .title = Összes megjelenítése + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Alternatív szöveg szerkesztése (képleírás) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Alternatív szöveg hozzáadása (képleírás) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Írja ide a leírását… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Rövid leírás azoknak, akik nem látják a képet, vagy arra az esetre, ha a kép nem tölt be. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Ez az alternatív szöveg automatikusan lett létrehozva, és pontatlan lehet. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = További tudnivalók +pdfjs-editor-new-alt-text-create-automatically-button-label = Alternatív szöveg automatikus létrehozása +pdfjs-editor-new-alt-text-not-now-button = Most nem +pdfjs-editor-new-alt-text-error-title = Az alternatív szöveg automatikus létrehozása nem sikerült +pdfjs-editor-new-alt-text-error-description = Írja meg a saját alternatív szövegét, vagy próbálja újra később. +pdfjs-editor-new-alt-text-error-close-button = Bezárás +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Alternatív szöveg MI modell letöltése ({ $downloadedSize } / { $totalSize } MB) + .aria-valuetext = Alternatív szöveg MI modell letöltése ({ $downloadedSize } / { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternatív szöveg hozzáadva +pdfjs-editor-new-alt-text-added-button-label = Alternatív szöveg hozzáadva +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Hiányzó alternatív szöveg +pdfjs-editor-new-alt-text-missing-button-label = Hiányzó alternatív szöveg +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternatív szöveg áttekintése +pdfjs-editor-new-alt-text-to-review-button-label = Alternatív szöveg szerkesztése +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Automatikusan létrehozva: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Kép alternatív szövegének beállításai +pdfjs-image-alt-text-settings-button-label = Kép alternatív szövegének beállításai +pdfjs-editor-alt-text-settings-dialog-label = Kép alternatív szövegének beállításai +pdfjs-editor-alt-text-settings-automatic-title = Automatikus alternatív szöveg +pdfjs-editor-alt-text-settings-create-model-button-label = Alternatív szöveg automatikus létrehozása +pdfjs-editor-alt-text-settings-create-model-description = Leírásokat javasol, hogy segítsen azoknak, akik nem látják a képet, vagy arra az esetre, ha a kép nem tölt be. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Alternatív szöveg MI modellje ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Helyben fut az eszközén, így az adatai privátok maradnak. Az automatikus alternatív szövegekhez szükséges. +pdfjs-editor-alt-text-settings-delete-model-button = Törlés +pdfjs-editor-alt-text-settings-download-model-button = Letöltés +pdfjs-editor-alt-text-settings-downloading-model-button = Letöltés… +pdfjs-editor-alt-text-settings-editor-title = Alternatív szöveg szerkesztője +pdfjs-editor-alt-text-settings-show-dialog-button-label = Az alternatív szöveg szerkesztőjének azonnali megjelenítése egy kép hozzáadásakor +pdfjs-editor-alt-text-settings-show-dialog-description = Segít elérni, hogy az összes képén legyen alternatív szöveg. +pdfjs-editor-alt-text-settings-close-button = Bezárás + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Kiemelés eltávolítva +pdfjs-editor-undo-bar-message-freetext = Szöveg eltávolítva +pdfjs-editor-undo-bar-message-ink = Rajz eltávolítva +pdfjs-editor-undo-bar-message-stamp = Kép eltávolítva +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } kommentár eltávolítva + *[other] { $count } kommentár eltávolítva + } +pdfjs-editor-undo-bar-undo-button = + .title = Visszavonás +pdfjs-editor-undo-bar-undo-button-label = Visszavonás +pdfjs-editor-undo-bar-close-button = + .title = Bezárás +pdfjs-editor-undo-bar-close-button-label = Bezárás diff --git a/public/pdfjs/web/locale/hy-AM/viewer.ftl b/public/pdfjs/web/locale/hy-AM/viewer.ftl new file mode 100644 index 0000000..5c9dd27 --- /dev/null +++ b/public/pdfjs/web/locale/hy-AM/viewer.ftl @@ -0,0 +1,272 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Նախորդ էջը +pdfjs-previous-button-label = Նախորդը +pdfjs-next-button = + .title = Հաջորդ էջը +pdfjs-next-button-label = Հաջորդը +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Էջ. +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = -ը՝ { $pagesCount }-ից +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber }-ը { $pagesCount })-ից +pdfjs-zoom-out-button = + .title = Փոքրացնել +pdfjs-zoom-out-button-label = Փոքրացնել +pdfjs-zoom-in-button = + .title = Խոշորացնել +pdfjs-zoom-in-button-label = Խոշորացնել +pdfjs-zoom-select = + .title = Դիտափոխում +pdfjs-presentation-mode-button = + .title = Անցնել Ներկայացման եղանակին +pdfjs-presentation-mode-button-label = Ներկայացման եղանակ +pdfjs-open-file-button = + .title = Բացել նիշք +pdfjs-open-file-button-label = Բացել +pdfjs-print-button = + .title = Տպել +pdfjs-print-button-label = Տպել +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Ներբեռնել +pdfjs-bookmark-button-label = Ընթացիկ էջ + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Գործիքներ +pdfjs-tools-button-label = Գործիքներ +pdfjs-first-page-button = + .title = Անցնել առաջին էջին +pdfjs-first-page-button-label = Անցնել առաջին էջին +pdfjs-last-page-button = + .title = Անցնել վերջին էջին +pdfjs-last-page-button-label = Անցնել վերջին էջին +pdfjs-page-rotate-cw-button = + .title = Պտտել ըստ ժամացույցի սլաքի +pdfjs-page-rotate-cw-button-label = Պտտել ըստ ժամացույցի սլաքի +pdfjs-page-rotate-ccw-button = + .title = Պտտել հակառակ ժամացույցի սլաքի +pdfjs-page-rotate-ccw-button-label = Պտտել հակառակ ժամացույցի սլաքի +pdfjs-cursor-text-select-tool-button = + .title = Միացնել գրույթ ընտրելու գործիքը +pdfjs-cursor-text-select-tool-button-label = Գրույթը ընտրելու գործիք +pdfjs-cursor-hand-tool-button = + .title = Միացնել Ձեռքի գործիքը +pdfjs-cursor-hand-tool-button-label = Ձեռքի գործիք +pdfjs-scroll-vertical-button = + .title = Օգտագործել ուղղահայաց ոլորում +pdfjs-scroll-vertical-button-label = Ուղղահայաց ոլորում +pdfjs-scroll-horizontal-button = + .title = Օգտագործել հորիզոնական ոլորում +pdfjs-scroll-horizontal-button-label = Հորիզոնական ոլորում +pdfjs-scroll-wrapped-button = + .title = Օգտագործել փաթաթված ոլորում +pdfjs-scroll-wrapped-button-label = Փաթաթված ոլորում +pdfjs-spread-none-button = + .title = Մի միացեք էջի վերածածկերին +pdfjs-spread-none-button-label = Չկա վերածածկեր +pdfjs-spread-odd-button = + .title = Միացեք էջի վերածածկերին սկսելով՝ կենտ համարակալված էջերով +pdfjs-spread-odd-button-label = Կենտ վերածածկեր +pdfjs-spread-even-button = + .title = Միացեք էջի վերածածկերին սկսելով՝ զույգ համարակալված էջերով +pdfjs-spread-even-button-label = Զույգ վերածածկեր + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Փաստաթղթի հատկությունները… +pdfjs-document-properties-button-label = Փաստաթղթի հատկությունները… +pdfjs-document-properties-file-name = Նիշքի անունը. +pdfjs-document-properties-file-size = Նիշք չափը. +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } ԿԲ ({ $size_b } բայթ) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } ՄԲ ({ $size_b } բայթ) +pdfjs-document-properties-title = Վերնագիր. +pdfjs-document-properties-author = Հեղինակ․ +pdfjs-document-properties-subject = Վերնագիր. +pdfjs-document-properties-keywords = Հիմնաբառ. +pdfjs-document-properties-creation-date = Ստեղծելու ամսաթիվը. +pdfjs-document-properties-modification-date = Փոփոխելու ամսաթիվը. +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Ստեղծող. +pdfjs-document-properties-producer = PDF-ի հեղինակը. +pdfjs-document-properties-version = PDF-ի տարբերակը. +pdfjs-document-properties-page-count = Էջերի քանակը. +pdfjs-document-properties-page-size = Էջի չափը. +pdfjs-document-properties-page-size-unit-inches = ում +pdfjs-document-properties-page-size-unit-millimeters = մմ +pdfjs-document-properties-page-size-orientation-portrait = ուղղաձիգ +pdfjs-document-properties-page-size-orientation-landscape = հորիզոնական +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Նամակ +pdfjs-document-properties-page-size-name-legal = Օրինական + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Արագ վեբ դիտում․ +pdfjs-document-properties-linearized-yes = Այո +pdfjs-document-properties-linearized-no = Ոչ +pdfjs-document-properties-close-button = Փակել + +## Print + +pdfjs-print-progress-message = Նախապատրաստում է փաստաթուղթը տպելուն... +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Չեղարկել +pdfjs-printing-not-supported = Զգուշացում. Տպելը ամբողջությամբ չի աջակցվում դիտարկիչի կողմից։ +pdfjs-printing-not-ready = Զգուշացում. PDF-ը ամբողջությամբ չի բեռնավորվել տպելու համար: + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Բացել/Փակել Կողային վահանակը +pdfjs-toggle-sidebar-button-label = Բացել/Փակել Կողային վահանակը +pdfjs-document-outline-button = + .title = Ցուցադրել փաստաթղթի ուրվագիծը (կրկնակի սեղմեք՝ միավորները ընդարձակելու/կոծկելու համար) +pdfjs-document-outline-button-label = Փաստաթղթի բովանդակությունը +pdfjs-attachments-button = + .title = Ցուցադրել կցորդները +pdfjs-attachments-button-label = Կցորդներ +pdfjs-thumbs-button = + .title = Ցուցադրել Մանրապատկերը +pdfjs-thumbs-button-label = Մանրապատկերը +pdfjs-findbar-button = + .title = Գտնել փաստաթղթում +pdfjs-findbar-button-label = Որոնում + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Էջը { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Էջի մանրապատկերը { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Որոնում + .placeholder = Գտնել փաստաթղթում... +pdfjs-find-previous-button = + .title = Գտնել անրահայտության նախորդ հանդիպումը +pdfjs-find-previous-button-label = Նախորդը +pdfjs-find-next-button = + .title = Գտիր արտահայտության հաջորդ հանդիպումը +pdfjs-find-next-button-label = Հաջորդը +pdfjs-find-highlight-checkbox = Գունանշել բոլորը +pdfjs-find-match-case-checkbox-label = Մեծ(փոքր)ատառ հաշվի առնել +pdfjs-find-entire-word-checkbox-label = Ամբողջ բառերը +pdfjs-find-reached-top = Հասել եք փաստաթղթի վերևին, կշարունակվի ներքևից +pdfjs-find-reached-bottom = Հասել եք փաստաթղթի վերջին, կշարունակվի վերևից +pdfjs-find-not-found = Արտահայտությունը չգտնվեց + +## Predefined zoom values + +pdfjs-page-scale-width = Էջի լայնքը +pdfjs-page-scale-fit = Ձգել էջը +pdfjs-page-scale-auto = Ինքնաշխատ +pdfjs-page-scale-actual = Իրական չափը +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Սխալ՝ PDF ֆայլը բացելիս։ +pdfjs-invalid-file-error = Սխալ կամ վնասված PDF ֆայլ: +pdfjs-missing-file-error = PDF ֆայլը բացակայում է: +pdfjs-unexpected-response-error = Սպասարկիչի անսպասելի պատասխան: +pdfjs-rendering-error = Սխալ՝ էջը ստեղծելիս: + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Ծանոթություն] + +## Password + +pdfjs-password-label = Մուտքագրեք PDF-ի գաղտնաբառը: +pdfjs-password-invalid = Գաղտնաբառը սխալ է: Կրկին փորձեք: +pdfjs-password-ok-button = Լավ +pdfjs-password-cancel-button = Չեղարկել +pdfjs-web-fonts-disabled = Վեբ-տառատեսակները անջատված են. հնարավոր չէ օգտագործել ներկառուցված PDF տառատեսակները: + +## Editing + + +## Remove button for the various kind of editor. + + +## + +pdfjs-free-text-default-content = Սկսել մուտքագրումը… + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Ցուցադրել բոլորը +pdfjs-editor-highlight-show-all-button = + .title = Ցուցադրել բոլորը diff --git a/public/pdfjs/web/locale/hye/viewer.ftl b/public/pdfjs/web/locale/hye/viewer.ftl new file mode 100644 index 0000000..75cdc06 --- /dev/null +++ b/public/pdfjs/web/locale/hye/viewer.ftl @@ -0,0 +1,268 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Նախորդ էջ +pdfjs-previous-button-label = Նախորդը +pdfjs-next-button = + .title = Յաջորդ էջ +pdfjs-next-button-label = Յաջորդը +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = էջ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount }-ից +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber }-ը { $pagesCount })-ից +pdfjs-zoom-out-button = + .title = Փոքրացնել +pdfjs-zoom-out-button-label = Փոքրացնել +pdfjs-zoom-in-button = + .title = Խոշորացնել +pdfjs-zoom-in-button-label = Խոշորացնել +pdfjs-zoom-select = + .title = Խոշորացում +pdfjs-presentation-mode-button = + .title = Անցնել ներկայացման եղանակին +pdfjs-presentation-mode-button-label = Ներկայացման եղանակ +pdfjs-open-file-button = + .title = Բացել նիշքը +pdfjs-open-file-button-label = Բացել +pdfjs-print-button = + .title = Տպել +pdfjs-print-button-label = Տպել + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Գործիքներ +pdfjs-tools-button-label = Գործիքներ +pdfjs-first-page-button = + .title = Գնալ դէպի առաջին էջ +pdfjs-first-page-button-label = Գնալ դէպի առաջին էջ +pdfjs-last-page-button = + .title = Գնալ դէպի վերջին էջ +pdfjs-last-page-button-label = Գնալ դէպի վերջին էջ +pdfjs-page-rotate-cw-button = + .title = Պտտել ժամացոյցի սլաքի ուղղութեամբ +pdfjs-page-rotate-cw-button-label = Պտտել ժամացոյցի սլաքի ուղղութեամբ +pdfjs-page-rotate-ccw-button = + .title = Պտտել ժամացոյցի սլաքի հակառակ ուղղութեամբ +pdfjs-page-rotate-ccw-button-label = Պտտել ժամացոյցի սլաքի հակառակ ուղղութեամբ +pdfjs-cursor-text-select-tool-button = + .title = Միացնել գրոյթ ընտրելու գործիքը +pdfjs-cursor-text-select-tool-button-label = Գրուածք ընտրելու գործիք +pdfjs-cursor-hand-tool-button = + .title = Միացնել ձեռքի գործիքը +pdfjs-cursor-hand-tool-button-label = Ձեռքի գործիք +pdfjs-scroll-page-button = + .title = Աւգտագործել էջի ոլորում +pdfjs-scroll-page-button-label = Էջի ոլորում +pdfjs-scroll-vertical-button = + .title = Աւգտագործել ուղղահայեաց ոլորում +pdfjs-scroll-vertical-button-label = Ուղղահայեաց ոլորում +pdfjs-scroll-horizontal-button = + .title = Աւգտագործել հորիզոնական ոլորում +pdfjs-scroll-horizontal-button-label = Հորիզոնական ոլորում +pdfjs-scroll-wrapped-button = + .title = Աւգտագործել փաթաթուած ոլորում +pdfjs-scroll-wrapped-button-label = Փաթաթուած ոլորում +pdfjs-spread-none-button = + .title = Մի միացէք էջի կոնտեքստում +pdfjs-spread-none-button-label = Չկայ կոնտեքստ +pdfjs-spread-odd-button = + .title = Միացէք էջի կոնտեքստին սկսելով՝ կենտ համարակալուած էջերով +pdfjs-spread-odd-button-label = Տարաւրինակ կոնտեքստ +pdfjs-spread-even-button = + .title = Միացէք էջի կոնտեքստին սկսելով՝ զոյգ համարակալուած էջերով +pdfjs-spread-even-button-label = Հաւասար վերածածկեր + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Փաստաթղթի հատկութիւնները… +pdfjs-document-properties-button-label = Փաստաթղթի յատկութիւնները… +pdfjs-document-properties-file-name = Նիշքի անունը․ +pdfjs-document-properties-file-size = Նիշք չափը. +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } ԿԲ ({ $size_b } բայթ) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } ՄԲ ({ $size_b } բայթ) +pdfjs-document-properties-title = Վերնագիր +pdfjs-document-properties-author = Հեղինակ․ +pdfjs-document-properties-subject = առարկայ +pdfjs-document-properties-keywords = Հիմնաբառեր +pdfjs-document-properties-creation-date = Ստեղծման ամսաթիւ +pdfjs-document-properties-modification-date = Փոփոխութեան ամսաթիւ. +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Ստեղծող +pdfjs-document-properties-producer = PDF-ի Արտադրողը. +pdfjs-document-properties-version = PDF-ի տարբերակը. +pdfjs-document-properties-page-count = Էջերի քանակը. +pdfjs-document-properties-page-size = Էջի չափը. +pdfjs-document-properties-page-size-unit-inches = ում +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = ուղղաձիգ +pdfjs-document-properties-page-size-orientation-landscape = հորիզոնական +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Նամակ +pdfjs-document-properties-page-size-name-legal = Աւրինական + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Արագ վեբ դիտում․ +pdfjs-document-properties-linearized-yes = Այո +pdfjs-document-properties-linearized-no = Ոչ +pdfjs-document-properties-close-button = Փակել + +## Print + +pdfjs-print-progress-message = Նախապատրաստում է փաստաթուղթը տպելուն… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Չեղարկել +pdfjs-printing-not-supported = Զգուշացում. Տպելը ամբողջութեամբ չի աջակցուում զննարկիչի կողմից։ +pdfjs-printing-not-ready = Զգուշացում. PDF֊ը ամբողջութեամբ չի բեռնաւորուել տպելու համար։ + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Փոխարկել կողային վահանակը +pdfjs-toggle-sidebar-notification-button = + .title = Փոխանջատել կողմնասիւնը (փաստաթուղթը պարունակում է ուրուագիծ/կցորդներ/շերտեր) +pdfjs-toggle-sidebar-button-label = Փոխարկել կողային վահանակը +pdfjs-document-outline-button = + .title = Ցուցադրել փաստաթղթի ուրուագիծը (կրկնակի սեղմէք՝ միաւորները ընդարձակելու/կոծկելու համար) +pdfjs-document-outline-button-label = Փաստաթղթի ուրուագիծ +pdfjs-attachments-button = + .title = Ցուցադրել կցորդները +pdfjs-attachments-button-label = Կցորդներ +pdfjs-layers-button = + .title = Ցուցադրել շերտերը (կրկնահպել վերակայելու բոլոր շերտերը սկզբնադիր վիճակի) +pdfjs-layers-button-label = Շերտեր +pdfjs-thumbs-button = + .title = Ցուցադրել մանրապատկերը +pdfjs-thumbs-button-label = Մանրապատկեր +pdfjs-current-outline-item-button = + .title = Գտէք ընթացիկ գծագրման տարրը +pdfjs-current-outline-item-button-label = Ընթացիկ գծագրման տարր +pdfjs-findbar-button = + .title = Գտնել փաստաթղթում +pdfjs-findbar-button-label = Որոնում +pdfjs-additional-layers = Լրացուցիչ շերտեր + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Էջը { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Էջի մանրապատկերը { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Որոնում + .placeholder = Գտնել փաստաթղթում… +pdfjs-find-previous-button = + .title = Գտնել արտայայտութեան նախորդ արտայայտութիւնը +pdfjs-find-previous-button-label = Նախորդը +pdfjs-find-next-button = + .title = Գտիր արտայայտութեան յաջորդ արտայայտութիւնը +pdfjs-find-next-button-label = Հաջորդը +pdfjs-find-highlight-checkbox = Գունանշել բոլորը +pdfjs-find-match-case-checkbox-label = Հաշուի առնել հանգամանքը +pdfjs-find-match-diacritics-checkbox-label = Հնչիւնատարբերիչ նշանների համապատասխանեցում +pdfjs-find-entire-word-checkbox-label = Ամբողջ բառերը +pdfjs-find-reached-top = Հասել եք փաստաթղթի վերեւին,շարունակել ներքեւից +pdfjs-find-reached-bottom = Հասել էք փաստաթղթի վերջին, շարունակել վերեւից +pdfjs-find-not-found = Արտայայտութիւնը չգտնուեց + +## Predefined zoom values + +pdfjs-page-scale-width = Էջի լայնութիւն +pdfjs-page-scale-fit = Հարմարեցնել էջը +pdfjs-page-scale-auto = Ինքնաշխատ խոշորացում +pdfjs-page-scale-actual = Իրական չափը +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Էջ { $page } + +## Loading indicator messages + +pdfjs-loading-error = PDF նիշքը բացելիս սխալ է տեղի ունեցել։ +pdfjs-invalid-file-error = Սխալ կամ վնասուած PDF նիշք։ +pdfjs-missing-file-error = PDF նիշքը բացակաիւմ է։ +pdfjs-unexpected-response-error = Սպասարկիչի անսպասելի պատասխան։ +pdfjs-rendering-error = Սխալ է տեղի ունեցել էջի մեկնաբանման ժամանակ + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Ծանոթութիւն] + +## Password + +pdfjs-password-label = Մուտքագրէք գաղտնաբառը այս PDF նիշքը բացելու համար +pdfjs-password-invalid = Գաղտնաբառը սխալ է: Կրկին փորձէք: +pdfjs-password-ok-button = Լաւ +pdfjs-password-cancel-button = Չեղարկել +pdfjs-web-fonts-disabled = Վեբ-տառատեսակները անջատուած են. հնարաւոր չէ աւգտագործել ներկառուցուած PDF տառատեսակները։ + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/ia/viewer.ftl b/public/pdfjs/web/locale/ia/viewer.ftl new file mode 100644 index 0000000..91fbaf9 --- /dev/null +++ b/public/pdfjs/web/locale/ia/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pagina previe +pdfjs-previous-button-label = Previe +pdfjs-next-button = + .title = Pagina sequente +pdfjs-next-button-label = Sequente +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pagina +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = de { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount }) +pdfjs-zoom-out-button = + .title = Distantiar +pdfjs-zoom-out-button-label = Distantiar +pdfjs-zoom-in-button = + .title = Approximar +pdfjs-zoom-in-button-label = Approximar +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Excambiar a modo presentation +pdfjs-presentation-mode-button-label = Modo presentation +pdfjs-open-file-button = + .title = Aperir le file +pdfjs-open-file-button-label = Aperir +pdfjs-print-button = + .title = Imprimer +pdfjs-print-button-label = Imprimer +pdfjs-save-button = + .title = Salvar +pdfjs-save-button-label = Salvar +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Discargar +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Discargar +pdfjs-bookmark-button = + .title = Pagina actual (vide le URL del pagina actual) +pdfjs-bookmark-button-label = Pagina actual + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Instrumentos +pdfjs-tools-button-label = Instrumentos +pdfjs-first-page-button = + .title = Ir al prime pagina +pdfjs-first-page-button-label = Ir al prime pagina +pdfjs-last-page-button = + .title = Ir al ultime pagina +pdfjs-last-page-button-label = Ir al ultime pagina +pdfjs-page-rotate-cw-button = + .title = Rotar in senso horari +pdfjs-page-rotate-cw-button-label = Rotar in senso horari +pdfjs-page-rotate-ccw-button = + .title = Rotar in senso antihorari +pdfjs-page-rotate-ccw-button-label = Rotar in senso antihorari +pdfjs-cursor-text-select-tool-button = + .title = Activar le instrumento de selection de texto +pdfjs-cursor-text-select-tool-button-label = Instrumento de selection de texto +pdfjs-cursor-hand-tool-button = + .title = Activar le instrumento mano +pdfjs-cursor-hand-tool-button-label = Instrumento mano +pdfjs-scroll-page-button = + .title = Usar rolamento de pagina +pdfjs-scroll-page-button-label = Rolamento de pagina +pdfjs-scroll-vertical-button = + .title = Usar rolamento vertical +pdfjs-scroll-vertical-button-label = Rolamento vertical +pdfjs-scroll-horizontal-button = + .title = Usar rolamento horizontal +pdfjs-scroll-horizontal-button-label = Rolamento horizontal +pdfjs-scroll-wrapped-button = + .title = Usar rolamento incapsulate +pdfjs-scroll-wrapped-button-label = Rolamento incapsulate +pdfjs-spread-none-button = + .title = Non junger paginas dual +pdfjs-spread-none-button-label = Sin paginas dual +pdfjs-spread-odd-button = + .title = Junger paginas dual a partir de paginas con numeros impar +pdfjs-spread-odd-button-label = Paginas dual impar +pdfjs-spread-even-button = + .title = Junger paginas dual a partir de paginas con numeros par +pdfjs-spread-even-button-label = Paginas dual par + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Proprietates del documento… +pdfjs-document-properties-button-label = Proprietates del documento… +pdfjs-document-properties-file-name = Nomine del file: +pdfjs-document-properties-file-size = Dimension de file: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Titulo: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Subjecto: +pdfjs-document-properties-keywords = Parolas clave: +pdfjs-document-properties-creation-date = Data de creation: +pdfjs-document-properties-modification-date = Data de modification: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Creator: +pdfjs-document-properties-producer = Productor PDF: +pdfjs-document-properties-version = Version PDF: +pdfjs-document-properties-page-count = Numero de paginas: +pdfjs-document-properties-page-size = Dimension del pagina: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = vertical +pdfjs-document-properties-page-size-orientation-landscape = horizontal +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Littera +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Vista web rapide: +pdfjs-document-properties-linearized-yes = Si +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Clauder + +## Print + +pdfjs-print-progress-message = Preparation del documento pro le impression… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cancellar +pdfjs-printing-not-supported = Attention : le impression non es totalmente supportate per ce navigator. +pdfjs-printing-not-ready = Attention: le file PDF non es integremente cargate pro lo poter imprimer. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Monstrar/celar le barra lateral +pdfjs-toggle-sidebar-notification-button = + .title = Monstrar/celar le barra lateral (le documento contine structura/attachamentos/stratos) +pdfjs-toggle-sidebar-button-label = Monstrar/celar le barra lateral +pdfjs-document-outline-button = + .title = Monstrar le schema del documento (clic duple pro expander/contraher tote le elementos) +pdfjs-document-outline-button-label = Schema del documento +pdfjs-attachments-button = + .title = Monstrar le annexos +pdfjs-attachments-button-label = Annexos +pdfjs-layers-button = + .title = Monstrar stratos (clicca duple pro remontar tote le stratos al stato predefinite) +pdfjs-layers-button-label = Stratos +pdfjs-thumbs-button = + .title = Monstrar le vignettes +pdfjs-thumbs-button-label = Vignettes +pdfjs-current-outline-item-button = + .title = Trovar le elemento de structura actual +pdfjs-current-outline-item-button-label = Elemento de structura actual +pdfjs-findbar-button = + .title = Cercar in le documento +pdfjs-findbar-button-label = Cercar +pdfjs-additional-layers = Altere stratos + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pagina { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Vignette del pagina { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Cercar + .placeholder = Cercar in le documento… +pdfjs-find-previous-button = + .title = Trovar le previe occurrentia del phrase +pdfjs-find-previous-button-label = Previe +pdfjs-find-next-button = + .title = Trovar le successive occurrentia del phrase +pdfjs-find-next-button-label = Sequente +pdfjs-find-highlight-checkbox = Evidentiar toto +pdfjs-find-match-case-checkbox-label = Distinguer majusculas/minusculas +pdfjs-find-match-diacritics-checkbox-label = Differentiar diacriticos +pdfjs-find-entire-word-checkbox-label = Parolas integre +pdfjs-find-reached-top = Initio del documento attingite, continuation ab fin +pdfjs-find-reached-bottom = Fin del documento attingite, continuation ab initio +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } de { $total } correspondentia + *[other] { $current } de { $total } correspondentias + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Plus de { $limit } correspondentia + *[other] Plus de { $limit } correspondentias + } +pdfjs-find-not-found = Phrase non trovate + +## Predefined zoom values + +pdfjs-page-scale-width = Plen largor del pagina +pdfjs-page-scale-fit = Pagina integre +pdfjs-page-scale-auto = Zoom automatic +pdfjs-page-scale-actual = Dimension real +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Pagina { $page } + +## Loading indicator messages + +pdfjs-loading-error = Un error occurreva durante que on cargava le file PDF. +pdfjs-invalid-file-error = File PDF corrumpite o non valide. +pdfjs-missing-file-error = File PDF mancante. +pdfjs-unexpected-response-error = Responsa del servitor inexpectate. +pdfjs-rendering-error = Un error occurreva durante que on processava le pagina. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Insere le contrasigno pro aperir iste file PDF. +pdfjs-password-invalid = Contrasigno invalide. Per favor retenta. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Cancellar +pdfjs-web-fonts-disabled = Le typos de litteras web es disactivate: impossibile usar le typos de litteras PDF incorporate. + +## Editing + +pdfjs-editor-free-text-button = + .title = Texto +pdfjs-editor-free-text-button-label = Texto +pdfjs-editor-ink-button = + .title = Designar +pdfjs-editor-ink-button-label = Designar +pdfjs-editor-stamp-button = + .title = Adder o rediger imagines +pdfjs-editor-stamp-button-label = Adder o rediger imagines +pdfjs-editor-highlight-button = + .title = Evidentia +pdfjs-editor-highlight-button-label = Evidentia +pdfjs-highlight-floating-button1 = + .title = Evidentiar + .aria-label = Evidentiar +pdfjs-highlight-floating-button-label = Evidentiar + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Remover le designo +pdfjs-editor-remove-freetext-button = + .title = Remover texto +pdfjs-editor-remove-stamp-button = + .title = Remover imagine +pdfjs-editor-remove-highlight-button = + .title = Remover evidentia + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Color +pdfjs-editor-free-text-size-input = Dimension +pdfjs-editor-ink-color-input = Color +pdfjs-editor-ink-thickness-input = Spissor +pdfjs-editor-ink-opacity-input = Opacitate +pdfjs-editor-stamp-add-image-button = + .title = Adder imagine +pdfjs-editor-stamp-add-image-button-label = Adder imagine +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Spissor +pdfjs-editor-free-highlight-thickness-title = + .title = Cambiar spissor evidentiante elementos differente de texto +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Initiar a inserer… +pdfjs-free-text = + .aria-label = Editor de texto +pdfjs-free-text-default-content = Comenciar a scriber… +pdfjs-ink = + .aria-label = Editor de designos +pdfjs-ink-canvas = + .aria-label = Imagine create per le usator + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Texto alternative +pdfjs-editor-alt-text-edit-button = + .aria-label = Rediger texto alternative +pdfjs-editor-alt-text-edit-button-label = Rediger texto alternative +pdfjs-editor-alt-text-dialog-label = Elige un option +pdfjs-editor-alt-text-dialog-description = Le texto alternative (alt text) adjuta quando le personas non pote vider le imagine o quando illo non carga. +pdfjs-editor-alt-text-add-description-label = Adder un description +pdfjs-editor-alt-text-add-description-description = Mira a 1-2 phrases que describe le subjecto, parametro, o actiones. +pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorative +pdfjs-editor-alt-text-mark-decorative-description = Isto es usate pro imagines ornamental, como bordaturas o filigranas. +pdfjs-editor-alt-text-cancel-button = Cancellar +pdfjs-editor-alt-text-save-button = Salvar +pdfjs-editor-alt-text-decorative-tooltip = Marcate como decorative +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Per exemplo, “Un juvene sede a un tabula pro mangiar un repasto” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternative + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Angulo superior sinistre — redimensionar +pdfjs-editor-resizer-label-top-middle = Medio superior — redimensionar +pdfjs-editor-resizer-label-top-right = Angulo superior dextre — redimensionar +pdfjs-editor-resizer-label-middle-right = Medio dextre — redimensionar +pdfjs-editor-resizer-label-bottom-right = Angulo inferior dextre — redimensionar +pdfjs-editor-resizer-label-bottom-middle = Medio inferior — redimensionar +pdfjs-editor-resizer-label-bottom-left = Angulo inferior sinistre — redimensionar +pdfjs-editor-resizer-label-middle-left = Medio sinistre — redimensionar +pdfjs-editor-resizer-top-left = + .aria-label = Angulo superior sinistre — redimensionar +pdfjs-editor-resizer-top-middle = + .aria-label = Medio superior — redimensionar +pdfjs-editor-resizer-top-right = + .aria-label = Angulo superior dextre — redimensionar +pdfjs-editor-resizer-middle-right = + .aria-label = Medio dextre — redimensionar +pdfjs-editor-resizer-bottom-right = + .aria-label = Angulo inferior dextre — redimensionar +pdfjs-editor-resizer-bottom-middle = + .aria-label = Medio inferior — redimensionar +pdfjs-editor-resizer-bottom-left = + .aria-label = Angulo inferior sinistre — redimensionar +pdfjs-editor-resizer-middle-left = + .aria-label = Medio sinistre — redimensionar + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Color pro evidentiar +pdfjs-editor-colorpicker-button = + .title = Cambiar color +pdfjs-editor-colorpicker-dropdown = + .aria-label = Electiones del color +pdfjs-editor-colorpicker-yellow = + .title = Jalne +pdfjs-editor-colorpicker-green = + .title = Verde +pdfjs-editor-colorpicker-blue = + .title = Blau +pdfjs-editor-colorpicker-pink = + .title = Rosate +pdfjs-editor-colorpicker-red = + .title = Rubie + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Monstrar toto +pdfjs-editor-highlight-show-all-button = + .title = Monstrar toto + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Rediger texto alternative (description del imagine) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Adder texto alternative (description del imagine) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Scribe tu description ci… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Breve description pro personas qui non pote vider le imagine o quando le imagine non se carga. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Iste texto alternative ha essite create automaticamente e pote esser inexacte. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Pro saper plus +pdfjs-editor-new-alt-text-create-automatically-button-label = Crear texto alternative automaticamente +pdfjs-editor-new-alt-text-not-now-button = Non ora +pdfjs-editor-new-alt-text-error-title = Impossibile crear texto alternative automaticamente +pdfjs-editor-new-alt-text-error-description = Scribe tu proprie texto alternative o retenta plus tarde. +pdfjs-editor-new-alt-text-error-close-button = Clauder +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Discargante modello de intelligentia artificial del texto alternative ({ $downloadedSize } de { $totalSize } MB) + .aria-valuetext = Discargante modello de intelligentia artificial del texto alternative ({ $downloadedSize } de { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Texto alternative addite +pdfjs-editor-new-alt-text-added-button-label = Texto alternative addite +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Texto alternative mancante +pdfjs-editor-new-alt-text-missing-button-label = Texto alternative mancante +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Revider texto alternative +pdfjs-editor-new-alt-text-to-review-button-label = Revider texto alternative +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Automaticamente create: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Parametros del texto alternative del imagine +pdfjs-image-alt-text-settings-button-label = Parametros del texto alternative del imagine +pdfjs-editor-alt-text-settings-dialog-label = Parametros del texto alternative del imagine +pdfjs-editor-alt-text-settings-automatic-title = Texto alternative automatic +pdfjs-editor-alt-text-settings-create-model-button-label = Crear texto alternative automaticamente +pdfjs-editor-alt-text-settings-create-model-description = Suggere descriptiones pro adjutar le personas qui non pote vider le imagine o quando le imagine non carga. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Modello de intelligentia artificial del texto alternative ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Flue localmente sur tu apparato assi tu datos remane private. Necessari pro texto alternative automatic. +pdfjs-editor-alt-text-settings-delete-model-button = Deler +pdfjs-editor-alt-text-settings-download-model-button = Discargar +pdfjs-editor-alt-text-settings-downloading-model-button = Discargante… +pdfjs-editor-alt-text-settings-editor-title = Rediger texto alternative +pdfjs-editor-alt-text-settings-show-dialog-button-label = Monstrar le redactor de texto alternative a pena on adde un imagine +pdfjs-editor-alt-text-settings-show-dialog-description = Te adjuta a verifica que tote tu imagines ha un texto alternative. +pdfjs-editor-alt-text-settings-close-button = Clauder + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Evidentiation removite +pdfjs-editor-undo-bar-message-freetext = Texto removite +pdfjs-editor-undo-bar-message-ink = Designo removite +pdfjs-editor-undo-bar-message-stamp = Imagine removite +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotation removite + *[other] { $count } annotationes removite + } +pdfjs-editor-undo-bar-undo-button = + .title = Disfacer +pdfjs-editor-undo-bar-undo-button-label = Disfacer +pdfjs-editor-undo-bar-close-button = + .title = Clauder +pdfjs-editor-undo-bar-close-button-label = Clauder diff --git a/public/pdfjs/web/locale/id/viewer.ftl b/public/pdfjs/web/locale/id/viewer.ftl new file mode 100644 index 0000000..c985a33 --- /dev/null +++ b/public/pdfjs/web/locale/id/viewer.ftl @@ -0,0 +1,374 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Laman Sebelumnya +pdfjs-previous-button-label = Sebelumnya +pdfjs-next-button = + .title = Laman Selanjutnya +pdfjs-next-button-label = Selanjutnya +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Halaman +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = dari { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } dari { $pagesCount }) +pdfjs-zoom-out-button = + .title = Perkecil +pdfjs-zoom-out-button-label = Perkecil +pdfjs-zoom-in-button = + .title = Perbesar +pdfjs-zoom-in-button-label = Perbesar +pdfjs-zoom-select = + .title = Perbesaran +pdfjs-presentation-mode-button = + .title = Ganti ke Mode Presentasi +pdfjs-presentation-mode-button-label = Mode Presentasi +pdfjs-open-file-button = + .title = Buka Berkas +pdfjs-open-file-button-label = Buka +pdfjs-print-button = + .title = Cetak +pdfjs-print-button-label = Cetak +pdfjs-save-button = + .title = Simpan +pdfjs-save-button-label = Simpan +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Unduh +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Unduh +pdfjs-bookmark-button = + .title = Laman Saat Ini (Lihat URL dari Laman Sekarang) +pdfjs-bookmark-button-label = Laman Saat Ini + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Alat +pdfjs-tools-button-label = Alat +pdfjs-first-page-button = + .title = Buka Halaman Pertama +pdfjs-first-page-button-label = Buka Halaman Pertama +pdfjs-last-page-button = + .title = Buka Halaman Terakhir +pdfjs-last-page-button-label = Buka Halaman Terakhir +pdfjs-page-rotate-cw-button = + .title = Putar Searah Jarum Jam +pdfjs-page-rotate-cw-button-label = Putar Searah Jarum Jam +pdfjs-page-rotate-ccw-button = + .title = Putar Berlawanan Arah Jarum Jam +pdfjs-page-rotate-ccw-button-label = Putar Berlawanan Arah Jarum Jam +pdfjs-cursor-text-select-tool-button = + .title = Aktifkan Alat Seleksi Teks +pdfjs-cursor-text-select-tool-button-label = Alat Seleksi Teks +pdfjs-cursor-hand-tool-button = + .title = Aktifkan Alat Tangan +pdfjs-cursor-hand-tool-button-label = Alat Tangan +pdfjs-scroll-page-button = + .title = Gunakan Pengguliran Laman +pdfjs-scroll-page-button-label = Pengguliran Laman +pdfjs-scroll-vertical-button = + .title = Gunakan Penggeseran Vertikal +pdfjs-scroll-vertical-button-label = Penggeseran Vertikal +pdfjs-scroll-horizontal-button = + .title = Gunakan Penggeseran Horizontal +pdfjs-scroll-horizontal-button-label = Penggeseran Horizontal +pdfjs-scroll-wrapped-button = + .title = Gunakan Penggeseran Terapit +pdfjs-scroll-wrapped-button-label = Penggeseran Terapit +pdfjs-spread-none-button = + .title = Jangan gabungkan lembar halaman +pdfjs-spread-none-button-label = Tidak Ada Lembaran +pdfjs-spread-odd-button = + .title = Gabungkan lembar lamanan mulai dengan halaman ganjil +pdfjs-spread-odd-button-label = Lembaran Ganjil +pdfjs-spread-even-button = + .title = Gabungkan lembar halaman dimulai dengan halaman genap +pdfjs-spread-even-button-label = Lembaran Genap + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Properti Dokumen… +pdfjs-document-properties-button-label = Properti Dokumen… +pdfjs-document-properties-file-name = Nama berkas: +pdfjs-document-properties-file-size = Ukuran berkas: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } byte) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } byte) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } byte) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } byte) +pdfjs-document-properties-title = Judul: +pdfjs-document-properties-author = Penyusun: +pdfjs-document-properties-subject = Subjek: +pdfjs-document-properties-keywords = Kata Kunci: +pdfjs-document-properties-creation-date = Tanggal Dibuat: +pdfjs-document-properties-modification-date = Tanggal Dimodifikasi: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Pembuat: +pdfjs-document-properties-producer = Pemroduksi PDF: +pdfjs-document-properties-version = Versi PDF: +pdfjs-document-properties-page-count = Jumlah Halaman: +pdfjs-document-properties-page-size = Ukuran Laman: +pdfjs-document-properties-page-size-unit-inches = inci +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = tegak +pdfjs-document-properties-page-size-orientation-landscape = mendatar +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Tampilan Web Kilat: +pdfjs-document-properties-linearized-yes = Ya +pdfjs-document-properties-linearized-no = Tidak +pdfjs-document-properties-close-button = Tutup + +## Print + +pdfjs-print-progress-message = Menyiapkan dokumen untuk pencetakan… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Batalkan +pdfjs-printing-not-supported = Peringatan: Pencetakan tidak didukung secara lengkap pada peramban ini. +pdfjs-printing-not-ready = Peringatan: Berkas PDF masih belum dimuat secara lengkap untuk dapat dicetak. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Aktif/Nonaktifkan Bilah Samping +pdfjs-toggle-sidebar-notification-button = + .title = Aktif/Nonaktifkan Bilah Samping (dokumen berisi kerangka/lampiran/lapisan) +pdfjs-toggle-sidebar-button-label = Aktif/Nonaktifkan Bilah Samping +pdfjs-document-outline-button = + .title = Tampilkan Kerangka Dokumen (klik ganda untuk membentangkan/menciutkan semua item) +pdfjs-document-outline-button-label = Kerangka Dokumen +pdfjs-attachments-button = + .title = Tampilkan Lampiran +pdfjs-attachments-button-label = Lampiran +pdfjs-layers-button = + .title = Tampilkan Lapisan (klik ganda untuk mengatur ulang semua lapisan ke keadaan baku) +pdfjs-layers-button-label = Lapisan +pdfjs-thumbs-button = + .title = Tampilkan Miniatur +pdfjs-thumbs-button-label = Miniatur +pdfjs-current-outline-item-button = + .title = Cari Butir Ikhtisar Saat Ini +pdfjs-current-outline-item-button-label = Butir Ikhtisar Saat Ini +pdfjs-findbar-button = + .title = Temukan di Dokumen +pdfjs-findbar-button-label = Temukan +pdfjs-additional-layers = Lapisan Tambahan + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Laman { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatur Laman { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Temukan + .placeholder = Temukan di dokumen… +pdfjs-find-previous-button = + .title = Temukan kata sebelumnya +pdfjs-find-previous-button-label = Sebelumnya +pdfjs-find-next-button = + .title = Temukan lebih lanjut +pdfjs-find-next-button-label = Selanjutnya +pdfjs-find-highlight-checkbox = Sorot semuanya +pdfjs-find-match-case-checkbox-label = Cocokkan BESAR/kecil +pdfjs-find-match-diacritics-checkbox-label = Pencocokan Diakritik +pdfjs-find-entire-word-checkbox-label = Seluruh teks +pdfjs-find-reached-top = Sampai di awal dokumen, dilanjutkan dari bawah +pdfjs-find-reached-bottom = Sampai di akhir dokumen, dilanjutkan dari atas +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = { $current } dari { $total } yang cocok +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = Lebih dari { $limit } kecocokan +pdfjs-find-not-found = Frasa tidak ditemukan + +## Predefined zoom values + +pdfjs-page-scale-width = Lebar Laman +pdfjs-page-scale-fit = Muat Laman +pdfjs-page-scale-auto = Perbesaran Otomatis +pdfjs-page-scale-actual = Ukuran Asli +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Halaman { $page } + +## Loading indicator messages + +pdfjs-loading-error = Galat terjadi saat memuat PDF. +pdfjs-invalid-file-error = Berkas PDF tidak valid atau rusak. +pdfjs-missing-file-error = Berkas PDF tidak ada. +pdfjs-unexpected-response-error = Balasan server yang tidak diharapkan. +pdfjs-rendering-error = Galat terjadi saat merender laman. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anotasi { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Masukkan sandi untuk membuka berkas PDF ini. +pdfjs-password-invalid = Sandi tidak valid. Silakan coba lagi. +pdfjs-password-ok-button = Oke +pdfjs-password-cancel-button = Batal +pdfjs-web-fonts-disabled = Font web dinonaktifkan: tidak dapat menggunakan font PDF yang tersemat. + +## Editing + +pdfjs-editor-free-text-button = + .title = Teks +pdfjs-editor-free-text-button-label = Teks +pdfjs-editor-ink-button = + .title = Gambar +pdfjs-editor-ink-button-label = Gambar +pdfjs-editor-stamp-button = + .title = Tambah atau edit gambar +pdfjs-editor-stamp-button-label = Tambah atau edit gambar +pdfjs-editor-highlight-button = + .title = Sorot +pdfjs-editor-highlight-button-label = Sorot +pdfjs-highlight-floating-button1 = + .title = Sorot + .aria-label = Sorot +pdfjs-highlight-floating-button-label = Sorot + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Hapus gambar +pdfjs-editor-remove-freetext-button = + .title = Hapus teks +pdfjs-editor-remove-stamp-button = + .title = Hapus gambar +pdfjs-editor-remove-highlight-button = + .title = Hapus sorotan + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Warna +pdfjs-editor-free-text-size-input = Ukuran +pdfjs-editor-ink-color-input = Warna +pdfjs-editor-ink-thickness-input = Ketebalan +pdfjs-editor-ink-opacity-input = Opasitas +pdfjs-editor-stamp-add-image-button = + .title = Tambahkan gambar +pdfjs-editor-stamp-add-image-button-label = Tambahkan gambar +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Ketebalan +pdfjs-editor-free-highlight-thickness-title = + .title = Ubah ketebalan saat menyorot item selain teks +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor Teks + .default-content = Mulai mengetik… +pdfjs-free-text = + .aria-label = Editor Teks +pdfjs-free-text-default-content = Mulai mengetik… +pdfjs-ink = + .aria-label = Editor Gambar +pdfjs-ink-canvas = + .aria-label = Gambar yang dibuat pengguna + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Teks alternatif +pdfjs-editor-alt-text-edit-button = + .aria-label = Edit teks alternatif +pdfjs-editor-alt-text-edit-button-label = Edit teks alternatif +pdfjs-editor-alt-text-dialog-label = Pilih opsi + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + diff --git a/public/pdfjs/web/locale/is/viewer.ftl b/public/pdfjs/web/locale/is/viewer.ftl new file mode 100644 index 0000000..deda510 --- /dev/null +++ b/public/pdfjs/web/locale/is/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Fyrri síða +pdfjs-previous-button-label = Fyrri +pdfjs-next-button = + .title = Næsta síða +pdfjs-next-button-label = Næsti +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Síða +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = af { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } af { $pagesCount }) +pdfjs-zoom-out-button = + .title = Minnka aðdrátt +pdfjs-zoom-out-button-label = Minnka aðdrátt +pdfjs-zoom-in-button = + .title = Auka aðdrátt +pdfjs-zoom-in-button-label = Auka aðdrátt +pdfjs-zoom-select = + .title = Aðdráttur +pdfjs-presentation-mode-button = + .title = Skipta yfir á kynningarham +pdfjs-presentation-mode-button-label = Kynningarhamur +pdfjs-open-file-button = + .title = Opna skrá +pdfjs-open-file-button-label = Opna +pdfjs-print-button = + .title = Prenta +pdfjs-print-button-label = Prenta +pdfjs-save-button = + .title = Vista +pdfjs-save-button-label = Vista +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Sækja +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Sækja +pdfjs-bookmark-button = + .title = Núverandi síða (Skoða vefslóð frá núverandi síðu) +pdfjs-bookmark-button-label = Núverandi síða + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Verkfæri +pdfjs-tools-button-label = Verkfæri +pdfjs-first-page-button = + .title = Fara á fyrstu síðu +pdfjs-first-page-button-label = Fara á fyrstu síðu +pdfjs-last-page-button = + .title = Fara á síðustu síðu +pdfjs-last-page-button-label = Fara á síðustu síðu +pdfjs-page-rotate-cw-button = + .title = Snúa réttsælis +pdfjs-page-rotate-cw-button-label = Snúa réttsælis +pdfjs-page-rotate-ccw-button = + .title = Snúa rangsælis +pdfjs-page-rotate-ccw-button-label = Snúa rangsælis +pdfjs-cursor-text-select-tool-button = + .title = Virkja textavalsáhald +pdfjs-cursor-text-select-tool-button-label = Textavalsáhald +pdfjs-cursor-hand-tool-button = + .title = Virkja handarverkfæri +pdfjs-cursor-hand-tool-button-label = Handarverkfæri +pdfjs-scroll-page-button = + .title = Nota síðuskrun +pdfjs-scroll-page-button-label = Síðuskrun +pdfjs-scroll-vertical-button = + .title = Nota lóðrétt skrun +pdfjs-scroll-vertical-button-label = Lóðrétt skrun +pdfjs-scroll-horizontal-button = + .title = Nota lárétt skrun +pdfjs-scroll-horizontal-button-label = Lárétt skrun +pdfjs-scroll-wrapped-button = + .title = Nota línuskipt síðuskrun +pdfjs-scroll-wrapped-button-label = Línuskipt síðuskrun +pdfjs-spread-none-button = + .title = Ekki taka þátt í dreifingu síðna +pdfjs-spread-none-button-label = Engin dreifing +pdfjs-spread-odd-button = + .title = Taka þátt í dreifingu síðna með oddatölum +pdfjs-spread-odd-button-label = Oddatöludreifing +pdfjs-spread-even-button = + .title = Taktu þátt í dreifingu síðna með jöfnuntölum +pdfjs-spread-even-button-label = Jafnatöludreifing + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Eiginleikar skjals… +pdfjs-document-properties-button-label = Eiginleikar skjals… +pdfjs-document-properties-file-name = Skráarnafn: +pdfjs-document-properties-file-size = Skrárstærð: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bæti) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bæti) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Titill: +pdfjs-document-properties-author = Hönnuður: +pdfjs-document-properties-subject = Efni: +pdfjs-document-properties-keywords = Stikkorð: +pdfjs-document-properties-creation-date = Búið til: +pdfjs-document-properties-modification-date = Dags breytingar: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Höfundur: +pdfjs-document-properties-producer = PDF framleiðandi: +pdfjs-document-properties-version = PDF útgáfa: +pdfjs-document-properties-page-count = Blaðsíðufjöldi: +pdfjs-document-properties-page-size = Stærð síðu: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = skammsnið +pdfjs-document-properties-page-size-orientation-landscape = langsnið +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Fljótleg vefskoðun: +pdfjs-document-properties-linearized-yes = Já +pdfjs-document-properties-linearized-no = Nei +pdfjs-document-properties-close-button = Loka + +## Print + +pdfjs-print-progress-message = Undirbý skjal fyrir prentun… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Hætta við +pdfjs-printing-not-supported = Aðvörun: Prentun er ekki með fyllilegan stuðning á þessum vafra. +pdfjs-printing-not-ready = Aðvörun: Ekki er búið að hlaða inn allri PDF skránni fyrir prentun. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Víxla hliðarspjaldi af/á +pdfjs-toggle-sidebar-notification-button = + .title = Víxla hliðarslá (skjal inniheldur yfirlit/viðhengi/lög) +pdfjs-toggle-sidebar-button-label = Víxla hliðarspjaldi af/á +pdfjs-document-outline-button = + .title = Sýna yfirlit skjals (tvísmelltu til að opna/loka öllum hlutum) +pdfjs-document-outline-button-label = Efnisskipan skjals +pdfjs-attachments-button = + .title = Sýna viðhengi +pdfjs-attachments-button-label = Viðhengi +pdfjs-layers-button = + .title = Birta lög (tvísmelltu til að endurstilla öll lög í sjálfgefna stöðu) +pdfjs-layers-button-label = Lög +pdfjs-thumbs-button = + .title = Sýna smámyndir +pdfjs-thumbs-button-label = Smámyndir +pdfjs-current-outline-item-button = + .title = Finna núverandi atriði efnisskipunar +pdfjs-current-outline-item-button-label = Núverandi atriði efnisskipunar +pdfjs-findbar-button = + .title = Leita í skjali +pdfjs-findbar-button-label = Leita +pdfjs-additional-layers = Viðbótarlög + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Síða { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Smámynd af síðu { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Leita + .placeholder = Leita í skjali… +pdfjs-find-previous-button = + .title = Leita að fyrra tilfelli þessara orða +pdfjs-find-previous-button-label = Fyrri +pdfjs-find-next-button = + .title = Leita að næsta tilfelli þessara orða +pdfjs-find-next-button-label = Næsti +pdfjs-find-highlight-checkbox = Lita allt +pdfjs-find-match-case-checkbox-label = Passa við stafstöðu +pdfjs-find-match-diacritics-checkbox-label = Passa við broddstafi +pdfjs-find-entire-word-checkbox-label = Heil orð +pdfjs-find-reached-top = Náði efst í skjal, held áfram neðst +pdfjs-find-reached-bottom = Náði enda skjals, held áfram efst +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } af { $total } passar við + *[other] { $current } af { $total } passa við + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Fleiri en { $limit } passar við + *[other] Fleiri en { $limit } passa við + } +pdfjs-find-not-found = Fann ekki orðið + +## Predefined zoom values + +pdfjs-page-scale-width = Síðubreidd +pdfjs-page-scale-fit = Passa á síðu +pdfjs-page-scale-auto = Sjálfvirkur aðdráttur +pdfjs-page-scale-actual = Raunstærð +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Síða { $page } + +## Loading indicator messages + +pdfjs-loading-error = Villa kom upp við að hlaða inn PDF. +pdfjs-invalid-file-error = Ógild eða skemmd PDF skrá. +pdfjs-missing-file-error = Vantar PDF skrá. +pdfjs-unexpected-response-error = Óvænt svar frá netþjóni. +pdfjs-rendering-error = Upp kom villa við að birta síðuna. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Skýring] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Settu inn lykilorð til að opna þessa PDF-skrá. +pdfjs-password-invalid = Ógilt lykilorð. Reyndu aftur. +pdfjs-password-ok-button = Í lagi +pdfjs-password-cancel-button = Hætta við +pdfjs-web-fonts-disabled = Vef leturgerðir eru óvirkar: get ekki notað innbyggðar PDF leturgerðir. + +## Editing + +pdfjs-editor-free-text-button = + .title = Texti +pdfjs-editor-free-text-button-label = Texti +pdfjs-editor-ink-button = + .title = Teikna +pdfjs-editor-ink-button-label = Teikna +pdfjs-editor-stamp-button = + .title = Bæta við eða breyta myndum +pdfjs-editor-stamp-button-label = Bæta við eða breyta myndum +pdfjs-editor-highlight-button = + .title = Áherslulita +pdfjs-editor-highlight-button-label = Áherslulita +pdfjs-highlight-floating-button1 = + .title = Áherslulita + .aria-label = Áherslulita +pdfjs-highlight-floating-button-label = Áherslulita + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Fjarlægja teikningu +pdfjs-editor-remove-freetext-button = + .title = Fjarlægja texta +pdfjs-editor-remove-stamp-button = + .title = Fjarlægja mynd +pdfjs-editor-remove-highlight-button = + .title = Fjarlægja áherslulit + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Litur +pdfjs-editor-free-text-size-input = Stærð +pdfjs-editor-ink-color-input = Litur +pdfjs-editor-ink-thickness-input = Þykkt +pdfjs-editor-ink-opacity-input = Ógegnsæi +pdfjs-editor-stamp-add-image-button = + .title = Bæta við mynd +pdfjs-editor-stamp-add-image-button-label = Bæta við mynd +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Þykkt +pdfjs-editor-free-highlight-thickness-title = + .title = Breyta þykkt við áherslulitun annarra atriða en texta +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Textaritill + .default-content = Byrjaðu að skrifa… +pdfjs-free-text = + .aria-label = Textaritill +pdfjs-free-text-default-content = Byrjaðu að skrifa… +pdfjs-ink = + .aria-label = Teikniritill +pdfjs-ink-canvas = + .aria-label = Mynd gerð af notanda + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alt-varatexti +pdfjs-editor-alt-text-edit-button = + .aria-label = Breyta alt-myndatexta +pdfjs-editor-alt-text-edit-button-label = Breyta alt-varatexta +pdfjs-editor-alt-text-dialog-label = Veldu valkost +pdfjs-editor-alt-text-dialog-description = Alt-varatexti (auka-myndatexti) hjálpar þegar fólk getur ekki séð myndina eða þegar hún hleðst ekki inn. +pdfjs-editor-alt-text-add-description-label = Bættu við lýsingu +pdfjs-editor-alt-text-add-description-description = Reyndu að takmarka þetta við 1-2 setningar sem lýsa efninu, umhverfi eða aðgerðum. +pdfjs-editor-alt-text-mark-decorative-label = Merkja sem skraut +pdfjs-editor-alt-text-mark-decorative-description = Þetta er notað fyrir skrautmyndir, eins og borða eða vatnsmerki. +pdfjs-editor-alt-text-cancel-button = Hætta við +pdfjs-editor-alt-text-save-button = Vista +pdfjs-editor-alt-text-decorative-tooltip = Merkt sem skraut +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Til dæmis: „Ungur maður sest við borð til að snæða máltíð“ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt-myndatexti + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Efst í vinstra horni - breyta stærð +pdfjs-editor-resizer-label-top-middle = Efst á miðju - breyta stærð +pdfjs-editor-resizer-label-top-right = Efst í hægra horni - breyta stærð +pdfjs-editor-resizer-label-middle-right = Miðja til hægri - breyta stærð +pdfjs-editor-resizer-label-bottom-right = Neðst í hægra horni - breyta stærð +pdfjs-editor-resizer-label-bottom-middle = Neðst á miðju - breyta stærð +pdfjs-editor-resizer-label-bottom-left = Neðst í vinstra horni - breyta stærð +pdfjs-editor-resizer-label-middle-left = Miðja til vinstri - breyta stærð +pdfjs-editor-resizer-top-left = + .aria-label = Efst í vinstra horni - breyta stærð +pdfjs-editor-resizer-top-middle = + .aria-label = Efst á miðju - breyta stærð +pdfjs-editor-resizer-top-right = + .aria-label = Efst í hægra horni - breyta stærð +pdfjs-editor-resizer-middle-right = + .aria-label = Miðja til hægri - breyta stærð +pdfjs-editor-resizer-bottom-right = + .aria-label = Neðst í hægra horni - breyta stærð +pdfjs-editor-resizer-bottom-middle = + .aria-label = Neðst á miðju - breyta stærð +pdfjs-editor-resizer-bottom-left = + .aria-label = Neðst í vinstra horni - breyta stærð +pdfjs-editor-resizer-middle-left = + .aria-label = Miðja til vinstri - breyta stærð + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Áherslulitur +pdfjs-editor-colorpicker-button = + .title = Skipta um lit +pdfjs-editor-colorpicker-dropdown = + .aria-label = Val lita +pdfjs-editor-colorpicker-yellow = + .title = Gult +pdfjs-editor-colorpicker-green = + .title = Grænt +pdfjs-editor-colorpicker-blue = + .title = Blátt +pdfjs-editor-colorpicker-pink = + .title = Bleikt +pdfjs-editor-colorpicker-red = + .title = Rautt + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Birta allt +pdfjs-editor-highlight-show-all-button = + .title = Birta allt + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Breyta alt-myndatexta (lýsingu á mynd) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Bæta við alt-myndatexta (lýsingu á mynd) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Skrifaðu lýsinguna þína hér… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Stutt lýsing fyrir fólk sem getur ekki séð myndina eða þegar myndin hleðst ekki inn. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Þessi alt-myndatexti var búinn til sjálfvirkt og gæti verið ónákvæmur. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Kanna nánar +pdfjs-editor-new-alt-text-create-automatically-button-label = Útbúa alt-myndatexta sjálfvirkt +pdfjs-editor-new-alt-text-not-now-button = Ekki núna +pdfjs-editor-new-alt-text-error-title = Gat ekki búið til alt-myndatexta sjálfkrafa +pdfjs-editor-new-alt-text-error-description = Skrifaðu þinn eiginn alt-myndatexta eða reyndu aftur síðar. +pdfjs-editor-new-alt-text-error-close-button = Loka +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Sækir gervigreindarlíkan með alt-myndatextum ({ $downloadedSize } af { $totalSize } MB) + .aria-valuetext = Sækir gervigreindarlíkan með alt-myndatextum ({ $downloadedSize } af { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alt-myndatexta bætt við +pdfjs-editor-new-alt-text-added-button-label = Alt-myndatexta bætt við +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Vantar alt-myndatexta +pdfjs-editor-new-alt-text-missing-button-label = Vantar alt-myndatexta +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Yfirfara alt-myndatexta +pdfjs-editor-new-alt-text-to-review-button-label = Yfirfara myndatexta +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Útbúið sjálfvirkt: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Stillingar fyrir alt-texta myndar +pdfjs-image-alt-text-settings-button-label = Stillingar fyrir alt-texta myndar +pdfjs-editor-alt-text-settings-dialog-label = Stillingar fyrir alt-texta myndar +pdfjs-editor-alt-text-settings-automatic-title = Sjálfvirkur alt-myndatexti +pdfjs-editor-alt-text-settings-create-model-button-label = Útbúa alt-myndatexta sjálfvirkt +pdfjs-editor-alt-text-settings-create-model-description = Stingur upp á lýsingum til að hjálpa fólki sem getur ekki séð myndina eða þegar myndin hleðst ekki inn. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Gervigreindarlíkan alt-myndatexta ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Keyrir staðbundið á tækinu þínu svo gögnin þín haldast undir þinni stjórn. Nauðsynlegt fyrir sjálfvirka alt-myndatexta. +pdfjs-editor-alt-text-settings-delete-model-button = Eyða +pdfjs-editor-alt-text-settings-download-model-button = Sækja +pdfjs-editor-alt-text-settings-downloading-model-button = Sæki… +pdfjs-editor-alt-text-settings-editor-title = Ritill fyrir alt-myndatexta +pdfjs-editor-alt-text-settings-show-dialog-button-label = Sýna alt-myndatextaritil strax þegar mynd er bætt við +pdfjs-editor-alt-text-settings-show-dialog-description = Hjálpar þér að tryggja að allar myndirnar þínar séu með alt-myndatexta. +pdfjs-editor-alt-text-settings-close-button = Loka + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Áherslulitun fjarlægð +pdfjs-editor-undo-bar-message-freetext = Texti fjarlægður +pdfjs-editor-undo-bar-message-ink = Teikning fjarlægð +pdfjs-editor-undo-bar-message-stamp = Mynd fjarlægð +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } glósa fjarlægð + *[other] { $count } glósur fjarlægðar + } +pdfjs-editor-undo-bar-undo-button = + .title = Afturkalla +pdfjs-editor-undo-bar-undo-button-label = Afturkalla +pdfjs-editor-undo-bar-close-button = + .title = Loka +pdfjs-editor-undo-bar-close-button-label = Loka diff --git a/public/pdfjs/web/locale/it/viewer.ftl b/public/pdfjs/web/locale/it/viewer.ftl new file mode 100644 index 0000000..d1de7e1 --- /dev/null +++ b/public/pdfjs/web/locale/it/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pagina precedente +pdfjs-previous-button-label = Precedente +pdfjs-next-button = + .title = Pagina successiva +pdfjs-next-button-label = Successiva +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pagina +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = di { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } di { $pagesCount }) +pdfjs-zoom-out-button = + .title = Riduci zoom +pdfjs-zoom-out-button-label = Riduci zoom +pdfjs-zoom-in-button = + .title = Aumenta zoom +pdfjs-zoom-in-button-label = Aumenta zoom +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Passa alla modalità presentazione +pdfjs-presentation-mode-button-label = Modalità presentazione +pdfjs-open-file-button = + .title = Apri file +pdfjs-open-file-button-label = Apri +pdfjs-print-button = + .title = Stampa +pdfjs-print-button-label = Stampa +pdfjs-save-button = + .title = Salva +pdfjs-save-button-label = Salva +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Scarica +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Scarica +pdfjs-bookmark-button = + .title = Pagina corrente (mostra URL della pagina corrente) +pdfjs-bookmark-button-label = Pagina corrente + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Strumenti +pdfjs-tools-button-label = Strumenti +pdfjs-first-page-button = + .title = Vai alla prima pagina +pdfjs-first-page-button-label = Vai alla prima pagina +pdfjs-last-page-button = + .title = Vai all’ultima pagina +pdfjs-last-page-button-label = Vai all’ultima pagina +pdfjs-page-rotate-cw-button = + .title = Ruota in senso orario +pdfjs-page-rotate-cw-button-label = Ruota in senso orario +pdfjs-page-rotate-ccw-button = + .title = Ruota in senso antiorario +pdfjs-page-rotate-ccw-button-label = Ruota in senso antiorario +pdfjs-cursor-text-select-tool-button = + .title = Attiva strumento di selezione testo +pdfjs-cursor-text-select-tool-button-label = Strumento di selezione testo +pdfjs-cursor-hand-tool-button = + .title = Attiva strumento mano +pdfjs-cursor-hand-tool-button-label = Strumento mano +pdfjs-scroll-page-button = + .title = Utilizza scorrimento pagine +pdfjs-scroll-page-button-label = Scorrimento pagine +pdfjs-scroll-vertical-button = + .title = Scorri le pagine in verticale +pdfjs-scroll-vertical-button-label = Scorrimento verticale +pdfjs-scroll-horizontal-button = + .title = Scorri le pagine in orizzontale +pdfjs-scroll-horizontal-button-label = Scorrimento orizzontale +pdfjs-scroll-wrapped-button = + .title = Scorri le pagine in verticale, disponendole da sinistra a destra e andando a capo automaticamente +pdfjs-scroll-wrapped-button-label = Scorrimento con a capo automatico +pdfjs-spread-none-button = + .title = Non raggruppare pagine +pdfjs-spread-none-button-label = Nessun raggruppamento +pdfjs-spread-odd-button = + .title = Crea gruppi di pagine che iniziano con numeri di pagina dispari +pdfjs-spread-odd-button-label = Raggruppamento dispari +pdfjs-spread-even-button = + .title = Crea gruppi di pagine che iniziano con numeri di pagina pari +pdfjs-spread-even-button-label = Raggruppamento pari + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Proprietà del documento… +pdfjs-document-properties-button-label = Proprietà del documento… +pdfjs-document-properties-file-name = Nome file: +pdfjs-document-properties-file-size = Dimensione file: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } byte) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } byte) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } byte) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } byte) +pdfjs-document-properties-title = Titolo: +pdfjs-document-properties-author = Autore: +pdfjs-document-properties-subject = Oggetto: +pdfjs-document-properties-keywords = Parole chiave: +pdfjs-document-properties-creation-date = Data creazione: +pdfjs-document-properties-modification-date = Data modifica: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Autore originale: +pdfjs-document-properties-producer = Produttore PDF: +pdfjs-document-properties-version = Versione PDF: +pdfjs-document-properties-page-count = Conteggio pagine: +pdfjs-document-properties-page-size = Dimensioni pagina: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = verticale +pdfjs-document-properties-page-size-orientation-landscape = orizzontale +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Lettera +pdfjs-document-properties-page-size-name-legal = Legale + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Visualizzazione web veloce: +pdfjs-document-properties-linearized-yes = Sì +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Chiudi + +## Print + +pdfjs-print-progress-message = Preparazione documento per la stampa… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Annulla +pdfjs-printing-not-supported = Attenzione: la stampa non è completamente supportata da questo browser. +pdfjs-printing-not-ready = Attenzione: il PDF non è ancora stato caricato completamente per la stampa. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Attiva/disattiva barra laterale +pdfjs-toggle-sidebar-notification-button = + .title = Attiva/disattiva barra laterale (il documento contiene struttura/allegati/livelli) +pdfjs-toggle-sidebar-button-label = Attiva/disattiva barra laterale +pdfjs-document-outline-button = + .title = Visualizza la struttura del documento (doppio clic per visualizzare/comprimere tutti gli elementi) +pdfjs-document-outline-button-label = Struttura documento +pdfjs-attachments-button = + .title = Visualizza allegati +pdfjs-attachments-button-label = Allegati +pdfjs-layers-button = + .title = Visualizza livelli (doppio clic per ripristinare tutti i livelli allo stato predefinito) +pdfjs-layers-button-label = Livelli +pdfjs-thumbs-button = + .title = Mostra le miniature +pdfjs-thumbs-button-label = Miniature +pdfjs-current-outline-item-button = + .title = Trova elemento struttura corrente +pdfjs-current-outline-item-button-label = Elemento struttura corrente +pdfjs-findbar-button = + .title = Trova nel documento +pdfjs-findbar-button-label = Trova +pdfjs-additional-layers = Livelli aggiuntivi + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pagina { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura della pagina { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Trova + .placeholder = Trova nel documento… +pdfjs-find-previous-button = + .title = Trova l’occorrenza precedente del testo da cercare +pdfjs-find-previous-button-label = Precedente +pdfjs-find-next-button = + .title = Trova l’occorrenza successiva del testo da cercare +pdfjs-find-next-button-label = Successivo +pdfjs-find-highlight-checkbox = Evidenzia +pdfjs-find-match-case-checkbox-label = Maiuscole/minuscole +pdfjs-find-match-diacritics-checkbox-label = Segni diacritici +pdfjs-find-entire-word-checkbox-label = Parole intere +pdfjs-find-reached-top = Raggiunto l’inizio della pagina, continua dalla fine +pdfjs-find-reached-bottom = Raggiunta la fine della pagina, continua dall’inizio +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } di { $total } corrispondenza + *[other] { $current } di { $total } corrispondenze + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Più di una { $limit } corrispondenza + *[other] Più di { $limit } corrispondenze + } +pdfjs-find-not-found = Testo non trovato + +## Predefined zoom values + +pdfjs-page-scale-width = Larghezza pagina +pdfjs-page-scale-fit = Adatta a una pagina +pdfjs-page-scale-auto = Zoom automatico +pdfjs-page-scale-actual = Dimensioni effettive +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Pagina { $page } + +## Loading indicator messages + +pdfjs-loading-error = Si è verificato un errore durante il caricamento del PDF. +pdfjs-invalid-file-error = File PDF non valido o danneggiato. +pdfjs-missing-file-error = File PDF non disponibile. +pdfjs-unexpected-response-error = Risposta imprevista del server +pdfjs-rendering-error = Si è verificato un errore durante il rendering della pagina. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Annotazione: { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Inserire la password per aprire questo file PDF. +pdfjs-password-invalid = Password non corretta. Riprovare. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Annulla +pdfjs-web-fonts-disabled = I web font risultano disattivati: impossibile utilizzare i caratteri incorporati nel PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Testo +pdfjs-editor-free-text-button-label = Testo +pdfjs-editor-ink-button = + .title = Disegno +pdfjs-editor-ink-button-label = Disegno +pdfjs-editor-stamp-button = + .title = Aggiungi o rimuovi immagine +pdfjs-editor-stamp-button-label = Aggiungi o rimuovi immagine +pdfjs-editor-highlight-button = + .title = Evidenzia +pdfjs-editor-highlight-button-label = Evidenzia +pdfjs-highlight-floating-button1 = + .title = Evidenzia + .aria-label = Evidenzia +pdfjs-highlight-floating-button-label = Evidenzia + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Rimuovi disegno +pdfjs-editor-remove-freetext-button = + .title = Rimuovi testo +pdfjs-editor-remove-stamp-button = + .title = Rimuovi immagine +pdfjs-editor-remove-highlight-button = + .title = Rimuovi evidenziazione + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Colore +pdfjs-editor-free-text-size-input = Dimensione +pdfjs-editor-ink-color-input = Colore +pdfjs-editor-ink-thickness-input = Spessore +pdfjs-editor-ink-opacity-input = Opacità +pdfjs-editor-stamp-add-image-button = + .title = Aggiungi immagine +pdfjs-editor-stamp-add-image-button-label = Aggiungi immagine +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Spessore +pdfjs-editor-free-highlight-thickness-title = + .title = Modifica lo spessore della selezione per elementi non testuali +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor di testo + .default-content = Inizia a digitare… +pdfjs-free-text = + .aria-label = Editor di testo +pdfjs-free-text-default-content = Inizia a digitare… +pdfjs-ink = + .aria-label = Editor disegni +pdfjs-ink-canvas = + .aria-label = Immagine creata dall’utente + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Testo alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Modifica testo alternativo +pdfjs-editor-alt-text-edit-button-label = Modifica testo alternativo +pdfjs-editor-alt-text-dialog-label = Scegli un’opzione +pdfjs-editor-alt-text-dialog-description = Il testo alternativo (“alt text”) aiuta quando le persone non possono vedere l’immagine o quando l’immagine non viene caricata. +pdfjs-editor-alt-text-add-description-label = Aggiungi una descrizione +pdfjs-editor-alt-text-add-description-description = Punta a una o due frasi che descrivono l’argomento, l’ambientazione o le azioni. +pdfjs-editor-alt-text-mark-decorative-label = Contrassegna come decorativa +pdfjs-editor-alt-text-mark-decorative-description = Viene utilizzato per immagini ornamentali, come bordi o filigrane. +pdfjs-editor-alt-text-cancel-button = Annulla +pdfjs-editor-alt-text-save-button = Salva +pdfjs-editor-alt-text-decorative-tooltip = Contrassegnata come decorativa +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Ad esempio, “Un giovane si siede a tavola per mangiare” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Testo alternativo + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Angolo in alto a sinistra — ridimensiona +pdfjs-editor-resizer-label-top-middle = Lato superiore nel mezzo — ridimensiona +pdfjs-editor-resizer-label-top-right = Angolo in alto a destra — ridimensiona +pdfjs-editor-resizer-label-middle-right = Lato destro nel mezzo — ridimensiona +pdfjs-editor-resizer-label-bottom-right = Angolo in basso a destra — ridimensiona +pdfjs-editor-resizer-label-bottom-middle = Lato inferiore nel mezzo — ridimensiona +pdfjs-editor-resizer-label-bottom-left = Angolo in basso a sinistra — ridimensiona +pdfjs-editor-resizer-label-middle-left = Lato sinistro nel mezzo — ridimensiona +pdfjs-editor-resizer-top-left = + .aria-label = Angolo in alto a sinistra — ridimensiona +pdfjs-editor-resizer-top-middle = + .aria-label = Lato superiore nel mezzo — ridimensiona +pdfjs-editor-resizer-top-right = + .aria-label = Angolo in alto a destra — ridimensiona +pdfjs-editor-resizer-middle-right = + .aria-label = Lato destro nel mezzo — ridimensiona +pdfjs-editor-resizer-bottom-right = + .aria-label = Angolo in basso a destra — ridimensiona +pdfjs-editor-resizer-bottom-middle = + .aria-label = Lato inferiore nel mezzo — ridimensiona +pdfjs-editor-resizer-bottom-left = + .aria-label = Angolo in basso a sinistra — ridimensiona +pdfjs-editor-resizer-middle-left = + .aria-label = Lato sinistro nel mezzo — ridimensiona + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Colore evidenziatore +pdfjs-editor-colorpicker-button = + .title = Cambia colore +pdfjs-editor-colorpicker-dropdown = + .aria-label = Colori disponibili +pdfjs-editor-colorpicker-yellow = + .title = Giallo +pdfjs-editor-colorpicker-green = + .title = Verde +pdfjs-editor-colorpicker-blue = + .title = Blu +pdfjs-editor-colorpicker-pink = + .title = Rosa +pdfjs-editor-colorpicker-red = + .title = Rosso + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Mostra tutto +pdfjs-editor-highlight-show-all-button = + .title = Mostra tutto + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Modifica testo alternativo (descrizione dell’immagine) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Aggiungi testo alternativo (descrizione dell’immagine) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Scrivi qui la tua descrizione… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Breve descrizione per le persone che non possono vedere l’immagine, o mostrata quando l’immagine non si carica. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Questo testo alternativo è stato creato automaticamente e potrebbe non essere accurato. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Ulteriori informazioni +pdfjs-editor-new-alt-text-create-automatically-button-label = Crea automaticamente testo alternativo +pdfjs-editor-new-alt-text-not-now-button = Non adesso +pdfjs-editor-new-alt-text-error-title = Impossibile creare automaticamente il testo alternativo +pdfjs-editor-new-alt-text-error-description = Scrivi il testo alternativo o riprova più tardi. +pdfjs-editor-new-alt-text-error-close-button = Chiudi +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Download in corso del modello IA per il testo alternativo ({ $downloadedSize } di { $totalSize } MB) + .aria-valuetext = Download in corso del modello IA per il testo alternativo ({ $downloadedSize } di { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Aggiunto testo alternativo +pdfjs-editor-new-alt-text-added-button-label = Aggiunto testo alternativo +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Testo alternativo mancante +pdfjs-editor-new-alt-text-missing-button-label = Testo alternativo mancante +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Verifica testo alternativo +pdfjs-editor-new-alt-text-to-review-button-label = Verifica testo alternativo +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Creato automaticamente: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Impostazioni testo alternativo per le immagini +pdfjs-image-alt-text-settings-button-label = Impostazioni testo alternativo per le immagini +pdfjs-editor-alt-text-settings-dialog-label = Impostazioni testo alternativo per le immagini +pdfjs-editor-alt-text-settings-automatic-title = Testo alternativo automatico +pdfjs-editor-alt-text-settings-create-model-button-label = Crea testo alternativo automaticamente +pdfjs-editor-alt-text-settings-create-model-description = Suggerisce una descrizione per le persone che non possono vedere l’immagine, o mostrata quando l’immagine non si carica. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Modello IA per il testo alternativo ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Viene eseguito localmente sul tuo dispositivo in modo che i tuoi dati rimangano riservati. È richiesto per la generazione automatica del testo alternativo. +pdfjs-editor-alt-text-settings-delete-model-button = Elimina +pdfjs-editor-alt-text-settings-download-model-button = Scarica +pdfjs-editor-alt-text-settings-downloading-model-button = Download… +pdfjs-editor-alt-text-settings-editor-title = Modifica testo alternativo +pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostra l’editor del testo alternativo non appena si aggiunge un’immagine +pdfjs-editor-alt-text-settings-show-dialog-description = Ti aiuta ad assicurarti che tutte le tue immagini abbiano il testo alternativo. +pdfjs-editor-alt-text-settings-close-button = Chiudi + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Evidenziazione rimossa +pdfjs-editor-undo-bar-message-freetext = Testo rimosso +pdfjs-editor-undo-bar-message-ink = Disegno rimosso +pdfjs-editor-undo-bar-message-stamp = Immagine rimossa +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotazione rimossa + *[other] { $count } annotazioni rimosse + } +pdfjs-editor-undo-bar-undo-button = + .title = Annulla +pdfjs-editor-undo-bar-undo-button-label = Annulla +pdfjs-editor-undo-bar-close-button = + .title = Chiudi +pdfjs-editor-undo-bar-close-button-label = Chiudi diff --git a/public/pdfjs/web/locale/ja/viewer.ftl b/public/pdfjs/web/locale/ja/viewer.ftl new file mode 100644 index 0000000..0f37f2a --- /dev/null +++ b/public/pdfjs/web/locale/ja/viewer.ftl @@ -0,0 +1,503 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = 前のページへ戻ります +pdfjs-previous-button-label = 前へ +pdfjs-next-button = + .title = 次のページへ進みます +pdfjs-next-button-label = 次へ +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = ページ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = / { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount }) +pdfjs-zoom-out-button = + .title = 表示を縮小します +pdfjs-zoom-out-button-label = 縮小 +pdfjs-zoom-in-button = + .title = 表示を拡大します +pdfjs-zoom-in-button-label = 拡大 +pdfjs-zoom-select = + .title = 拡大/縮小 +pdfjs-presentation-mode-button = + .title = プレゼンテーションモードに切り替えます +pdfjs-presentation-mode-button-label = プレゼンテーションモード +pdfjs-open-file-button = + .title = ファイルを開きます +pdfjs-open-file-button-label = 開く +pdfjs-print-button = + .title = 印刷します +pdfjs-print-button-label = 印刷 +pdfjs-save-button = + .title = 保存します +pdfjs-save-button-label = 保存 +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = ダウンロードします +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = ダウンロード +pdfjs-bookmark-button = + .title = 現在のページの URL です (現在のページを表示する URL) +pdfjs-bookmark-button-label = 現在のページ + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = ツール +pdfjs-tools-button-label = ツール +pdfjs-first-page-button = + .title = 最初のページへ移動します +pdfjs-first-page-button-label = 最初のページへ移動 +pdfjs-last-page-button = + .title = 最後のページへ移動します +pdfjs-last-page-button-label = 最後のページへ移動 +pdfjs-page-rotate-cw-button = + .title = ページを右へ回転します +pdfjs-page-rotate-cw-button-label = 右回転 +pdfjs-page-rotate-ccw-button = + .title = ページを左へ回転します +pdfjs-page-rotate-ccw-button-label = 左回転 +pdfjs-cursor-text-select-tool-button = + .title = テキスト選択ツールを有効にします +pdfjs-cursor-text-select-tool-button-label = テキスト選択ツール +pdfjs-cursor-hand-tool-button = + .title = 手のひらツールを有効にします +pdfjs-cursor-hand-tool-button-label = 手のひらツール +pdfjs-scroll-page-button = + .title = ページ単位でスクロールします +pdfjs-scroll-page-button-label = ページ単位でスクロール +pdfjs-scroll-vertical-button = + .title = 縦スクロールにします +pdfjs-scroll-vertical-button-label = 縦スクロール +pdfjs-scroll-horizontal-button = + .title = 横スクロールにします +pdfjs-scroll-horizontal-button-label = 横スクロール +pdfjs-scroll-wrapped-button = + .title = 折り返しスクロールにします +pdfjs-scroll-wrapped-button-label = 折り返しスクロール +pdfjs-spread-none-button = + .title = 見開きにしません +pdfjs-spread-none-button-label = 見開きにしない +pdfjs-spread-odd-button = + .title = 奇数ページ開始で見開きにします +pdfjs-spread-odd-button-label = 奇数ページ見開き +pdfjs-spread-even-button = + .title = 偶数ページ開始で見開きにします +pdfjs-spread-even-button-label = 偶数ページ見開き + +## Document properties dialog + +pdfjs-document-properties-button = + .title = 文書のプロパティ... +pdfjs-document-properties-button-label = 文書のプロパティ... +pdfjs-document-properties-file-name = ファイル名: +pdfjs-document-properties-file-size = ファイルサイズ: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } バイト) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } バイト) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } バイト) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } バイト) +pdfjs-document-properties-title = タイトル: +pdfjs-document-properties-author = 作成者: +pdfjs-document-properties-subject = 件名: +pdfjs-document-properties-keywords = キーワード: +pdfjs-document-properties-creation-date = 作成日: +pdfjs-document-properties-modification-date = 更新日: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = アプリケーション: +pdfjs-document-properties-producer = PDF 作成: +pdfjs-document-properties-version = PDF のバージョン: +pdfjs-document-properties-page-count = ページ数: +pdfjs-document-properties-page-size = ページサイズ: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = 縦 +pdfjs-document-properties-page-size-orientation-landscape = 横 +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = レター +pdfjs-document-properties-page-size-name-legal = リーガル + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = ウェブ表示用に最適化: +pdfjs-document-properties-linearized-yes = はい +pdfjs-document-properties-linearized-no = いいえ +pdfjs-document-properties-close-button = 閉じる + +## Print + +pdfjs-print-progress-message = 文書の印刷を準備しています... +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = キャンセル +pdfjs-printing-not-supported = 警告: このブラウザーでは印刷が完全にサポートされていません。 +pdfjs-printing-not-ready = 警告: PDF を印刷するための読み込みが終了していません。 + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = サイドバー表示を切り替えます +pdfjs-toggle-sidebar-notification-button = + .title = サイドバー表示を切り替えます (文書に含まれるアウトライン / 添付 / レイヤー) +pdfjs-toggle-sidebar-button-label = サイドバーの切り替え +pdfjs-document-outline-button = + .title = 文書の目次を表示します (ダブルクリックで項目を開閉します) +pdfjs-document-outline-button-label = 文書の目次 +pdfjs-attachments-button = + .title = 添付ファイルを表示します +pdfjs-attachments-button-label = 添付ファイル +pdfjs-layers-button = + .title = レイヤーを表示します (ダブルクリックですべてのレイヤーが初期状態に戻ります) +pdfjs-layers-button-label = レイヤー +pdfjs-thumbs-button = + .title = 縮小版を表示します +pdfjs-thumbs-button-label = 縮小版 +pdfjs-current-outline-item-button = + .title = 現在のアウトライン項目を検索 +pdfjs-current-outline-item-button-label = 現在のアウトライン項目 +pdfjs-findbar-button = + .title = 文書内を検索します +pdfjs-findbar-button-label = 検索 +pdfjs-additional-layers = 追加レイヤー + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = { $page } ページ +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page } ページの縮小版 + +## Find panel button title and messages + +pdfjs-find-input = + .title = 検索 + .placeholder = 文書内を検索... +pdfjs-find-previous-button = + .title = 現在より前の位置で指定文字列が現れる部分を検索します +pdfjs-find-previous-button-label = 前へ +pdfjs-find-next-button = + .title = 現在より後の位置で指定文字列が現れる部分を検索します +pdfjs-find-next-button-label = 次へ +pdfjs-find-highlight-checkbox = すべて強調表示 +pdfjs-find-match-case-checkbox-label = 大文字/小文字を区別 +pdfjs-find-match-diacritics-checkbox-label = 発音区別符号を区別 +pdfjs-find-entire-word-checkbox-label = 単語一致 +pdfjs-find-reached-top = 文書先頭に到達したので末尾から続けて検索します +pdfjs-find-reached-bottom = 文書末尾に到達したので先頭から続けて検索します +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = { $total } 件中 { $current } 件目 +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = { $limit } 件以上一致 +pdfjs-find-not-found = 見つかりませんでした + +## Predefined zoom values + +pdfjs-page-scale-width = 幅に合わせる +pdfjs-page-scale-fit = ページのサイズに合わせる +pdfjs-page-scale-auto = 自動ズーム +pdfjs-page-scale-actual = 実際のサイズ +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = { $page } ページ + +## Loading indicator messages + +pdfjs-loading-error = PDF の読み込み中にエラーが発生しました。 +pdfjs-invalid-file-error = 無効または破損した PDF ファイル。 +pdfjs-missing-file-error = PDF ファイルが見つかりません。 +pdfjs-unexpected-response-error = サーバーから予期せぬ応答がありました。 +pdfjs-rendering-error = ページのレンダリング中にエラーが発生しました。 + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } 注釈] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = この PDF ファイルを開くためのパスワードを入力してください。 +pdfjs-password-invalid = パスワードが正しくありません。もう一度試してください。 +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = キャンセル +pdfjs-web-fonts-disabled = ウェブフォントが無効になっています: 埋め込まれた PDF のフォントを使用できません。 + +## Editing + +pdfjs-editor-free-text-button = + .title = フリーテキスト注釈を追加します +pdfjs-editor-free-text-button-label = フリーテキスト注釈 +pdfjs-editor-ink-button = + .title = インク注釈を追加します +pdfjs-editor-ink-button-label = インク注釈 +pdfjs-editor-stamp-button = + .title = 画像を追加または編集します +pdfjs-editor-stamp-button-label = 画像を追加または編集 +pdfjs-editor-highlight-button = + .title = 強調します +pdfjs-editor-highlight-button-label = 強調 +pdfjs-highlight-floating-button1 = + .title = 強調 + .aria-label = 強調します +pdfjs-highlight-floating-button-label = 強調 + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = インク注釈を削除します +pdfjs-editor-remove-freetext-button = + .title = テキストを削除します +pdfjs-editor-remove-stamp-button = + .title = 画像を削除します +pdfjs-editor-remove-highlight-button = + .title = 強調を削除します + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = 色 +pdfjs-editor-free-text-size-input = サイズ +pdfjs-editor-ink-color-input = 色 +pdfjs-editor-ink-thickness-input = 太さ +pdfjs-editor-ink-opacity-input = 不透明度 +pdfjs-editor-stamp-add-image-button = + .title = 画像を追加します +pdfjs-editor-stamp-add-image-button-label = 画像を追加 +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = 太さ +pdfjs-editor-free-highlight-thickness-title = + .title = テキスト以外のアイテムを強調する時の太さを変更します +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = フリーテキスト注釈エディター + .default-content = テキストを入力してください... +pdfjs-free-text = + .aria-label = フリーテキスト注釈エディター +pdfjs-free-text-default-content = テキストを入力してください... +pdfjs-ink = + .aria-label = インク注釈エディター +pdfjs-ink-canvas = + .aria-label = ユーザー作成画像 + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = 代替テキスト +pdfjs-editor-alt-text-edit-button = + .aria-label = 代替テキストを編集 +pdfjs-editor-alt-text-edit-button-label = 代替テキストを編集 +pdfjs-editor-alt-text-dialog-label = オプションの選択 +pdfjs-editor-alt-text-dialog-description = 代替テキストは画像が表示されない場合や読み込まれない場合にユーザーの助けになります。 +pdfjs-editor-alt-text-add-description-label = 説明を追加 +pdfjs-editor-alt-text-add-description-description = 対象や設定、動作を説明する短い文章を記入してください。 +pdfjs-editor-alt-text-mark-decorative-label = 装飾マークを付ける +pdfjs-editor-alt-text-mark-decorative-description = これは区切り線やウォーターマークなどの装飾画像に使用されます。 +pdfjs-editor-alt-text-cancel-button = キャンセル +pdfjs-editor-alt-text-save-button = 保存 +pdfjs-editor-alt-text-decorative-tooltip = 装飾マークが付いています +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = 例:「若い人がテーブルの席について食事をしています」 +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = 代替テキスト + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = 左上隅 — サイズ変更 +pdfjs-editor-resizer-label-top-middle = 上中央 — サイズ変更 +pdfjs-editor-resizer-label-top-right = 右上隅 — サイズ変更 +pdfjs-editor-resizer-label-middle-right = 右中央 — サイズ変更 +pdfjs-editor-resizer-label-bottom-right = 右下隅 — サイズ変更 +pdfjs-editor-resizer-label-bottom-middle = 下中央 — サイズ変更 +pdfjs-editor-resizer-label-bottom-left = 左下隅 — サイズ変更 +pdfjs-editor-resizer-label-middle-left = 左中央 — サイズ変更 +pdfjs-editor-resizer-top-left = + .aria-label = 左上隅 — サイズ変更 +pdfjs-editor-resizer-top-middle = + .aria-label = 上中央 — サイズ変更 +pdfjs-editor-resizer-top-right = + .aria-label = 右上隅 — サイズ変更 +pdfjs-editor-resizer-middle-right = + .aria-label = 右中央 — サイズ変更 +pdfjs-editor-resizer-bottom-right = + .aria-label = 右下隅 — サイズ変更 +pdfjs-editor-resizer-bottom-middle = + .aria-label = 下中央 — サイズ変更 +pdfjs-editor-resizer-bottom-left = + .aria-label = 左下隅 — サイズ変更 +pdfjs-editor-resizer-middle-left = + .aria-label = 左中央 — サイズ変更 + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = 強調色 +pdfjs-editor-colorpicker-button = + .title = 色を変更します +pdfjs-editor-colorpicker-dropdown = + .aria-label = 色の選択 +pdfjs-editor-colorpicker-yellow = + .title = 黄色 +pdfjs-editor-colorpicker-green = + .title = 緑色 +pdfjs-editor-colorpicker-blue = + .title = 青色 +pdfjs-editor-colorpicker-pink = + .title = ピンク色 +pdfjs-editor-colorpicker-red = + .title = 赤色 + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = すべて表示 +# (^m^) en-US: .title = Show all +pdfjs-editor-highlight-show-all-button = + .title = 強調の表示を切り替えます + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = 代替テキストを編集 (画像の説明) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = 代替テキストを追加 (画像の説明) +pdfjs-editor-new-alt-text-textarea = + .placeholder = ここに説明を記入してください... +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = 画像が読み込まれない場合や見えない人のための短い説明です。 +pdfjs-editor-new-alt-text-disclaimer1 = この代替テキストは自動的に生成されたため正確でない可能性があります。 +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = 詳細情報 +pdfjs-editor-new-alt-text-create-automatically-button-label = 代替テキストを自動生成 +pdfjs-editor-new-alt-text-not-now-button = 後で +pdfjs-editor-new-alt-text-error-title = 代替テキストを自動生成できませんでした +pdfjs-editor-new-alt-text-error-description = ご自分で代替テキストを書くか後でもう一度試してください。 +pdfjs-editor-new-alt-text-error-close-button = 閉じる +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = 代替テキスト AI モデルをダウンロードしています ({ $downloadedSize } / { $totalSize } MB) + .aria-valuetext = 代替テキスト AI モデルをダウンロードしています ({ $downloadedSize } / { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = 代替テキストを追加しました +pdfjs-editor-new-alt-text-added-button-label = 代替テキストを追加しました +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = 代替テキストがありません +pdfjs-editor-new-alt-text-missing-button-label = 代替テキストがありません +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = 代替テキストをレビュー +pdfjs-editor-new-alt-text-to-review-button-label = 代替テキストをレビュー +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = 自動生成されました: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = 画像の代替テキスト設定 +pdfjs-image-alt-text-settings-button-label = 画像の代替テキスト設定 +pdfjs-editor-alt-text-settings-dialog-label = 画像の代替テキスト設定 +pdfjs-editor-alt-text-settings-automatic-title = 自動代替テキスト +pdfjs-editor-alt-text-settings-create-model-button-label = 代替テキストを自動生成 +pdfjs-editor-alt-text-settings-create-model-description = 画像が読み込まれない場合や見えない人のために説明を提案します。 +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = 代替テキスト AI モデル ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = ローカルの端末上で実行されるためデータは非公開になります。代替テキストの自動生成に必要です。 +pdfjs-editor-alt-text-settings-delete-model-button = 削除 +pdfjs-editor-alt-text-settings-download-model-button = ダウンロード +pdfjs-editor-alt-text-settings-downloading-model-button = ダウンロード中... +pdfjs-editor-alt-text-settings-editor-title = 代替テキストエディター +pdfjs-editor-alt-text-settings-show-dialog-button-label = 画像の追加時に代替テキストエディターを表示する +pdfjs-editor-alt-text-settings-show-dialog-description = すべての画像に代替テキストを追加する助けになります。 +pdfjs-editor-alt-text-settings-close-button = 閉じる + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = 強調表示が削除されました +pdfjs-editor-undo-bar-message-freetext = フリーテキスト注釈が削除されました +pdfjs-editor-undo-bar-message-ink = インク注釈が削除されました +pdfjs-editor-undo-bar-message-stamp = 画像が削除されました +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = { $count } 個の注釈が削除されました +pdfjs-editor-undo-bar-undo-button = + .title = 元に戻す +pdfjs-editor-undo-bar-undo-button-label = 元に戻す +pdfjs-editor-undo-bar-close-button = + .title = 閉じる +pdfjs-editor-undo-bar-close-button-label = 閉じる diff --git a/public/pdfjs/web/locale/ka/viewer.ftl b/public/pdfjs/web/locale/ka/viewer.ftl new file mode 100644 index 0000000..d500f3e --- /dev/null +++ b/public/pdfjs/web/locale/ka/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = წინა გვერდი +pdfjs-previous-button-label = წინა +pdfjs-next-button = + .title = შემდეგი გვერდი +pdfjs-next-button-label = შემდეგი +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = გვერდი +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount }-დან +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } { $pagesCount }-დან) +pdfjs-zoom-out-button = + .title = ზომის შემცირება +pdfjs-zoom-out-button-label = დაშორება +pdfjs-zoom-in-button = + .title = ზომის გაზრდა +pdfjs-zoom-in-button-label = მოახლოება +pdfjs-zoom-select = + .title = ზომა +pdfjs-presentation-mode-button = + .title = წარდგენის რეჟიმზე გადართვა +pdfjs-presentation-mode-button-label = წარდგენის რეჟიმი +pdfjs-open-file-button = + .title = ფაილის გახსნა +pdfjs-open-file-button-label = გახსნა +pdfjs-print-button = + .title = ამობეჭდვა +pdfjs-print-button-label = ამობეჭდვა +pdfjs-save-button = + .title = შენახვა +pdfjs-save-button-label = შენახვა +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = ჩამოტვირთვა +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = ჩამოტვირთვა +pdfjs-bookmark-button = + .title = მიმდინარე გვერდი (ბმული ამ გვერდისთვის) +pdfjs-bookmark-button-label = მიმდინარე გვერდი + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = ხელსაწყოები +pdfjs-tools-button-label = ხელსაწყოები +pdfjs-first-page-button = + .title = პირველ გვერდზე გადასვლა +pdfjs-first-page-button-label = პირველ გვერდზე გადასვლა +pdfjs-last-page-button = + .title = ბოლო გვერდზე გადასვლა +pdfjs-last-page-button-label = ბოლო გვერდზე გადასვლა +pdfjs-page-rotate-cw-button = + .title = საათის ისრის მიმართულებით შებრუნება +pdfjs-page-rotate-cw-button-label = მარჯვნივ გადაბრუნება +pdfjs-page-rotate-ccw-button = + .title = საათის ისრის საპირისპიროდ შებრუნება +pdfjs-page-rotate-ccw-button-label = მარცხნივ გადაბრუნება +pdfjs-cursor-text-select-tool-button = + .title = მოსანიშნი მაჩვენებლის გამოყენება +pdfjs-cursor-text-select-tool-button-label = მოსანიშნი მაჩვენებელი +pdfjs-cursor-hand-tool-button = + .title = გადასაადგილებელი მაჩვენებლის გამოყენება +pdfjs-cursor-hand-tool-button-label = გადასაადგილებელი +pdfjs-scroll-page-button = + .title = გვერდზე გადაადგილების გამოყენება +pdfjs-scroll-page-button-label = გვერდშივე გადაადგილება +pdfjs-scroll-vertical-button = + .title = გვერდების შვეულად ჩვენება +pdfjs-scroll-vertical-button-label = შვეული გადაადგილება +pdfjs-scroll-horizontal-button = + .title = გვერდების თარაზულად ჩვენება +pdfjs-scroll-horizontal-button-label = განივი გადაადგილება +pdfjs-scroll-wrapped-button = + .title = გვერდების ცხრილურად ჩვენება +pdfjs-scroll-wrapped-button-label = ცხრილური გადაადგილება +pdfjs-spread-none-button = + .title = ორ გვერდზე გაშლის გარეშე +pdfjs-spread-none-button-label = ცალგვერდიანი ჩვენება +pdfjs-spread-odd-button = + .title = ორ გვერდზე გაშლა კენტი გვერდიდან +pdfjs-spread-odd-button-label = ორ გვერდზე კენტიდან +pdfjs-spread-even-button = + .title = ორ გვერდზე გაშლა ლუწი გვერდიდან +pdfjs-spread-even-button-label = ორ გვერდზე ლუწიდან + +## Document properties dialog + +pdfjs-document-properties-button = + .title = დოკუმენტის შესახებ… +pdfjs-document-properties-button-label = დოკუმენტის შესახებ… +pdfjs-document-properties-file-name = ფაილის სახელი: +pdfjs-document-properties-file-size = ფაილის მოცულობა: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } კბაიტი ({ $b } ბაიტი) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } მბაიტი ({ $b } ბაიტი) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } კბ ({ $size_b } ბაიტი) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } მბ ({ $size_b } ბაიტი) +pdfjs-document-properties-title = სათაური: +pdfjs-document-properties-author = შემქმნელი: +pdfjs-document-properties-subject = თემა: +pdfjs-document-properties-keywords = საკვანძო სიტყვები: +pdfjs-document-properties-creation-date = შექმნის დრო: +pdfjs-document-properties-modification-date = ჩასწორების დრო: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = შემდგენელი: +pdfjs-document-properties-producer = PDF-შემდგენელი: +pdfjs-document-properties-version = PDF-ვერსია: +pdfjs-document-properties-page-count = გვერდები: +pdfjs-document-properties-page-size = გვერდის ზომა: +pdfjs-document-properties-page-size-unit-inches = დუიმი +pdfjs-document-properties-page-size-unit-millimeters = მმ +pdfjs-document-properties-page-size-orientation-portrait = შვეულად +pdfjs-document-properties-page-size-orientation-landscape = თარაზულად +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = მსუბუქი ვებჩვენება: +pdfjs-document-properties-linearized-yes = დიახ +pdfjs-document-properties-linearized-no = არა +pdfjs-document-properties-close-button = დახურვა + +## Print + +pdfjs-print-progress-message = დოკუმენტი მზადდება ამოსაბეჭდად… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = გაუქმება +pdfjs-printing-not-supported = გაფრთხილება: ამობეჭდვა ამ ბრაუზერში არაა სრულად მხარდაჭერილი. +pdfjs-printing-not-ready = გაფრთხილება: PDF სრულად ჩატვირთული არაა, ამობეჭდვის დასაწყებად. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = გვერდითა ზოლის გამოჩენა/დამალვა +pdfjs-toggle-sidebar-notification-button = + .title = გვერდითი ზოლის გამოჩენა (შეიცავს სარჩევს/დანართს/შრეებს) +pdfjs-toggle-sidebar-button-label = გვერდითა ზოლის გამოჩენა/დამალვა +pdfjs-document-outline-button = + .title = დოკუმენტის სარჩევის ჩვენება (ორმაგი წკაპით თითოეულის ჩამოშლა/აკეცვა) +pdfjs-document-outline-button-label = დოკუმენტის სარჩევი +pdfjs-attachments-button = + .title = დანართების ჩვენება +pdfjs-attachments-button-label = დანართები +pdfjs-layers-button = + .title = შრეების გამოჩენა (ორმაგი წკაპით ყველა შრის ნაგულისხმევზე დაბრუნება) +pdfjs-layers-button-label = შრეები +pdfjs-thumbs-button = + .title = შეთვალიერება +pdfjs-thumbs-button-label = ესკიზები +pdfjs-current-outline-item-button = + .title = მიმდინარე გვერდის მონახვა სარჩევში +pdfjs-current-outline-item-button-label = მიმდინარე გვერდი სარჩევში +pdfjs-findbar-button = + .title = პოვნა დოკუმენტში +pdfjs-findbar-button-label = ძიება +pdfjs-additional-layers = დამატებითი შრეები + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = გვერდი { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = გვერდის შეთვალიერება { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = ძიება + .placeholder = პოვნა დოკუმენტში… +pdfjs-find-previous-button = + .title = წინა დამთხვევის პოვნა +pdfjs-find-previous-button-label = წინა +pdfjs-find-next-button = + .title = მომდევნო დამთხვევის პოვნა +pdfjs-find-next-button-label = შემდეგი +pdfjs-find-highlight-checkbox = ყველაფრის მონიშვნა +pdfjs-find-match-case-checkbox-label = მთავრულით +pdfjs-find-match-diacritics-checkbox-label = ნიშნებით +pdfjs-find-entire-word-checkbox-label = მთლიანი სიტყვები +pdfjs-find-reached-top = მიღწეულია დოკუმენტის დასაწყისი, გრძელდება ბოლოდან +pdfjs-find-reached-bottom = მიღწეულია დოკუმენტის ბოლო, გრძელდება დასაწყისიდან +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] თანხვედრა { $current }, სულ { $total } + *[other] თანხვედრა { $current }, სულ { $total } + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] არანაკლებ { $limit } თანხვედრა + *[other] არანაკლებ { $limit } თანხვედრა + } +pdfjs-find-not-found = ფრაზა ვერ მოიძებნა + +## Predefined zoom values + +pdfjs-page-scale-width = გვერდის სიგანეზე +pdfjs-page-scale-fit = მთლიანი გვერდი +pdfjs-page-scale-auto = ავტომატური +pdfjs-page-scale-actual = საწყისი ზომა +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = გვერდი { $page } + +## Loading indicator messages + +pdfjs-loading-error = შეცდომა, PDF-ფაილის ჩატვირთვისას. +pdfjs-invalid-file-error = არამართებული ან დაზიანებული PDF-ფაილი. +pdfjs-missing-file-error = ნაკლული PDF-ფაილი. +pdfjs-unexpected-response-error = სერვერის მოულოდნელი პასუხი. +pdfjs-rendering-error = შეცდომა, გვერდის ჩვენებისას. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } შენიშვნა] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = შეიყვანეთ პაროლი PDF-ფაილის გასახსნელად. +pdfjs-password-invalid = არასწორი პაროლი. გთხოვთ, სცადოთ ხელახლა. +pdfjs-password-ok-button = კარგი +pdfjs-password-cancel-button = გაუქმება +pdfjs-web-fonts-disabled = ვებშრიფტები გამორთულია: ჩაშენებული PDF-შრიფტების გამოყენება ვერ ხერხდება. + +## Editing + +pdfjs-editor-free-text-button = + .title = წარწერა +pdfjs-editor-free-text-button-label = წარწერა +pdfjs-editor-ink-button = + .title = ხაზვა +pdfjs-editor-ink-button-label = ხაზვა +pdfjs-editor-stamp-button = + .title = სურათების დართვა ან ჩასწორება +pdfjs-editor-stamp-button-label = სურათების დართვა ან ჩასწორება +pdfjs-editor-highlight-button = + .title = მონიშვნა +pdfjs-editor-highlight-button-label = მონიშვნა +pdfjs-highlight-floating-button1 = + .title = მონიშვნა + .aria-label = მონიშვნა +pdfjs-highlight-floating-button-label = მონიშვნა + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = დახაზულის მოცილება +pdfjs-editor-remove-freetext-button = + .title = წარწერის მოცილება +pdfjs-editor-remove-stamp-button = + .title = სურათის მოცილება +pdfjs-editor-remove-highlight-button = + .title = მონიშვნის მოცილება + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = ფერი +pdfjs-editor-free-text-size-input = ზომა +pdfjs-editor-ink-color-input = ფერი +pdfjs-editor-ink-thickness-input = სისქე +pdfjs-editor-ink-opacity-input = გაუმჭვირვალობა +pdfjs-editor-stamp-add-image-button = + .title = სურათის დამატება +pdfjs-editor-stamp-add-image-button-label = სურათის დამატება +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = სისქე +pdfjs-editor-free-highlight-thickness-title = + .title = სისქის შეცვლა წარწერის გარდა სხვა ნაწილების მონიშვნისას +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = ნაწერის ჩასწორება + .default-content = დაიწყეთ აკრეფა… +pdfjs-free-text = + .aria-label = ნაწერის ჩასწორება +pdfjs-free-text-default-content = აკრიფეთ… +pdfjs-ink = + .aria-label = დახაზულის შესწორება +pdfjs-ink-canvas = + .aria-label = მომხმარებლის შექმნილი სურათი + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = თანდართული წარწერა +pdfjs-editor-alt-text-edit-button = + .aria-label = დართული წარწერის ჩასწორება +pdfjs-editor-alt-text-edit-button-label = თანდართული წარწერის ჩასწორება +pdfjs-editor-alt-text-dialog-label = არჩევა +pdfjs-editor-alt-text-dialog-description = თანდართული (შემნაცვლებელი) წარწერა გამოსადეგია მათთვის, ვინც ვერ ხედავს სურათებს ან გამოისახება მაშინ, როცა სურათი ვერ ჩაიტვირთება. +pdfjs-editor-alt-text-add-description-label = აღწერილობის მითითება +pdfjs-editor-alt-text-add-description-description = განკუთვნილია 1-2 წინადადებით საგნის, მახასიათებლის ან მოქმედების აღსაწერად. +pdfjs-editor-alt-text-mark-decorative-label = მოინიშნოს მორთულობად +pdfjs-editor-alt-text-mark-decorative-description = განკუთვნილია შესამკობი სურათებისთვის, გარსშემოსავლები ჩარჩოებისა და ჭვირნიშნებისთვის. +pdfjs-editor-alt-text-cancel-button = გაუქმება +pdfjs-editor-alt-text-save-button = შენახვა +pdfjs-editor-alt-text-decorative-tooltip = მოინიშნოს მორთულობად +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = მაგალითად, „ახალგაზრდა მამაკაცი მაგიდასთან ზის და სადილობს“ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = დართული წარწერა + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = ზევით მარცხნივ — ზომაცვლა +pdfjs-editor-resizer-label-top-middle = ზევით შუაში — ზომაცვლა +pdfjs-editor-resizer-label-top-right = ზევით მარჯვნივ — ზომაცვლა +pdfjs-editor-resizer-label-middle-right = შუაში მარჯვნივ — ზომაცვლა +pdfjs-editor-resizer-label-bottom-right = ქვევით მარჯვნივ — ზომაცვლა +pdfjs-editor-resizer-label-bottom-middle = ქვევით შუაში — ზომაცვლა +pdfjs-editor-resizer-label-bottom-left = ზვევით მარცხნივ — ზომაცვლა +pdfjs-editor-resizer-label-middle-left = შუაში მარცხნივ — ზომაცვლა +pdfjs-editor-resizer-top-left = + .aria-label = ზევით მარცხნივ — ზომაცვლა +pdfjs-editor-resizer-top-middle = + .aria-label = ზევით შუაში — ზომაცვლა +pdfjs-editor-resizer-top-right = + .aria-label = ზევით მარჯვნივ — ზომაცვლა +pdfjs-editor-resizer-middle-right = + .aria-label = შუაში მარჯვნივ — ზომაცვლა +pdfjs-editor-resizer-bottom-right = + .aria-label = ქვევით მარჯვნივ — ზომაცვლა +pdfjs-editor-resizer-bottom-middle = + .aria-label = ქვევით შუაში — ზომაცვლა +pdfjs-editor-resizer-bottom-left = + .aria-label = ზვევით მარცხნივ — ზომაცვლა +pdfjs-editor-resizer-middle-left = + .aria-label = შუაში მარცხნივ — ზომაცვლა + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = მოსანიშნი ფერი +pdfjs-editor-colorpicker-button = + .title = ფერის შეცვლა +pdfjs-editor-colorpicker-dropdown = + .aria-label = ფერის არჩევა +pdfjs-editor-colorpicker-yellow = + .title = ყვითელი +pdfjs-editor-colorpicker-green = + .title = მწვანე +pdfjs-editor-colorpicker-blue = + .title = ლურჯი +pdfjs-editor-colorpicker-pink = + .title = ვარდისფერი +pdfjs-editor-colorpicker-red = + .title = წითელი + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = ყველას ჩვენება +pdfjs-editor-highlight-show-all-button = + .title = ყველას ჩვენება + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = დართული წარწერის ჩასწორება (სურათის აღწერის) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = დართული წარწერის დამატება (სურათის აღწერის) +pdfjs-editor-new-alt-text-textarea = + .placeholder = დაწერეთ თქვენი აღწერა აქ… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = მოკლე აღწერა მათთვის, ვინც ვერ ხედავს სურათს ან ვისთანაც ვერ ჩაიტვირთება სურათი. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = ეს დართული წარწერა ავტომატურადაა შედგენილი და შესაძლოა, უმართებულო იყოს. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = ვრცლად +pdfjs-editor-new-alt-text-create-automatically-button-label = დართული წარწერის ავტომატური შედგენა +pdfjs-editor-new-alt-text-not-now-button = ახლა არა +pdfjs-editor-new-alt-text-error-title = დართული წარწერის შედგენა ვერ მოხერხდა +pdfjs-editor-new-alt-text-error-description = გთხოვთ დაწეროთ საკუთარი დანართი და კვლავ სცადოთ მოგვიანებით. +pdfjs-editor-new-alt-text-error-close-button = დახურვა +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = ჩამოიტვირთება დართული წარწერის შესადეგი AI-მოდელი ({ $downloadedSize } ზომით { $totalSize } მბაიტი) + .aria-valuetext = ჩამოიტვირთება დართული წარწერის შესადეგი AI-მოდელი ({ $downloadedSize } ზომით { $totalSize } მბაიტი) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = დართული წარწერა დამატებულია +pdfjs-editor-new-alt-text-added-button-label = დართული წარწერა დამატებულია +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = აკლია დართული წარწერა +pdfjs-editor-new-alt-text-missing-button-label = აკლია დართული წარწერა +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = დართული წარწერის გადახედვა +pdfjs-editor-new-alt-text-to-review-button-label = დართული წარწერის გადახედვა +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = შედგენილია ავტომატურად: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = სურათის დართული წარწერის პარამეტრები +pdfjs-image-alt-text-settings-button-label = სურათის დართული წარწერის პარამეტრები +pdfjs-editor-alt-text-settings-dialog-label = სურათის დართული წარწერის პარამეტრები +pdfjs-editor-alt-text-settings-automatic-title = ავტომატურად დართული წარწერა +pdfjs-editor-alt-text-settings-create-model-button-label = დართული წარწერის ავტომატური შედგენა +pdfjs-editor-alt-text-settings-create-model-description = აღწერს სურათს მათთვის, ვინც ვერ ხედავს ან ვისთანაც ვერ ჩაიტვირთება. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = დართული წარწერის შესადგენი AI-მოდელი ({ $totalSize } მბაიტი) +pdfjs-editor-alt-text-settings-ai-model-description = ეშვება ადგილობრივად თქვენს მოწყობილობასა, ასე რომ მონაცემები დარჩება პირადი. საჭიროა წარწერის ავტომატურად დართვისთვის. +pdfjs-editor-alt-text-settings-delete-model-button = წაშლა +pdfjs-editor-alt-text-settings-download-model-button = ჩამოტვირთვა +pdfjs-editor-alt-text-settings-downloading-model-button = ჩამოიტვრითება... +pdfjs-editor-alt-text-settings-editor-title = დართული წარწერის ჩამსწორებელი +pdfjs-editor-alt-text-settings-show-dialog-button-label = გამოჩნდეს დართული წარწერის ჩამსწორებელი სურათის დამატებისთანავე +pdfjs-editor-alt-text-settings-show-dialog-description = უზრუნველყოფს, რომ თქვენს ყველა სურათს ახლდეს დართული წარწერა. +pdfjs-editor-alt-text-settings-close-button = დახურვა + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = მონიშვნა მოცილებულია +pdfjs-editor-undo-bar-message-freetext = წარწერა მოცილებულია +pdfjs-editor-undo-bar-message-ink = ნახატი მოცილებულია +pdfjs-editor-undo-bar-message-stamp = სურათი მოცილებულია +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } შენიშვნა მოცილებულია + *[other] { $count } შენიშვნა მოცილებულია + } +pdfjs-editor-undo-bar-undo-button = + .title = დაბრუნება +pdfjs-editor-undo-bar-undo-button-label = დაბრუნება +pdfjs-editor-undo-bar-close-button = + .title = დახურვა +pdfjs-editor-undo-bar-close-button-label = დახურვა diff --git a/public/pdfjs/web/locale/kab/viewer.ftl b/public/pdfjs/web/locale/kab/viewer.ftl new file mode 100644 index 0000000..dda88c1 --- /dev/null +++ b/public/pdfjs/web/locale/kab/viewer.ftl @@ -0,0 +1,438 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Asebter azewwar +pdfjs-previous-button-label = Azewwar +pdfjs-next-button = + .title = Asebter d-iteddun +pdfjs-next-button-label = Ddu ɣer zdat +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Asebter +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = ɣef { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } n { $pagesCount }) +pdfjs-zoom-out-button = + .title = Semẓi +pdfjs-zoom-out-button-label = Semẓi +pdfjs-zoom-in-button = + .title = Semɣeṛ +pdfjs-zoom-in-button-label = Semɣeṛ +pdfjs-zoom-select = + .title = Semɣeṛ/Semẓi +pdfjs-presentation-mode-button = + .title = Uɣal ɣer Uskar Tihawt +pdfjs-presentation-mode-button-label = Askar Tihawt +pdfjs-open-file-button = + .title = Ldi Afaylu +pdfjs-open-file-button-label = Ldi +pdfjs-print-button = + .title = Siggez +pdfjs-print-button-label = Siggez +pdfjs-save-button = + .title = Sekles +pdfjs-save-button-label = Sekles +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Sader +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Sader +pdfjs-bookmark-button = + .title = Asebter amiran (Sken-d tansa URL seg usebter amiran) +pdfjs-bookmark-button-label = Asebter amiran + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Ifecka +pdfjs-tools-button-label = Ifecka +pdfjs-first-page-button = + .title = Ddu ɣer usebter amezwaru +pdfjs-first-page-button-label = Ddu ɣer usebter amezwaru +pdfjs-last-page-button = + .title = Ddu ɣer usebter aneggaru +pdfjs-last-page-button-label = Ddu ɣer usebter aneggaru +pdfjs-page-rotate-cw-button = + .title = Tuzzya tusrigt +pdfjs-page-rotate-cw-button-label = Tuzzya tusrigt +pdfjs-page-rotate-ccw-button = + .title = Tuzzya amgal-usrig +pdfjs-page-rotate-ccw-button-label = Tuzzya amgal-usrig +pdfjs-cursor-text-select-tool-button = + .title = Rmed afecku n tefrant n uḍris +pdfjs-cursor-text-select-tool-button-label = Afecku n tefrant n uḍris +pdfjs-cursor-hand-tool-button = + .title = Rmed afecku afus +pdfjs-cursor-hand-tool-button-label = Afecku afus +pdfjs-scroll-page-button = + .title = Seqdec adrurem n usebter +pdfjs-scroll-page-button-label = Adrurem n usebter +pdfjs-scroll-vertical-button = + .title = Seqdec adrurem ubdid +pdfjs-scroll-vertical-button-label = Adrurem ubdid +pdfjs-scroll-horizontal-button = + .title = Seqdec adrurem aglawan +pdfjs-scroll-horizontal-button-label = Adrurem aglawan +pdfjs-scroll-wrapped-button = + .title = Seqdec adrurem yuẓen +pdfjs-scroll-wrapped-button-label = Adrurem yuẓen +pdfjs-spread-none-button = + .title = Ur sedday ara isiɣzaf n usebter +pdfjs-spread-none-button-label = Ulac isiɣzaf +pdfjs-spread-odd-button = + .title = Seddu isiɣzaf n usebter ibeddun s yisebtar irayuganen +pdfjs-spread-odd-button-label = Isiɣzaf irayuganen +pdfjs-spread-even-button = + .title = Seddu isiɣzaf n usebter ibeddun s yisebtar iyuganen +pdfjs-spread-even-button-label = Isiɣzaf iyuganen + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Taɣaṛa n isemli… +pdfjs-document-properties-button-label = Taɣaṛa n isemli… +pdfjs-document-properties-file-name = Isem n ufaylu: +pdfjs-document-properties-file-size = Teɣzi n ufaylu: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } yibiten) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } yibiten) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KAṬ ({ $size_b } ibiten) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MAṬ ({ $size_b } iṭamḍanen) +pdfjs-document-properties-title = Azwel: +pdfjs-document-properties-author = Ameskar: +pdfjs-document-properties-subject = Amgay: +pdfjs-document-properties-keywords = Awalen n tsaruţ +pdfjs-document-properties-creation-date = Azemz n tmerna: +pdfjs-document-properties-modification-date = Azemz n usnifel: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Yerna-t: +pdfjs-document-properties-producer = Afecku n uselket PDF: +pdfjs-document-properties-version = Lqem PDF: +pdfjs-document-properties-page-count = Amḍan n yisebtar: +pdfjs-document-properties-page-size = Tuγzi n usebter: +pdfjs-document-properties-page-size-unit-inches = deg +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = s teɣzi +pdfjs-document-properties-page-size-orientation-landscape = s tehri +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Asekkil +pdfjs-document-properties-page-size-name-legal = Usḍif + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Taskant Web taruradt: +pdfjs-document-properties-linearized-yes = Ih +pdfjs-document-properties-linearized-no = Ala +pdfjs-document-properties-close-button = Mdel + +## Print + +pdfjs-print-progress-message = Aheggi i usiggez n isemli… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Sefsex +pdfjs-printing-not-supported = Ɣuṛ-k: Asiggez ur ittusefrak ara yakan imaṛṛa deg iminig-a. +pdfjs-printing-not-ready = Ɣuṛ-k: Afaylu PDF ur d-yuli ara imeṛṛa akken ad ittusiggez. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Sken/Fer agalis adisan +pdfjs-toggle-sidebar-notification-button = + .title = Ffer/Sekn agalis adisan (isemli yegber aɣawas/ticeqqufin yeddan/tissiwin) +pdfjs-toggle-sidebar-button-label = Sken/Fer agalis adisan +pdfjs-document-outline-button = + .title = Sken isemli (Senned snat tikal i wesemɣer/Afneẓ n iferdisen meṛṛa) +pdfjs-document-outline-button-label = Isɣalen n isebtar +pdfjs-attachments-button = + .title = Sken ticeqqufin yeddan +pdfjs-attachments-button-label = Ticeqqufin yeddan +pdfjs-layers-button = + .title = Skeen tissiwin (sit sin yiberdan i uwennez n meṛṛa tissiwin ɣer waddad amezwer) +pdfjs-layers-button-label = Tissiwin +pdfjs-thumbs-button = + .title = Sken tanfult. +pdfjs-thumbs-button-label = Tinfulin +pdfjs-current-outline-item-button = + .title = Af-d aferdis n uɣawas amiran +pdfjs-current-outline-item-button-label = Aferdis n uɣawas amiran +pdfjs-findbar-button = + .title = Nadi deg isemli +pdfjs-findbar-button-label = Nadi +pdfjs-additional-layers = Tissiwin-nniḍen + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Asebter { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Tanfult n usebter { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Nadi + .placeholder = Nadi deg isemli… +pdfjs-find-previous-button = + .title = Aff-d tamseḍriwt n twinest n deffir +pdfjs-find-previous-button-label = Azewwar +pdfjs-find-next-button = + .title = Aff-d timseḍriwt n twinest d-iteddun +pdfjs-find-next-button-label = Ddu ɣer zdat +pdfjs-find-highlight-checkbox = Err izirig imaṛṛa +pdfjs-find-match-case-checkbox-label = Qadeṛ amasal n isekkilen +pdfjs-find-match-diacritics-checkbox-label = Qadeṛ ifeskilen +pdfjs-find-entire-word-checkbox-label = Awalen iččuranen +pdfjs-find-reached-top = Yabbeḍ s afella n usebter, tuɣalin s wadda +pdfjs-find-reached-bottom = Tebḍeḍ s adda n usebter, tuɣalin s afella +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] Timeḍriwt { $current } ɣef { $total } + *[other] Timeḍriwin { $current } ɣef { $total } + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Ugar n { $limit } umṣada + *[other] Ugar n { $limit } yimṣadayen + } +pdfjs-find-not-found = Ulac tawinest + +## Predefined zoom values + +pdfjs-page-scale-width = Tehri n usebter +pdfjs-page-scale-fit = Asebter imaṛṛa +pdfjs-page-scale-auto = Asemɣeṛ/Asemẓi awurman +pdfjs-page-scale-actual = Teɣzi tilawt +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Asebter { $page } + +## Loading indicator messages + +pdfjs-loading-error = Teḍra-d tuccḍa deg alluy n PDF: +pdfjs-invalid-file-error = Afaylu PDF arameɣtu neɣ yexṣeṛ. +pdfjs-missing-file-error = Ulac afaylu PDF. +pdfjs-unexpected-response-error = Aqeddac yerra-d yir tiririt ur nettwaṛǧi ara. +pdfjs-rendering-error = Teḍra-d tuccḍa deg uskan n usebter. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Tabzimt { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Sekcem awal uffir akken ad ldiḍ afaylu-yagi PDF +pdfjs-password-invalid = Awal uffir mačči d ameɣtu, Ɛreḍ tikelt-nniḍen. +pdfjs-password-ok-button = IH +pdfjs-password-cancel-button = Sefsex +pdfjs-web-fonts-disabled = Tisefsiyin web ttwassensent; D awezɣi useqdec n tsefsiyin yettwarnan ɣer PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Aḍris +pdfjs-editor-free-text-button-label = Aḍris +pdfjs-editor-ink-button = + .title = Suneɣ +pdfjs-editor-ink-button-label = Suneɣ +pdfjs-editor-stamp-button = + .title = Rnu neɣ ẓreg tugniwin +pdfjs-editor-stamp-button-label = Rnu neɣ ẓreg tugniwin +pdfjs-editor-highlight-button = + .title = Derrer +pdfjs-editor-highlight-button-label = Derrer +pdfjs-highlight-floating-button1 = + .title = Derrer + .aria-label = Derrer +pdfjs-highlight-floating-button-label = Derrer + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Kkes asuneɣ +pdfjs-editor-remove-freetext-button = + .title = Kkes aḍris +pdfjs-editor-remove-stamp-button = + .title = Kkes tugna +pdfjs-editor-remove-highlight-button = + .title = Kkes aderrer + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Initen +pdfjs-editor-free-text-size-input = Teɣzi +pdfjs-editor-ink-color-input = Ini +pdfjs-editor-ink-thickness-input = Tuzert +pdfjs-editor-ink-opacity-input = Tebrek +pdfjs-editor-stamp-add-image-button = + .title = Rnu tawlaft +pdfjs-editor-stamp-add-image-button-label = Rnu tawlaft +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Tuzert +pdfjs-editor-free-highlight-thickness-title = + .title = Beddel tuzert mi ara d-tesbeggneḍ iferdisen niḍen ur nelli d aḍris +pdfjs-free-text = + .aria-label = Amaẓrag n uḍris +pdfjs-free-text-default-content = Bdu tira... +pdfjs-ink = + .aria-label = Amaẓrag n usuneɣ +pdfjs-ink-canvas = + .aria-label = Tugna yettwarnan sɣur useqdac + +## Alt-text dialog + +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button-label = Aḍris amaskal +pdfjs-editor-alt-text-edit-button-label = Ẓreg aḍris amaskal +pdfjs-editor-alt-text-dialog-label = Fren taxtirt +pdfjs-editor-alt-text-add-description-label = Rnu aglam +pdfjs-editor-alt-text-mark-decorative-label = Creḍ d adlag +pdfjs-editor-alt-text-cancel-button = Sefsex +pdfjs-editor-alt-text-save-button = Sekles +pdfjs-editor-alt-text-decorative-tooltip = Yettwacreḍ d adlag + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Tiɣmert n ufella n zelmeḍ — semsawi teɣzi +pdfjs-editor-resizer-label-top-middle = Talemmat n ufella — semsawi teɣzi +pdfjs-editor-resizer-label-top-right = Tiɣmert n ufella n yeffus — semsawi teɣzi +pdfjs-editor-resizer-label-middle-right = Talemmast tayeffust — semsawi teɣzi +pdfjs-editor-resizer-label-bottom-right = Tiɣmert n wadda n yeffus — semsawi teɣzi +pdfjs-editor-resizer-label-bottom-middle = Talemmat n wadda — semsawi teɣzi +pdfjs-editor-resizer-label-bottom-left = Tiɣmert n wadda n zelmeḍ — semsawi teɣzi +pdfjs-editor-resizer-label-middle-left = Talemmast tazelmdaḍt — semsawi teɣzi +pdfjs-editor-resizer-top-left = + .aria-label = Tiɣmert n ufella n zelmeḍ — semsawi teɣzi +pdfjs-editor-resizer-top-middle = + .aria-label = Talemmat n ufella — semsawi teɣzi +pdfjs-editor-resizer-top-right = + .aria-label = Tiɣmert n ufella n yeffus — semsawi teɣzi +pdfjs-editor-resizer-middle-right = + .aria-label = Talemmast tayeffust — semsawi teɣzi +pdfjs-editor-resizer-bottom-right = + .aria-label = Tiɣmert n wadda n yeffus — semsawi teɣzi +pdfjs-editor-resizer-bottom-middle = + .aria-label = Talemmat n wadda — semsawi teɣzi +pdfjs-editor-resizer-bottom-left = + .aria-label = Tiɣmert n wadda n zelmeḍ — semsawi teɣzi +pdfjs-editor-resizer-middle-left = + .aria-label = Talemmast tazelmdaḍt — semsawi teɣzi + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Ini n uderrer +pdfjs-editor-colorpicker-button = + .title = Senfel ini +pdfjs-editor-colorpicker-dropdown = + .aria-label = Afran n yiniten +pdfjs-editor-colorpicker-yellow = + .title = Awraɣ +pdfjs-editor-colorpicker-green = + .title = Azegzaw +pdfjs-editor-colorpicker-blue = + .title = Amidadi +pdfjs-editor-colorpicker-pink = + .title = Axuxi +pdfjs-editor-colorpicker-red = + .title = Azggaɣ + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Sken akk +pdfjs-editor-highlight-show-all-button = + .title = Sken akk + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Rnu aḍris niḍen (aglam n tugna) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Aru aglam-ik dagi… +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Issin ugar +pdfjs-editor-new-alt-text-create-automatically-button-label = Rnu aḍris niḍen s wudem awurman +pdfjs-editor-new-alt-text-not-now-button = Mačči tura +pdfjs-editor-new-alt-text-error-title = D awezɣi timerna n uḍris niḍen s wudem awurman +pdfjs-editor-new-alt-text-error-close-button = Mdel + +## Image alt-text settings + +pdfjs-editor-alt-text-settings-delete-model-button = Kkes +pdfjs-editor-alt-text-settings-download-model-button = Sader +pdfjs-editor-alt-text-settings-downloading-model-button = Asader… +pdfjs-editor-alt-text-settings-close-button = Mdel diff --git a/public/pdfjs/web/locale/kk/viewer.ftl b/public/pdfjs/web/locale/kk/viewer.ftl new file mode 100644 index 0000000..fb14226 --- /dev/null +++ b/public/pdfjs/web/locale/kk/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Алдыңғы парақ +pdfjs-previous-button-label = Алдыңғысы +pdfjs-next-button = + .title = Келесі парақ +pdfjs-next-button-label = Келесі +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Парақ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount } ішінен +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = (парақ { $pageNumber }, { $pagesCount } ішінен) +pdfjs-zoom-out-button = + .title = Кішірейту +pdfjs-zoom-out-button-label = Кішірейту +pdfjs-zoom-in-button = + .title = Үлкейту +pdfjs-zoom-in-button-label = Үлкейту +pdfjs-zoom-select = + .title = Масштаб +pdfjs-presentation-mode-button = + .title = Презентация режиміне ауысу +pdfjs-presentation-mode-button-label = Презентация режимі +pdfjs-open-file-button = + .title = Файлды ашу +pdfjs-open-file-button-label = Ашу +pdfjs-print-button = + .title = Баспаға шығару +pdfjs-print-button-label = Баспаға шығару +pdfjs-save-button = + .title = Сақтау +pdfjs-save-button-label = Сақтау +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Жүктеп алу +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Жүктеп алу +pdfjs-bookmark-button = + .title = Ағымдағы бет (Ағымдағы беттен URL адресін көру) +pdfjs-bookmark-button-label = Ағымдағы бет + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Құралдар +pdfjs-tools-button-label = Құралдар +pdfjs-first-page-button = + .title = Алғашқы параққа өту +pdfjs-first-page-button-label = Алғашқы параққа өту +pdfjs-last-page-button = + .title = Соңғы параққа өту +pdfjs-last-page-button-label = Соңғы параққа өту +pdfjs-page-rotate-cw-button = + .title = Сағат тілі бағытымен айналдыру +pdfjs-page-rotate-cw-button-label = Сағат тілі бағытымен бұру +pdfjs-page-rotate-ccw-button = + .title = Сағат тілі бағытына қарсы бұру +pdfjs-page-rotate-ccw-button-label = Сағат тілі бағытына қарсы бұру +pdfjs-cursor-text-select-tool-button = + .title = Мәтінді таңдау құралын іске қосу +pdfjs-cursor-text-select-tool-button-label = Мәтінді таңдау құралы +pdfjs-cursor-hand-tool-button = + .title = Қол құралын іске қосу +pdfjs-cursor-hand-tool-button-label = Қол құралы +pdfjs-scroll-page-button = + .title = Беттерді айналдыруды пайдалану +pdfjs-scroll-page-button-label = Беттерді айналдыру +pdfjs-scroll-vertical-button = + .title = Вертикалды айналдыруды қолдану +pdfjs-scroll-vertical-button-label = Вертикалды айналдыру +pdfjs-scroll-horizontal-button = + .title = Горизонталды айналдыруды қолдану +pdfjs-scroll-horizontal-button-label = Горизонталды айналдыру +pdfjs-scroll-wrapped-button = + .title = Масштабталатын айналдыруды қолдану +pdfjs-scroll-wrapped-button-label = Масштабталатын айналдыру +pdfjs-spread-none-button = + .title = Жазық беттер режимін қолданбау +pdfjs-spread-none-button-label = Жазық беттер режимсіз +pdfjs-spread-odd-button = + .title = Жазық беттер тақ нөмірлі беттерден басталады +pdfjs-spread-odd-button-label = Тақ нөмірлі беттер сол жақтан +pdfjs-spread-even-button = + .title = Жазық беттер жұп нөмірлі беттерден басталады +pdfjs-spread-even-button-label = Жұп нөмірлі беттер сол жақтан + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Құжат қасиеттері… +pdfjs-document-properties-button-label = Құжат қасиеттері… +pdfjs-document-properties-file-name = Файл аты: +pdfjs-document-properties-file-size = Файл өлшемі: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } КБ ({ $b } байт) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } МБ ({ $b } байт) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байт) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } байт) +pdfjs-document-properties-title = Тақырыбы: +pdfjs-document-properties-author = Авторы: +pdfjs-document-properties-subject = Тақырыбы: +pdfjs-document-properties-keywords = Кілт сөздер: +pdfjs-document-properties-creation-date = Жасалған күні: +pdfjs-document-properties-modification-date = Түзету күні: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Жасаған: +pdfjs-document-properties-producer = PDF өндірген: +pdfjs-document-properties-version = PDF нұсқасы: +pdfjs-document-properties-page-count = Беттер саны: +pdfjs-document-properties-page-size = Бет өлшемі: +pdfjs-document-properties-page-size-unit-inches = дюйм +pdfjs-document-properties-page-size-unit-millimeters = мм +pdfjs-document-properties-page-size-orientation-portrait = тік +pdfjs-document-properties-page-size-orientation-landscape = жатық +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Жылдам Web көрінісі: +pdfjs-document-properties-linearized-yes = Иә +pdfjs-document-properties-linearized-no = Жоқ +pdfjs-document-properties-close-button = Жабу + +## Print + +pdfjs-print-progress-message = Құжатты баспаға шығару үшін дайындау… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Бас тарту +pdfjs-printing-not-supported = Ескерту: Баспаға шығаруды бұл браузер толығымен қолдамайды. +pdfjs-printing-not-ready = Ескерту: Баспаға шығару үшін, бұл PDF толығымен жүктеліп алынбады. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Бүйір панелін көрсету/жасыру +pdfjs-toggle-sidebar-notification-button = + .title = Бүйір панелін көрсету/жасыру (құжатта құрылымы/салынымдар/қабаттар бар) +pdfjs-toggle-sidebar-button-label = Бүйір панелін көрсету/жасыру +pdfjs-document-outline-button = + .title = Құжат құрылымын көрсету (барлық нәрселерді жазық қылу/жинау үшін қос шерту керек) +pdfjs-document-outline-button-label = Құжат құрамасы +pdfjs-attachments-button = + .title = Салынымдарды көрсету +pdfjs-attachments-button-label = Салынымдар +pdfjs-layers-button = + .title = Қабаттарды көрсету (барлық қабаттарды бастапқы күйге келтіру үшін екі рет шертіңіз) +pdfjs-layers-button-label = Қабаттар +pdfjs-thumbs-button = + .title = Кіші көріністерді көрсету +pdfjs-thumbs-button-label = Кіші көріністер +pdfjs-current-outline-item-button = + .title = Құрылымның ағымдағы элементін табу +pdfjs-current-outline-item-button-label = Құрылымның ағымдағы элементі +pdfjs-findbar-button = + .title = Құжаттан табу +pdfjs-findbar-button-label = Табу +pdfjs-additional-layers = Қосымша қабаттар + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = { $page } парағы +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page } парағы үшін кіші көрінісі + +## Find panel button title and messages + +pdfjs-find-input = + .title = Табу + .placeholder = Құжаттан табу… +pdfjs-find-previous-button = + .title = Осы сөздердің мәтіннен алдыңғы кездесуін табу +pdfjs-find-previous-button-label = Алдыңғысы +pdfjs-find-next-button = + .title = Осы сөздердің мәтіннен келесі кездесуін табу +pdfjs-find-next-button-label = Келесі +pdfjs-find-highlight-checkbox = Барлығын түспен ерекшелеу +pdfjs-find-match-case-checkbox-label = Регистрді ескеру +pdfjs-find-match-diacritics-checkbox-label = Диакритиканы ескеру +pdfjs-find-entire-word-checkbox-label = Сөздер толығымен +pdfjs-find-reached-top = Құжаттың басына жеттік, соңынан бастап жалғастырамыз +pdfjs-find-reached-bottom = Құжаттың соңына жеттік, басынан бастап жалғастырамыз +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } сәйкестік, барлығы { $total } + *[other] { $current } сәйкестік, барлығы { $total } + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] { $limit } сәйкестіктен көп + *[other] { $limit } сәйкестіктен көп + } +pdfjs-find-not-found = Сөз(дер) табылмады + +## Predefined zoom values + +pdfjs-page-scale-width = Парақ ені +pdfjs-page-scale-fit = Парақты сыйдыру +pdfjs-page-scale-auto = Автомасштабтау +pdfjs-page-scale-actual = Нақты өлшемі +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Бет { $page } + +## Loading indicator messages + +pdfjs-loading-error = PDF жүктеу кезінде қате кетті. +pdfjs-invalid-file-error = Зақымдалған немесе қате PDF файл. +pdfjs-missing-file-error = PDF файлы жоқ. +pdfjs-unexpected-response-error = Сервердің күтпеген жауабы. +pdfjs-rendering-error = Парақты өңдеу кезінде қате кетті. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } аңдатпасы] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Бұл PDF файлын ашу үшін парольді енгізіңіз. +pdfjs-password-invalid = Пароль дұрыс емес. Қайталап көріңіз. +pdfjs-password-ok-button = ОК +pdfjs-password-cancel-button = Бас тарту +pdfjs-web-fonts-disabled = Веб қаріптері сөндірілген: құрамына енгізілген PDF қаріптерін қолдану мүмкін емес. + +## Editing + +pdfjs-editor-free-text-button = + .title = Мәтін +pdfjs-editor-free-text-button-label = Мәтін +pdfjs-editor-ink-button = + .title = Сурет салу +pdfjs-editor-ink-button-label = Сурет салу +pdfjs-editor-stamp-button = + .title = Суреттерді қосу немесе түзету +pdfjs-editor-stamp-button-label = Суреттерді қосу немесе түзету +pdfjs-editor-highlight-button = + .title = Ерекшелеу +pdfjs-editor-highlight-button-label = Ерекшелеу +pdfjs-highlight-floating-button1 = + .title = Ерекшелеу + .aria-label = Ерекшелеу +pdfjs-highlight-floating-button-label = Ерекшелеу + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Сызбаны өшіру +pdfjs-editor-remove-freetext-button = + .title = Мәтінді өшіру +pdfjs-editor-remove-stamp-button = + .title = Суретті өшіру +pdfjs-editor-remove-highlight-button = + .title = Түспен ерекшелеуді өшіру + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Түс +pdfjs-editor-free-text-size-input = Өлшемі +pdfjs-editor-ink-color-input = Түс +pdfjs-editor-ink-thickness-input = Қалыңдығы +pdfjs-editor-ink-opacity-input = Мөлдірсіздігі +pdfjs-editor-stamp-add-image-button = + .title = Суретті қосу +pdfjs-editor-stamp-add-image-button-label = Суретті қосу +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Қалыңдығы +pdfjs-editor-free-highlight-thickness-title = + .title = Мәтіннен басқа элементтерді ерекшелеу кезінде қалыңдықты өзгерту +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Мәтін түзеткіші + .default-content = Теріп бастаңыз… +pdfjs-free-text = + .aria-label = Мәтін түзеткіші +pdfjs-free-text-default-content = Теруді бастау… +pdfjs-ink = + .aria-label = Сурет түзеткіші +pdfjs-ink-canvas = + .aria-label = Пайдаланушы жасаған сурет + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Балама мәтін +pdfjs-editor-alt-text-edit-button = + .aria-label = Балама мәтінді өңдеу +pdfjs-editor-alt-text-edit-button-label = Балама мәтінді өңдеу +pdfjs-editor-alt-text-dialog-label = Опцияны таңдау +pdfjs-editor-alt-text-dialog-description = Балама мәтін адамдар суретті көре алмағанда немесе ол жүктелмегенде көмектеседі. +pdfjs-editor-alt-text-add-description-label = Сипаттаманы қосу +pdfjs-editor-alt-text-add-description-description = Тақырыпты, баптауды немесе әрекетті сипаттайтын 1-2 сөйлемді қолдануға тырысыңыз. +pdfjs-editor-alt-text-mark-decorative-label = Декоративті деп белгілеу +pdfjs-editor-alt-text-mark-decorative-description = Бұл жиектер немесе су белгілері сияқты оюлық суреттер үшін пайдаланылады. +pdfjs-editor-alt-text-cancel-button = Бас тарту +pdfjs-editor-alt-text-save-button = Сақтау +pdfjs-editor-alt-text-decorative-tooltip = Декоративті деп белгіленген +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Мысалы, "Жас жігіт тамақ ішу үшін үстел басына отырады" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Балама мәтін + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Жоғарғы сол жақ бұрыш — өлшемін өзгерту +pdfjs-editor-resizer-label-top-middle = Жоғарғы ортасы — өлшемін өзгерту +pdfjs-editor-resizer-label-top-right = Жоғарғы оң жақ бұрыш — өлшемін өзгерту +pdfjs-editor-resizer-label-middle-right = Ортаңғы оң жақ — өлшемін өзгерту +pdfjs-editor-resizer-label-bottom-right = Төменгі оң жақ бұрыш — өлшемін өзгерту +pdfjs-editor-resizer-label-bottom-middle = Төменгі ортасы — өлшемін өзгерту +pdfjs-editor-resizer-label-bottom-left = Төменгі сол жақ бұрыш — өлшемін өзгерту +pdfjs-editor-resizer-label-middle-left = Ортаңғы сол жақ — өлшемін өзгерту +pdfjs-editor-resizer-top-left = + .aria-label = Жоғарғы сол жақ бұрыш — өлшемін өзгерту +pdfjs-editor-resizer-top-middle = + .aria-label = Жоғарғы ортасы — өлшемін өзгерту +pdfjs-editor-resizer-top-right = + .aria-label = Жоғарғы оң жақ бұрыш — өлшемін өзгерту +pdfjs-editor-resizer-middle-right = + .aria-label = Ортаңғы оң жақ — өлшемін өзгерту +pdfjs-editor-resizer-bottom-right = + .aria-label = Төменгі оң жақ бұрыш — өлшемін өзгерту +pdfjs-editor-resizer-bottom-middle = + .aria-label = Төменгі ортасы — өлшемін өзгерту +pdfjs-editor-resizer-bottom-left = + .aria-label = Төменгі сол жақ бұрыш — өлшемін өзгерту +pdfjs-editor-resizer-middle-left = + .aria-label = Ортаңғы сол жақ — өлшемін өзгерту + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Ерекшелеу түсі +pdfjs-editor-colorpicker-button = + .title = Түсті өзгерту +pdfjs-editor-colorpicker-dropdown = + .aria-label = Түс таңдаулары +pdfjs-editor-colorpicker-yellow = + .title = Сары +pdfjs-editor-colorpicker-green = + .title = Жасыл +pdfjs-editor-colorpicker-blue = + .title = Көк +pdfjs-editor-colorpicker-pink = + .title = Қызғылт +pdfjs-editor-colorpicker-red = + .title = Қызыл + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Барлығын көрсету +pdfjs-editor-highlight-show-all-button = + .title = Барлығын көрсету + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Балама мәтінді өңдеу (сурет сипаттамасы) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Балама мәтінді қосу (сурет сипаттамасы) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Сипаттамаңызды осында жазыңыз… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Суретті көре алмайтын адамдар үшін немесе сурет жүктелмеген кезіне арналған қысқаша сипаттама. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Бұл балама мәтін автоматты түрде жасалды және дәлсіз болуы мүмкін. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Көбірек білу +pdfjs-editor-new-alt-text-create-automatically-button-label = Балама мәтінді автоматты түрде жасау +pdfjs-editor-new-alt-text-not-now-button = Қазір емес +pdfjs-editor-new-alt-text-error-title = Балама мәтінді автоматты түрде жасау мүмкін болмады +pdfjs-editor-new-alt-text-error-description = Өзіңіздің балама мәтініңізді жазыңыз немесе кейінірек қайталап көріңіз. +pdfjs-editor-new-alt-text-error-close-button = Жабу +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Балама мәтін үшін ЖИ моделі жүктеп алынуда ({ $downloadedSize }/{ $totalSize } МБ) + .aria-valuetext = Балама мәтін үшін ЖИ моделі жүктеп алынуда ({ $downloadedSize }/{ $totalSize } МБ) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Балама мәтін қосылды +pdfjs-editor-new-alt-text-added-button-label = Балама мәтін қосылды +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Балама мәтін жоқ +pdfjs-editor-new-alt-text-missing-button-label = Балама мәтін жоқ +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Балама мәтінге пікір қалдыру +pdfjs-editor-new-alt-text-to-review-button-label = Балама мәтінге пікір қалдыру +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Автоматты түрде жасалды: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Суреттің балама мәтінінің баптаулары +pdfjs-image-alt-text-settings-button-label = Суреттің балама мәтінінің баптаулары +pdfjs-editor-alt-text-settings-dialog-label = Суреттің балама мәтінінің баптаулары +pdfjs-editor-alt-text-settings-automatic-title = Автоматты балама мәтін +pdfjs-editor-alt-text-settings-create-model-button-label = Балама мәтінді автоматты түрде жасау +pdfjs-editor-alt-text-settings-create-model-description = Суретті көре алмайтын адамдар үшін немесе сурет жүктелмеген кезіне арналған сипаттамаларды ұсынады. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Баламалы мәтіннің ЖИ моделі ({ $totalSize } МБ) +pdfjs-editor-alt-text-settings-ai-model-description = Деректеріңіз жеке болып қалуы үшін құрылғыңызда жергілікті түрде жұмыс істейді. Автоматты балама мәтін үшін қажет. +pdfjs-editor-alt-text-settings-delete-model-button = Өшіру +pdfjs-editor-alt-text-settings-download-model-button = Жүктеп алу +pdfjs-editor-alt-text-settings-downloading-model-button = Жүктеліп алынуда… +pdfjs-editor-alt-text-settings-editor-title = Баламалы мәтін редакторы +pdfjs-editor-alt-text-settings-show-dialog-button-label = Суретті қосқанда балама мәтін редакторын бірден көрсету +pdfjs-editor-alt-text-settings-show-dialog-description = Барлық суреттерде балама мәтін бар екеніне көз жеткізуге көмектеседі. +pdfjs-editor-alt-text-settings-close-button = Жабу + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Ерекшелеу өшірілді +pdfjs-editor-undo-bar-message-freetext = Мәтін өшірілді +pdfjs-editor-undo-bar-message-ink = Сызба өшірілді +pdfjs-editor-undo-bar-message-stamp = Сурет өшірілді +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } анимация өшірілді + *[other] { $count } анимация өшірілді + } +pdfjs-editor-undo-bar-undo-button = + .title = Болдырмау +pdfjs-editor-undo-bar-undo-button-label = Болдырмау +pdfjs-editor-undo-bar-close-button = + .title = Жабу +pdfjs-editor-undo-bar-close-button-label = Жабу diff --git a/public/pdfjs/web/locale/km/viewer.ftl b/public/pdfjs/web/locale/km/viewer.ftl new file mode 100644 index 0000000..6efd105 --- /dev/null +++ b/public/pdfjs/web/locale/km/viewer.ftl @@ -0,0 +1,223 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = ទំព័រ​មុន +pdfjs-previous-button-label = មុន +pdfjs-next-button = + .title = ទំព័រ​បន្ទាប់ +pdfjs-next-button-label = បន្ទាប់ +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = ទំព័រ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = នៃ { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } នៃ { $pagesCount }) +pdfjs-zoom-out-button = + .title = ​បង្រួម +pdfjs-zoom-out-button-label = ​បង្រួម +pdfjs-zoom-in-button = + .title = ​ពង្រីក +pdfjs-zoom-in-button-label = ​ពង្រីក +pdfjs-zoom-select = + .title = ពង្រីក +pdfjs-presentation-mode-button = + .title = ប្ដូរ​ទៅ​របៀប​បទ​បង្ហាញ +pdfjs-presentation-mode-button-label = របៀប​បទ​បង្ហាញ +pdfjs-open-file-button = + .title = បើក​ឯកសារ +pdfjs-open-file-button-label = បើក +pdfjs-print-button = + .title = បោះពុម្ព +pdfjs-print-button-label = បោះពុម្ព + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = ឧបករណ៍ +pdfjs-tools-button-label = ឧបករណ៍ +pdfjs-first-page-button = + .title = ទៅកាន់​ទំព័រ​ដំបូង​ +pdfjs-first-page-button-label = ទៅកាន់​ទំព័រ​ដំបូង​ +pdfjs-last-page-button = + .title = ទៅកាន់​ទំព័រ​ចុងក្រោយ​ +pdfjs-last-page-button-label = ទៅកាន់​ទំព័រ​ចុងក្រោយ +pdfjs-page-rotate-cw-button = + .title = បង្វិល​ស្រប​ទ្រនិច​នាឡិកា +pdfjs-page-rotate-cw-button-label = បង្វិល​ស្រប​ទ្រនិច​នាឡិកា +pdfjs-page-rotate-ccw-button = + .title = បង្វិល​ច្រាស​ទ្រនិច​នាឡិកា​​ +pdfjs-page-rotate-ccw-button-label = បង្វិល​ច្រាស​ទ្រនិច​នាឡិកា​​ +pdfjs-cursor-text-select-tool-button = + .title = បើក​ឧបករណ៍​ជ្រើស​អត្ថបទ +pdfjs-cursor-text-select-tool-button-label = ឧបករណ៍​ជ្រើស​អត្ថបទ +pdfjs-cursor-hand-tool-button = + .title = បើក​ឧបករណ៍​ដៃ +pdfjs-cursor-hand-tool-button-label = ឧបករណ៍​ដៃ + +## Document properties dialog + +pdfjs-document-properties-button = + .title = លក្ខណ​សម្បត្តិ​ឯកសារ… +pdfjs-document-properties-button-label = លក្ខណ​សម្បត្តិ​ឯកសារ… +pdfjs-document-properties-file-name = ឈ្មោះ​ឯកសារ៖ +pdfjs-document-properties-file-size = ទំហំ​ឯកសារ៖ +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } បៃ) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } បៃ) +pdfjs-document-properties-title = ចំណងជើង៖ +pdfjs-document-properties-author = អ្នក​និពន្ធ៖ +pdfjs-document-properties-subject = ប្រធានបទ៖ +pdfjs-document-properties-keywords = ពាក្យ​គន្លឹះ៖ +pdfjs-document-properties-creation-date = កាលបរិច្ឆេទ​បង្កើត៖ +pdfjs-document-properties-modification-date = កាលបរិច្ឆេទ​កែប្រែ៖ +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = អ្នក​បង្កើត៖ +pdfjs-document-properties-producer = កម្មវិធី​បង្កើត PDF ៖ +pdfjs-document-properties-version = កំណែ PDF ៖ +pdfjs-document-properties-page-count = ចំនួន​ទំព័រ៖ +pdfjs-document-properties-page-size-unit-inches = អ៊ីញ +pdfjs-document-properties-page-size-unit-millimeters = មម +pdfjs-document-properties-page-size-orientation-portrait = បញ្ឈរ +pdfjs-document-properties-page-size-orientation-landscape = ផ្តេក +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = សំបុត្រ + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +pdfjs-document-properties-linearized-yes = បាទ/ចាស +pdfjs-document-properties-linearized-no = ទេ +pdfjs-document-properties-close-button = បិទ + +## Print + +pdfjs-print-progress-message = កំពុង​រៀបចំ​ឯកសារ​សម្រាប់​បោះពុម្ព… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = បោះបង់ +pdfjs-printing-not-supported = ការ​ព្រមាន ៖ កា​រ​បោះពុម្ព​មិន​ត្រូវ​បាន​គាំទ្រ​ពេញលេញ​ដោយ​កម្មវិធី​រុករក​នេះ​ទេ ។ +pdfjs-printing-not-ready = ព្រមាន៖ PDF មិន​ត្រូវ​បាន​ផ្ទុក​ទាំងស្រុង​ដើម្បី​បោះពុម្ព​ទេ។ + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = បិទ/បើក​គ្រាប់​រំកិល +pdfjs-toggle-sidebar-button-label = បិទ/បើក​គ្រាប់​រំកិល +pdfjs-document-outline-button = + .title = បង្ហាញ​គ្រោង​ឯកសារ (ចុច​ទ្វេ​ដង​ដើម្បី​ពង្រីក/បង្រួម​ធាតុ​ទាំងអស់) +pdfjs-document-outline-button-label = គ្រោង​ឯកសារ +pdfjs-attachments-button = + .title = បង្ហាញ​ឯកសារ​ភ្ជាប់ +pdfjs-attachments-button-label = ឯកសារ​ភ្ជាប់ +pdfjs-thumbs-button = + .title = បង្ហាញ​រូបភាព​តូចៗ +pdfjs-thumbs-button-label = រួបភាព​តូចៗ +pdfjs-findbar-button = + .title = រក​នៅ​ក្នុង​ឯកសារ +pdfjs-findbar-button-label = រក + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = ទំព័រ { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = រូបភាព​តូច​របស់​ទំព័រ { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = រក + .placeholder = រក​នៅ​ក្នុង​ឯកសារ... +pdfjs-find-previous-button = + .title = រក​ពាក្យ ឬ​ឃ្លា​ដែល​បាន​ជួប​មុន +pdfjs-find-previous-button-label = មុន +pdfjs-find-next-button = + .title = រក​ពាក្យ ឬ​ឃ្លា​ដែល​បាន​ជួប​បន្ទាប់ +pdfjs-find-next-button-label = បន្ទាប់ +pdfjs-find-highlight-checkbox = បន្លិច​ទាំងអស់ +pdfjs-find-match-case-checkbox-label = ករណី​ដំណូច +pdfjs-find-reached-top = បាន​បន្ត​ពី​ខាង​ក្រោម ទៅ​ដល់​ខាង​​លើ​នៃ​ឯកសារ +pdfjs-find-reached-bottom = បាន​បន្ត​ពី​ខាងលើ ទៅដល់​ចុង​​នៃ​ឯកសារ +pdfjs-find-not-found = រក​មិន​ឃើញ​ពាក្យ ឬ​ឃ្លា + +## Predefined zoom values + +pdfjs-page-scale-width = ទទឹង​ទំព័រ +pdfjs-page-scale-fit = សម​ទំព័រ +pdfjs-page-scale-auto = ពង្រីក​ស្វ័យប្រវត្តិ +pdfjs-page-scale-actual = ទំហំ​ជាក់ស្ដែង +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = មាន​កំហុស​បាន​កើតឡើង​ពេល​កំពុង​ផ្ទុក PDF ។ +pdfjs-invalid-file-error = ឯកសារ PDF ខូច ឬ​មិន​ត្រឹមត្រូវ ។ +pdfjs-missing-file-error = បាត់​ឯកសារ PDF +pdfjs-unexpected-response-error = ការ​ឆ្លើយ​តម​ម៉ាស៊ីន​មេ​ដែល​មិន​បាន​រំពឹង។ +pdfjs-rendering-error = មាន​កំហុស​បាន​កើតឡើង​ពេល​បង្ហាញ​ទំព័រ ។ + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } ចំណារ​ពន្យល់] + +## Password + +pdfjs-password-label = បញ្ចូល​ពាក្យសម្ងាត់​ដើម្បី​បើក​ឯកសារ PDF នេះ។ +pdfjs-password-invalid = ពាក្យសម្ងាត់​មិន​ត្រឹមត្រូវ។ សូម​ព្យាយាម​ម្ដងទៀត។ +pdfjs-password-ok-button = យល់​ព្រម +pdfjs-password-cancel-button = បោះបង់ +pdfjs-web-fonts-disabled = បាន​បិទ​ពុម្ពអក្សរ​បណ្ដាញ ៖ មិន​អាច​ប្រើ​ពុម្ពអក្សរ PDF ដែល​បាន​បង្កប់​បាន​ទេ ។ + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/kn/viewer.ftl b/public/pdfjs/web/locale/kn/viewer.ftl new file mode 100644 index 0000000..0332255 --- /dev/null +++ b/public/pdfjs/web/locale/kn/viewer.ftl @@ -0,0 +1,213 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = ಹಿಂದಿನ ಪುಟ +pdfjs-previous-button-label = ಹಿಂದಿನ +pdfjs-next-button = + .title = ಮುಂದಿನ ಪುಟ +pdfjs-next-button-label = ಮುಂದಿನ +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = ಪುಟ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount } ರಲ್ಲಿ +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pagesCount } ರಲ್ಲಿ { $pageNumber }) +pdfjs-zoom-out-button = + .title = ಕಿರಿದಾಗಿಸು +pdfjs-zoom-out-button-label = ಕಿರಿದಾಗಿಸಿ +pdfjs-zoom-in-button = + .title = ಹಿರಿದಾಗಿಸು +pdfjs-zoom-in-button-label = ಹಿರಿದಾಗಿಸಿ +pdfjs-zoom-select = + .title = ಗಾತ್ರಬದಲಿಸು +pdfjs-presentation-mode-button = + .title = ಪ್ರಸ್ತುತಿ (ಪ್ರಸೆಂಟೇಶನ್) ಕ್ರಮಕ್ಕೆ ಬದಲಾಯಿಸು +pdfjs-presentation-mode-button-label = ಪ್ರಸ್ತುತಿ (ಪ್ರಸೆಂಟೇಶನ್) ಕ್ರಮ +pdfjs-open-file-button = + .title = ಕಡತವನ್ನು ತೆರೆ +pdfjs-open-file-button-label = ತೆರೆಯಿರಿ +pdfjs-print-button = + .title = ಮುದ್ರಿಸು +pdfjs-print-button-label = ಮುದ್ರಿಸಿ + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = ಉಪಕರಣಗಳು +pdfjs-tools-button-label = ಉಪಕರಣಗಳು +pdfjs-first-page-button = + .title = ಮೊದಲ ಪುಟಕ್ಕೆ ತೆರಳು +pdfjs-first-page-button-label = ಮೊದಲ ಪುಟಕ್ಕೆ ತೆರಳು +pdfjs-last-page-button = + .title = ಕೊನೆಯ ಪುಟಕ್ಕೆ ತೆರಳು +pdfjs-last-page-button-label = ಕೊನೆಯ ಪುಟಕ್ಕೆ ತೆರಳು +pdfjs-page-rotate-cw-button = + .title = ಪ್ರದಕ್ಷಿಣೆಯಲ್ಲಿ ತಿರುಗಿಸು +pdfjs-page-rotate-cw-button-label = ಪ್ರದಕ್ಷಿಣೆಯಲ್ಲಿ ತಿರುಗಿಸು +pdfjs-page-rotate-ccw-button = + .title = ಅಪ್ರದಕ್ಷಿಣೆಯಲ್ಲಿ ತಿರುಗಿಸು +pdfjs-page-rotate-ccw-button-label = ಅಪ್ರದಕ್ಷಿಣೆಯಲ್ಲಿ ತಿರುಗಿಸು +pdfjs-cursor-text-select-tool-button = + .title = ಪಠ್ಯ ಆಯ್ಕೆ ಉಪಕರಣವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ +pdfjs-cursor-text-select-tool-button-label = ಪಠ್ಯ ಆಯ್ಕೆಯ ಉಪಕರಣ +pdfjs-cursor-hand-tool-button = + .title = ಕೈ ಉಪಕರಣವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ +pdfjs-cursor-hand-tool-button-label = ಕೈ ಉಪಕರಣ + +## Document properties dialog + +pdfjs-document-properties-button = + .title = ಡಾಕ್ಯುಮೆಂಟ್‌ ಗುಣಗಳು... +pdfjs-document-properties-button-label = ಡಾಕ್ಯುಮೆಂಟ್‌ ಗುಣಗಳು... +pdfjs-document-properties-file-name = ಕಡತದ ಹೆಸರು: +pdfjs-document-properties-file-size = ಕಡತದ ಗಾತ್ರ: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } ಬೈಟ್‍ಗಳು) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } ಬೈಟ್‍ಗಳು) +pdfjs-document-properties-title = ಶೀರ್ಷಿಕೆ: +pdfjs-document-properties-author = ಕರ್ತೃ: +pdfjs-document-properties-subject = ವಿಷಯ: +pdfjs-document-properties-keywords = ಮುಖ್ಯಪದಗಳು: +pdfjs-document-properties-creation-date = ರಚಿಸಿದ ದಿನಾಂಕ: +pdfjs-document-properties-modification-date = ಮಾರ್ಪಡಿಸಲಾದ ದಿನಾಂಕ: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = ರಚಿಸಿದವರು: +pdfjs-document-properties-producer = PDF ಉತ್ಪಾದಕ: +pdfjs-document-properties-version = PDF ಆವೃತ್ತಿ: +pdfjs-document-properties-page-count = ಪುಟದ ಎಣಿಕೆ: +pdfjs-document-properties-page-size-unit-inches = ಇದರಲ್ಲಿ +pdfjs-document-properties-page-size-orientation-portrait = ಭಾವಚಿತ್ರ +pdfjs-document-properties-page-size-orientation-landscape = ಪ್ರಕೃತಿ ಚಿತ್ರ + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + + +## + +pdfjs-document-properties-close-button = ಮುಚ್ಚು + +## Print + +pdfjs-print-progress-message = ಮುದ್ರಿಸುವುದಕ್ಕಾಗಿ ದಸ್ತಾವೇಜನ್ನು ಸಿದ್ಧಗೊಳಿಸಲಾಗುತ್ತಿದೆ… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = ರದ್ದು ಮಾಡು +pdfjs-printing-not-supported = ಎಚ್ಚರಿಕೆ: ಈ ಜಾಲವೀಕ್ಷಕದಲ್ಲಿ ಮುದ್ರಣಕ್ಕೆ ಸಂಪೂರ್ಣ ಬೆಂಬಲವಿಲ್ಲ. +pdfjs-printing-not-ready = ಎಚ್ಚರಿಕೆ: PDF ಕಡತವು ಮುದ್ರಿಸಲು ಸಂಪೂರ್ಣವಾಗಿ ಲೋಡ್ ಆಗಿಲ್ಲ. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = ಬದಿಪಟ್ಟಿಯನ್ನು ಹೊರಳಿಸು +pdfjs-toggle-sidebar-button-label = ಬದಿಪಟ್ಟಿಯನ್ನು ಹೊರಳಿಸು +pdfjs-document-outline-button-label = ದಸ್ತಾವೇಜಿನ ಹೊರರೇಖೆ +pdfjs-attachments-button = + .title = ಲಗತ್ತುಗಳನ್ನು ತೋರಿಸು +pdfjs-attachments-button-label = ಲಗತ್ತುಗಳು +pdfjs-thumbs-button = + .title = ಚಿಕ್ಕಚಿತ್ರದಂತೆ ತೋರಿಸು +pdfjs-thumbs-button-label = ಚಿಕ್ಕಚಿತ್ರಗಳು +pdfjs-findbar-button = + .title = ದಸ್ತಾವೇಜಿನಲ್ಲಿ ಹುಡುಕು +pdfjs-findbar-button-label = ಹುಡುಕು + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = ಪುಟ { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = ಪುಟವನ್ನು ಚಿಕ್ಕಚಿತ್ರದಂತೆ ತೋರಿಸು { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = ಹುಡುಕು + .placeholder = ದಸ್ತಾವೇಜಿನಲ್ಲಿ ಹುಡುಕು… +pdfjs-find-previous-button = + .title = ವಾಕ್ಯದ ಹಿಂದಿನ ಇರುವಿಕೆಯನ್ನು ಹುಡುಕು +pdfjs-find-previous-button-label = ಹಿಂದಿನ +pdfjs-find-next-button = + .title = ವಾಕ್ಯದ ಮುಂದಿನ ಇರುವಿಕೆಯನ್ನು ಹುಡುಕು +pdfjs-find-next-button-label = ಮುಂದಿನ +pdfjs-find-highlight-checkbox = ಎಲ್ಲವನ್ನು ಹೈಲೈಟ್ ಮಾಡು +pdfjs-find-match-case-checkbox-label = ಕೇಸನ್ನು ಹೊಂದಿಸು +pdfjs-find-reached-top = ದಸ್ತಾವೇಜಿನ ಮೇಲ್ಭಾಗವನ್ನು ತಲುಪಿದೆ, ಕೆಳಗಿನಿಂದ ಆರಂಭಿಸು +pdfjs-find-reached-bottom = ದಸ್ತಾವೇಜಿನ ಕೊನೆಯನ್ನು ತಲುಪಿದೆ, ಮೇಲಿನಿಂದ ಆರಂಭಿಸು +pdfjs-find-not-found = ವಾಕ್ಯವು ಕಂಡು ಬಂದಿಲ್ಲ + +## Predefined zoom values + +pdfjs-page-scale-width = ಪುಟದ ಅಗಲ +pdfjs-page-scale-fit = ಪುಟದ ಸರಿಹೊಂದಿಕೆ +pdfjs-page-scale-auto = ಸ್ವಯಂಚಾಲಿತ ಗಾತ್ರಬದಲಾವಣೆ +pdfjs-page-scale-actual = ನಿಜವಾದ ಗಾತ್ರ +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = PDF ಅನ್ನು ಲೋಡ್ ಮಾಡುವಾಗ ಒಂದು ದೋಷ ಎದುರಾಗಿದೆ. +pdfjs-invalid-file-error = ಅಮಾನ್ಯವಾದ ಅಥವ ಹಾಳಾದ PDF ಕಡತ. +pdfjs-missing-file-error = PDF ಕಡತ ಇಲ್ಲ. +pdfjs-unexpected-response-error = ಅನಿರೀಕ್ಷಿತವಾದ ಪೂರೈಕೆಗಣಕದ ಪ್ರತಿಕ್ರಿಯೆ. +pdfjs-rendering-error = ಪುಟವನ್ನು ನಿರೂಪಿಸುವಾಗ ಒಂದು ದೋಷ ಎದುರಾಗಿದೆ. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } ಟಿಪ್ಪಣಿ] + +## Password + +pdfjs-password-label = PDF ಅನ್ನು ತೆರೆಯಲು ಗುಪ್ತಪದವನ್ನು ನಮೂದಿಸಿ. +pdfjs-password-invalid = ಅಮಾನ್ಯವಾದ ಗುಪ್ತಪದ, ದಯವಿಟ್ಟು ಇನ್ನೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಿ. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = ರದ್ದು ಮಾಡು +pdfjs-web-fonts-disabled = ಜಾಲ ಅಕ್ಷರಶೈಲಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ: ಅಡಕಗೊಳಿಸಿದ PDF ಅಕ್ಷರಶೈಲಿಗಳನ್ನು ಬಳಸಲು ಸಾಧ್ಯವಾಗಿಲ್ಲ. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/ko/viewer.ftl b/public/pdfjs/web/locale/ko/viewer.ftl new file mode 100644 index 0000000..a321a11 --- /dev/null +++ b/public/pdfjs/web/locale/ko/viewer.ftl @@ -0,0 +1,503 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = 이전 페이지 +pdfjs-previous-button-label = 이전 +pdfjs-next-button = + .title = 다음 페이지 +pdfjs-next-button-label = 다음 +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = 페이지 +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = / { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount }) +pdfjs-zoom-out-button = + .title = 축소 +pdfjs-zoom-out-button-label = 축소 +pdfjs-zoom-in-button = + .title = 확대 +pdfjs-zoom-in-button-label = 확대 +pdfjs-zoom-select = + .title = 확대/축소 +pdfjs-presentation-mode-button = + .title = 프레젠테이션 모드로 전환 +pdfjs-presentation-mode-button-label = 프레젠테이션 모드 +pdfjs-open-file-button = + .title = 파일 열기 +pdfjs-open-file-button-label = 열기 +pdfjs-print-button = + .title = 인쇄 +pdfjs-print-button-label = 인쇄 +pdfjs-save-button = + .title = 저장 +pdfjs-save-button-label = 저장 +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = 다운로드 +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = 다운로드 +pdfjs-bookmark-button = + .title = 현재 페이지 (현재 페이지에서 URL 보기) +pdfjs-bookmark-button-label = 현재 페이지 + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = 도구 +pdfjs-tools-button-label = 도구 +pdfjs-first-page-button = + .title = 첫 페이지로 이동 +pdfjs-first-page-button-label = 첫 페이지로 이동 +pdfjs-last-page-button = + .title = 마지막 페이지로 이동 +pdfjs-last-page-button-label = 마지막 페이지로 이동 +pdfjs-page-rotate-cw-button = + .title = 시계방향으로 회전 +pdfjs-page-rotate-cw-button-label = 시계방향으로 회전 +pdfjs-page-rotate-ccw-button = + .title = 시계 반대방향으로 회전 +pdfjs-page-rotate-ccw-button-label = 시계 반대방향으로 회전 +pdfjs-cursor-text-select-tool-button = + .title = 텍스트 선택 도구 활성화 +pdfjs-cursor-text-select-tool-button-label = 텍스트 선택 도구 +pdfjs-cursor-hand-tool-button = + .title = 손 도구 활성화 +pdfjs-cursor-hand-tool-button-label = 손 도구 +pdfjs-scroll-page-button = + .title = 페이지 스크롤 사용 +pdfjs-scroll-page-button-label = 페이지 스크롤 +pdfjs-scroll-vertical-button = + .title = 세로 스크롤 사용 +pdfjs-scroll-vertical-button-label = 세로 스크롤 +pdfjs-scroll-horizontal-button = + .title = 가로 스크롤 사용 +pdfjs-scroll-horizontal-button-label = 가로 스크롤 +pdfjs-scroll-wrapped-button = + .title = 래핑(자동 줄 바꿈) 스크롤 사용 +pdfjs-scroll-wrapped-button-label = 래핑 스크롤 +pdfjs-spread-none-button = + .title = 한 페이지 보기 +pdfjs-spread-none-button-label = 펼침 없음 +pdfjs-spread-odd-button = + .title = 홀수 페이지로 시작하는 두 페이지 보기 +pdfjs-spread-odd-button-label = 홀수 펼침 +pdfjs-spread-even-button = + .title = 짝수 페이지로 시작하는 두 페이지 보기 +pdfjs-spread-even-button-label = 짝수 펼침 + +## Document properties dialog + +pdfjs-document-properties-button = + .title = 문서 속성… +pdfjs-document-properties-button-label = 문서 속성… +pdfjs-document-properties-file-name = 파일 이름: +pdfjs-document-properties-file-size = 파일 크기: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } 바이트) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } 바이트) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b }바이트) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b }바이트) +pdfjs-document-properties-title = 제목: +pdfjs-document-properties-author = 작성자: +pdfjs-document-properties-subject = 주제: +pdfjs-document-properties-keywords = 키워드: +pdfjs-document-properties-creation-date = 작성 날짜: +pdfjs-document-properties-modification-date = 수정 날짜: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = 작성 프로그램: +pdfjs-document-properties-producer = PDF 변환 소프트웨어: +pdfjs-document-properties-version = PDF 버전: +pdfjs-document-properties-page-count = 페이지 수: +pdfjs-document-properties-page-size = 페이지 크기: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = 세로 방향 +pdfjs-document-properties-page-size-orientation-landscape = 가로 방향 +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = 레터 +pdfjs-document-properties-page-size-name-legal = 리걸 + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = 빠른 웹 보기: +pdfjs-document-properties-linearized-yes = 예 +pdfjs-document-properties-linearized-no = 아니요 +pdfjs-document-properties-close-button = 닫기 + +## Print + +pdfjs-print-progress-message = 인쇄 문서 준비 중… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = 취소 +pdfjs-printing-not-supported = 경고: 이 브라우저는 인쇄를 완전히 지원하지 않습니다. +pdfjs-printing-not-ready = 경고: 이 PDF를 인쇄를 할 수 있을 정도로 읽어들이지 못했습니다. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = 사이드바 표시/숨기기 +pdfjs-toggle-sidebar-notification-button = + .title = 사이드바 표시/숨기기 (문서에 아웃라인/첨부파일/레이어 포함됨) +pdfjs-toggle-sidebar-button-label = 사이드바 표시/숨기기 +pdfjs-document-outline-button = + .title = 문서 아웃라인 보기 (더블 클릭해서 모든 항목 펼치기/접기) +pdfjs-document-outline-button-label = 문서 아웃라인 +pdfjs-attachments-button = + .title = 첨부파일 보기 +pdfjs-attachments-button-label = 첨부파일 +pdfjs-layers-button = + .title = 레이어 보기 (더블 클릭해서 모든 레이어를 기본 상태로 재설정) +pdfjs-layers-button-label = 레이어 +pdfjs-thumbs-button = + .title = 미리보기 +pdfjs-thumbs-button-label = 미리보기 +pdfjs-current-outline-item-button = + .title = 현재 아웃라인 항목 찾기 +pdfjs-current-outline-item-button-label = 현재 아웃라인 항목 +pdfjs-findbar-button = + .title = 검색 +pdfjs-findbar-button-label = 검색 +pdfjs-additional-layers = 추가 레이어 + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = { $page } 페이지 +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page } 페이지 미리보기 + +## Find panel button title and messages + +pdfjs-find-input = + .title = 찾기 + .placeholder = 문서에서 찾기… +pdfjs-find-previous-button = + .title = 지정 문자열에 일치하는 1개 부분을 검색 +pdfjs-find-previous-button-label = 이전 +pdfjs-find-next-button = + .title = 지정 문자열에 일치하는 다음 부분을 검색 +pdfjs-find-next-button-label = 다음 +pdfjs-find-highlight-checkbox = 모두 강조 표시 +pdfjs-find-match-case-checkbox-label = 대/소문자 구분 +pdfjs-find-match-diacritics-checkbox-label = 분음 부호 일치 +pdfjs-find-entire-word-checkbox-label = 단어 단위로 +pdfjs-find-reached-top = 문서 처음까지 검색하고 끝으로 돌아와 검색했습니다. +pdfjs-find-reached-bottom = 문서 끝까지 검색하고 앞으로 돌아와 검색했습니다. +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = { $current } / { $total } 일치 +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = { $limit }개 이상 일치 +pdfjs-find-not-found = 검색 결과 없음 + +## Predefined zoom values + +pdfjs-page-scale-width = 페이지 너비에 맞추기 +pdfjs-page-scale-fit = 페이지에 맞추기 +pdfjs-page-scale-auto = 자동 +pdfjs-page-scale-actual = 실제 크기 +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = { $page } 페이지 + +## Loading indicator messages + +pdfjs-loading-error = PDF를 로드하는 동안 오류가 발생했습니다. +pdfjs-invalid-file-error = 잘못되었거나 손상된 PDF 파일. +pdfjs-missing-file-error = PDF 파일 없음. +pdfjs-unexpected-response-error = 예기치 않은 서버 응답입니다. +pdfjs-rendering-error = 페이지를 렌더링하는 동안 오류가 발생했습니다. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date } { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } 주석] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = 이 PDF 파일을 열 수 있는 비밀번호를 입력하세요. +pdfjs-password-invalid = 잘못된 비밀번호입니다. 다시 시도하세요. +pdfjs-password-ok-button = 확인 +pdfjs-password-cancel-button = 취소 +pdfjs-web-fonts-disabled = 웹 폰트가 비활성화됨: 내장된 PDF 글꼴을 사용할 수 없습니다. + +## Editing + +pdfjs-editor-free-text-button = + .title = 텍스트 +pdfjs-editor-free-text-button-label = 텍스트 +pdfjs-editor-ink-button = + .title = 그리기 +pdfjs-editor-ink-button-label = 그리기 +pdfjs-editor-stamp-button = + .title = 이미지 추가 또는 편집 +pdfjs-editor-stamp-button-label = 이미지 추가 또는 편집 +pdfjs-editor-highlight-button = + .title = 강조 표시 +pdfjs-editor-highlight-button-label = 강조 표시 +pdfjs-highlight-floating-button1 = + .title = 강조 표시 + .aria-label = 강조 표시 +pdfjs-highlight-floating-button-label = 강조 표시 + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = 그리기 제거 +pdfjs-editor-remove-freetext-button = + .title = 텍스트 제거 +pdfjs-editor-remove-stamp-button = + .title = 이미지 제거 +pdfjs-editor-remove-highlight-button = + .title = 강조 표시 제거 + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = 색상 +pdfjs-editor-free-text-size-input = 크기 +pdfjs-editor-ink-color-input = 색상 +pdfjs-editor-ink-thickness-input = 두께 +pdfjs-editor-ink-opacity-input = 불투명도 +pdfjs-editor-stamp-add-image-button = + .title = 이미지 추가 +pdfjs-editor-stamp-add-image-button-label = 이미지 추가 +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = 두께 +pdfjs-editor-free-highlight-thickness-title = + .title = 텍스트 이외의 항목을 강조 표시할 때 두께 변경 +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = 텍스트 편집기 + .default-content = 입력을 시작하세요… +pdfjs-free-text = + .aria-label = 텍스트 편집기 +pdfjs-free-text-default-content = 입력하세요… +pdfjs-ink = + .aria-label = 그리기 편집기 +pdfjs-ink-canvas = + .aria-label = 사용자 생성 이미지 + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = 대체 텍스트 +pdfjs-editor-alt-text-edit-button = + .aria-label = 대체 텍스트 편집 +pdfjs-editor-alt-text-edit-button-label = 대체 텍스트 편집 +pdfjs-editor-alt-text-dialog-label = 옵션을 선택하세요 +pdfjs-editor-alt-text-dialog-description = 대체 텍스트는 사람들이 이미지를 볼 수 없거나 이미지가 로드되지 않을 때 도움이 됩니다. +pdfjs-editor-alt-text-add-description-label = 설명 추가 +pdfjs-editor-alt-text-add-description-description = 주제, 설정, 동작을 설명하는 1~2개의 문장을 목표로 하세요. +pdfjs-editor-alt-text-mark-decorative-label = 장식용으로 표시 +pdfjs-editor-alt-text-mark-decorative-description = 테두리나 워터마크와 같은 장식적인 이미지에 사용됩니다. +pdfjs-editor-alt-text-cancel-button = 취소 +pdfjs-editor-alt-text-save-button = 저장 +pdfjs-editor-alt-text-decorative-tooltip = 장식용으로 표시됨 +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = 예를 들어, “한 청년이 식탁에 앉아 식사를 하고 있습니다.” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = 대체 텍스트 + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = 왼쪽 위 — 크기 조정 +pdfjs-editor-resizer-label-top-middle = 가운데 위 - 크기 조정 +pdfjs-editor-resizer-label-top-right = 오른쪽 위 — 크기 조정 +pdfjs-editor-resizer-label-middle-right = 오른쪽 가운데 — 크기 조정 +pdfjs-editor-resizer-label-bottom-right = 오른쪽 아래 - 크기 조정 +pdfjs-editor-resizer-label-bottom-middle = 가운데 아래 — 크기 조정 +pdfjs-editor-resizer-label-bottom-left = 왼쪽 아래 - 크기 조정 +pdfjs-editor-resizer-label-middle-left = 왼쪽 가운데 — 크기 조정 +pdfjs-editor-resizer-top-left = + .aria-label = 왼쪽 위 — 크기 조정 +pdfjs-editor-resizer-top-middle = + .aria-label = 가운데 위 - 크기 조정 +pdfjs-editor-resizer-top-right = + .aria-label = 오른쪽 위 — 크기 조정 +pdfjs-editor-resizer-middle-right = + .aria-label = 오른쪽 가운데 — 크기 조정 +pdfjs-editor-resizer-bottom-right = + .aria-label = 오른쪽 아래 - 크기 조정 +pdfjs-editor-resizer-bottom-middle = + .aria-label = 가운데 아래 — 크기 조정 +pdfjs-editor-resizer-bottom-left = + .aria-label = 왼쪽 아래 - 크기 조정 +pdfjs-editor-resizer-middle-left = + .aria-label = 왼쪽 가운데 — 크기 조정 + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = 색상 +pdfjs-editor-colorpicker-button = + .title = 색상 변경 +pdfjs-editor-colorpicker-dropdown = + .aria-label = 색상 선택 +pdfjs-editor-colorpicker-yellow = + .title = 노란색 +pdfjs-editor-colorpicker-green = + .title = 녹색 +pdfjs-editor-colorpicker-blue = + .title = 파란색 +pdfjs-editor-colorpicker-pink = + .title = 분홍색 +pdfjs-editor-colorpicker-red = + .title = 빨간색 + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = 모두 보기 +pdfjs-editor-highlight-show-all-button = + .title = 모두 보기 + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = 대체 텍스트 (이미지 설명) 편집 +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = 대체 텍스트 (이미지 설명) 추가 +pdfjs-editor-new-alt-text-textarea = + .placeholder = 여기에 설명을 작성하세요… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = 이미지가 보이지 않거나 이미지가 로딩되지 않는 경우를 위한 간단한 설명입니다. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = 이 대체 텍스트는 자동으로 생성되었으므로 정확하지 않을 수 있습니다. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = 더 알아보기 +pdfjs-editor-new-alt-text-create-automatically-button-label = 자동으로 대체 텍스트 생성 +pdfjs-editor-new-alt-text-not-now-button = 나중에 +pdfjs-editor-new-alt-text-error-title = 대체 텍스트를 자동으로 생성할 수 없습니다. +pdfjs-editor-new-alt-text-error-description = 대체 텍스트를 직접 작성하거나 나중에 다시 시도하세요. +pdfjs-editor-new-alt-text-error-close-button = 닫기 +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = 대체 텍스트 AI 모델 다운로드 중 ({ $downloadedSize } / { $totalSize } MB) + .aria-valuetext = 대체 텍스트 AI 모델 다운로드 중 ({ $downloadedSize } / { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = 대체 텍스트 추가됨 +pdfjs-editor-new-alt-text-added-button-label = 대체 텍스트 추가됨 +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = 대체 텍스트 누락 +pdfjs-editor-new-alt-text-missing-button-label = 대체 텍스트 누락 +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = 대체 텍스트 검토 +pdfjs-editor-new-alt-text-to-review-button-label = 대체 텍스트 검토 +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = 자동으로 생성됨: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = 이미지 대체 텍스트 설정 +pdfjs-image-alt-text-settings-button-label = 이미지 대체 텍스트 설정 +pdfjs-editor-alt-text-settings-dialog-label = 이미지 대체 텍스트 설정 +pdfjs-editor-alt-text-settings-automatic-title = 자동 대체 텍스트 +pdfjs-editor-alt-text-settings-create-model-button-label = 자동으로 대체 텍스트 생성 +pdfjs-editor-alt-text-settings-create-model-description = 이미지가 보이지 않거나 이미지가 로딩되지 않을 때 도움이 되는 설명을 제안합니다. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = 대체 텍스트 AI 모델 ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = 사용자의 장치에서 로컬로 실행되므로 데이터가 비공개로 유지됩니다. 자동 대체 텍스트에 필요합니다. +pdfjs-editor-alt-text-settings-delete-model-button = 삭제 +pdfjs-editor-alt-text-settings-download-model-button = 다운로드 +pdfjs-editor-alt-text-settings-downloading-model-button = 다운로드 중… +pdfjs-editor-alt-text-settings-editor-title = 대체 텍스트 편집기 +pdfjs-editor-alt-text-settings-show-dialog-button-label = 이미지 추가 시 바로 대체 텍스트 편집기 표시 +pdfjs-editor-alt-text-settings-show-dialog-description = 모든 이미지에 대체 텍스트가 있는지 확인하는 데 도움이 됩니다. +pdfjs-editor-alt-text-settings-close-button = 닫기 + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = 강조 표시 제거됨 +pdfjs-editor-undo-bar-message-freetext = 텍스트 제거됨 +pdfjs-editor-undo-bar-message-ink = 그리기 제거됨 +pdfjs-editor-undo-bar-message-stamp = 이미지 제거됨 +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = 주석 { $count }개 제거됨 +pdfjs-editor-undo-bar-undo-button = + .title = 실행 취소 +pdfjs-editor-undo-bar-undo-button-label = 실행 취소 +pdfjs-editor-undo-bar-close-button = + .title = 닫기 +pdfjs-editor-undo-bar-close-button-label = 닫기 diff --git a/public/pdfjs/web/locale/lij/viewer.ftl b/public/pdfjs/web/locale/lij/viewer.ftl new file mode 100644 index 0000000..b2941f9 --- /dev/null +++ b/public/pdfjs/web/locale/lij/viewer.ftl @@ -0,0 +1,247 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pagina primma +pdfjs-previous-button-label = Precedente +pdfjs-next-button = + .title = Pagina dòppo +pdfjs-next-button-label = Pròscima +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pagina +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = de { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount }) +pdfjs-zoom-out-button = + .title = Diminoisci zoom +pdfjs-zoom-out-button-label = Diminoisci zoom +pdfjs-zoom-in-button = + .title = Aomenta zoom +pdfjs-zoom-in-button-label = Aomenta zoom +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Vanni into mòddo de prezentaçion +pdfjs-presentation-mode-button-label = Mòddo de prezentaçion +pdfjs-open-file-button = + .title = Arvi file +pdfjs-open-file-button-label = Arvi +pdfjs-print-button = + .title = Stanpa +pdfjs-print-button-label = Stanpa + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Atressi +pdfjs-tools-button-label = Atressi +pdfjs-first-page-button = + .title = Vanni a-a primma pagina +pdfjs-first-page-button-label = Vanni a-a primma pagina +pdfjs-last-page-button = + .title = Vanni a l'urtima pagina +pdfjs-last-page-button-label = Vanni a l'urtima pagina +pdfjs-page-rotate-cw-button = + .title = Gia into verso oraio +pdfjs-page-rotate-cw-button-label = Gia into verso oraio +pdfjs-page-rotate-ccw-button = + .title = Gia into verso antioraio +pdfjs-page-rotate-ccw-button-label = Gia into verso antioraio +pdfjs-cursor-text-select-tool-button = + .title = Abilita strumento de seleçion do testo +pdfjs-cursor-text-select-tool-button-label = Strumento de seleçion do testo +pdfjs-cursor-hand-tool-button = + .title = Abilita strumento man +pdfjs-cursor-hand-tool-button-label = Strumento man +pdfjs-scroll-vertical-button = + .title = Deuvia rebelamento verticale +pdfjs-scroll-vertical-button-label = Rebelamento verticale +pdfjs-scroll-horizontal-button = + .title = Deuvia rebelamento orizontâ +pdfjs-scroll-horizontal-button-label = Rebelamento orizontâ +pdfjs-scroll-wrapped-button = + .title = Deuvia rebelamento incapsolou +pdfjs-scroll-wrapped-button-label = Rebelamento incapsolou +pdfjs-spread-none-button = + .title = No unite a-a difuxon de pagina +pdfjs-spread-none-button-label = No difuxon +pdfjs-spread-odd-button = + .title = Uniscite a-a difuxon de pagina co-o numero dèspa +pdfjs-spread-odd-button-label = Difuxon dèspa +pdfjs-spread-even-button = + .title = Uniscite a-a difuxon de pagina co-o numero pari +pdfjs-spread-even-button-label = Difuxon pari + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Propietæ do documento… +pdfjs-document-properties-button-label = Propietæ do documento… +pdfjs-document-properties-file-name = Nomme schedaio: +pdfjs-document-properties-file-size = Dimenscion schedaio: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } byte) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } byte) +pdfjs-document-properties-title = Titolo: +pdfjs-document-properties-author = Aoto: +pdfjs-document-properties-subject = Ogetto: +pdfjs-document-properties-keywords = Paròlle ciave: +pdfjs-document-properties-creation-date = Dæta creaçion: +pdfjs-document-properties-modification-date = Dæta cangiamento: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Aotô originale: +pdfjs-document-properties-producer = Produtô PDF: +pdfjs-document-properties-version = Verscion PDF: +pdfjs-document-properties-page-count = Contezzo pagine: +pdfjs-document-properties-page-size = Dimenscion da pagina: +pdfjs-document-properties-page-size-unit-inches = dii gròsci +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = drito +pdfjs-document-properties-page-size-orientation-landscape = desteizo +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letia +pdfjs-document-properties-page-size-name-legal = Lezze + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Vista veloce do Web: +pdfjs-document-properties-linearized-yes = Sci +pdfjs-document-properties-linearized-no = No +pdfjs-document-properties-close-button = Særa + +## Print + +pdfjs-print-progress-message = Praparo o documento pe-a stanpa… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Anulla +pdfjs-printing-not-supported = Atençion: a stanpa a no l'é conpletamente soportâ da sto navegatô. +pdfjs-printing-not-ready = Atençion: o PDF o no l'é ancon caregou conpletamente pe-a stanpa. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Ativa/dizativa bara de scianco +pdfjs-toggle-sidebar-button-label = Ativa/dizativa bara de scianco +pdfjs-document-outline-button = + .title = Fanni vedde o contorno do documento (scicca doggio pe espande/ridue tutti i elementi) +pdfjs-document-outline-button-label = Contorno do documento +pdfjs-attachments-button = + .title = Fanni vedde alegæ +pdfjs-attachments-button-label = Alegæ +pdfjs-thumbs-button = + .title = Mostra miniatue +pdfjs-thumbs-button-label = Miniatue +pdfjs-findbar-button = + .title = Treuva into documento +pdfjs-findbar-button-label = Treuva + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pagina { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatua da pagina { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Treuva + .placeholder = Treuva into documento… +pdfjs-find-previous-button = + .title = Treuva a ripetiçion precedente do testo da çercâ +pdfjs-find-previous-button-label = Precedente +pdfjs-find-next-button = + .title = Treuva a ripetiçion dòppo do testo da çercâ +pdfjs-find-next-button-label = Segoente +pdfjs-find-highlight-checkbox = Evidençia +pdfjs-find-match-case-checkbox-label = Maioscole/minoscole +pdfjs-find-entire-word-checkbox-label = Poula intrega +pdfjs-find-reached-top = Razonto a fin da pagina, continoa da l'iniçio +pdfjs-find-reached-bottom = Razonto l'iniçio da pagina, continoa da-a fin +pdfjs-find-not-found = Testo no trovou + +## Predefined zoom values + +pdfjs-page-scale-width = Larghessa pagina +pdfjs-page-scale-fit = Adatta a una pagina +pdfjs-page-scale-auto = Zoom aotomatico +pdfjs-page-scale-actual = Dimenscioin efetive +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = S'é verificou 'n'erô itno caregamento do PDF. +pdfjs-invalid-file-error = O schedaio PDF o l'é no valido ò aroinou. +pdfjs-missing-file-error = O schedaio PDF o no gh'é. +pdfjs-unexpected-response-error = Risposta inprevista do-u server +pdfjs-rendering-error = Gh'é stæto 'n'erô itno rendering da pagina. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anotaçion: { $type }] + +## Password + +pdfjs-password-label = Dimme a paròlla segreta pe arvî sto schedaio PDF. +pdfjs-password-invalid = Paròlla segreta sbalia. Preuva torna. +pdfjs-password-ok-button = Va ben +pdfjs-password-cancel-button = Anulla +pdfjs-web-fonts-disabled = I font do web en dizativæ: inposcibile adeuviâ i carateri do PDF. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/lo/viewer.ftl b/public/pdfjs/web/locale/lo/viewer.ftl new file mode 100644 index 0000000..557e201 --- /dev/null +++ b/public/pdfjs/web/locale/lo/viewer.ftl @@ -0,0 +1,313 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = ຫນ້າກ່ອນຫນ້າ +pdfjs-previous-button-label = ກ່ອນຫນ້າ +pdfjs-next-button = + .title = ຫນ້າຖັດໄປ +pdfjs-next-button-label = ຖັດໄປ +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = ຫນ້າ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = ຈາກ { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } ຈາກ { $pagesCount }) +pdfjs-zoom-out-button = + .title = ຂະຫຍາຍອອກ +pdfjs-zoom-out-button-label = ຂະຫຍາຍອອກ +pdfjs-zoom-in-button = + .title = ຂະຫຍາຍເຂົ້າ +pdfjs-zoom-in-button-label = ຂະຫຍາຍເຂົ້າ +pdfjs-zoom-select = + .title = ຂະຫຍາຍ +pdfjs-presentation-mode-button = + .title = ສັບປ່ຽນເປັນໂຫມດການນຳສະເຫນີ +pdfjs-presentation-mode-button-label = ໂຫມດການນຳສະເຫນີ +pdfjs-open-file-button = + .title = ເປີດໄຟລ໌ +pdfjs-open-file-button-label = ເປີດ +pdfjs-print-button = + .title = ພິມ +pdfjs-print-button-label = ພິມ +pdfjs-save-button = + .title = ບັນທຶກ +pdfjs-save-button-label = ບັນທຶກ +pdfjs-bookmark-button = + .title = ໜ້າປັດຈຸບັນ (ເບິ່ງ URL ຈາກໜ້າປັດຈຸບັນ) +pdfjs-bookmark-button-label = ຫນ້າ​ປັດ​ຈຸ​ບັນ + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = ເຄື່ອງມື +pdfjs-tools-button-label = ເຄື່ອງມື +pdfjs-first-page-button = + .title = ໄປທີ່ຫນ້າທຳອິດ +pdfjs-first-page-button-label = ໄປທີ່ຫນ້າທຳອິດ +pdfjs-last-page-button = + .title = ໄປທີ່ຫນ້າສຸດທ້າຍ +pdfjs-last-page-button-label = ໄປທີ່ຫນ້າສຸດທ້າຍ +pdfjs-page-rotate-cw-button = + .title = ຫມູນຕາມເຂັມໂມງ +pdfjs-page-rotate-cw-button-label = ຫມູນຕາມເຂັມໂມງ +pdfjs-page-rotate-ccw-button = + .title = ຫມູນທວນເຂັມໂມງ +pdfjs-page-rotate-ccw-button-label = ຫມູນທວນເຂັມໂມງ +pdfjs-cursor-text-select-tool-button = + .title = ເປີດໃຊ້ເຄື່ອງມືການເລືອກຂໍ້ຄວາມ +pdfjs-cursor-text-select-tool-button-label = ເຄື່ອງມືເລືອກຂໍ້ຄວາມ +pdfjs-cursor-hand-tool-button = + .title = ເປີດໃຊ້ເຄື່ອງມືມື +pdfjs-cursor-hand-tool-button-label = ເຄື່ອງມືມື +pdfjs-scroll-page-button = + .title = ໃຊ້ການເລື່ອນໜ້າ +pdfjs-scroll-page-button-label = ເລື່ອນໜ້າ +pdfjs-scroll-vertical-button = + .title = ໃຊ້ການເລື່ອນແນວຕັ້ງ +pdfjs-scroll-vertical-button-label = ເລື່ອນແນວຕັ້ງ +pdfjs-scroll-horizontal-button = + .title = ໃຊ້ການເລື່ອນແນວນອນ +pdfjs-scroll-horizontal-button-label = ເລື່ອນແນວນອນ +pdfjs-scroll-wrapped-button = + .title = ໃຊ້ Wrapped Scrolling +pdfjs-scroll-wrapped-button-label = Wrapped Scrolling +pdfjs-spread-none-button = + .title = ບໍ່ຕ້ອງຮ່ວມການແຜ່ກະຈາຍຫນ້າ +pdfjs-spread-none-button-label = ບໍ່ມີການແຜ່ກະຈາຍ +pdfjs-spread-odd-button = + .title = ເຂົ້າຮ່ວມການແຜ່ກະຈາຍຫນ້າເລີ່ມຕົ້ນດ້ວຍຫນ້າເລກຄີກ +pdfjs-spread-odd-button-label = ການແຜ່ກະຈາຍຄີກ +pdfjs-spread-even-button = + .title = ເຂົ້າຮ່ວມການແຜ່ກະຈາຍຂອງຫນ້າເລີ່ມຕົ້ນດ້ວຍຫນ້າເລກຄູ່ +pdfjs-spread-even-button-label = ການແຜ່ກະຈາຍຄູ່ + +## Document properties dialog + +pdfjs-document-properties-button = + .title = ຄຸນສົມບັດເອກະສານ... +pdfjs-document-properties-button-label = ຄຸນສົມບັດເອກະສານ... +pdfjs-document-properties-file-name = ຊື່ໄຟລ໌: +pdfjs-document-properties-file-size = ຂະຫນາດໄຟລ໌: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } ໄບຕ໌) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } ໄບຕ໌) +pdfjs-document-properties-title = ຫົວຂໍ້: +pdfjs-document-properties-author = ຜູ້ຂຽນ: +pdfjs-document-properties-subject = ຫົວຂໍ້: +pdfjs-document-properties-keywords = ຄໍາທີ່ຕ້ອງການຄົ້ນຫາ: +pdfjs-document-properties-creation-date = ວັນທີສ້າງ: +pdfjs-document-properties-modification-date = ວັນທີແກ້ໄຂ: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = ຜູ້ສ້າງ: +pdfjs-document-properties-producer = ຜູ້ຜະລິດ PDF: +pdfjs-document-properties-version = ເວີຊັ່ນ PDF: +pdfjs-document-properties-page-count = ຈຳນວນໜ້າ: +pdfjs-document-properties-page-size = ຂະໜາດໜ້າ: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = ລວງຕັ້ງ +pdfjs-document-properties-page-size-orientation-landscape = ລວງນອນ +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = ຈົດໝາຍ +pdfjs-document-properties-page-size-name-legal = ຂໍ້ກົດຫມາຍ + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = ມຸມມອງເວັບທີ່ໄວ: +pdfjs-document-properties-linearized-yes = ແມ່ນ +pdfjs-document-properties-linearized-no = ບໍ່ +pdfjs-document-properties-close-button = ປິດ + +## Print + +pdfjs-print-progress-message = ກຳລັງກະກຽມເອກະສານສຳລັບການພິມ... +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = ຍົກເລີກ +pdfjs-printing-not-supported = ຄຳເຕືອນ: ບຼາວເຊີນີ້ບໍ່ຮອງຮັບການພິມຢ່າງເຕັມທີ່. +pdfjs-printing-not-ready = ຄໍາ​ເຕືອນ​: PDF ບໍ່​ໄດ້​ຖືກ​ໂຫຼດ​ຢ່າງ​ເຕັມ​ທີ່​ສໍາ​ລັບ​ການ​ພິມ​. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = ເປີດ/ປິດແຖບຂ້າງ +pdfjs-toggle-sidebar-notification-button = + .title = ສະຫຼັບແຖບດ້ານຂ້າງ (ເອກະສານປະກອບມີໂຄງຮ່າງ/ໄຟລ໌ແນບ/ຊັ້ນຂໍ້ມູນ) +pdfjs-toggle-sidebar-button-label = ເປີດ/ປິດແຖບຂ້າງ +pdfjs-document-outline-button = + .title = ສະ​ແດງ​ໂຄງ​ຮ່າງ​ເອ​ກະ​ສານ (ກົດ​ສອງ​ຄັ້ງ​ເພື່ອ​ຂະ​ຫຍາຍ / ຫຍໍ້​ລາຍ​ການ​ທັງ​ຫມົດ​) +pdfjs-document-outline-button-label = ເຄົ້າຮ່າງເອກະສານ +pdfjs-attachments-button = + .title = ສະແດງໄຟລ໌ແນບ +pdfjs-attachments-button-label = ໄຟລ໌ແນບ +pdfjs-layers-button = + .title = ສະແດງຊັ້ນຂໍ້ມູນ (ຄລິກສອງເທື່ອເພື່ອຣີເຊັດຊັ້ນຂໍ້ມູນທັງໝົດໃຫ້ເປັນສະຖານະເລີ່ມຕົ້ນ) +pdfjs-layers-button-label = ຊັ້ນ +pdfjs-thumbs-button = + .title = ສະແດງຮູບຫຍໍ້ +pdfjs-thumbs-button-label = ຮູບຕົວຢ່າງ +pdfjs-current-outline-item-button = + .title = ຊອກຫາລາຍການໂຄງຮ່າງປະຈຸບັນ +pdfjs-current-outline-item-button-label = ລາຍການໂຄງຮ່າງປະຈຸບັນ +pdfjs-findbar-button = + .title = ຊອກຫາໃນເອກະສານ +pdfjs-findbar-button-label = ຄົ້ນຫາ +pdfjs-additional-layers = ຊັ້ນຂໍ້ມູນເພີ່ມເຕີມ + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = ໜ້າ { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = ຮູບຕົວຢ່າງຂອງໜ້າ { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = ຄົ້ນຫາ + .placeholder = ຊອກຫາໃນເອກະສານ... +pdfjs-find-previous-button = + .title = ຊອກຫາການປະກົດຕົວທີ່ຜ່ານມາຂອງປະໂຫຍກ +pdfjs-find-previous-button-label = ກ່ອນຫນ້ານີ້ +pdfjs-find-next-button = + .title = ຊອກຫາຕຳແຫນ່ງຖັດໄປຂອງວະລີ +pdfjs-find-next-button-label = ຕໍ່ໄປ +pdfjs-find-highlight-checkbox = ໄຮໄລທ໌ທັງຫມົດ +pdfjs-find-match-case-checkbox-label = ກໍລະນີທີ່ກົງກັນ +pdfjs-find-match-diacritics-checkbox-label = ເຄື່ອງໝາຍກຳກັບການອອກສຽງກົງກັນ +pdfjs-find-entire-word-checkbox-label = ກົງກັນທຸກຄຳ +pdfjs-find-reached-top = ມາຮອດເທິງຂອງເອກະສານ, ສືບຕໍ່ຈາກລຸ່ມ +pdfjs-find-reached-bottom = ຮອດຕອນທ້າຍຂອງເອກະສານ, ສືບຕໍ່ຈາກເທິງ +pdfjs-find-not-found = ບໍ່ພົບວະລີທີ່ຕ້ອງການ + +## Predefined zoom values + +pdfjs-page-scale-width = ຄວາມກວ້າງໜ້າ +pdfjs-page-scale-fit = ໜ້າພໍດີ +pdfjs-page-scale-auto = ຊູມອັດຕະໂນມັດ +pdfjs-page-scale-actual = ຂະໜາດຕົວຈິງ +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = ໜ້າ { $page } + +## Loading indicator messages + +pdfjs-loading-error = ມີຂໍ້ຜິດພາດເກີດຂື້ນຂະນະທີ່ກຳລັງໂຫລດ PDF. +pdfjs-invalid-file-error = ໄຟລ໌ PDF ບໍ່ຖືກຕ້ອງຫລືເສຍຫາຍ. +pdfjs-missing-file-error = ບໍ່ມີໄຟລ໌ PDF. +pdfjs-unexpected-response-error = ການຕອບສະໜອງຂອງເຊີບເວີທີ່ບໍ່ຄາດຄິດ. +pdfjs-rendering-error = ມີຂໍ້ຜິດພາດເກີດຂື້ນຂະນະທີ່ກຳລັງເຣັນເດີຫນ້າ. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } ຄຳບັນຍາຍ] + +## Password + +pdfjs-password-label = ໃສ່ລະຫັດຜ່ານເພື່ອເປີດໄຟລ໌ PDF ນີ້. +pdfjs-password-invalid = ລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ. ກະລຸນາລອງອີກຄັ້ງ. +pdfjs-password-ok-button = ຕົກລົງ +pdfjs-password-cancel-button = ຍົກເລີກ +pdfjs-web-fonts-disabled = ຟອນເວັບຖືກປິດໃຊ້ງານ: ບໍ່ສາມາດໃຊ້ຟອນ PDF ທີ່ຝັງໄວ້ໄດ້. + +## Editing + +pdfjs-editor-free-text-button = + .title = ຂໍ້ຄວາມ +pdfjs-editor-free-text-button-label = ຂໍ້ຄວາມ +pdfjs-editor-ink-button = + .title = ແຕ້ມ +pdfjs-editor-ink-button-label = ແຕ້ມ + +## Remove button for the various kind of editor. + + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = ສີ +pdfjs-editor-free-text-size-input = ຂະຫນາດ +pdfjs-editor-ink-color-input = ສີ +pdfjs-editor-ink-thickness-input = ຄວາມຫນາ +pdfjs-editor-ink-opacity-input = ຄວາມໂປ່ງໃສ +pdfjs-free-text = + .aria-label = ຕົວແກ້ໄຂຂໍ້ຄວາມ +pdfjs-free-text-default-content = ເລີ່ມພິມ... +pdfjs-ink = + .aria-label = ຕົວແກ້ໄຂຮູບແຕ້ມ +pdfjs-ink-canvas = + .aria-label = ຮູບພາບທີ່ຜູ້ໃຊ້ສ້າງ + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + diff --git a/public/pdfjs/web/locale/locale.json b/public/pdfjs/web/locale/locale.json new file mode 100644 index 0000000..2012211 --- /dev/null +++ b/public/pdfjs/web/locale/locale.json @@ -0,0 +1 @@ +{"ach":"ach/viewer.ftl","af":"af/viewer.ftl","an":"an/viewer.ftl","ar":"ar/viewer.ftl","ast":"ast/viewer.ftl","az":"az/viewer.ftl","be":"be/viewer.ftl","bg":"bg/viewer.ftl","bn":"bn/viewer.ftl","bo":"bo/viewer.ftl","br":"br/viewer.ftl","brx":"brx/viewer.ftl","bs":"bs/viewer.ftl","ca":"ca/viewer.ftl","cak":"cak/viewer.ftl","ckb":"ckb/viewer.ftl","cs":"cs/viewer.ftl","cy":"cy/viewer.ftl","da":"da/viewer.ftl","de":"de/viewer.ftl","dsb":"dsb/viewer.ftl","el":"el/viewer.ftl","en-ca":"en-CA/viewer.ftl","en-gb":"en-GB/viewer.ftl","en-us":"en-US/viewer.ftl","eo":"eo/viewer.ftl","es-ar":"es-AR/viewer.ftl","es-cl":"es-CL/viewer.ftl","es-es":"es-ES/viewer.ftl","es-mx":"es-MX/viewer.ftl","et":"et/viewer.ftl","eu":"eu/viewer.ftl","fa":"fa/viewer.ftl","ff":"ff/viewer.ftl","fi":"fi/viewer.ftl","fr":"fr/viewer.ftl","fur":"fur/viewer.ftl","fy-nl":"fy-NL/viewer.ftl","ga-ie":"ga-IE/viewer.ftl","gd":"gd/viewer.ftl","gl":"gl/viewer.ftl","gn":"gn/viewer.ftl","gu-in":"gu-IN/viewer.ftl","he":"he/viewer.ftl","hi-in":"hi-IN/viewer.ftl","hr":"hr/viewer.ftl","hsb":"hsb/viewer.ftl","hu":"hu/viewer.ftl","hy-am":"hy-AM/viewer.ftl","hye":"hye/viewer.ftl","ia":"ia/viewer.ftl","id":"id/viewer.ftl","is":"is/viewer.ftl","it":"it/viewer.ftl","ja":"ja/viewer.ftl","ka":"ka/viewer.ftl","kab":"kab/viewer.ftl","kk":"kk/viewer.ftl","km":"km/viewer.ftl","kn":"kn/viewer.ftl","ko":"ko/viewer.ftl","lij":"lij/viewer.ftl","lo":"lo/viewer.ftl","lt":"lt/viewer.ftl","ltg":"ltg/viewer.ftl","lv":"lv/viewer.ftl","meh":"meh/viewer.ftl","mk":"mk/viewer.ftl","mr":"mr/viewer.ftl","ms":"ms/viewer.ftl","my":"my/viewer.ftl","nb-no":"nb-NO/viewer.ftl","ne-np":"ne-NP/viewer.ftl","nl":"nl/viewer.ftl","nn-no":"nn-NO/viewer.ftl","oc":"oc/viewer.ftl","pa-in":"pa-IN/viewer.ftl","pl":"pl/viewer.ftl","pt-br":"pt-BR/viewer.ftl","pt-pt":"pt-PT/viewer.ftl","rm":"rm/viewer.ftl","ro":"ro/viewer.ftl","ru":"ru/viewer.ftl","sat":"sat/viewer.ftl","sc":"sc/viewer.ftl","scn":"scn/viewer.ftl","sco":"sco/viewer.ftl","si":"si/viewer.ftl","sk":"sk/viewer.ftl","skr":"skr/viewer.ftl","sl":"sl/viewer.ftl","son":"son/viewer.ftl","sq":"sq/viewer.ftl","sr":"sr/viewer.ftl","sv-se":"sv-SE/viewer.ftl","szl":"szl/viewer.ftl","ta":"ta/viewer.ftl","te":"te/viewer.ftl","tg":"tg/viewer.ftl","th":"th/viewer.ftl","tl":"tl/viewer.ftl","tr":"tr/viewer.ftl","trs":"trs/viewer.ftl","uk":"uk/viewer.ftl","ur":"ur/viewer.ftl","uz":"uz/viewer.ftl","vi":"vi/viewer.ftl","wo":"wo/viewer.ftl","xh":"xh/viewer.ftl","zh-cn":"zh-CN/viewer.ftl","zh-tw":"zh-TW/viewer.ftl"} \ No newline at end of file diff --git a/public/pdfjs/web/locale/lt/viewer.ftl b/public/pdfjs/web/locale/lt/viewer.ftl new file mode 100644 index 0000000..a8ee7a0 --- /dev/null +++ b/public/pdfjs/web/locale/lt/viewer.ftl @@ -0,0 +1,268 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Ankstesnis puslapis +pdfjs-previous-button-label = Ankstesnis +pdfjs-next-button = + .title = Kitas puslapis +pdfjs-next-button-label = Kitas +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Puslapis +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = iš { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } iš { $pagesCount }) +pdfjs-zoom-out-button = + .title = Sumažinti +pdfjs-zoom-out-button-label = Sumažinti +pdfjs-zoom-in-button = + .title = Padidinti +pdfjs-zoom-in-button-label = Padidinti +pdfjs-zoom-select = + .title = Mastelis +pdfjs-presentation-mode-button = + .title = Pereiti į pateikties veikseną +pdfjs-presentation-mode-button-label = Pateikties veiksena +pdfjs-open-file-button = + .title = Atverti failą +pdfjs-open-file-button-label = Atverti +pdfjs-print-button = + .title = Spausdinti +pdfjs-print-button-label = Spausdinti + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Priemonės +pdfjs-tools-button-label = Priemonės +pdfjs-first-page-button = + .title = Eiti į pirmą puslapį +pdfjs-first-page-button-label = Eiti į pirmą puslapį +pdfjs-last-page-button = + .title = Eiti į paskutinį puslapį +pdfjs-last-page-button-label = Eiti į paskutinį puslapį +pdfjs-page-rotate-cw-button = + .title = Pasukti pagal laikrodžio rodyklę +pdfjs-page-rotate-cw-button-label = Pasukti pagal laikrodžio rodyklę +pdfjs-page-rotate-ccw-button = + .title = Pasukti prieš laikrodžio rodyklę +pdfjs-page-rotate-ccw-button-label = Pasukti prieš laikrodžio rodyklę +pdfjs-cursor-text-select-tool-button = + .title = Įjungti teksto žymėjimo įrankį +pdfjs-cursor-text-select-tool-button-label = Teksto žymėjimo įrankis +pdfjs-cursor-hand-tool-button = + .title = Įjungti vilkimo įrankį +pdfjs-cursor-hand-tool-button-label = Vilkimo įrankis +pdfjs-scroll-page-button = + .title = Naudoti puslapio slinkimą +pdfjs-scroll-page-button-label = Puslapio slinkimas +pdfjs-scroll-vertical-button = + .title = Naudoti vertikalų slinkimą +pdfjs-scroll-vertical-button-label = Vertikalus slinkimas +pdfjs-scroll-horizontal-button = + .title = Naudoti horizontalų slinkimą +pdfjs-scroll-horizontal-button-label = Horizontalus slinkimas +pdfjs-scroll-wrapped-button = + .title = Naudoti išklotą slinkimą +pdfjs-scroll-wrapped-button-label = Išklotas slinkimas +pdfjs-spread-none-button = + .title = Nejungti puslapių į dvilapius +pdfjs-spread-none-button-label = Be dvilapių +pdfjs-spread-odd-button = + .title = Sujungti į dvilapius pradedant nelyginiais puslapiais +pdfjs-spread-odd-button-label = Nelyginiai dvilapiai +pdfjs-spread-even-button = + .title = Sujungti į dvilapius pradedant lyginiais puslapiais +pdfjs-spread-even-button-label = Lyginiai dvilapiai + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumento savybės… +pdfjs-document-properties-button-label = Dokumento savybės… +pdfjs-document-properties-file-name = Failo vardas: +pdfjs-document-properties-file-size = Failo dydis: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } B) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } B) +pdfjs-document-properties-title = Antraštė: +pdfjs-document-properties-author = Autorius: +pdfjs-document-properties-subject = Tema: +pdfjs-document-properties-keywords = Reikšminiai žodžiai: +pdfjs-document-properties-creation-date = Sukūrimo data: +pdfjs-document-properties-modification-date = Modifikavimo data: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Kūrėjas: +pdfjs-document-properties-producer = PDF generatorius: +pdfjs-document-properties-version = PDF versija: +pdfjs-document-properties-page-count = Puslapių skaičius: +pdfjs-document-properties-page-size = Puslapio dydis: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = stačias +pdfjs-document-properties-page-size-orientation-landscape = gulsčias +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Laiškas +pdfjs-document-properties-page-size-name-legal = Dokumentas + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Spartus žiniatinklio rodinys: +pdfjs-document-properties-linearized-yes = Taip +pdfjs-document-properties-linearized-no = Ne +pdfjs-document-properties-close-button = Užverti + +## Print + +pdfjs-print-progress-message = Dokumentas ruošiamas spausdinimui… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Atsisakyti +pdfjs-printing-not-supported = Dėmesio! Spausdinimas šioje naršyklėje nėra pilnai realizuotas. +pdfjs-printing-not-ready = Dėmesio! PDF failas dar nėra pilnai įkeltas spausdinimui. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Rodyti / slėpti šoninį polangį +pdfjs-toggle-sidebar-notification-button = + .title = Parankinė (dokumentas turi struktūrą / priedų / sluoksnių) +pdfjs-toggle-sidebar-button-label = Šoninis polangis +pdfjs-document-outline-button = + .title = Rodyti dokumento struktūrą (spustelėkite dukart norėdami išplėsti/suskleisti visus elementus) +pdfjs-document-outline-button-label = Dokumento struktūra +pdfjs-attachments-button = + .title = Rodyti priedus +pdfjs-attachments-button-label = Priedai +pdfjs-layers-button = + .title = Rodyti sluoksnius (spustelėkite dukart, norėdami atstatyti visus sluoksnius į numatytąją būseną) +pdfjs-layers-button-label = Sluoksniai +pdfjs-thumbs-button = + .title = Rodyti puslapių miniatiūras +pdfjs-thumbs-button-label = Miniatiūros +pdfjs-current-outline-item-button = + .title = Rasti dabartinį struktūros elementą +pdfjs-current-outline-item-button-label = Dabartinis struktūros elementas +pdfjs-findbar-button = + .title = Ieškoti dokumente +pdfjs-findbar-button-label = Rasti +pdfjs-additional-layers = Papildomi sluoksniai + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = { $page } puslapis +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page } puslapio miniatiūra + +## Find panel button title and messages + +pdfjs-find-input = + .title = Rasti + .placeholder = Rasti dokumente… +pdfjs-find-previous-button = + .title = Ieškoti ankstesnio frazės egzemplioriaus +pdfjs-find-previous-button-label = Ankstesnis +pdfjs-find-next-button = + .title = Ieškoti tolesnio frazės egzemplioriaus +pdfjs-find-next-button-label = Tolesnis +pdfjs-find-highlight-checkbox = Viską paryškinti +pdfjs-find-match-case-checkbox-label = Skirti didžiąsias ir mažąsias raides +pdfjs-find-match-diacritics-checkbox-label = Skirti diakritinius ženklus +pdfjs-find-entire-word-checkbox-label = Ištisi žodžiai +pdfjs-find-reached-top = Pasiekus dokumento pradžią, paieška pratęsta nuo pabaigos +pdfjs-find-reached-bottom = Pasiekus dokumento pabaigą, paieška pratęsta nuo pradžios +pdfjs-find-not-found = Ieškoma frazė nerasta + +## Predefined zoom values + +pdfjs-page-scale-width = Priderinti prie lapo pločio +pdfjs-page-scale-fit = Pritaikyti prie lapo dydžio +pdfjs-page-scale-auto = Automatinis mastelis +pdfjs-page-scale-actual = Tikras dydis +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = { $page } puslapis + +## Loading indicator messages + +pdfjs-loading-error = Įkeliant PDF failą įvyko klaida. +pdfjs-invalid-file-error = Tai nėra PDF failas arba jis yra sugadintas. +pdfjs-missing-file-error = PDF failas nerastas. +pdfjs-unexpected-response-error = Netikėtas serverio atsakas. +pdfjs-rendering-error = Atvaizduojant puslapį įvyko klaida. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [„{ $type }“ tipo anotacija] + +## Password + +pdfjs-password-label = Įveskite slaptažodį šiam PDF failui atverti. +pdfjs-password-invalid = Slaptažodis neteisingas. Bandykite dar kartą. +pdfjs-password-ok-button = Gerai +pdfjs-password-cancel-button = Atsisakyti +pdfjs-web-fonts-disabled = Saityno šriftai išjungti – PDF faile esančių šriftų naudoti negalima. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/ltg/viewer.ftl b/public/pdfjs/web/locale/ltg/viewer.ftl new file mode 100644 index 0000000..d262165 --- /dev/null +++ b/public/pdfjs/web/locale/ltg/viewer.ftl @@ -0,0 +1,246 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Īprīkšejā lopa +pdfjs-previous-button-label = Īprīkšejā +pdfjs-next-button = + .title = Nuokomuo lopa +pdfjs-next-button-label = Nuokomuo +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Lopa +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = nu { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } nu { $pagesCount }) +pdfjs-zoom-out-button = + .title = Attuolynuot +pdfjs-zoom-out-button-label = Attuolynuot +pdfjs-zoom-in-button = + .title = Pītuvynuot +pdfjs-zoom-in-button-label = Pītuvynuot +pdfjs-zoom-select = + .title = Palelynuojums +pdfjs-presentation-mode-button = + .title = Puorslēgtīs iz Prezentacejis režymu +pdfjs-presentation-mode-button-label = Prezentacejis režyms +pdfjs-open-file-button = + .title = Attaiseit failu +pdfjs-open-file-button-label = Attaiseit +pdfjs-print-button = + .title = Drukuošona +pdfjs-print-button-label = Drukōt + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Reiki +pdfjs-tools-button-label = Reiki +pdfjs-first-page-button = + .title = Īt iz pyrmū lopu +pdfjs-first-page-button-label = Īt iz pyrmū lopu +pdfjs-last-page-button = + .title = Īt iz piedejū lopu +pdfjs-last-page-button-label = Īt iz piedejū lopu +pdfjs-page-rotate-cw-button = + .title = Pagrīzt pa pulksteni +pdfjs-page-rotate-cw-button-label = Pagrīzt pa pulksteni +pdfjs-page-rotate-ccw-button = + .title = Pagrīzt pret pulksteni +pdfjs-page-rotate-ccw-button-label = Pagrīzt pret pulksteni +pdfjs-cursor-text-select-tool-button = + .title = Aktivizēt teksta izvieles reiku +pdfjs-cursor-text-select-tool-button-label = Teksta izvieles reiks +pdfjs-cursor-hand-tool-button = + .title = Aktivēt rūkys reiku +pdfjs-cursor-hand-tool-button-label = Rūkys reiks +pdfjs-scroll-vertical-button = + .title = Izmontōt vertikalū ritinōšonu +pdfjs-scroll-vertical-button-label = Vertikalō ritinōšona +pdfjs-scroll-horizontal-button = + .title = Izmontōt horizontalū ritinōšonu +pdfjs-scroll-horizontal-button-label = Horizontalō ritinōšona +pdfjs-scroll-wrapped-button = + .title = Izmontōt mārūgojamū ritinōšonu +pdfjs-scroll-wrapped-button-label = Mārūgojamō ritinōšona +pdfjs-spread-none-button = + .title = Naizmontōt lopu atvāruma režimu +pdfjs-spread-none-button-label = Bez atvārumim +pdfjs-spread-odd-button = + .title = Izmontōt lopu atvārumus sōkut nu napōra numeru lopom +pdfjs-spread-odd-button-label = Napōra lopys pa kreisi +pdfjs-spread-even-button = + .title = Izmontōt lopu atvārumus sōkut nu pōra numeru lopom +pdfjs-spread-even-button-label = Pōra lopys pa kreisi + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumenta īstatiejumi… +pdfjs-document-properties-button-label = Dokumenta īstatiejumi… +pdfjs-document-properties-file-name = Faila nūsaukums: +pdfjs-document-properties-file-size = Faila izmārs: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } biti) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } biti) +pdfjs-document-properties-title = Nūsaukums: +pdfjs-document-properties-author = Autors: +pdfjs-document-properties-subject = Tema: +pdfjs-document-properties-keywords = Atslāgi vuordi: +pdfjs-document-properties-creation-date = Izveides datums: +pdfjs-document-properties-modification-date = lobuošonys datums: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Radeituojs: +pdfjs-document-properties-producer = PDF producents: +pdfjs-document-properties-version = PDF verseja: +pdfjs-document-properties-page-count = Lopu skaits: +pdfjs-document-properties-page-size = Lopas izmārs: +pdfjs-document-properties-page-size-unit-inches = collas +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = portreta orientaceja +pdfjs-document-properties-page-size-orientation-landscape = ainovys orientaceja +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Fast Web View: +pdfjs-document-properties-linearized-yes = Jā +pdfjs-document-properties-linearized-no = Nā +pdfjs-document-properties-close-button = Aiztaiseit + +## Print + +pdfjs-print-progress-message = Preparing document for printing… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Atceļt +pdfjs-printing-not-supported = Uzmaneibu: Drukuošona nu itei puorlūka dorbojās tikai daleji. +pdfjs-printing-not-ready = Uzmaneibu: PDF nav pilneibā īluodeits drukuošonai. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Puorslēgt suonu jūslu +pdfjs-toggle-sidebar-button-label = Puorslēgt suonu jūslu +pdfjs-document-outline-button = + .title = Show Document Outline (double-click to expand/collapse all items) +pdfjs-document-outline-button-label = Dokumenta saturs +pdfjs-attachments-button = + .title = Show Attachments +pdfjs-attachments-button-label = Attachments +pdfjs-thumbs-button = + .title = Paruodeit seiktālus +pdfjs-thumbs-button-label = Seiktāli +pdfjs-findbar-button = + .title = Mekleit dokumentā +pdfjs-findbar-button-label = Mekleit + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Lopa { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Lopys { $page } seiktāls + +## Find panel button title and messages + +pdfjs-find-input = + .title = Mekleit + .placeholder = Mekleit dokumentā… +pdfjs-find-previous-button = + .title = Atrast īprīkšejū +pdfjs-find-previous-button-label = Īprīkšejā +pdfjs-find-next-button = + .title = Atrast nuokamū +pdfjs-find-next-button-label = Nuokomuo +pdfjs-find-highlight-checkbox = Īkruosuot vysys +pdfjs-find-match-case-checkbox-label = Lelū, mozū burtu jiuteigs +pdfjs-find-reached-top = Sasnīgts dokumenta suokums, turpynojom nu beigom +pdfjs-find-reached-bottom = Sasnīgtys dokumenta beigys, turpynojom nu suokuma +pdfjs-find-not-found = Frāze nav atrosta + +## Predefined zoom values + +pdfjs-page-scale-width = Lopys plotumā +pdfjs-page-scale-fit = Ītylpynūt lopu +pdfjs-page-scale-auto = Automatiskais izmārs +pdfjs-page-scale-actual = Patīsais izmārs +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Īluodejūt PDF nūtyka klaida. +pdfjs-invalid-file-error = Nadereigs voi būjuots PDF fails. +pdfjs-missing-file-error = PDF fails nav atrosts. +pdfjs-unexpected-response-error = Unexpected server response. +pdfjs-rendering-error = Attālojūt lopu rodās klaida + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] + +## Password + +pdfjs-password-label = Īvodit paroli, kab attaiseitu PDF failu. +pdfjs-password-invalid = Napareiza parole, raugit vēļreiz. +pdfjs-password-ok-button = Labi +pdfjs-password-cancel-button = Atceļt +pdfjs-web-fonts-disabled = Šķārsteikla fonti nav aktivizāti: Navar īgult PDF fontus. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/lv/viewer.ftl b/public/pdfjs/web/locale/lv/viewer.ftl new file mode 100644 index 0000000..067dc10 --- /dev/null +++ b/public/pdfjs/web/locale/lv/viewer.ftl @@ -0,0 +1,247 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Iepriekšējā lapa +pdfjs-previous-button-label = Iepriekšējā +pdfjs-next-button = + .title = Nākamā lapa +pdfjs-next-button-label = Nākamā +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Lapa +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = no { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } no { $pagesCount }) +pdfjs-zoom-out-button = + .title = Attālināt +pdfjs-zoom-out-button-label = Attālināt +pdfjs-zoom-in-button = + .title = Pietuvināt +pdfjs-zoom-in-button-label = Pietuvināt +pdfjs-zoom-select = + .title = Palielinājums +pdfjs-presentation-mode-button = + .title = Pārslēgties uz Prezentācijas režīmu +pdfjs-presentation-mode-button-label = Prezentācijas režīms +pdfjs-open-file-button = + .title = Atvērt failu +pdfjs-open-file-button-label = Atvērt +pdfjs-print-button = + .title = Drukāšana +pdfjs-print-button-label = Drukāt + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Rīki +pdfjs-tools-button-label = Rīki +pdfjs-first-page-button = + .title = Iet uz pirmo lapu +pdfjs-first-page-button-label = Iet uz pirmo lapu +pdfjs-last-page-button = + .title = Iet uz pēdējo lapu +pdfjs-last-page-button-label = Iet uz pēdējo lapu +pdfjs-page-rotate-cw-button = + .title = Pagriezt pa pulksteni +pdfjs-page-rotate-cw-button-label = Pagriezt pa pulksteni +pdfjs-page-rotate-ccw-button = + .title = Pagriezt pret pulksteni +pdfjs-page-rotate-ccw-button-label = Pagriezt pret pulksteni +pdfjs-cursor-text-select-tool-button = + .title = Aktivizēt teksta izvēles rīku +pdfjs-cursor-text-select-tool-button-label = Teksta izvēles rīks +pdfjs-cursor-hand-tool-button = + .title = Aktivēt rokas rīku +pdfjs-cursor-hand-tool-button-label = Rokas rīks +pdfjs-scroll-vertical-button = + .title = Izmantot vertikālo ritināšanu +pdfjs-scroll-vertical-button-label = Vertikālā ritināšana +pdfjs-scroll-horizontal-button = + .title = Izmantot horizontālo ritināšanu +pdfjs-scroll-horizontal-button-label = Horizontālā ritināšana +pdfjs-scroll-wrapped-button = + .title = Izmantot apkļauto ritināšanu +pdfjs-scroll-wrapped-button-label = Apkļautā ritināšana +pdfjs-spread-none-button = + .title = Nepievienoties lapu izpletumiem +pdfjs-spread-none-button-label = Neizmantot izpletumus +pdfjs-spread-odd-button = + .title = Izmantot lapu izpletumus sākot ar nepāra numuru lapām +pdfjs-spread-odd-button-label = Nepāra izpletumi +pdfjs-spread-even-button = + .title = Izmantot lapu izpletumus sākot ar pāra numuru lapām +pdfjs-spread-even-button-label = Pāra izpletumi + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumenta iestatījumi… +pdfjs-document-properties-button-label = Dokumenta iestatījumi… +pdfjs-document-properties-file-name = Faila nosaukums: +pdfjs-document-properties-file-size = Faila izmērs: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } biti) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } biti) +pdfjs-document-properties-title = Nosaukums: +pdfjs-document-properties-author = Autors: +pdfjs-document-properties-subject = Tēma: +pdfjs-document-properties-keywords = Atslēgas vārdi: +pdfjs-document-properties-creation-date = Izveides datums: +pdfjs-document-properties-modification-date = LAbošanas datums: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Radītājs: +pdfjs-document-properties-producer = PDF producents: +pdfjs-document-properties-version = PDF versija: +pdfjs-document-properties-page-count = Lapu skaits: +pdfjs-document-properties-page-size = Papīra izmērs: +pdfjs-document-properties-page-size-unit-inches = collas +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = portretorientācija +pdfjs-document-properties-page-size-orientation-landscape = ainavorientācija +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Vēstule +pdfjs-document-properties-page-size-name-legal = Juridiskie teksti + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Ātrā tīmekļa skats: +pdfjs-document-properties-linearized-yes = Jā +pdfjs-document-properties-linearized-no = Nē +pdfjs-document-properties-close-button = Aizvērt + +## Print + +pdfjs-print-progress-message = Gatavo dokumentu drukāšanai... +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Atcelt +pdfjs-printing-not-supported = Uzmanību: Drukāšana no šī pārlūka darbojas tikai daļēji. +pdfjs-printing-not-ready = Uzmanību: PDF nav pilnībā ielādēts drukāšanai. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Pārslēgt sānu joslu +pdfjs-toggle-sidebar-button-label = Pārslēgt sānu joslu +pdfjs-document-outline-button = + .title = Rādīt dokumenta struktūru (veiciet dubultklikšķi lai izvērstu/sakļautu visus vienumus) +pdfjs-document-outline-button-label = Dokumenta saturs +pdfjs-attachments-button = + .title = Rādīt pielikumus +pdfjs-attachments-button-label = Pielikumi +pdfjs-thumbs-button = + .title = Parādīt sīktēlus +pdfjs-thumbs-button-label = Sīktēli +pdfjs-findbar-button = + .title = Meklēt dokumentā +pdfjs-findbar-button-label = Meklēt + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Lapa { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Lapas { $page } sīktēls + +## Find panel button title and messages + +pdfjs-find-input = + .title = Meklēt + .placeholder = Meklēt dokumentā… +pdfjs-find-previous-button = + .title = Atrast iepriekšējo +pdfjs-find-previous-button-label = Iepriekšējā +pdfjs-find-next-button = + .title = Atrast nākamo +pdfjs-find-next-button-label = Nākamā +pdfjs-find-highlight-checkbox = Iekrāsot visas +pdfjs-find-match-case-checkbox-label = Lielo, mazo burtu jutīgs +pdfjs-find-entire-word-checkbox-label = Veselus vārdus +pdfjs-find-reached-top = Sasniegts dokumenta sākums, turpinām no beigām +pdfjs-find-reached-bottom = Sasniegtas dokumenta beigas, turpinām no sākuma +pdfjs-find-not-found = Frāze nav atrasta + +## Predefined zoom values + +pdfjs-page-scale-width = Lapas platumā +pdfjs-page-scale-fit = Ietilpinot lapu +pdfjs-page-scale-auto = Automātiskais izmērs +pdfjs-page-scale-actual = Patiesais izmērs +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Ielādējot PDF notika kļūda. +pdfjs-invalid-file-error = Nederīgs vai bojāts PDF fails. +pdfjs-missing-file-error = PDF fails nav atrasts. +pdfjs-unexpected-response-error = Negaidīa servera atbilde. +pdfjs-rendering-error = Attēlojot lapu radās kļūda + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } anotācija] + +## Password + +pdfjs-password-label = Ievadiet paroli, lai atvērtu PDF failu. +pdfjs-password-invalid = Nepareiza parole, mēģiniet vēlreiz. +pdfjs-password-ok-button = Labi +pdfjs-password-cancel-button = Atcelt +pdfjs-web-fonts-disabled = Tīmekļa fonti nav aktivizēti: Nevar iegult PDF fontus. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/meh/viewer.ftl b/public/pdfjs/web/locale/meh/viewer.ftl new file mode 100644 index 0000000..d8bddc9 --- /dev/null +++ b/public/pdfjs/web/locale/meh/viewer.ftl @@ -0,0 +1,87 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Página yata +pdfjs-zoom-select = + .title = Nasa´a ka´nu/Nasa´a luli +pdfjs-open-file-button-label = Síne + +## Secondary toolbar and context menu + + +## Document properties dialog + +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +pdfjs-document-properties-linearized-yes = Kuvi +pdfjs-document-properties-close-button = Nakasɨ + +## Print + +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Nkuvi-ka + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-findbar-button-label = Nánuku + +## Thumbnails panel item (tooltip and alt text for images) + + +## Find panel button title and messages + + +## Predefined zoom values + +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } + +## Password + +pdfjs-password-cancel-button = Nkuvi-ka + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/mk/viewer.ftl b/public/pdfjs/web/locale/mk/viewer.ftl new file mode 100644 index 0000000..47d24b2 --- /dev/null +++ b/public/pdfjs/web/locale/mk/viewer.ftl @@ -0,0 +1,215 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Претходна страница +pdfjs-previous-button-label = Претходна +pdfjs-next-button = + .title = Следна страница +pdfjs-next-button-label = Следна +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Страница +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = од { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } од { $pagesCount }) +pdfjs-zoom-out-button = + .title = Намалување +pdfjs-zoom-out-button-label = Намали +pdfjs-zoom-in-button = + .title = Зголемување +pdfjs-zoom-in-button-label = Зголеми +pdfjs-zoom-select = + .title = Променување на големина +pdfjs-presentation-mode-button = + .title = Премини во презентациски режим +pdfjs-presentation-mode-button-label = Презентациски режим +pdfjs-open-file-button = + .title = Отворање датотека +pdfjs-open-file-button-label = Отвори +pdfjs-print-button = + .title = Печатење +pdfjs-print-button-label = Печати + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Алатки +pdfjs-tools-button-label = Алатки +pdfjs-first-page-button = + .title = Оди до првата страница +pdfjs-first-page-button-label = Оди до првата страница +pdfjs-last-page-button = + .title = Оди до последната страница +pdfjs-last-page-button-label = Оди до последната страница +pdfjs-page-rotate-cw-button = + .title = Ротирај по стрелките на часовникот +pdfjs-page-rotate-cw-button-label = Ротирај по стрелките на часовникот +pdfjs-page-rotate-ccw-button = + .title = Ротирај спротивно од стрелките на часовникот +pdfjs-page-rotate-ccw-button-label = Ротирај спротивно од стрелките на часовникот +pdfjs-cursor-text-select-tool-button = + .title = Овозможи алатка за избор на текст +pdfjs-cursor-text-select-tool-button-label = Алатка за избор на текст + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Својства на документот… +pdfjs-document-properties-button-label = Својства на документот… +pdfjs-document-properties-file-name = Име на датотека: +pdfjs-document-properties-file-size = Големина на датотеката: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } бајти) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } бајти) +pdfjs-document-properties-title = Наслов: +pdfjs-document-properties-author = Автор: +pdfjs-document-properties-subject = Тема: +pdfjs-document-properties-keywords = Клучни зборови: +pdfjs-document-properties-creation-date = Датум на создавање: +pdfjs-document-properties-modification-date = Датум на промена: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Креатор: +pdfjs-document-properties-version = Верзија на PDF: +pdfjs-document-properties-page-count = Број на страници: +pdfjs-document-properties-page-size = Големина на страница: +pdfjs-document-properties-page-size-unit-inches = инч +pdfjs-document-properties-page-size-unit-millimeters = мм +pdfjs-document-properties-page-size-orientation-portrait = портрет +pdfjs-document-properties-page-size-orientation-landscape = пејзаж +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Писмо + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +pdfjs-document-properties-linearized-yes = Да +pdfjs-document-properties-linearized-no = Не +pdfjs-document-properties-close-button = Затвори + +## Print + +pdfjs-print-progress-message = Документ се подготвува за печатење… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Откажи +pdfjs-printing-not-supported = Предупредување: Печатењето не е целосно поддржано во овој прелистувач. +pdfjs-printing-not-ready = Предупредување: PDF документот не е целосно вчитан за печатење. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Вклучи странична лента +pdfjs-toggle-sidebar-button-label = Вклучи странична лента +pdfjs-document-outline-button-label = Содржина на документот +pdfjs-attachments-button = + .title = Прикажи додатоци +pdfjs-thumbs-button = + .title = Прикажување на икони +pdfjs-thumbs-button-label = Икони +pdfjs-findbar-button = + .title = Најди во документот +pdfjs-findbar-button-label = Најди + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Страница { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Икона од страница { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Пронајди + .placeholder = Пронајди во документот… +pdfjs-find-previous-button = + .title = Најди ја предходната појава на фразата +pdfjs-find-previous-button-label = Претходно +pdfjs-find-next-button = + .title = Најди ја следната појава на фразата +pdfjs-find-next-button-label = Следно +pdfjs-find-highlight-checkbox = Означи сѐ +pdfjs-find-match-case-checkbox-label = Токму така +pdfjs-find-entire-word-checkbox-label = Цели зборови +pdfjs-find-reached-top = Барањето стигна до почетокот на документот и почнува од крајот +pdfjs-find-reached-bottom = Барањето стигна до крајот на документот и почнува од почеток +pdfjs-find-not-found = Фразата не е пронајдена + +## Predefined zoom values + +pdfjs-page-scale-width = Ширина на страница +pdfjs-page-scale-fit = Цела страница +pdfjs-page-scale-auto = Автоматска големина +pdfjs-page-scale-actual = Вистинска големина +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Настана грешка при вчитувањето на PDF-от. +pdfjs-invalid-file-error = Невалидна или корумпирана PDF датотека. +pdfjs-missing-file-error = Недостасува PDF документ. +pdfjs-unexpected-response-error = Неочекуван одговор од серверот. +pdfjs-rendering-error = Настана грешка при прикажувањето на страницата. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } + +## Password + +pdfjs-password-label = Внесете ја лозинката за да ја отворите оваа датотека. +pdfjs-password-invalid = Невалидна лозинка. Обидете се повторно. +pdfjs-password-ok-button = Во ред +pdfjs-password-cancel-button = Откажи +pdfjs-web-fonts-disabled = Интернет фонтовите се оневозможени: не може да се користат вградените PDF фонтови. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/mr/viewer.ftl b/public/pdfjs/web/locale/mr/viewer.ftl new file mode 100644 index 0000000..49948b1 --- /dev/null +++ b/public/pdfjs/web/locale/mr/viewer.ftl @@ -0,0 +1,239 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = मागील पृष्ठ +pdfjs-previous-button-label = मागील +pdfjs-next-button = + .title = पुढील पृष्ठ +pdfjs-next-button-label = पुढील +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = पृष्ठ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount }पैकी +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pagesCount } पैकी { $pageNumber }) +pdfjs-zoom-out-button = + .title = छोटे करा +pdfjs-zoom-out-button-label = छोटे करा +pdfjs-zoom-in-button = + .title = मोठे करा +pdfjs-zoom-in-button-label = मोठे करा +pdfjs-zoom-select = + .title = लहान किंवा मोठे करा +pdfjs-presentation-mode-button = + .title = प्रस्तुतिकरण मोडचा वापर करा +pdfjs-presentation-mode-button-label = प्रस्तुतिकरण मोड +pdfjs-open-file-button = + .title = फाइल उघडा +pdfjs-open-file-button-label = उघडा +pdfjs-print-button = + .title = छपाई करा +pdfjs-print-button-label = छपाई करा + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = साधने +pdfjs-tools-button-label = साधने +pdfjs-first-page-button = + .title = पहिल्या पृष्ठावर जा +pdfjs-first-page-button-label = पहिल्या पृष्ठावर जा +pdfjs-last-page-button = + .title = शेवटच्या पृष्ठावर जा +pdfjs-last-page-button-label = शेवटच्या पृष्ठावर जा +pdfjs-page-rotate-cw-button = + .title = घड्याळाच्या काट्याच्या दिशेने फिरवा +pdfjs-page-rotate-cw-button-label = घड्याळाच्या काट्याच्या दिशेने फिरवा +pdfjs-page-rotate-ccw-button = + .title = घड्याळाच्या काट्याच्या उलट दिशेने फिरवा +pdfjs-page-rotate-ccw-button-label = घड्याळाच्या काट्याच्या उलट दिशेने फिरवा +pdfjs-cursor-text-select-tool-button = + .title = मजकूर निवड साधन कार्यान्वयीत करा +pdfjs-cursor-text-select-tool-button-label = मजकूर निवड साधन +pdfjs-cursor-hand-tool-button = + .title = हात साधन कार्यान्वित करा +pdfjs-cursor-hand-tool-button-label = हस्त साधन +pdfjs-scroll-vertical-button = + .title = अनुलंब स्क्रोलिंग वापरा +pdfjs-scroll-vertical-button-label = अनुलंब स्क्रोलिंग +pdfjs-scroll-horizontal-button = + .title = क्षैतिज स्क्रोलिंग वापरा +pdfjs-scroll-horizontal-button-label = क्षैतिज स्क्रोलिंग + +## Document properties dialog + +pdfjs-document-properties-button = + .title = दस्तऐवज गुणधर्म… +pdfjs-document-properties-button-label = दस्तऐवज गुणधर्म… +pdfjs-document-properties-file-name = फाइलचे नाव: +pdfjs-document-properties-file-size = फाइल आकार: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } बाइट्स) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } बाइट्स) +pdfjs-document-properties-title = शिर्षक: +pdfjs-document-properties-author = लेखक: +pdfjs-document-properties-subject = विषय: +pdfjs-document-properties-keywords = मुख्यशब्द: +pdfjs-document-properties-creation-date = निर्माण दिनांक: +pdfjs-document-properties-modification-date = दुरूस्ती दिनांक: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = निर्माता: +pdfjs-document-properties-producer = PDF निर्माता: +pdfjs-document-properties-version = PDF आवृत्ती: +pdfjs-document-properties-page-count = पृष्ठ संख्या: +pdfjs-document-properties-page-size = पृष्ठ आकार: +pdfjs-document-properties-page-size-unit-inches = इंच +pdfjs-document-properties-page-size-unit-millimeters = मीमी +pdfjs-document-properties-page-size-orientation-portrait = उभी मांडणी +pdfjs-document-properties-page-size-orientation-landscape = आडवे +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = जलद वेब दृष्य: +pdfjs-document-properties-linearized-yes = हो +pdfjs-document-properties-linearized-no = नाही +pdfjs-document-properties-close-button = बंद करा + +## Print + +pdfjs-print-progress-message = छपाई करीता पृष्ठ तयार करीत आहे… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = रद्द करा +pdfjs-printing-not-supported = सावधानता: या ब्राउझरतर्फे छपाइ पूर्णपणे समर्थीत नाही. +pdfjs-printing-not-ready = सावधानता: छपाईकरिता PDF पूर्णतया लोड झाले नाही. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = बाजूचीपट्टी टॉगल करा +pdfjs-toggle-sidebar-button-label = बाजूचीपट्टी टॉगल करा +pdfjs-document-outline-button = + .title = दस्तऐवज बाह्यरेखा दर्शवा (विस्तृत करण्यासाठी दोनवेळा क्लिक करा /सर्व घटक दाखवा) +pdfjs-document-outline-button-label = दस्तऐवज रूपरेषा +pdfjs-attachments-button = + .title = जोडपत्र दाखवा +pdfjs-attachments-button-label = जोडपत्र +pdfjs-thumbs-button = + .title = थंबनेल्स् दाखवा +pdfjs-thumbs-button-label = थंबनेल्स् +pdfjs-findbar-button = + .title = दस्तऐवजात शोधा +pdfjs-findbar-button-label = शोधा + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = पृष्ठ { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = पृष्ठाचे थंबनेल { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = शोधा + .placeholder = दस्तऐवजात शोधा… +pdfjs-find-previous-button = + .title = वाकप्रयोगची मागील घटना शोधा +pdfjs-find-previous-button-label = मागील +pdfjs-find-next-button = + .title = वाकप्रयोगची पुढील घटना शोधा +pdfjs-find-next-button-label = पुढील +pdfjs-find-highlight-checkbox = सर्व ठळक करा +pdfjs-find-match-case-checkbox-label = आकार जुळवा +pdfjs-find-entire-word-checkbox-label = संपूर्ण शब्द +pdfjs-find-reached-top = दस्तऐवजाच्या शीर्षकास पोहचले, तळपासून पुढे +pdfjs-find-reached-bottom = दस्तऐवजाच्या तळाला पोहचले, शीर्षकापासून पुढे +pdfjs-find-not-found = वाकप्रयोग आढळले नाही + +## Predefined zoom values + +pdfjs-page-scale-width = पृष्ठाची रूंदी +pdfjs-page-scale-fit = पृष्ठ बसवा +pdfjs-page-scale-auto = स्वयं लाहन किंवा मोठे करणे +pdfjs-page-scale-actual = प्रत्यक्ष आकार +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = PDF लोड करतेवेळी त्रुटी आढळली. +pdfjs-invalid-file-error = अवैध किंवा दोषीत PDF फाइल. +pdfjs-missing-file-error = न आढळणारी PDF फाइल. +pdfjs-unexpected-response-error = अनपेक्षित सर्व्हर प्रतिसाद. +pdfjs-rendering-error = पृष्ठ दाखवतेवेळी त्रुटी आढळली. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } टिपण्णी] + +## Password + +pdfjs-password-label = ही PDF फाइल उघडण्याकरिता पासवर्ड द्या. +pdfjs-password-invalid = अवैध पासवर्ड. कृपया पुन्हा प्रयत्न करा. +pdfjs-password-ok-button = ठीक आहे +pdfjs-password-cancel-button = रद्द करा +pdfjs-web-fonts-disabled = वेब टंक असमर्थीत आहेत: एम्बेडेड PDF टंक वापर अशक्य. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/ms/viewer.ftl b/public/pdfjs/web/locale/ms/viewer.ftl new file mode 100644 index 0000000..11b8665 --- /dev/null +++ b/public/pdfjs/web/locale/ms/viewer.ftl @@ -0,0 +1,247 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Halaman Dahulu +pdfjs-previous-button-label = Dahulu +pdfjs-next-button = + .title = Halaman Berikut +pdfjs-next-button-label = Berikut +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Halaman +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = daripada { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } daripada { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zum Keluar +pdfjs-zoom-out-button-label = Zum Keluar +pdfjs-zoom-in-button = + .title = Zum Masuk +pdfjs-zoom-in-button-label = Zum Masuk +pdfjs-zoom-select = + .title = Zum +pdfjs-presentation-mode-button = + .title = Tukar ke Mod Persembahan +pdfjs-presentation-mode-button-label = Mod Persembahan +pdfjs-open-file-button = + .title = Buka Fail +pdfjs-open-file-button-label = Buka +pdfjs-print-button = + .title = Cetak +pdfjs-print-button-label = Cetak + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Alatan +pdfjs-tools-button-label = Alatan +pdfjs-first-page-button = + .title = Pergi ke Halaman Pertama +pdfjs-first-page-button-label = Pergi ke Halaman Pertama +pdfjs-last-page-button = + .title = Pergi ke Halaman Terakhir +pdfjs-last-page-button-label = Pergi ke Halaman Terakhir +pdfjs-page-rotate-cw-button = + .title = Berputar ikut arah Jam +pdfjs-page-rotate-cw-button-label = Berputar ikut arah Jam +pdfjs-page-rotate-ccw-button = + .title = Pusing berlawan arah jam +pdfjs-page-rotate-ccw-button-label = Pusing berlawan arah jam +pdfjs-cursor-text-select-tool-button = + .title = Dayakan Alatan Pilihan Teks +pdfjs-cursor-text-select-tool-button-label = Alatan Pilihan Teks +pdfjs-cursor-hand-tool-button = + .title = Dayakan Alatan Tangan +pdfjs-cursor-hand-tool-button-label = Alatan Tangan +pdfjs-scroll-vertical-button = + .title = Guna Skrol Menegak +pdfjs-scroll-vertical-button-label = Skrol Menegak +pdfjs-scroll-horizontal-button = + .title = Guna Skrol Mengufuk +pdfjs-scroll-horizontal-button-label = Skrol Mengufuk +pdfjs-scroll-wrapped-button = + .title = Guna Skrol Berbalut +pdfjs-scroll-wrapped-button-label = Skrol Berbalut +pdfjs-spread-none-button = + .title = Jangan hubungkan hamparan halaman +pdfjs-spread-none-button-label = Tanpa Hamparan +pdfjs-spread-odd-button = + .title = Hubungkan hamparan halaman dengan halaman nombor ganjil +pdfjs-spread-odd-button-label = Hamparan Ganjil +pdfjs-spread-even-button = + .title = Hubungkan hamparan halaman dengan halaman nombor genap +pdfjs-spread-even-button-label = Hamparan Seimbang + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Sifat Dokumen… +pdfjs-document-properties-button-label = Sifat Dokumen… +pdfjs-document-properties-file-name = Nama fail: +pdfjs-document-properties-file-size = Saiz fail: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bait) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bait) +pdfjs-document-properties-title = Tajuk: +pdfjs-document-properties-author = Pengarang: +pdfjs-document-properties-subject = Subjek: +pdfjs-document-properties-keywords = Kata kunci: +pdfjs-document-properties-creation-date = Masa Dicipta: +pdfjs-document-properties-modification-date = Tarikh Ubahsuai: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Pencipta: +pdfjs-document-properties-producer = Pengeluar PDF: +pdfjs-document-properties-version = Versi PDF: +pdfjs-document-properties-page-count = Kiraan Laman: +pdfjs-document-properties-page-size = Saiz Halaman: +pdfjs-document-properties-page-size-unit-inches = dalam +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = potret +pdfjs-document-properties-page-size-orientation-landscape = landskap +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Paparan Web Pantas: +pdfjs-document-properties-linearized-yes = Ya +pdfjs-document-properties-linearized-no = Tidak +pdfjs-document-properties-close-button = Tutup + +## Print + +pdfjs-print-progress-message = Menyediakan dokumen untuk dicetak… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Batal +pdfjs-printing-not-supported = Amaran: Cetakan ini tidak sepenuhnya disokong oleh pelayar ini. +pdfjs-printing-not-ready = Amaran: PDF tidak sepenuhnya dimuatkan untuk dicetak. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Togol Bar Sisi +pdfjs-toggle-sidebar-button-label = Togol Bar Sisi +pdfjs-document-outline-button = + .title = Papar Rangka Dokumen (klik-dua-kali untuk kembangkan/kolaps semua item) +pdfjs-document-outline-button-label = Rangka Dokumen +pdfjs-attachments-button = + .title = Papar Lampiran +pdfjs-attachments-button-label = Lampiran +pdfjs-thumbs-button = + .title = Papar Thumbnails +pdfjs-thumbs-button-label = Imej kecil +pdfjs-findbar-button = + .title = Cari didalam Dokumen +pdfjs-findbar-button-label = Cari + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Halaman { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Halaman Imej kecil { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Cari + .placeholder = Cari dalam dokumen… +pdfjs-find-previous-button = + .title = Cari teks frasa berkenaan yang terdahulu +pdfjs-find-previous-button-label = Dahulu +pdfjs-find-next-button = + .title = Cari teks frasa berkenaan yang berikut +pdfjs-find-next-button-label = Berikut +pdfjs-find-highlight-checkbox = Serlahkan semua +pdfjs-find-match-case-checkbox-label = Huruf sepadan +pdfjs-find-entire-word-checkbox-label = Seluruh perkataan +pdfjs-find-reached-top = Mencapai teratas daripada dokumen, sambungan daripada bawah +pdfjs-find-reached-bottom = Mencapai terakhir daripada dokumen, sambungan daripada atas +pdfjs-find-not-found = Frasa tidak ditemui + +## Predefined zoom values + +pdfjs-page-scale-width = Lebar Halaman +pdfjs-page-scale-fit = Muat Halaman +pdfjs-page-scale-auto = Zoom Automatik +pdfjs-page-scale-actual = Saiz Sebenar +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Masalah berlaku semasa menuatkan sebuah PDF. +pdfjs-invalid-file-error = Tidak sah atau fail PDF rosak. +pdfjs-missing-file-error = Fail PDF Hilang. +pdfjs-unexpected-response-error = Respon pelayan yang tidak dijangka. +pdfjs-rendering-error = Ralat berlaku ketika memberikan halaman. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Anotasi] + +## Password + +pdfjs-password-label = Masukan kata kunci untuk membuka fail PDF ini. +pdfjs-password-invalid = Kata laluan salah. Cuba lagi. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Batal +pdfjs-web-fonts-disabled = Fon web dinyahdayakan: tidak dapat menggunakan fon terbenam PDF. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/my/viewer.ftl b/public/pdfjs/web/locale/my/viewer.ftl new file mode 100644 index 0000000..d3b973d --- /dev/null +++ b/public/pdfjs/web/locale/my/viewer.ftl @@ -0,0 +1,206 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = အရင် စာမျက်နှာ +pdfjs-previous-button-label = အရင်နေရာ +pdfjs-next-button = + .title = ရှေ့ စာမျက်နှာ +pdfjs-next-button-label = နောက်တခု +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = စာမျက်နှာ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount } ၏ +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pagesCount } ၏ { $pageNumber }) +pdfjs-zoom-out-button = + .title = ချုံ့ပါ +pdfjs-zoom-out-button-label = ချုံ့ပါ +pdfjs-zoom-in-button = + .title = ချဲ့ပါ +pdfjs-zoom-in-button-label = ချဲ့ပါ +pdfjs-zoom-select = + .title = ချုံ့/ချဲ့ပါ +pdfjs-presentation-mode-button = + .title = ဆွေးနွေးတင်ပြစနစ်သို့ ကူးပြောင်းပါ +pdfjs-presentation-mode-button-label = ဆွေးနွေးတင်ပြစနစ် +pdfjs-open-file-button = + .title = ဖိုင်အားဖွင့်ပါ။ +pdfjs-open-file-button-label = ဖွင့်ပါ +pdfjs-print-button = + .title = ပုံနှိုပ်ပါ +pdfjs-print-button-label = ပုံနှိုပ်ပါ + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = ကိရိယာများ +pdfjs-tools-button-label = ကိရိယာများ +pdfjs-first-page-button = + .title = ပထမ စာမျက်နှာသို့ +pdfjs-first-page-button-label = ပထမ စာမျက်နှာသို့ +pdfjs-last-page-button = + .title = နောက်ဆုံး စာမျက်နှာသို့ +pdfjs-last-page-button-label = နောက်ဆုံး စာမျက်နှာသို့ +pdfjs-page-rotate-cw-button = + .title = နာရီလက်တံ အတိုင်း +pdfjs-page-rotate-cw-button-label = နာရီလက်တံ အတိုင်း +pdfjs-page-rotate-ccw-button = + .title = နာရီလက်တံ ပြောင်းပြန် +pdfjs-page-rotate-ccw-button-label = နာရီလက်တံ ပြောင်းပြန် + +## Document properties dialog + +pdfjs-document-properties-button = + .title = မှတ်တမ်းမှတ်ရာ ဂုဏ်သတ္တိများ +pdfjs-document-properties-button-label = မှတ်တမ်းမှတ်ရာ ဂုဏ်သတ္တိများ +pdfjs-document-properties-file-name = ဖိုင် : +pdfjs-document-properties-file-size = ဖိုင်ဆိုဒ် : +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } ကီလိုဘိုတ် ({ $size_b }ဘိုတ်) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = ခေါင်းစဉ်‌ - +pdfjs-document-properties-author = ရေးသားသူ: +pdfjs-document-properties-subject = အကြောင်းအရာ: +pdfjs-document-properties-keywords = သော့ချက် စာလုံး: +pdfjs-document-properties-creation-date = ထုတ်လုပ်ရက်စွဲ: +pdfjs-document-properties-modification-date = ပြင်ဆင်ရက်စွဲ: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = ဖန်တီးသူ: +pdfjs-document-properties-producer = PDF ထုတ်လုပ်သူ: +pdfjs-document-properties-version = PDF ဗားရှင်း: +pdfjs-document-properties-page-count = စာမျက်နှာအရေအတွက်: + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + + +## + +pdfjs-document-properties-close-button = ပိတ် + +## Print + +pdfjs-print-progress-message = Preparing document for printing… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = ပယ်​ဖျက်ပါ +pdfjs-printing-not-supported = သတိပေးချက်၊ပရင့်ထုတ်ခြင်းကိုဤဘယောက်ဆာသည် ပြည့်ဝစွာထောက်ပံ့မထားပါ ။ +pdfjs-printing-not-ready = သတိပေးချက်: ယခု PDF ဖိုင်သည် ပုံနှိပ်ရန် မပြည့်စုံပါ + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = ဘေးတန်းဖွင့်ပိတ် +pdfjs-toggle-sidebar-button-label = ဖွင့်ပိတ် ဆလိုက်ဒါ +pdfjs-document-outline-button = + .title = စာတမ်းအကျဉ်းချုပ်ကို ပြပါ (စာရင်းအားလုံးကို ချုံ့/ချဲ့ရန် ကလစ်နှစ်ချက်နှိပ်ပါ) +pdfjs-document-outline-button-label = စာတမ်းအကျဉ်းချုပ် +pdfjs-attachments-button = + .title = တွဲချက်များ ပြပါ +pdfjs-attachments-button-label = တွဲထားချက်များ +pdfjs-thumbs-button = + .title = ပုံရိပ်ငယ်များကို ပြပါ +pdfjs-thumbs-button-label = ပုံရိပ်ငယ်များ +pdfjs-findbar-button = + .title = Find in Document +pdfjs-findbar-button-label = ရှာဖွေပါ + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = စာမျက်နှာ { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = စာမျက်နှာရဲ့ ပုံရိပ်ငယ် { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = ရှာဖွေပါ + .placeholder = စာတမ်းထဲတွင် ရှာဖွေရန်… +pdfjs-find-previous-button = + .title = စကားစုရဲ့ အရင် ​ဖြစ်ပွားမှုကို ရှာဖွေပါ +pdfjs-find-previous-button-label = နောက်သို့ +pdfjs-find-next-button = + .title = စကားစုရဲ့ နောက်ထပ် ​ဖြစ်ပွားမှုကို ရှာဖွေပါ +pdfjs-find-next-button-label = ရှေ့သို့ +pdfjs-find-highlight-checkbox = အားလုံးကို မျဉ်းသားပါ +pdfjs-find-match-case-checkbox-label = စာလုံး တိုက်ဆိုင်ပါ +pdfjs-find-reached-top = စာမျက်နှာထိပ် ရောက်နေပြီ၊ အဆုံးကနေ ပြန်စပါ +pdfjs-find-reached-bottom = စာမျက်နှာအဆုံး ရောက်နေပြီ၊ ထိပ်ကနေ ပြန်စပါ +pdfjs-find-not-found = စကားစု မတွေ့ရဘူး + +## Predefined zoom values + +pdfjs-page-scale-width = စာမျက်နှာ အကျယ် +pdfjs-page-scale-fit = စာမျက်နှာ ကွက်တိ +pdfjs-page-scale-auto = အလိုအလျောက် ချုံ့ချဲ့ +pdfjs-page-scale-actual = အမှန်တကယ်ရှိတဲ့ အရွယ် +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = PDF ဖိုင် ကိုဆွဲတင်နေချိန်မှာ အမှားတစ်ခုတွေ့ရပါတယ်။ +pdfjs-invalid-file-error = မရသော သို့ ပျက်နေသော PDF ဖိုင် +pdfjs-missing-file-error = PDF ပျောက်ဆုံး +pdfjs-unexpected-response-error = မမျှော်လင့်ထားသော ဆာဗာမှ ပြန်ကြားချက် +pdfjs-rendering-error = စာမျက်နှာကို ပုံဖော်နေချိန်မှာ အမှားတစ်ခုတွေ့ရပါတယ်။ + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } အဓိပ္ပာယ်ဖွင့်ဆိုချက်] + +## Password + +pdfjs-password-label = ယခု PDF ကို ဖွင့်ရန် စကားဝှက်ကို ရိုက်ပါ။ +pdfjs-password-invalid = စာဝှက် မှားသည်။ ထပ်ကြိုးစားကြည့်ပါ။ +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = ပယ်​ဖျက်ပါ +pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/nb-NO/viewer.ftl b/public/pdfjs/web/locale/nb-NO/viewer.ftl new file mode 100644 index 0000000..a644157 --- /dev/null +++ b/public/pdfjs/web/locale/nb-NO/viewer.ftl @@ -0,0 +1,495 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Forrige side +pdfjs-previous-button-label = Forrige +pdfjs-next-button = + .title = Neste side +pdfjs-next-button-label = Neste +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Side +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = av { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } av { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zoom ut +pdfjs-zoom-out-button-label = Zoom ut +pdfjs-zoom-in-button = + .title = Zoom inn +pdfjs-zoom-in-button-label = Zoom inn +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Bytt til presentasjonsmodus +pdfjs-presentation-mode-button-label = Presentasjonsmodus +pdfjs-open-file-button = + .title = Åpne fil +pdfjs-open-file-button-label = Åpne +pdfjs-print-button = + .title = Skriv ut +pdfjs-print-button-label = Skriv ut +pdfjs-save-button = + .title = Lagre +pdfjs-save-button-label = Lagre +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Last ned +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Last ned +pdfjs-bookmark-button = + .title = Gjeldende side (se URL fra gjeldende side) +pdfjs-bookmark-button-label = Gjeldende side + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Verktøy +pdfjs-tools-button-label = Verktøy +pdfjs-first-page-button = + .title = Gå til første side +pdfjs-first-page-button-label = Gå til første side +pdfjs-last-page-button = + .title = Gå til siste side +pdfjs-last-page-button-label = Gå til siste side +pdfjs-page-rotate-cw-button = + .title = Roter med klokken +pdfjs-page-rotate-cw-button-label = Roter med klokken +pdfjs-page-rotate-ccw-button = + .title = Roter mot klokken +pdfjs-page-rotate-ccw-button-label = Roter mot klokken +pdfjs-cursor-text-select-tool-button = + .title = Aktiver tekstmarkeringsverktøy +pdfjs-cursor-text-select-tool-button-label = Tekstmarkeringsverktøy +pdfjs-cursor-hand-tool-button = + .title = Aktiver handverktøy +pdfjs-cursor-hand-tool-button-label = Handverktøy +pdfjs-scroll-page-button = + .title = Bruk siderulling +pdfjs-scroll-page-button-label = Siderulling +pdfjs-scroll-vertical-button = + .title = Bruk vertikal rulling +pdfjs-scroll-vertical-button-label = Vertikal rulling +pdfjs-scroll-horizontal-button = + .title = Bruk horisontal rulling +pdfjs-scroll-horizontal-button-label = Horisontal rulling +pdfjs-scroll-wrapped-button = + .title = Bruk flersiderulling +pdfjs-scroll-wrapped-button-label = Flersiderulling +pdfjs-spread-none-button = + .title = Vis enkeltsider +pdfjs-spread-none-button-label = Enkeltsider +pdfjs-spread-odd-button = + .title = Vis oppslag med ulike sidenumre til venstre +pdfjs-spread-odd-button-label = Oppslag med forside +pdfjs-spread-even-button = + .title = Vis oppslag med like sidenumre til venstre +pdfjs-spread-even-button-label = Oppslag uten forside + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumentegenskaper … +pdfjs-document-properties-button-label = Dokumentegenskaper … +pdfjs-document-properties-file-name = Filnavn: +pdfjs-document-properties-file-size = Filstørrelse: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } byte) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } byte) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Dokumentegenskaper … +pdfjs-document-properties-author = Forfatter: +pdfjs-document-properties-subject = Emne: +pdfjs-document-properties-keywords = Nøkkelord: +pdfjs-document-properties-creation-date = Opprettet dato: +pdfjs-document-properties-modification-date = Endret dato: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Opprettet av: +pdfjs-document-properties-producer = PDF-verktøy: +pdfjs-document-properties-version = PDF-versjon: +pdfjs-document-properties-page-count = Sideantall: +pdfjs-document-properties-page-size = Sidestørrelse: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = stående +pdfjs-document-properties-page-size-orientation-landscape = liggende +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Hurtig nettvisning: +pdfjs-document-properties-linearized-yes = Ja +pdfjs-document-properties-linearized-no = Nei +pdfjs-document-properties-close-button = Lukk + +## Print + +pdfjs-print-progress-message = Forbereder dokument for utskrift … +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Avbryt +pdfjs-printing-not-supported = Advarsel: Utskrift er ikke fullstendig støttet av denne nettleseren. +pdfjs-printing-not-ready = Advarsel: PDF er ikke fullstendig innlastet for utskrift. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Slå av/på sidestolpe +pdfjs-toggle-sidebar-notification-button = + .title = Vis/gjem sidestolpe (dokumentet inneholder oversikt/vedlegg/lag) +pdfjs-toggle-sidebar-button-label = Slå av/på sidestolpe +pdfjs-document-outline-button = + .title = Vis dokumentdisposisjonen (dobbeltklikk for å utvide/skjule alle elementer) +pdfjs-document-outline-button-label = Dokumentdisposisjon +pdfjs-attachments-button = + .title = Vis vedlegg +pdfjs-attachments-button-label = Vedlegg +pdfjs-layers-button = + .title = Vis lag (dobbeltklikk for å tilbakestille alle lag til standardtilstand) +pdfjs-layers-button-label = Lag +pdfjs-thumbs-button = + .title = Vis miniatyrbilde +pdfjs-thumbs-button-label = Miniatyrbilde +pdfjs-current-outline-item-button = + .title = Finn gjeldende disposisjonselement +pdfjs-current-outline-item-button-label = Gjeldende disposisjonselement +pdfjs-findbar-button = + .title = Finn i dokumentet +pdfjs-findbar-button-label = Finn +pdfjs-additional-layers = Ytterligere lag + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Side { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatyrbilde av side { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Søk + .placeholder = Søk i dokument… +pdfjs-find-previous-button = + .title = Finn forrige forekomst av frasen +pdfjs-find-previous-button-label = Forrige +pdfjs-find-next-button = + .title = Finn neste forekomst av frasen +pdfjs-find-next-button-label = Neste +pdfjs-find-highlight-checkbox = Uthev alle +pdfjs-find-match-case-checkbox-label = Skill store/små bokstaver +pdfjs-find-match-diacritics-checkbox-label = Samsvar diakritiske tegn +pdfjs-find-entire-word-checkbox-label = Hele ord +pdfjs-find-reached-top = Nådde toppen av dokumentet, fortsetter fra bunnen +pdfjs-find-reached-bottom = Nådde bunnen av dokumentet, fortsetter fra toppen +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } av { $total } treff + *[other] { $current } av { $total } treff + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Mer enn { $limit } treff + *[other] Mer enn { $limit } treff + } +pdfjs-find-not-found = Fant ikke teksten + +## Predefined zoom values + +pdfjs-page-scale-width = Sidebredde +pdfjs-page-scale-fit = Tilpass til siden +pdfjs-page-scale-auto = Automatisk zoom +pdfjs-page-scale-actual = Virkelig størrelse +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale } % + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Side { $page } + +## Loading indicator messages + +pdfjs-loading-error = En feil oppstod ved lasting av PDF. +pdfjs-invalid-file-error = Ugyldig eller skadet PDF-fil. +pdfjs-missing-file-error = Manglende PDF-fil. +pdfjs-unexpected-response-error = Uventet serverrespons. +pdfjs-rendering-error = En feil oppstod ved opptegning av siden. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } annotasjon] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Skriv inn passordet for å åpne denne PDF-filen. +pdfjs-password-invalid = Ugyldig passord. Prøv igjen. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Avbryt +pdfjs-web-fonts-disabled = Web-fonter er avslått: Kan ikke bruke innbundne PDF-fonter. + +## Editing + +pdfjs-editor-free-text-button = + .title = Tekst +pdfjs-editor-free-text-button-label = Tekst +pdfjs-editor-ink-button = + .title = Tegn +pdfjs-editor-ink-button-label = Tegn +pdfjs-editor-stamp-button = + .title = Legg til eller rediger bilder +pdfjs-editor-stamp-button-label = Legg til eller rediger bilder +pdfjs-editor-highlight-button = + .title = Markere +pdfjs-editor-highlight-button-label = Markere +pdfjs-highlight-floating-button1 = + .title = Markere + .aria-label = Markere +pdfjs-highlight-floating-button-label = Markere + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Fjern tegningen +pdfjs-editor-remove-freetext-button = + .title = Fjern tekst +pdfjs-editor-remove-stamp-button = + .title = Fjern bildet +pdfjs-editor-remove-highlight-button = + .title = Fjern utheving + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Farge +pdfjs-editor-free-text-size-input = Størrelse +pdfjs-editor-ink-color-input = Farge +pdfjs-editor-ink-thickness-input = Tykkelse +pdfjs-editor-ink-opacity-input = Ugjennomsiktighet +pdfjs-editor-stamp-add-image-button = + .title = Legg til bilde +pdfjs-editor-stamp-add-image-button-label = Legg til bilde +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Tykkelse +pdfjs-editor-free-highlight-thickness-title = + .title = Endre tykkelse når du markerer andre elementer enn tekst +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstredigering + .default-content = Begynn å skrive… +pdfjs-free-text = + .aria-label = Tekstredigering +pdfjs-free-text-default-content = Begynn å skrive… +pdfjs-ink = + .aria-label = Tegneredigering +pdfjs-ink-canvas = + .aria-label = Brukerskapt bilde + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alt-tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Rediger alt-tekst +pdfjs-editor-alt-text-edit-button-label = Rediger alt-tekst tekst +pdfjs-editor-alt-text-dialog-label = Velg et alternativ +pdfjs-editor-alt-text-dialog-description = Alt-tekst (alternativ tekst) hjelper når folk ikke kan se bildet eller når det ikke lastes inn. +pdfjs-editor-alt-text-add-description-label = Legg til en beskrivelse +pdfjs-editor-alt-text-add-description-description = Gå etter 1-2 setninger som beskriver emnet, settingen eller handlingene. +pdfjs-editor-alt-text-mark-decorative-label = Merk som dekorativt +pdfjs-editor-alt-text-mark-decorative-description = Dette brukes til dekorative bilder, som kantlinjer eller vannmerker. +pdfjs-editor-alt-text-cancel-button = Avbryt +pdfjs-editor-alt-text-save-button = Lagre +pdfjs-editor-alt-text-decorative-tooltip = Merket som dekorativ +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = For eksempel, «En ung mann setter seg ved et bord for å spise et måltid» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt-tekst + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Øverste venstre hjørne – endre størrelse +pdfjs-editor-resizer-label-top-middle = Øverst i midten — endre størrelse +pdfjs-editor-resizer-label-top-right = Øverste høyre hjørne – endre størrelse +pdfjs-editor-resizer-label-middle-right = Midt til høyre – endre størrelse +pdfjs-editor-resizer-label-bottom-right = Nederste høyre hjørne – endre størrelse +pdfjs-editor-resizer-label-bottom-middle = Nederst i midten — endre størrelse +pdfjs-editor-resizer-label-bottom-left = Nederste venstre hjørne – endre størrelse +pdfjs-editor-resizer-label-middle-left = Midt til venstre — endre størrelse +pdfjs-editor-resizer-top-left = + .aria-label = Øverste venstre hjørne – endre størrelse +pdfjs-editor-resizer-top-middle = + .aria-label = Øverst i midten — endre størrelse +pdfjs-editor-resizer-top-right = + .aria-label = Øverste høyre hjørne – endre størrelse +pdfjs-editor-resizer-middle-right = + .aria-label = Midt til høyre – endre størrelse +pdfjs-editor-resizer-bottom-right = + .aria-label = Nederste høyre hjørne – endre størrelse +pdfjs-editor-resizer-bottom-middle = + .aria-label = Nederst i midten — endre størrelse +pdfjs-editor-resizer-bottom-left = + .aria-label = Nederste venstre hjørne – endre størrelse +pdfjs-editor-resizer-middle-left = + .aria-label = Midt til venstre — endre størrelse + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Uthevingsfarge +pdfjs-editor-colorpicker-button = + .title = Endre farge +pdfjs-editor-colorpicker-dropdown = + .aria-label = Fargevalg +pdfjs-editor-colorpicker-yellow = + .title = Gul +pdfjs-editor-colorpicker-green = + .title = Grønn +pdfjs-editor-colorpicker-blue = + .title = Blå +pdfjs-editor-colorpicker-pink = + .title = Rosa +pdfjs-editor-colorpicker-red = + .title = Rød + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Vis alle +pdfjs-editor-highlight-show-all-button = + .title = Vis alle + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Rediger alternativ tekst (bildebeskrivelse) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Legg til alternativ tekst (bildebeskrivelse) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Skriv din beskrivelse her… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Kort beskrivelse for folk som ikke kan se bildet eller når bildet ikke lastes inn. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Denne alternative teksten ble opprettet automatisk og kan være unøyaktig. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Les mer +pdfjs-editor-new-alt-text-create-automatically-button-label = Lag alternativ tekst automatisk +pdfjs-editor-new-alt-text-not-now-button = Ikke nå +pdfjs-editor-new-alt-text-error-title = Kunne ikke opprette alternativ tekst automatisk +pdfjs-editor-new-alt-text-error-description = Skriv din egen alternativ-tekst eller prøv igjen senere. +pdfjs-editor-new-alt-text-error-close-button = Lukk +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Laster ned alternativ tekst AI-modell ({ $downloadedSize } av { $totalSize } MB) + .aria-valuetext = Laster ned alternativ tekst AI-modell ({ $downloadedSize } av { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alt-tekst lagt til +pdfjs-editor-new-alt-text-added-button-label = Alternativ tekst lagt til +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Mangler alt-tekst +pdfjs-editor-new-alt-text-missing-button-label = Mangler alternativ tekst +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Gjennomgå alt-tekst +pdfjs-editor-new-alt-text-to-review-button-label = Gjennomgå alternativ tekst +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Opprettet automatisk: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Innstillinger for alternativ tekst for bilde +pdfjs-image-alt-text-settings-button-label = Innstillinger for alternativ tekst for bilde +pdfjs-editor-alt-text-settings-dialog-label = Innstillinger for alternativ tekst for bilde +pdfjs-editor-alt-text-settings-automatic-title = Automatisk alternativ tekst +pdfjs-editor-alt-text-settings-create-model-button-label = Opprett alternativ tekst automatisk +pdfjs-editor-alt-text-settings-create-model-description = Foreslår beskrivelser for å hjelpe folk som ikke kan se bildet eller når bildet ikke lastes inn. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Alternativ tekst AI-modell ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Kjører lokalt på enheten din slik at dataene dine forblir private. Nødvendig for automatisk alternativ tekst. +pdfjs-editor-alt-text-settings-delete-model-button = Slett +pdfjs-editor-alt-text-settings-download-model-button = Last ned +pdfjs-editor-alt-text-settings-downloading-model-button = Laster ned… +pdfjs-editor-alt-text-settings-editor-title = Alternativ tekst-redigerer +pdfjs-editor-alt-text-settings-show-dialog-button-label = Vis alternativ tekst-redigerer direkte når du legger til et bilde +pdfjs-editor-alt-text-settings-show-dialog-description = Hjelper deg å sørge for at alle bildene dine har alternativ tekst. +pdfjs-editor-alt-text-settings-close-button = Lukk diff --git a/public/pdfjs/web/locale/ne-NP/viewer.ftl b/public/pdfjs/web/locale/ne-NP/viewer.ftl new file mode 100644 index 0000000..65193b6 --- /dev/null +++ b/public/pdfjs/web/locale/ne-NP/viewer.ftl @@ -0,0 +1,234 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = अघिल्लो पृष्ठ +pdfjs-previous-button-label = अघिल्लो +pdfjs-next-button = + .title = पछिल्लो पृष्ठ +pdfjs-next-button-label = पछिल्लो +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = पृष्ठ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount } मध्ये +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pagesCount } को { $pageNumber }) +pdfjs-zoom-out-button = + .title = जुम घटाउनुहोस् +pdfjs-zoom-out-button-label = जुम घटाउनुहोस् +pdfjs-zoom-in-button = + .title = जुम बढाउनुहोस् +pdfjs-zoom-in-button-label = जुम बढाउनुहोस् +pdfjs-zoom-select = + .title = जुम गर्नुहोस् +pdfjs-presentation-mode-button = + .title = प्रस्तुति मोडमा जानुहोस् +pdfjs-presentation-mode-button-label = प्रस्तुति मोड +pdfjs-open-file-button = + .title = फाइल खोल्नुहोस् +pdfjs-open-file-button-label = खोल्नुहोस् +pdfjs-print-button = + .title = मुद्रण गर्नुहोस् +pdfjs-print-button-label = मुद्रण गर्नुहोस् + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = औजारहरू +pdfjs-tools-button-label = औजारहरू +pdfjs-first-page-button = + .title = पहिलो पृष्ठमा जानुहोस् +pdfjs-first-page-button-label = पहिलो पृष्ठमा जानुहोस् +pdfjs-last-page-button = + .title = पछिल्लो पृष्ठमा जानुहोस् +pdfjs-last-page-button-label = पछिल्लो पृष्ठमा जानुहोस् +pdfjs-page-rotate-cw-button = + .title = घडीको दिशामा घुमाउनुहोस् +pdfjs-page-rotate-cw-button-label = घडीको दिशामा घुमाउनुहोस् +pdfjs-page-rotate-ccw-button = + .title = घडीको विपरित दिशामा घुमाउनुहोस् +pdfjs-page-rotate-ccw-button-label = घडीको विपरित दिशामा घुमाउनुहोस् +pdfjs-cursor-text-select-tool-button = + .title = पाठ चयन उपकरण सक्षम गर्नुहोस् +pdfjs-cursor-text-select-tool-button-label = पाठ चयन उपकरण +pdfjs-cursor-hand-tool-button = + .title = हाते उपकरण सक्षम गर्नुहोस् +pdfjs-cursor-hand-tool-button-label = हाते उपकरण +pdfjs-scroll-vertical-button = + .title = ठाडो स्क्रोलिङ्ग प्रयोग गर्नुहोस् +pdfjs-scroll-vertical-button-label = ठाडो स्क्र्रोलिङ्ग +pdfjs-scroll-horizontal-button = + .title = तेर्सो स्क्रोलिङ्ग प्रयोग गर्नुहोस् +pdfjs-scroll-horizontal-button-label = तेर्सो स्क्रोलिङ्ग +pdfjs-scroll-wrapped-button = + .title = लिपि स्क्रोलिङ्ग प्रयोग गर्नुहोस् +pdfjs-scroll-wrapped-button-label = लिपि स्क्रोलिङ्ग +pdfjs-spread-none-button = + .title = पृष्ठ स्प्रेडमा सामेल हुनुहुन्न +pdfjs-spread-none-button-label = स्प्रेड छैन + +## Document properties dialog + +pdfjs-document-properties-button = + .title = कागजात विशेषताहरू... +pdfjs-document-properties-button-label = कागजात विशेषताहरू... +pdfjs-document-properties-file-name = फाइल नाम: +pdfjs-document-properties-file-size = फाइल आकार: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = शीर्षक: +pdfjs-document-properties-author = लेखक: +pdfjs-document-properties-subject = विषयः +pdfjs-document-properties-keywords = शब्दकुञ्जीः +pdfjs-document-properties-creation-date = सिर्जना गरिएको मिति: +pdfjs-document-properties-modification-date = परिमार्जित मिति: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = सर्जक: +pdfjs-document-properties-producer = PDF निर्माता: +pdfjs-document-properties-version = PDF संस्करण +pdfjs-document-properties-page-count = पृष्ठ गणना: +pdfjs-document-properties-page-size = पृष्ठ आकार: +pdfjs-document-properties-page-size-unit-inches = इन्च +pdfjs-document-properties-page-size-unit-millimeters = मि.मि. +pdfjs-document-properties-page-size-orientation-portrait = पोट्रेट +pdfjs-document-properties-page-size-orientation-landscape = परिदृश्य +pdfjs-document-properties-page-size-name-letter = अक्षर +pdfjs-document-properties-page-size-name-legal = कानूनी + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + + +## + +pdfjs-document-properties-linearized-yes = हो +pdfjs-document-properties-linearized-no = होइन +pdfjs-document-properties-close-button = बन्द गर्नुहोस् + +## Print + +pdfjs-print-progress-message = मुद्रणका लागि कागजात तयारी गरिदै… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = रद्द गर्नुहोस् +pdfjs-printing-not-supported = चेतावनी: यो ब्राउजरमा मुद्रण पूर्णतया समर्थित छैन। +pdfjs-printing-not-ready = चेतावनी: PDF मुद्रणका लागि पूर्णतया लोड भएको छैन। + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = टगल साइडबार +pdfjs-toggle-sidebar-button-label = टगल साइडबार +pdfjs-document-outline-button = + .title = कागजातको रूपरेखा देखाउनुहोस् (सबै वस्तुहरू विस्तार/पतन गर्न डबल-क्लिक गर्नुहोस्) +pdfjs-document-outline-button-label = दस्तावेजको रूपरेखा +pdfjs-attachments-button = + .title = संलग्नहरू देखाउनुहोस् +pdfjs-attachments-button-label = संलग्नकहरू +pdfjs-thumbs-button = + .title = थम्बनेलहरू देखाउनुहोस् +pdfjs-thumbs-button-label = थम्बनेलहरू +pdfjs-findbar-button = + .title = कागजातमा फेला पार्नुहोस् +pdfjs-findbar-button-label = फेला पार्नुहोस् + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = पृष्ठ { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page } पृष्ठको थम्बनेल + +## Find panel button title and messages + +pdfjs-find-input = + .title = फेला पार्नुहोस् + .placeholder = कागजातमा फेला पार्नुहोस्… +pdfjs-find-previous-button = + .title = यस वाक्यांशको अघिल्लो घटना फेला पार्नुहोस् +pdfjs-find-previous-button-label = अघिल्लो +pdfjs-find-next-button = + .title = यस वाक्यांशको पछिल्लो घटना फेला पार्नुहोस् +pdfjs-find-next-button-label = अर्को +pdfjs-find-highlight-checkbox = सबै हाइलाइट गर्ने +pdfjs-find-match-case-checkbox-label = केस जोडा मिलाउनुहोस् +pdfjs-find-entire-word-checkbox-label = पुरा शब्दहरु +pdfjs-find-reached-top = पृष्ठको शिर्षमा पुगीयो, तलबाट जारी गरिएको थियो +pdfjs-find-reached-bottom = पृष्ठको अन्त्यमा पुगीयो, शिर्षबाट जारी गरिएको थियो +pdfjs-find-not-found = वाक्यांश फेला परेन + +## Predefined zoom values + +pdfjs-page-scale-width = पृष्ठ चौडाइ +pdfjs-page-scale-fit = पृष्ठ ठिक्क मिल्ने +pdfjs-page-scale-auto = स्वचालित जुम +pdfjs-page-scale-actual = वास्तविक आकार +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = यो PDF लोड गर्दा एउटा त्रुटि देखापर्‍यो। +pdfjs-invalid-file-error = अवैध वा दुषित PDF फाइल। +pdfjs-missing-file-error = हराईरहेको PDF फाइल। +pdfjs-unexpected-response-error = अप्रत्याशित सर्भर प्रतिक्रिया। +pdfjs-rendering-error = पृष्ठ प्रतिपादन गर्दा एउटा त्रुटि देखापर्‍यो। + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] + +## Password + +pdfjs-password-label = यस PDF फाइललाई खोल्न गोप्यशब्द प्रविष्ट गर्नुहोस्। +pdfjs-password-invalid = अवैध गोप्यशब्द। पुनः प्रयास गर्नुहोस्। +pdfjs-password-ok-button = ठिक छ +pdfjs-password-cancel-button = रद्द गर्नुहोस् +pdfjs-web-fonts-disabled = वेब फन्ट असक्षम छन्: एम्बेडेड PDF फन्ट प्रयोग गर्न असमर्थ। + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/nl/viewer.ftl b/public/pdfjs/web/locale/nl/viewer.ftl new file mode 100644 index 0000000..fe24ce7 --- /dev/null +++ b/public/pdfjs/web/locale/nl/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Vorige pagina +pdfjs-previous-button-label = Vorige +pdfjs-next-button = + .title = Volgende pagina +pdfjs-next-button-label = Volgende +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pagina +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = van { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } van { $pagesCount }) +pdfjs-zoom-out-button = + .title = Uitzoomen +pdfjs-zoom-out-button-label = Uitzoomen +pdfjs-zoom-in-button = + .title = Inzoomen +pdfjs-zoom-in-button-label = Inzoomen +pdfjs-zoom-select = + .title = Zoomen +pdfjs-presentation-mode-button = + .title = Wisselen naar presentatiemodus +pdfjs-presentation-mode-button-label = Presentatiemodus +pdfjs-open-file-button = + .title = Bestand openen +pdfjs-open-file-button-label = Openen +pdfjs-print-button = + .title = Afdrukken +pdfjs-print-button-label = Afdrukken +pdfjs-save-button = + .title = Opslaan +pdfjs-save-button-label = Opslaan +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Downloaden +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Downloaden +pdfjs-bookmark-button = + .title = Huidige pagina (URL van huidige pagina bekijken) +pdfjs-bookmark-button-label = Huidige pagina + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Hulpmiddelen +pdfjs-tools-button-label = Hulpmiddelen +pdfjs-first-page-button = + .title = Naar eerste pagina gaan +pdfjs-first-page-button-label = Naar eerste pagina gaan +pdfjs-last-page-button = + .title = Naar laatste pagina gaan +pdfjs-last-page-button-label = Naar laatste pagina gaan +pdfjs-page-rotate-cw-button = + .title = Rechtsom draaien +pdfjs-page-rotate-cw-button-label = Rechtsom draaien +pdfjs-page-rotate-ccw-button = + .title = Linksom draaien +pdfjs-page-rotate-ccw-button-label = Linksom draaien +pdfjs-cursor-text-select-tool-button = + .title = Tekstselectiehulpmiddel inschakelen +pdfjs-cursor-text-select-tool-button-label = Tekstselectiehulpmiddel +pdfjs-cursor-hand-tool-button = + .title = Handhulpmiddel inschakelen +pdfjs-cursor-hand-tool-button-label = Handhulpmiddel +pdfjs-scroll-page-button = + .title = Paginascrollen gebruiken +pdfjs-scroll-page-button-label = Paginascrollen +pdfjs-scroll-vertical-button = + .title = Verticaal scrollen gebruiken +pdfjs-scroll-vertical-button-label = Verticaal scrollen +pdfjs-scroll-horizontal-button = + .title = Horizontaal scrollen gebruiken +pdfjs-scroll-horizontal-button-label = Horizontaal scrollen +pdfjs-scroll-wrapped-button = + .title = Scrollen met terugloop gebruiken +pdfjs-scroll-wrapped-button-label = Scrollen met terugloop +pdfjs-spread-none-button = + .title = Dubbele pagina’s niet samenvoegen +pdfjs-spread-none-button-label = Geen dubbele pagina’s +pdfjs-spread-odd-button = + .title = Dubbele pagina’s samenvoegen vanaf oneven pagina’s +pdfjs-spread-odd-button-label = Oneven dubbele pagina’s +pdfjs-spread-even-button = + .title = Dubbele pagina’s samenvoegen vanaf even pagina’s +pdfjs-spread-even-button-label = Even dubbele pagina’s + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Documenteigenschappen… +pdfjs-document-properties-button-label = Documenteigenschappen… +pdfjs-document-properties-file-name = Bestandsnaam: +pdfjs-document-properties-file-size = Bestandsgrootte: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Titel: +pdfjs-document-properties-author = Auteur: +pdfjs-document-properties-subject = Onderwerp: +pdfjs-document-properties-keywords = Sleutelwoorden: +pdfjs-document-properties-creation-date = Aanmaakdatum: +pdfjs-document-properties-modification-date = Wijzigingsdatum: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Maker: +pdfjs-document-properties-producer = PDF-producent: +pdfjs-document-properties-version = PDF-versie: +pdfjs-document-properties-page-count = Aantal pagina’s: +pdfjs-document-properties-page-size = Paginagrootte: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = staand +pdfjs-document-properties-page-size-orientation-landscape = liggend +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Snelle webweergave: +pdfjs-document-properties-linearized-yes = Ja +pdfjs-document-properties-linearized-no = Nee +pdfjs-document-properties-close-button = Sluiten + +## Print + +pdfjs-print-progress-message = Document voorbereiden voor afdrukken… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Annuleren +pdfjs-printing-not-supported = Waarschuwing: afdrukken wordt niet volledig ondersteund door deze browser. +pdfjs-printing-not-ready = Waarschuwing: de PDF is niet volledig geladen voor afdrukken. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Zijbalk in-/uitschakelen +pdfjs-toggle-sidebar-notification-button = + .title = Zijbalk in-/uitschakelen (document bevat overzicht/bijlagen/lagen) +pdfjs-toggle-sidebar-button-label = Zijbalk in-/uitschakelen +pdfjs-document-outline-button = + .title = Documentoverzicht tonen (dubbelklik om alle items uit/samen te vouwen) +pdfjs-document-outline-button-label = Documentoverzicht +pdfjs-attachments-button = + .title = Bijlagen tonen +pdfjs-attachments-button-label = Bijlagen +pdfjs-layers-button = + .title = Lagen tonen (dubbelklik om alle lagen naar de standaardstatus terug te zetten) +pdfjs-layers-button-label = Lagen +pdfjs-thumbs-button = + .title = Miniaturen tonen +pdfjs-thumbs-button-label = Miniaturen +pdfjs-current-outline-item-button = + .title = Huidig item in inhoudsopgave zoeken +pdfjs-current-outline-item-button-label = Huidig item in inhoudsopgave +pdfjs-findbar-button = + .title = Zoeken in document +pdfjs-findbar-button-label = Zoeken +pdfjs-additional-layers = Aanvullende lagen + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pagina { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatuur van pagina { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Zoeken + .placeholder = Zoeken in document… +pdfjs-find-previous-button = + .title = De vorige overeenkomst van de tekst zoeken +pdfjs-find-previous-button-label = Vorige +pdfjs-find-next-button = + .title = De volgende overeenkomst van de tekst zoeken +pdfjs-find-next-button-label = Volgende +pdfjs-find-highlight-checkbox = Alles markeren +pdfjs-find-match-case-checkbox-label = Hoofdlettergevoelig +pdfjs-find-match-diacritics-checkbox-label = Diakritische tekens gebruiken +pdfjs-find-entire-word-checkbox-label = Hele woorden +pdfjs-find-reached-top = Bovenkant van document bereikt, doorgegaan vanaf onderkant +pdfjs-find-reached-bottom = Onderkant van document bereikt, doorgegaan vanaf bovenkant +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } van { $total } overeenkomst + *[other] { $current } van { $total } overeenkomsten + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Meer dan { $limit } overeenkomst + *[other] Meer dan { $limit } overeenkomsten + } +pdfjs-find-not-found = Tekst niet gevonden + +## Predefined zoom values + +pdfjs-page-scale-width = Paginabreedte +pdfjs-page-scale-fit = Hele pagina +pdfjs-page-scale-auto = Automatisch zoomen +pdfjs-page-scale-actual = Werkelijke grootte +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Pagina { $page } + +## Loading indicator messages + +pdfjs-loading-error = Er is een fout opgetreden bij het laden van de PDF. +pdfjs-invalid-file-error = Ongeldig of beschadigd PDF-bestand. +pdfjs-missing-file-error = PDF-bestand ontbreekt. +pdfjs-unexpected-response-error = Onverwacht serverantwoord. +pdfjs-rendering-error = Er is een fout opgetreden bij het weergeven van de pagina. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type }-aantekening] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Voer het wachtwoord in om dit PDF-bestand te openen. +pdfjs-password-invalid = Ongeldig wachtwoord. Probeer het opnieuw. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Annuleren +pdfjs-web-fonts-disabled = Weblettertypen zijn uitgeschakeld: gebruik van ingebedde PDF-lettertypen is niet mogelijk. + +## Editing + +pdfjs-editor-free-text-button = + .title = Tekst +pdfjs-editor-free-text-button-label = Tekst +pdfjs-editor-ink-button = + .title = Tekenen +pdfjs-editor-ink-button-label = Tekenen +pdfjs-editor-stamp-button = + .title = Afbeeldingen toevoegen of bewerken +pdfjs-editor-stamp-button-label = Afbeeldingen toevoegen of bewerken +pdfjs-editor-highlight-button = + .title = Markeren +pdfjs-editor-highlight-button-label = Markeren +pdfjs-highlight-floating-button1 = + .title = Markeren + .aria-label = Markeren +pdfjs-highlight-floating-button-label = Markeren + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Tekening verwijderen +pdfjs-editor-remove-freetext-button = + .title = Tekst verwijderen +pdfjs-editor-remove-stamp-button = + .title = Afbeelding verwijderen +pdfjs-editor-remove-highlight-button = + .title = Markering verwijderen + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Kleur +pdfjs-editor-free-text-size-input = Grootte +pdfjs-editor-ink-color-input = Kleur +pdfjs-editor-ink-thickness-input = Dikte +pdfjs-editor-ink-opacity-input = Opaciteit +pdfjs-editor-stamp-add-image-button = + .title = Afbeelding toevoegen +pdfjs-editor-stamp-add-image-button-label = Afbeelding toevoegen +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Dikte +pdfjs-editor-free-highlight-thickness-title = + .title = Dikte wijzigen bij accentuering van andere items dan tekst +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstbewerker + .default-content = Start met typen… +pdfjs-free-text = + .aria-label = Tekstbewerker +pdfjs-free-text-default-content = Begin met typen… +pdfjs-ink = + .aria-label = Tekeningbewerker +pdfjs-ink-canvas = + .aria-label = Door gebruiker gemaakte afbeelding + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alternatieve tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternatieve tekst bewerken +pdfjs-editor-alt-text-edit-button-label = Alternatieve tekst bewerken +pdfjs-editor-alt-text-dialog-label = Kies een optie +pdfjs-editor-alt-text-dialog-description = Alternatieve tekst helpt wanneer mensen de afbeelding niet kunnen zien of wanneer deze niet wordt geladen. +pdfjs-editor-alt-text-add-description-label = Voeg een beschrijving toe +pdfjs-editor-alt-text-add-description-description = Streef naar 1-2 zinnen die het onderwerp, de omgeving of de acties beschrijven. +pdfjs-editor-alt-text-mark-decorative-label = Als decoratief markeren +pdfjs-editor-alt-text-mark-decorative-description = Dit wordt gebruikt voor sierafbeeldingen, zoals randen of watermerken. +pdfjs-editor-alt-text-cancel-button = Annuleren +pdfjs-editor-alt-text-save-button = Opslaan +pdfjs-editor-alt-text-decorative-tooltip = Als decoratief gemarkeerd +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Bijvoorbeeld: ‘Een jonge man gaat aan een tafel zitten om te eten’ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternatieve tekst + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Linkerbovenhoek – formaat wijzigen +pdfjs-editor-resizer-label-top-middle = Midden boven – formaat wijzigen +pdfjs-editor-resizer-label-top-right = Rechterbovenhoek – formaat wijzigen +pdfjs-editor-resizer-label-middle-right = Midden rechts – formaat wijzigen +pdfjs-editor-resizer-label-bottom-right = Rechterbenedenhoek – formaat wijzigen +pdfjs-editor-resizer-label-bottom-middle = Midden onder – formaat wijzigen +pdfjs-editor-resizer-label-bottom-left = Linkerbenedenhoek – formaat wijzigen +pdfjs-editor-resizer-label-middle-left = Links midden – formaat wijzigen +pdfjs-editor-resizer-top-left = + .aria-label = Linkerbovenhoek – formaat wijzigen +pdfjs-editor-resizer-top-middle = + .aria-label = Midden boven – formaat wijzigen +pdfjs-editor-resizer-top-right = + .aria-label = Rechterbovenhoek – formaat wijzigen +pdfjs-editor-resizer-middle-right = + .aria-label = Midden rechts – formaat wijzigen +pdfjs-editor-resizer-bottom-right = + .aria-label = Rechterbenedenhoek – formaat wijzigen +pdfjs-editor-resizer-bottom-middle = + .aria-label = Midden onder – formaat wijzigen +pdfjs-editor-resizer-bottom-left = + .aria-label = Linkerbenedenhoek – formaat wijzigen +pdfjs-editor-resizer-middle-left = + .aria-label = Links midden – formaat wijzigen + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Markeringskleur +pdfjs-editor-colorpicker-button = + .title = Kleur wijzigen +pdfjs-editor-colorpicker-dropdown = + .aria-label = Kleurkeuzes +pdfjs-editor-colorpicker-yellow = + .title = Geel +pdfjs-editor-colorpicker-green = + .title = Groen +pdfjs-editor-colorpicker-blue = + .title = Blauw +pdfjs-editor-colorpicker-pink = + .title = Roze +pdfjs-editor-colorpicker-red = + .title = Rood + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Alles tonen +pdfjs-editor-highlight-show-all-button = + .title = Alles tonen + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Alternatieve tekst (afbeeldingsbeschrijving) bewerken +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Alternatieve tekst (afbeeldingsbeschrijving) toevoegen +pdfjs-editor-new-alt-text-textarea = + .placeholder = Schrijf hier uw beschrijving… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Korte beschrijving voor mensen die de afbeelding niet kunnen zien of wanneer de afbeelding niet wordt geladen. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Deze alternatieve tekst is automatisch gemaakt en is mogelijk onjuist. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Meer info +pdfjs-editor-new-alt-text-create-automatically-button-label = Alternatieve tekst automatisch aanmaken +pdfjs-editor-new-alt-text-not-now-button = Niet nu +pdfjs-editor-new-alt-text-error-title = Kan alternatieve tekst niet automatisch aanmaken +pdfjs-editor-new-alt-text-error-description = Schrijf uw eigen alternatieve tekst of probeer het later nog eens. +pdfjs-editor-new-alt-text-error-close-button = Sluiten +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = AI-model voor alternatieve tekst downloaden ({ $downloadedSize } van { $totalSize } MB) + .aria-valuetext = AI-model voor alternatieve tekst downloaden ({ $downloadedSize } van { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternatieve tekst toegevoegd +pdfjs-editor-new-alt-text-added-button-label = Alternatieve tekst toegevoegd +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Alternatieve tekst ontbreekt +pdfjs-editor-new-alt-text-missing-button-label = Alternatieve tekst ontbreekt +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternatieve tekst beoordelen +pdfjs-editor-new-alt-text-to-review-button-label = Alternatieve tekst beoordelen +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Automatisch aangemaakt: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Instellingen voor alternatieve tekst van afbeeldingen +pdfjs-image-alt-text-settings-button-label = Instellingen voor alternatieve tekst van afbeeldingen +pdfjs-editor-alt-text-settings-dialog-label = Instellingen voor alternatieve tekst van afbeeldingen +pdfjs-editor-alt-text-settings-automatic-title = Automatische alternatieve tekst +pdfjs-editor-alt-text-settings-create-model-button-label = Alternatieve tekst automatisch aanmaken +pdfjs-editor-alt-text-settings-create-model-description = Stelt beschrijvingen voor om mensen te helpen die de afbeelding niet kunnen zien of voor wie de afbeelding niet wordt geladen. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = AI-model voor alternatieve tekst ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Wordt lokaal op uw apparaat uitgevoerd, zodat uw gegevens privé blijven. Vereist voor automatische alternatieve tekst. +pdfjs-editor-alt-text-settings-delete-model-button = Verwijderen +pdfjs-editor-alt-text-settings-download-model-button = Downloaden +pdfjs-editor-alt-text-settings-downloading-model-button = Downloaden… +pdfjs-editor-alt-text-settings-editor-title = Alternatieve-tekstbewerker +pdfjs-editor-alt-text-settings-show-dialog-button-label = Alternatieve-tekstbewerker meteen tonen bij toevoegen van een afbeelding +pdfjs-editor-alt-text-settings-show-dialog-description = Helpt u ervoor te zorgen dat al uw afbeeldingen alternatieve tekst hebben. +pdfjs-editor-alt-text-settings-close-button = Sluiten + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Markering verwijderd +pdfjs-editor-undo-bar-message-freetext = Tekst verwijderd +pdfjs-editor-undo-bar-message-ink = Tekening verwijderd +pdfjs-editor-undo-bar-message-stamp = Afbeelding verwijderd +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotatie verwijderd + *[other] { $count } annotaties verwijderd + } +pdfjs-editor-undo-bar-undo-button = + .title = Ongedaan maken +pdfjs-editor-undo-bar-undo-button-label = Ongedaan maken +pdfjs-editor-undo-bar-close-button = + .title = Sluiten +pdfjs-editor-undo-bar-close-button-label = Sluiten diff --git a/public/pdfjs/web/locale/nn-NO/viewer.ftl b/public/pdfjs/web/locale/nn-NO/viewer.ftl new file mode 100644 index 0000000..d617e16 --- /dev/null +++ b/public/pdfjs/web/locale/nn-NO/viewer.ftl @@ -0,0 +1,498 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Føregåande side +pdfjs-previous-button-label = Føregåande +pdfjs-next-button = + .title = Neste side +pdfjs-next-button-label = Neste +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Side +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = av { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } av { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zoom ut +pdfjs-zoom-out-button-label = Zoom ut +pdfjs-zoom-in-button = + .title = Zoom inn +pdfjs-zoom-in-button-label = Zoom inn +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Byt til presentasjonsmodus +pdfjs-presentation-mode-button-label = Presentasjonsmodus +pdfjs-open-file-button = + .title = Opne fil +pdfjs-open-file-button-label = Opne +pdfjs-print-button = + .title = Skriv ut +pdfjs-print-button-label = Skriv ut +pdfjs-save-button = + .title = Lagre +pdfjs-save-button-label = Lagre +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Last ned +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Last ned +pdfjs-bookmark-button = + .title = Gjeldande side (sjå URL frå gjeldande side) +pdfjs-bookmark-button-label = Gjeldande side + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Verktøy +pdfjs-tools-button-label = Verktøy +pdfjs-first-page-button = + .title = Gå til første side +pdfjs-first-page-button-label = Gå til første side +pdfjs-last-page-button = + .title = Gå til siste side +pdfjs-last-page-button-label = Gå til siste side +pdfjs-page-rotate-cw-button = + .title = Roter med klokka +pdfjs-page-rotate-cw-button-label = Roter med klokka +pdfjs-page-rotate-ccw-button = + .title = Roter mot klokka +pdfjs-page-rotate-ccw-button-label = Roter mot klokka +pdfjs-cursor-text-select-tool-button = + .title = Aktiver tekstmarkeringsverktøy +pdfjs-cursor-text-select-tool-button-label = Tekstmarkeringsverktøy +pdfjs-cursor-hand-tool-button = + .title = Aktiver handverktøy +pdfjs-cursor-hand-tool-button-label = Handverktøy +pdfjs-scroll-page-button = + .title = Bruk siderulling +pdfjs-scroll-page-button-label = Siderulling +pdfjs-scroll-vertical-button = + .title = Bruk vertikal rulling +pdfjs-scroll-vertical-button-label = Vertikal rulling +pdfjs-scroll-horizontal-button = + .title = Bruk horisontal rulling +pdfjs-scroll-horizontal-button-label = Horisontal rulling +pdfjs-scroll-wrapped-button = + .title = Bruk fleirsiderulling +pdfjs-scroll-wrapped-button-label = Fleirsiderulling +pdfjs-spread-none-button = + .title = Vis enkeltsider +pdfjs-spread-none-button-label = Enkeltside +pdfjs-spread-odd-button = + .title = Vis oppslag med ulike sidenummer til venstre +pdfjs-spread-odd-button-label = Oppslag med framside +pdfjs-spread-even-button = + .title = Vis oppslag med like sidenummmer til venstre +pdfjs-spread-even-button-label = Oppslag utan framside + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumenteigenskapar… +pdfjs-document-properties-button-label = Dokumenteigenskapar… +pdfjs-document-properties-file-name = Filnamn: +pdfjs-document-properties-file-size = Filstorleik: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } kB ({ $b } byte) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } byte) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Tittel: +pdfjs-document-properties-author = Forfattar: +pdfjs-document-properties-subject = Emne: +pdfjs-document-properties-keywords = Stikkord: +pdfjs-document-properties-creation-date = Dato oppretta: +pdfjs-document-properties-modification-date = Dato endra: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Oppretta av: +pdfjs-document-properties-producer = PDF-verktøy: +pdfjs-document-properties-version = PDF-versjon: +pdfjs-document-properties-page-count = Sidetal: +pdfjs-document-properties-page-size = Sidestørrelse: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = ståande (portrait) +pdfjs-document-properties-page-size-orientation-landscape = liggande (landscape) +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Brev +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Rask nettvising: +pdfjs-document-properties-linearized-yes = Ja +pdfjs-document-properties-linearized-no = Nei +pdfjs-document-properties-close-button = Lat att + +## Print + +pdfjs-print-progress-message = Førebur dokumentet for utskrift… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Avbryt +pdfjs-printing-not-supported = Åtvaring: Utskrift er ikkje fullstendig støtta av denne nettlesaren. +pdfjs-printing-not-ready = Åtvaring: PDF ikkje fullstendig innlasta for utskrift. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Slå av/på sidestolpe +pdfjs-toggle-sidebar-notification-button = + .title = Vis/gøym sidestolpe (dokumentet inneheld oversikt/vedlegg/lag) +pdfjs-toggle-sidebar-button-label = Slå av/på sidestolpe +pdfjs-document-outline-button = + .title = Vis dokumentdisposisjonen (dobbelklikk for å utvide/gøyme alle elementa) +pdfjs-document-outline-button-label = Dokumentdisposisjon +pdfjs-attachments-button = + .title = Vis vedlegg +pdfjs-attachments-button-label = Vedlegg +pdfjs-layers-button = + .title = Vis lag (dobbeltklikk for å tilbakestille alle lag til standardtilstand) +pdfjs-layers-button-label = Lag +pdfjs-thumbs-button = + .title = Vis miniatyrbilde +pdfjs-thumbs-button-label = Miniatyrbilde +pdfjs-current-outline-item-button = + .title = Finn gjeldande disposisjonselement +pdfjs-current-outline-item-button-label = Gjeldande disposisjonselement +pdfjs-findbar-button = + .title = Finn i dokumentet +pdfjs-findbar-button-label = Finn +pdfjs-additional-layers = Ytterlegare lag + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Side { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatyrbilde av side { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Søk + .placeholder = Søk i dokument… +pdfjs-find-previous-button = + .title = Finn førre førekomst av frasen +pdfjs-find-previous-button-label = Førre +pdfjs-find-next-button = + .title = Finn neste førekomst av frasen +pdfjs-find-next-button-label = Neste +pdfjs-find-highlight-checkbox = Uthev alle +pdfjs-find-match-case-checkbox-label = Skil store/små bokstavar +pdfjs-find-match-diacritics-checkbox-label = Samsvar diakritiske teikn +pdfjs-find-entire-word-checkbox-label = Heile ord +pdfjs-find-reached-top = Nådde toppen av dokumentet, fortset frå botnen +pdfjs-find-reached-bottom = Nådde botnen av dokumentet, fortset frå toppen +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } av { $total } treff + *[other] { $current } av { $total } treff + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Meir enn { $limit } treff + *[other] Meir enn { $limit } treff + } +pdfjs-find-not-found = Fann ikkje teksten + +## Predefined zoom values + +pdfjs-page-scale-width = Sidebreidde +pdfjs-page-scale-fit = Tilpass til sida +pdfjs-page-scale-auto = Automatisk skalering +pdfjs-page-scale-actual = Verkeleg storleik +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Side { $page } + +## Loading indicator messages + +pdfjs-loading-error = Ein feil oppstod ved lasting av PDF. +pdfjs-invalid-file-error = Ugyldig eller korrupt PDF-fil. +pdfjs-missing-file-error = Manglande PDF-fil. +pdfjs-unexpected-response-error = Uventa tenarrespons. +pdfjs-rendering-error = Ein feil oppstod under vising av sida. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date } { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } annotasjon] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Skriv inn passordet for å opne denne PDF-fila. +pdfjs-password-invalid = Ugyldig passord. Prøv på nytt. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Avbryt +pdfjs-web-fonts-disabled = Web-skrifter er slått av: Kan ikkje bruke innbundne PDF-skrifter. + +## Editing + +pdfjs-editor-free-text-button = + .title = Tekst +pdfjs-editor-free-text-button-label = Tekst +pdfjs-editor-ink-button = + .title = Teikne +pdfjs-editor-ink-button-label = Teikne +pdfjs-editor-stamp-button = + .title = Legg til eller rediger bilde +pdfjs-editor-stamp-button-label = Legg til eller rediger bilde +pdfjs-editor-highlight-button = + .title = Markere +pdfjs-editor-highlight-button-label = Markere +pdfjs-highlight-floating-button1 = + .title = Markere + .aria-label = Markere +pdfjs-highlight-floating-button-label = Markere + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Fjern teikninga +pdfjs-editor-remove-freetext-button = + .title = Fjern tekst +pdfjs-editor-remove-stamp-button = + .title = Fjern bildet +pdfjs-editor-remove-highlight-button = + .title = Fjern utheving + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Farge +pdfjs-editor-free-text-size-input = Storleik +pdfjs-editor-ink-color-input = Farge +pdfjs-editor-ink-thickness-input = Tjukn +pdfjs-editor-ink-opacity-input = Ugjennomskinleg +pdfjs-editor-stamp-add-image-button = + .title = Legg til bilde +pdfjs-editor-stamp-add-image-button-label = Legg til bilde +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Tjukn +pdfjs-editor-free-highlight-thickness-title = + .title = Endre tjukn når du markerer andre element enn tekst +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstredigering + .default-content = Begynn å skrive… +pdfjs-free-text = + .aria-label = Tekstredigering +pdfjs-free-text-default-content = Byrje å skrive… +pdfjs-ink = + .aria-label = Teikneredigering +pdfjs-ink-canvas = + .aria-label = Brukarskapt bilde + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alt-tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Rediger alt-tekst tekst +pdfjs-editor-alt-text-edit-button-label = Rediger alt-tekst tekst +pdfjs-editor-alt-text-dialog-label = Vel eit alternativ +pdfjs-editor-alt-text-dialog-description = Alt-tekst (alternativ tekst) hjelper når folk ikkje kan sjå bildet eller når det ikkje vert lasta inn. +pdfjs-editor-alt-text-add-description-label = Legg til ei skildring +pdfjs-editor-alt-text-add-description-description = Gå etter 1-2 setninger som skildrar emnet, settinga eller handlingane. +pdfjs-editor-alt-text-mark-decorative-label = Merk som dekorativt +pdfjs-editor-alt-text-mark-decorative-description = Dette vert brukt til dekorative bilde, som kantlinjer eller vassmerke. +pdfjs-editor-alt-text-cancel-button = Avbryt +pdfjs-editor-alt-text-save-button = Lagre +pdfjs-editor-alt-text-decorative-tooltip = Merkt som dekorativ +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Til dømes, «Ein ung mann set seg ved eit bord for å ete eit måltid» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt-tekst + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Øvste venstre hjørne – endre størrelse +pdfjs-editor-resizer-label-top-middle = Øvst i midten — endre størrelse +pdfjs-editor-resizer-label-top-right = Øvste høgre hjørne – endre størrelse +pdfjs-editor-resizer-label-middle-right = Midt til høgre – endre størrelse +pdfjs-editor-resizer-label-bottom-right = Nedste høgre hjørne – endre størrelse +pdfjs-editor-resizer-label-bottom-middle = Nedst i midten — endre størrelse +pdfjs-editor-resizer-label-bottom-left = Nedste venstre hjørne – endre størrelse +pdfjs-editor-resizer-label-middle-left = Midt til venstre — endre størrelse +pdfjs-editor-resizer-top-left = + .aria-label = Øvste venstre hjørne – endre størrelse +pdfjs-editor-resizer-top-middle = + .aria-label = Øvst i midten — endre størrelse +pdfjs-editor-resizer-top-right = + .aria-label = Øvste høgre hjørne – endre størrelse +pdfjs-editor-resizer-middle-right = + .aria-label = Midt til høgre – endre størrelse +pdfjs-editor-resizer-bottom-right = + .aria-label = Nedste høgre hjørne – endre størrelse +pdfjs-editor-resizer-bottom-middle = + .aria-label = Nedst i midten — endre størrelse +pdfjs-editor-resizer-bottom-left = + .aria-label = Nedste venstre hjørne – endre størrelse +pdfjs-editor-resizer-middle-left = + .aria-label = Midt til venstre — endre størrelse + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Uthevingsfarge +pdfjs-editor-colorpicker-button = + .title = Endre farge +pdfjs-editor-colorpicker-dropdown = + .aria-label = Fargeval +pdfjs-editor-colorpicker-yellow = + .title = Gul +pdfjs-editor-colorpicker-green = + .title = Grøn +pdfjs-editor-colorpicker-blue = + .title = Blå +pdfjs-editor-colorpicker-pink = + .title = Rosa +pdfjs-editor-colorpicker-red = + .title = Raud + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Vis alle +pdfjs-editor-highlight-show-all-button = + .title = Vis alle + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Rediger alternativ tekst (bildeskildring) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Legg til alternativ tekst (bildeskildring) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Skriv skildringa di her… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Kort skildring for personar som ikkje kan sjå bildet, eller når bildet ikkje lastar inn. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Denne alternative teksten vart oppretta automatisk, og kan vere unøyaktig. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Les meir +pdfjs-editor-new-alt-text-create-automatically-button-label = Opprett alternativ tekt automatisk +pdfjs-editor-new-alt-text-not-now-button = Ikkje no +pdfjs-editor-new-alt-text-error-title = Klarte ikkje å opprette alternativ tekst automatisk +pdfjs-editor-new-alt-text-error-description = Skriv din eigen alternative tekst eller prøv igjen seinare. +pdfjs-editor-new-alt-text-error-close-button = Lat att +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Lastar ned AI-modell med alternativ tekst ({ $downloadedSize } av { $totalSize } MB) + .aria-valuetext = Lastar ned AI-modell med alternativ tekst ({ $downloadedSize } av { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternativ tekst lagt til +pdfjs-editor-new-alt-text-added-button-label = Alternativ tekst lagt til +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Manglande alternativ tekst +pdfjs-editor-new-alt-text-missing-button-label = Manglande alternativ tekst +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Vurder alternativ tekst +pdfjs-editor-new-alt-text-to-review-button-label = Vurder alternativ tekst +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Oppretta automatisk: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Alternative tekst-innstillingar for bilde +pdfjs-image-alt-text-settings-button-label = Alternative tekst-innstillingar for bilde +pdfjs-editor-alt-text-settings-dialog-label = Alternative tekst-innstillingar for bilde +pdfjs-editor-alt-text-settings-automatic-title = Automatisk alternativ tekst +pdfjs-editor-alt-text-settings-create-model-button-label = Opprett alternativ tekt automatisk +pdfjs-editor-alt-text-settings-create-model-description = Foreslår skildringar for å hjelpe folk som ikkje kan sjå bildet eller når bildet ikkje blir lasta inn. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = AI-modell for alternativ tekst ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Køyrer lokalt på eininga di slik at dataa dine blir verande private. Påkravd for automatisk alternativ tekst. +pdfjs-editor-alt-text-settings-delete-model-button = Slett +pdfjs-editor-alt-text-settings-download-model-button = Last ned +pdfjs-editor-alt-text-settings-downloading-model-button = Lastar ned… +pdfjs-editor-alt-text-settings-editor-title = Alternativ tekst-redigerar +pdfjs-editor-alt-text-settings-show-dialog-button-label = Vis alternativ tekst-redigerar direkte når du legg til eit bilde +pdfjs-editor-alt-text-settings-show-dialog-description = Hjelper deg med å sørgje for at alle bilda dine har alternativ tekst. +pdfjs-editor-alt-text-settings-close-button = Lat att + +## "Annotations removed" bar + diff --git a/public/pdfjs/web/locale/oc/viewer.ftl b/public/pdfjs/web/locale/oc/viewer.ftl new file mode 100644 index 0000000..b347aef --- /dev/null +++ b/public/pdfjs/web/locale/oc/viewer.ftl @@ -0,0 +1,409 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pagina precedenta +pdfjs-previous-button-label = Precedent +pdfjs-next-button = + .title = Pagina seguenta +pdfjs-next-button-label = Seguent +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pagina +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = sus { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zoom arrièr +pdfjs-zoom-out-button-label = Zoom arrièr +pdfjs-zoom-in-button = + .title = Zoom avant +pdfjs-zoom-in-button-label = Zoom avant +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Bascular en mòde presentacion +pdfjs-presentation-mode-button-label = Mòde Presentacion +pdfjs-open-file-button = + .title = Dobrir lo fichièr +pdfjs-open-file-button-label = Dobrir +pdfjs-print-button = + .title = Imprimir +pdfjs-print-button-label = Imprimir +pdfjs-save-button = + .title = Enregistrar +pdfjs-save-button-label = Enregistrar +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Telecargar +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Telecargar +pdfjs-bookmark-button = + .title = Pagina actuala (mostrar l’adreça de la pagina actuala) +pdfjs-bookmark-button-label = Pagina actuala + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Aisinas +pdfjs-tools-button-label = Aisinas +pdfjs-first-page-button = + .title = Anar a la primièra pagina +pdfjs-first-page-button-label = Anar a la primièra pagina +pdfjs-last-page-button = + .title = Anar a la darrièra pagina +pdfjs-last-page-button-label = Anar a la darrièra pagina +pdfjs-page-rotate-cw-button = + .title = Rotacion orària +pdfjs-page-rotate-cw-button-label = Rotacion orària +pdfjs-page-rotate-ccw-button = + .title = Rotacion antiorària +pdfjs-page-rotate-ccw-button-label = Rotacion antiorària +pdfjs-cursor-text-select-tool-button = + .title = Activar l'aisina de seleccion de tèxte +pdfjs-cursor-text-select-tool-button-label = Aisina de seleccion de tèxte +pdfjs-cursor-hand-tool-button = + .title = Activar l’aisina man +pdfjs-cursor-hand-tool-button-label = Aisina man +pdfjs-scroll-page-button = + .title = Activar lo defilament per pagina +pdfjs-scroll-page-button-label = Defilament per pagina +pdfjs-scroll-vertical-button = + .title = Utilizar lo defilament vertical +pdfjs-scroll-vertical-button-label = Defilament vertical +pdfjs-scroll-horizontal-button = + .title = Utilizar lo defilament orizontal +pdfjs-scroll-horizontal-button-label = Defilament orizontal +pdfjs-scroll-wrapped-button = + .title = Activar lo defilament continú +pdfjs-scroll-wrapped-button-label = Defilament continú +pdfjs-spread-none-button = + .title = Agropar pas las paginas doas a doas +pdfjs-spread-none-button-label = Una sola pagina +pdfjs-spread-odd-button = + .title = Mostrar doas paginas en començant per las paginas imparas a esquèrra +pdfjs-spread-odd-button-label = Dobla pagina, impara a drecha +pdfjs-spread-even-button = + .title = Mostrar doas paginas en començant per las paginas paras a esquèrra +pdfjs-spread-even-button-label = Dobla pagina, para a drecha + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Proprietats del document… +pdfjs-document-properties-button-label = Proprietats del document… +pdfjs-document-properties-file-name = Nom del fichièr : +pdfjs-document-properties-file-size = Talha del fichièr : +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } Ko ({ $size_b } octets) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } Mo ({ $size_b } octets) +pdfjs-document-properties-title = Títol : +pdfjs-document-properties-author = Autor : +pdfjs-document-properties-subject = Subjècte : +pdfjs-document-properties-keywords = Mots claus : +pdfjs-document-properties-creation-date = Data de creacion : +pdfjs-document-properties-modification-date = Data de modificacion : +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, a { $time } +pdfjs-document-properties-creator = Creator : +pdfjs-document-properties-producer = Aisina de conversion PDF : +pdfjs-document-properties-version = Version PDF : +pdfjs-document-properties-page-count = Nombre de paginas : +pdfjs-document-properties-page-size = Talha de la pagina : +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = retrach +pdfjs-document-properties-page-size-orientation-landscape = païsatge +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letra +pdfjs-document-properties-page-size-name-legal = Document juridic + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Vista web rapida : +pdfjs-document-properties-linearized-yes = Òc +pdfjs-document-properties-linearized-no = Non +pdfjs-document-properties-close-button = Tampar + +## Print + +pdfjs-print-progress-message = Preparacion del document per l’impression… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Anullar +pdfjs-printing-not-supported = Atencion : l'impression es pas complètament gerida per aqueste navegador. +pdfjs-printing-not-ready = Atencion : lo PDF es pas entièrament cargat per lo poder imprimir. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Afichar/amagar lo panèl lateral +pdfjs-toggle-sidebar-notification-button = + .title = Afichar/amagar lo panèl lateral (lo document conten esquèmas/pèças juntas/calques) +pdfjs-toggle-sidebar-button-label = Afichar/amagar lo panèl lateral +pdfjs-document-outline-button = + .title = Mostrar los esquèmas del document (dobleclicar per espandre/reduire totes los elements) +pdfjs-document-outline-button-label = Marcapaginas del document +pdfjs-attachments-button = + .title = Visualizar las pèças juntas +pdfjs-attachments-button-label = Pèças juntas +pdfjs-layers-button = + .title = Afichar los calques (doble-clicar per reïnicializar totes los calques a l’estat per defaut) +pdfjs-layers-button-label = Calques +pdfjs-thumbs-button = + .title = Afichar las vinhetas +pdfjs-thumbs-button-label = Vinhetas +pdfjs-current-outline-item-button = + .title = Trobar l’element de plan actual +pdfjs-current-outline-item-button-label = Element de plan actual +pdfjs-findbar-button = + .title = Cercar dins lo document +pdfjs-findbar-button-label = Recercar +pdfjs-additional-layers = Calques suplementaris + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pagina { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Vinheta de la pagina { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Recercar + .placeholder = Cercar dins lo document… +pdfjs-find-previous-button = + .title = Tròba l'ocurréncia precedenta de la frasa +pdfjs-find-previous-button-label = Precedent +pdfjs-find-next-button = + .title = Tròba l'ocurréncia venenta de la frasa +pdfjs-find-next-button-label = Seguent +pdfjs-find-highlight-checkbox = Suslinhar tot +pdfjs-find-match-case-checkbox-label = Respectar la cassa +pdfjs-find-match-diacritics-checkbox-label = Respectar los diacritics +pdfjs-find-entire-word-checkbox-label = Mots entièrs +pdfjs-find-reached-top = Naut de la pagina atenh, perseguida del bas +pdfjs-find-reached-bottom = Bas de la pagina atench, perseguida al començament +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] Ocurréncia { $current } de { $total } + *[other] Ocurréncia { $current } de { $total } + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Mai de { $limit } ocurréncia + *[other] Mai de { $limit } ocurréncias + } +pdfjs-find-not-found = Frasa pas trobada + +## Predefined zoom values + +pdfjs-page-scale-width = Largor plena +pdfjs-page-scale-fit = Pagina entièra +pdfjs-page-scale-auto = Zoom automatic +pdfjs-page-scale-actual = Talha vertadièra +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Pagina { $page } + +## Loading indicator messages + +pdfjs-loading-error = Una error s'es producha pendent lo cargament del fichièr PDF. +pdfjs-invalid-file-error = Fichièr PDF invalid o corromput. +pdfjs-missing-file-error = Fichièr PDF mancant. +pdfjs-unexpected-response-error = Responsa de servidor imprevista. +pdfjs-rendering-error = Una error s'es producha pendent l'afichatge de la pagina. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date } a { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anotacion { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Picatz lo senhal per dobrir aqueste fichièr PDF. +pdfjs-password-invalid = Senhal incorrècte. Tornatz ensajar. +pdfjs-password-ok-button = D'acòrdi +pdfjs-password-cancel-button = Anullar +pdfjs-web-fonts-disabled = Las polissas web son desactivadas : impossible d'utilizar las polissas integradas al PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Tèxte +pdfjs-editor-free-text-button-label = Tèxte +pdfjs-editor-ink-button = + .title = Dessenhar +pdfjs-editor-ink-button-label = Dessenhar +pdfjs-editor-stamp-button = + .title = Apondre o modificar d’imatges +pdfjs-editor-stamp-button-label = Apondre o modificar d’imatges +pdfjs-editor-highlight-button = + .title = Subrelinhar +pdfjs-editor-highlight-button-label = Subrelinhar +pdfjs-highlight-floating-button1 = + .title = Subrelinhar + .aria-label = Subrelinhar +pdfjs-highlight-floating-button-label = Subrelinhar + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Levar lo dessenh +pdfjs-editor-remove-freetext-button = + .title = Suprimir lo tèxte +pdfjs-editor-remove-stamp-button = + .title = Suprimir l’imatge +pdfjs-editor-remove-highlight-button = + .title = Levar lo suslinhatge + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Color +pdfjs-editor-free-text-size-input = Talha +pdfjs-editor-ink-color-input = Color +pdfjs-editor-ink-thickness-input = Espessor +pdfjs-editor-ink-opacity-input = Opacitat +pdfjs-editor-stamp-add-image-button = + .title = Apondre imatge +pdfjs-editor-stamp-add-image-button-label = Apondre imatge +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Espessor +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de tèxte + .default-content = Començatz de picar… +pdfjs-free-text = + .aria-label = Editor de tèxte +pdfjs-free-text-default-content = Començatz d’escriure… +pdfjs-ink = + .aria-label = Editor de dessenh +pdfjs-ink-canvas = + .aria-label = Imatge creat per l’utilizaire + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Tèxt alternatiu +pdfjs-editor-alt-text-edit-button-label = Modificar lo tèxt alternatiu +pdfjs-editor-alt-text-dialog-label = Causir una opcion +pdfjs-editor-alt-text-add-description-label = Apondre una descripcion +pdfjs-editor-alt-text-cancel-button = Anullar +pdfjs-editor-alt-text-save-button = Enregistrar + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Color de suslinhatge +pdfjs-editor-colorpicker-button = + .title = Cambiar de color +pdfjs-editor-colorpicker-dropdown = + .aria-label = Causida de colors +pdfjs-editor-colorpicker-yellow = + .title = Jaune +pdfjs-editor-colorpicker-green = + .title = Verd +pdfjs-editor-colorpicker-blue = + .title = Blau +pdfjs-editor-colorpicker-pink = + .title = Ròse +pdfjs-editor-colorpicker-red = + .title = Roge + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = O afichar tot +pdfjs-editor-highlight-show-all-button = + .title = O afichar tot + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +pdfjs-editor-new-alt-text-error-close-button = Tampar + +## Image alt-text settings + +pdfjs-editor-alt-text-settings-automatic-title = Tèxte alternatiu automatic +pdfjs-editor-alt-text-settings-create-model-button-label = Crear un tèxte alternatiu automaticament +pdfjs-editor-alt-text-settings-delete-model-button = Suprimir +pdfjs-editor-alt-text-settings-download-model-button = Telecargar +pdfjs-editor-alt-text-settings-downloading-model-button = Telecargament… +pdfjs-editor-alt-text-settings-editor-title = Editor de tèxte alternatiu +pdfjs-editor-alt-text-settings-close-button = Tampar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-freetext = Tèxte suprimit +pdfjs-editor-undo-bar-message-ink = Dessenh suprimit +pdfjs-editor-undo-bar-message-stamp = Imatge suprimit +pdfjs-editor-undo-bar-undo-button = + .title = Anullar +pdfjs-editor-undo-bar-undo-button-label = Anullar +pdfjs-editor-undo-bar-close-button = + .title = Tampar +pdfjs-editor-undo-bar-close-button-label = Tampar diff --git a/public/pdfjs/web/locale/pa-IN/viewer.ftl b/public/pdfjs/web/locale/pa-IN/viewer.ftl new file mode 100644 index 0000000..10a6112 --- /dev/null +++ b/public/pdfjs/web/locale/pa-IN/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = ਪਿਛਲਾ ਸਫ਼ਾ +pdfjs-previous-button-label = ਪਿੱਛੇ +pdfjs-next-button = + .title = ਅਗਲਾ ਸਫ਼ਾ +pdfjs-next-button-label = ਅੱਗੇ +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = ਸਫ਼ਾ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount } ਵਿੱਚੋਂ +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = { $pagesCount }) ਵਿੱਚੋਂ ({ $pageNumber } +pdfjs-zoom-out-button = + .title = ਜ਼ੂਮ ਆਉਟ +pdfjs-zoom-out-button-label = ਜ਼ੂਮ ਆਉਟ +pdfjs-zoom-in-button = + .title = ਜ਼ੂਮ ਇਨ +pdfjs-zoom-in-button-label = ਜ਼ੂਮ ਇਨ +pdfjs-zoom-select = + .title = ਜ਼ੂਨ +pdfjs-presentation-mode-button = + .title = ਪਰਿਜੈਂਟੇਸ਼ਨ ਮੋਡ ਵਿੱਚ ਜਾਓ +pdfjs-presentation-mode-button-label = ਪਰਿਜੈਂਟੇਸ਼ਨ ਮੋਡ +pdfjs-open-file-button = + .title = ਫਾਈਲ ਨੂੰ ਖੋਲ੍ਹੋ +pdfjs-open-file-button-label = ਖੋਲ੍ਹੋ +pdfjs-print-button = + .title = ਪਰਿੰਟ +pdfjs-print-button-label = ਪਰਿੰਟ +pdfjs-save-button = + .title = ਸੰਭਾਲੋ +pdfjs-save-button-label = ਸੰਭਾਲੋ +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = ਡਾਊਨਲੋਡ +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = ਡਾਊਨਲੋਡ +pdfjs-bookmark-button = + .title = ਮੌਜੂਦਾ ਸਫ਼਼ਾ (ਮੌਜੂਦਾ ਸਫ਼ੇ ਤੋਂ URL ਵੇਖੋ) +pdfjs-bookmark-button-label = ਮੌਜੂਦਾ ਸਫ਼਼ਾ + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = ਟੂਲ +pdfjs-tools-button-label = ਟੂਲ +pdfjs-first-page-button = + .title = ਪਹਿਲੇ ਸਫ਼ੇ ਉੱਤੇ ਜਾਓ +pdfjs-first-page-button-label = ਪਹਿਲੇ ਸਫ਼ੇ ਉੱਤੇ ਜਾਓ +pdfjs-last-page-button = + .title = ਆਖਰੀ ਸਫ਼ੇ ਉੱਤੇ ਜਾਓ +pdfjs-last-page-button-label = ਆਖਰੀ ਸਫ਼ੇ ਉੱਤੇ ਜਾਓ +pdfjs-page-rotate-cw-button = + .title = ਸੱਜੇ ਦਾਅ ਘੁੰਮਾਓ +pdfjs-page-rotate-cw-button-label = ਸੱਜੇ ਦਾਅ ਘੁੰਮਾਓ +pdfjs-page-rotate-ccw-button = + .title = ਖੱਬੇ ਦਾਅ ਘੁੰਮਾਓ +pdfjs-page-rotate-ccw-button-label = ਖੱਬੇ ਦਾਅ ਘੁੰਮਾਓ +pdfjs-cursor-text-select-tool-button = + .title = ਲਿਖਤ ਚੋਣ ਟੂਲ ਸਮਰੱਥ ਕਰੋ +pdfjs-cursor-text-select-tool-button-label = ਲਿਖਤ ਚੋਣ ਟੂਲ +pdfjs-cursor-hand-tool-button = + .title = ਹੱਥ ਟੂਲ ਸਮਰੱਥ ਕਰੋ +pdfjs-cursor-hand-tool-button-label = ਹੱਥ ਟੂਲ +pdfjs-scroll-page-button = + .title = ਸਫ਼ਾ ਖਿਸਕਾਉਣ ਨੂੰ ਵਰਤੋਂ +pdfjs-scroll-page-button-label = ਸਫ਼ਾ ਖਿਸਕਾਉਣਾ +pdfjs-scroll-vertical-button = + .title = ਖੜ੍ਹਵੇਂ ਸਕਰਾਉਣ ਨੂੰ ਵਰਤੋਂ +pdfjs-scroll-vertical-button-label = ਖੜ੍ਹਵਾਂ ਸਰਕਾਉਣਾ +pdfjs-scroll-horizontal-button = + .title = ਲੇਟਵੇਂ ਸਰਕਾਉਣ ਨੂੰ ਵਰਤੋਂ +pdfjs-scroll-horizontal-button-label = ਲੇਟਵਾਂ ਸਰਕਾਉਣਾ +pdfjs-scroll-wrapped-button = + .title = ਸਮੇਟੇ ਸਰਕਾਉਣ ਨੂੰ ਵਰਤੋਂ +pdfjs-scroll-wrapped-button-label = ਸਮੇਟਿਆ ਸਰਕਾਉਣਾ +pdfjs-spread-none-button = + .title = ਸਫ਼ਾ ਫੈਲਾਅ ਵਿੱਚ ਸ਼ਾਮਲ ਨਾ ਹੋਵੋ +pdfjs-spread-none-button-label = ਕੋਈ ਫੈਲਾਅ ਨਹੀਂ +pdfjs-spread-odd-button = + .title = ਟਾਂਕ ਅੰਕ ਵਾਲੇ ਸਫ਼ਿਆਂ ਨਾਲ ਸ਼ੁਰੂ ਹੋਣ ਵਾਲੇ ਸਫਿਆਂ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਵੋ +pdfjs-spread-odd-button-label = ਟਾਂਕ ਫੈਲਾਅ +pdfjs-spread-even-button = + .title = ਜਿਸਤ ਅੰਕ ਵਾਲੇ ਸਫ਼ਿਆਂ ਨਾਲ ਸ਼ੁਰੂ ਹੋਣ ਵਾਲੇ ਸਫਿਆਂ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਵੋ +pdfjs-spread-even-button-label = ਜਿਸਤ ਫੈਲਾਅ + +## Document properties dialog + +pdfjs-document-properties-button = + .title = …ਦਸਤਾਵੇਜ਼ ਦੀ ਵਿਸ਼ੇਸ਼ਤਾ +pdfjs-document-properties-button-label = …ਦਸਤਾਵੇਜ਼ ਦੀ ਵਿਸ਼ੇਸ਼ਤਾ +pdfjs-document-properties-file-name = ਫਾਈਲ ਦਾ ਨਾਂ: +pdfjs-document-properties-file-size = ਫਾਈਲ ਦਾ ਆਕਾਰ: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } ਬਾਈਟ) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } ਬਾਈਟ) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } ਬਾਈਟ) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } ਬਾਈਟ) +pdfjs-document-properties-title = ਟਾਈਟਲ: +pdfjs-document-properties-author = ਲੇਖਕ: +pdfjs-document-properties-subject = ਵਿਸ਼ਾ: +pdfjs-document-properties-keywords = ਸ਼ਬਦ: +pdfjs-document-properties-creation-date = ਬਣਾਉਣ ਦੀ ਮਿਤੀ: +pdfjs-document-properties-modification-date = ਸੋਧ ਦੀ ਮਿਤੀ: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = ਨਿਰਮਾਤਾ: +pdfjs-document-properties-producer = PDF ਪ੍ਰੋਡਿਊਸਰ: +pdfjs-document-properties-version = PDF ਵਰਜਨ: +pdfjs-document-properties-page-count = ਸਫ਼ੇ ਦੀ ਗਿਣਤੀ: +pdfjs-document-properties-page-size = ਸਫ਼ਾ ਆਕਾਰ: +pdfjs-document-properties-page-size-unit-inches = ਇੰਚ +pdfjs-document-properties-page-size-unit-millimeters = ਮਿਮੀ +pdfjs-document-properties-page-size-orientation-portrait = ਪੋਰਟਰੇਟ +pdfjs-document-properties-page-size-orientation-landscape = ਲੈਂਡਸਕੇਪ +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = ਲੈਟਰ +pdfjs-document-properties-page-size-name-legal = ਕਨੂੰਨੀ + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = ਤੇਜ਼ ਵੈੱਬ ਝਲਕ: +pdfjs-document-properties-linearized-yes = ਹਾਂ +pdfjs-document-properties-linearized-no = ਨਹੀਂ +pdfjs-document-properties-close-button = ਬੰਦ ਕਰੋ + +## Print + +pdfjs-print-progress-message = …ਪਰਿੰਟ ਕਰਨ ਲਈ ਦਸਤਾਵੇਜ਼ ਨੂੰ ਤਿਆਰ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = ਰੱਦ ਕਰੋ +pdfjs-printing-not-supported = ਸਾਵਧਾਨ: ਇਹ ਬਰਾਊਜ਼ਰ ਪਰਿੰਟ ਕਰਨ ਲਈ ਪੂਰੀ ਤਰ੍ਹਾਂ ਸਹਾਇਕ ਨਹੀਂ ਹੈ। +pdfjs-printing-not-ready = ਸਾਵਧਾਨ: PDF ਨੂੰ ਪਰਿੰਟ ਕਰਨ ਲਈ ਪੂਰੀ ਤਰ੍ਹਾਂ ਲੋਡ ਨਹੀਂ ਹੈ। + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = ਬਾਹੀ ਬਦਲੋ +pdfjs-toggle-sidebar-notification-button = + .title = ਬਾਹੀ ਨੂੰ ਬਦਲੋ (ਦਸਤਾਵੇਜ਼ ਖਾਕਾ/ਅਟੈਚਮੈਂਟ/ਪਰਤਾਂ ਰੱਖਦਾ ਹੈ) +pdfjs-toggle-sidebar-button-label = ਬਾਹੀ ਬਦਲੋ +pdfjs-document-outline-button = + .title = ਦਸਤਾਵੇਜ਼ ਖਾਕਾ ਦਿਖਾਓ (ਸਾਰੀਆਂ ਆਈਟਮਾਂ ਨੂੰ ਫੈਲਾਉਣ/ਸਮੇਟਣ ਲਈ ਦੋ ਵਾਰ ਕਲਿੱਕ ਕਰੋ) +pdfjs-document-outline-button-label = ਦਸਤਾਵੇਜ਼ ਖਾਕਾ +pdfjs-attachments-button = + .title = ਅਟੈਚਮੈਂਟ ਵੇਖਾਓ +pdfjs-attachments-button-label = ਅਟੈਚਮੈਂਟਾਂ +pdfjs-layers-button = + .title = ਪਰਤਾਂ ਵੇਖਾਓ (ਸਾਰੀਆਂ ਪਰਤਾਂ ਨੂੰ ਮੂਲ ਹਾਲਤ ਉੱਤੇ ਮੁੜ-ਸੈੱਟ ਕਰਨ ਲਈ ਦੋ ਵਾਰ ਕਲਿੱਕ ਕਰੋ) +pdfjs-layers-button-label = ਪਰਤਾਂ +pdfjs-thumbs-button = + .title = ਥੰਮਨੇਲ ਨੂੰ ਵੇਖਾਓ +pdfjs-thumbs-button-label = ਥੰਮਨੇਲ +pdfjs-current-outline-item-button = + .title = ਮੌੌਜੂਦਾ ਖਾਕਾ ਚੀਜ਼ ਲੱਭੋ +pdfjs-current-outline-item-button-label = ਮੌਜੂਦਾ ਖਾਕਾ ਚੀਜ਼ +pdfjs-findbar-button = + .title = ਦਸਤਾਵੇਜ਼ ਵਿੱਚ ਲੱਭੋ +pdfjs-findbar-button-label = ਲੱਭੋ +pdfjs-additional-layers = ਵਾਧੂ ਪਰਤਾਂ + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = ਸਫ਼ਾ { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page } ਸਫ਼ੇ ਦਾ ਥੰਮਨੇਲ + +## Find panel button title and messages + +pdfjs-find-input = + .title = ਲੱਭੋ + .placeholder = …ਦਸਤਾਵੇਜ਼ 'ਚ ਲੱਭੋ +pdfjs-find-previous-button = + .title = ਵਾਕ ਦੀ ਪਿਛਲੀ ਮੌਜੂਦਗੀ ਲੱਭੋ +pdfjs-find-previous-button-label = ਪਿੱਛੇ +pdfjs-find-next-button = + .title = ਵਾਕ ਦੀ ਅਗਲੀ ਮੌਜੂਦਗੀ ਲੱਭੋ +pdfjs-find-next-button-label = ਅੱਗੇ +pdfjs-find-highlight-checkbox = ਸਭ ਉਭਾਰੋ +pdfjs-find-match-case-checkbox-label = ਅੱਖਰ ਆਕਾਰ ਨੂੰ ਮਿਲਾਉ +pdfjs-find-match-diacritics-checkbox-label = ਭੇਦਸੂਚਕ ਮੇਲ +pdfjs-find-entire-word-checkbox-label = ਪੂਰੇ ਸ਼ਬਦ +pdfjs-find-reached-top = ਦਸਤਾਵੇਜ਼ ਦੇ ਉੱਤੇ ਆ ਗਏ ਹਾਂ, ਥੱਲੇ ਤੋਂ ਜਾਰੀ ਰੱਖਿਆ ਹੈ +pdfjs-find-reached-bottom = ਦਸਤਾਵੇਜ਼ ਦੇ ਅੰਤ ਉੱਤੇ ਆ ਗਏ ਹਾਂ, ਉੱਤੇ ਤੋਂ ਜਾਰੀ ਰੱਖਿਆ ਹੈ +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $total } ਵਿੱਚੋਂ { $current } ਮੇਲ + *[other] { $total } ਵਿੱਚੋਂ { $current } ਮੇਲ + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] { $limit } ਤੋਂ ਵੱਧ ਮੇਲ + *[other] { $limit } ਤੋਂ ਵੱਧ ਮੇਲ + } +pdfjs-find-not-found = ਵਾਕ ਨਹੀਂ ਲੱਭਿਆ + +## Predefined zoom values + +pdfjs-page-scale-width = ਸਫ਼ੇ ਦੀ ਚੌੜਾਈ +pdfjs-page-scale-fit = ਸਫ਼ਾ ਫਿੱਟ +pdfjs-page-scale-auto = ਆਟੋਮੈਟਿਕ ਜ਼ੂਮ ਕਰੋ +pdfjs-page-scale-actual = ਆਟੋਮੈਟਿਕ ਆਕਾਰ +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = ਸਫ਼ਾ { $page } + +## Loading indicator messages + +pdfjs-loading-error = PDF ਲੋਡ ਕਰਨ ਦੇ ਦੌਰਾਨ ਗਲਤੀ ਆਈ ਹੈ। +pdfjs-invalid-file-error = ਗਲਤ ਜਾਂ ਨਿਕਾਰਾ PDF ਫਾਈਲ ਹੈ। +pdfjs-missing-file-error = ਨਾ-ਮੌਜੂਦ PDF ਫਾਈਲ। +pdfjs-unexpected-response-error = ਅਣਜਾਣ ਸਰਵਰ ਜਵਾਬ। +pdfjs-rendering-error = ਸਫ਼ਾ ਰੈਡਰ ਕਰਨ ਦੇ ਦੌਰਾਨ ਗਲਤੀ ਆਈ ਹੈ। + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } ਵਿਆਖਿਆ] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = ਇਹ PDF ਫਾਈਲ ਨੂੰ ਖੋਲ੍ਹਣ ਲਈ ਪਾਸਵਰਡ ਦਿਉ। +pdfjs-password-invalid = ਗਲਤ ਪਾਸਵਰਡ। ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ। +pdfjs-password-ok-button = ਠੀਕ ਹੈ +pdfjs-password-cancel-button = ਰੱਦ ਕਰੋ +pdfjs-web-fonts-disabled = ਵੈਬ ਫੋਂਟ ਬੰਦ ਹਨ: ਇੰਬੈਡ PDF ਫੋਂਟ ਨੂੰ ਵਰਤਣ ਲਈ ਅਸਮਰੱਥ ਹੈ। + +## Editing + +pdfjs-editor-free-text-button = + .title = ਲਿਖਤ +pdfjs-editor-free-text-button-label = ਲਿਖਤ +pdfjs-editor-ink-button = + .title = ਵਾਹੋ +pdfjs-editor-ink-button-label = ਵਾਹੋ +pdfjs-editor-stamp-button = + .title = ਚਿੱਤਰ ਜੋੜੋ ਜਾਂ ਸੋਧੋ +pdfjs-editor-stamp-button-label = ਚਿੱਤਰ ਜੋੜੋ ਜਾਂ ਸੋਧੋ +pdfjs-editor-highlight-button = + .title = ਹਾਈਲਾਈਟ +pdfjs-editor-highlight-button-label = ਹਾਈਲਾਈਟ +pdfjs-highlight-floating-button1 = + .title = ਹਾਈਲਾਈਟ + .aria-label = ਹਾਈਲਾਈਟ +pdfjs-highlight-floating-button-label = ਹਾਈਲਾਈਟ + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = ਡਰਾਇੰਗ ਨੂੰ ਹਟਾਓ +pdfjs-editor-remove-freetext-button = + .title = ਲਿਖਤ ਨੂੰ ਹਟਾਓ +pdfjs-editor-remove-stamp-button = + .title = ਚਿੱਤਰ ਨੂੰ ਹਟਾਓ +pdfjs-editor-remove-highlight-button = + .title = ਹਾਈਲਾਈਟ ਨੂੰ ਹਟਾਓ + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = ਰੰਗ +pdfjs-editor-free-text-size-input = ਆਕਾਰ +pdfjs-editor-ink-color-input = ਰੰਗ +pdfjs-editor-ink-thickness-input = ਮੋਟਾਈ +pdfjs-editor-ink-opacity-input = ਧੁੰਦਲਾਪਨ +pdfjs-editor-stamp-add-image-button = + .title = ਚਿੱਤਰ ਜੋੜੋ +pdfjs-editor-stamp-add-image-button-label = ਚਿੱਤਰ ਜੋੜੋ +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = ਮੋਟਾਈ +pdfjs-editor-free-highlight-thickness-title = + .title = ਚੀਜ਼ਾਂ ਨੂੰ ਹੋਰ ਲਿਖਤਾਂ ਤੋਂ ਉਘਾੜਨ ਸਮੇਂ ਮੋਟਾਈ ਨੂੰ ਬਦਲੋ +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = ਲਿਖਤ ਐਡੀਟਰ + .default-content = …ਲਿਖਣਾ ਸ਼ੁਰੂ ਕਰੋ +pdfjs-free-text = + .aria-label = ਲਿਖਤ ਐਡੀਟਰ +pdfjs-free-text-default-content = …ਲਿਖਣਾ ਸ਼ੁਰੂ ਕਰੋ +pdfjs-ink = + .aria-label = ਵਹਾਉਣ ਐਡੀਟਰ +pdfjs-ink-canvas = + .aria-label = ਵਰਤੋਂਕਾਰ ਵਲੋਂ ਬਣਾਇਆ ਚਿੱਤਰ + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = ਬਦਲਵੀਂ ਲਿਖਤ +pdfjs-editor-alt-text-edit-button = + .aria-label = ਬਦਲਵੀ ਲਿਖਤ ਨੂੰ ਸੋਧੋ +pdfjs-editor-alt-text-edit-button-label = ਬਦਲਵੀ ਲਿਖਤ ਨੂੰ ਸੋਧੋ +pdfjs-editor-alt-text-dialog-label = ਚੋਣ ਕਰੋ +pdfjs-editor-alt-text-dialog-description = ਚਿੱਤਰ ਨਾ ਦਿੱਸਣ ਜਾਂ ਲੋਡ ਨਾ ਹੋਣ ਦੀ ਹਾਲਤ ਵਿੱਚ Alt ਲਿਖਤ (ਬਦਲਵੀਂ ਲਿਖਤ) ਲੋਕਾਂ ਲਈ ਮਦਦਗਾਰ ਹੁੰਦੀ ਹੈ। +pdfjs-editor-alt-text-add-description-label = ਵਰਣਨ ਜੋੜੋ +pdfjs-editor-alt-text-add-description-description = 1-2 ਵਾਕ ਰੱਖੋ, ਜੋ ਕਿ ਵਿਸ਼ੇ, ਸੈਟਿੰਗ ਜਾਂ ਕਾਰਵਾਈਆਂ ਬਾਰੇ ਦਰਸਾਉਂਦੇ ਹੋਣ। +pdfjs-editor-alt-text-mark-decorative-label = ਸਜਾਵਟ ਵਜੋਂ ਨਿਸ਼ਾਨ ਲਾਇਆ +pdfjs-editor-alt-text-mark-decorative-description = ਇਸ ਨੂੰ ਸਜਾਵਟੀ ਚਿੱਤਰਾਂ ਲਈ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ ਜਿਵੇਂ ਕਿ ਹਾਸ਼ੀਆ ਜਾਂ ਵਾਟਰਮਾਰਕ ਆਦਿ। +pdfjs-editor-alt-text-cancel-button = ਰੱਦ ਕਰੋ +pdfjs-editor-alt-text-save-button = ਸੰਭਾਲੋ +pdfjs-editor-alt-text-decorative-tooltip = ਸਜਾਵਟ ਵਜੋਂ ਨਿਸ਼ਾਨ ਲਾਓ +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = ਮਿਸਾਲ ਵਜੋਂ, “ਗੱਭਰੂ ਭੋਜਨ ਲੈ ਕੇ ਮੇਜ਼ ਉੱਤੇ ਬੈਠਾ ਹੈ” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = ਬਦਲਵੀਂ ਲਿਖਤ + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = ਉੱਤੇ ਖੱਬਾ ਕੋਨਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-label-top-middle = ਉੱਤੇ ਮੱਧ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-label-top-right = ਉੱਤੇ ਸੱਜਾ ਕੋਨਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-label-middle-right = ਮੱਧ ਸੱਜਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-label-bottom-right = ਹੇਠਾਂ ਸੱਜਾ ਕੋਨਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-label-bottom-middle = ਹੇਠਾਂ ਮੱਧ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-label-bottom-left = ਹੇਠਾਂ ਖੱਬਾ ਕੋਨਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-label-middle-left = ਮੱਧ ਖੱਬਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-top-left = + .aria-label = ਉੱਤੇ ਖੱਬਾ ਕੋਨਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-top-middle = + .aria-label = ਉੱਤੇ ਮੱਧ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-top-right = + .aria-label = ਉੱਤੇ ਸੱਜਾ ਕੋਨਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-middle-right = + .aria-label = ਮੱਧ ਸੱਜਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-bottom-right = + .aria-label = ਹੇਠਾਂ ਸੱਜਾ ਕੋਨਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-bottom-middle = + .aria-label = ਹੇਠਾਂ ਮੱਧ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-bottom-left = + .aria-label = ਹੇਠਾਂ ਖੱਬਾ ਕੋਨਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ +pdfjs-editor-resizer-middle-left = + .aria-label = ਮੱਧ ਖੱਬਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = ਹਾਈਟਲਾਈਟ ਦਾ ਰੰਗ +pdfjs-editor-colorpicker-button = + .title = ਰੰਗ ਨੂੰ ਬਦਲੋ +pdfjs-editor-colorpicker-dropdown = + .aria-label = ਰੰਗ ਚੋਣਾਂ +pdfjs-editor-colorpicker-yellow = + .title = ਪੀਲਾ +pdfjs-editor-colorpicker-green = + .title = ਹਰਾ +pdfjs-editor-colorpicker-blue = + .title = ਨੀਲਾ +pdfjs-editor-colorpicker-pink = + .title = ਗੁਲਾਬੀ +pdfjs-editor-colorpicker-red = + .title = ਲਾਲ + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = ਸਭ ਵੇਖੋ +pdfjs-editor-highlight-show-all-button = + .title = ਸਭ ਵੇਖੋ + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = ਬਦਲਵੀਂ ਲਿਖਤ (ਚਿੱਤਰ ਦਾ ਵਰਣਨ) ਨੂੰ ਸੋਧੋ +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = ਬਦਲਵੀਂ ਲਿਖਤ (ਚਿੱਤਰ ਦਾ ਵਰਣਨ) ਨੂੰ ਜੋੜੋ +pdfjs-editor-new-alt-text-textarea = + .placeholder = …ਆਪਣਾ ਵਰਣਨਾ ਇੱਥੇ ਲਿਖੋ +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = ਲੋਕ, ਜੋ ਕਿ ਚਿੱਤਰ ਨਹੀਂ ਵੇਖ ਸਕਦੇ ਜਾਂ ਜਦ ਵੀ ਚਿੱਤਰਾਂ ਨੂੰ ਲੋਡ ਨਹੀਂ ਜਾ ਸਕਦਾ, ਉਸ ਲਈ ਛੋਟਾ ਵੇਰਵਾ ਦਿਓ। +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = ਇਹ ਬਦਲਵੀਂ ਲਿਖਤ ਆਪਣੇ-ਆਪ ਤਿਆਰ ਕੀਤੀ ਗਈ ਸੀ ਅਤੇ ਗਲਤ ਵੀ ਹੋ ਸਕਦੀ ਹੈ। +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = ਹੋਰ ਜਾਣੋ +pdfjs-editor-new-alt-text-create-automatically-button-label = ਬਲਦਵੀਂ ਲਿਖਤ ਆਪਣੇ-ਆਪ ਬਣਾਓ +pdfjs-editor-new-alt-text-not-now-button = ਹੁਣੇ ਨਹੀਂ +pdfjs-editor-new-alt-text-error-title = ਬਦਲਵੀਂ ਲਿਖਤ ਆਪਣੇ-ਆਪ ਬਣਾਈ ਨਹੀਂ ਜਾ ਸਕੀ +pdfjs-editor-new-alt-text-error-description = ਆਪਣਾ ਖੁਦ ਦੀ ਬਦਲਵੀਂ ਲਿਖਤ ਲਿਖੋ ਜਾਂ ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ। +pdfjs-editor-new-alt-text-error-close-button = ਬੰਦ ਕਰੋ +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = ਬਦਲਵਾਂ ਲਿਖਤ AI ਮਾਡਲ ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ ({ $totalSize } MB ਵਿੱਚੋਂ { $downloadedSize }) + .aria-valuetext = ਬਦਲਵਾਂ ਲਿਖਤ AI ਮਾਡਲ ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ ({ $totalSize } MB ਵਿੱਚੋਂ { $downloadedSize }) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = ਬਦਲਵੀਂ ਲਿਖਤ ਜੋੜੀ +pdfjs-editor-new-alt-text-added-button-label = ਬਦਲਵੀਂ ਲਿਖਤ ਜੋੜੀ +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = ਬਦਲਵਾਂ ਲਿਖਤ ਗੁੰਮ ਹੈ +pdfjs-editor-new-alt-text-missing-button-label = ਬਦਲਵਾਂ ਲਿਖਤ ਗੁੰਮ ਹੈ +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = ਬਦਲਵੀਂ ਲਿਖਤ ਦਾ ਰੀਵਿਊ ਕਰੋ +pdfjs-editor-new-alt-text-to-review-button-label = ਬਦਲਵੀਂ ਲਿਖਤ ਦਾ ਰੀਵਿਊ ਕਰੋ +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = ਆਪਣੇ-ਆਪ ਬਣਾਇਆ: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = ਚਿੱਤਰ ਬਦਲਵੀਂ ਲਿਖਤ ਦੀਆਂ ਸੈਟਿੰਗਾਂ +pdfjs-image-alt-text-settings-button-label = ਚਿੱਤਰ ਬਦਲਵੀਂ ਲਿਖਤ ਦੀਆਂ ਸੈਟਿੰਗਾਂ +pdfjs-editor-alt-text-settings-dialog-label = ਚਿੱਤਰ ਬਦਲਵੀਂ ਲਿਖਤ ਦੀਆਂ ਸੈਟਿੰਗਾਂ +pdfjs-editor-alt-text-settings-automatic-title = ਆਟੋਮਮੈਟਿਕ ਬਦਲਵੀਂ ਲਿਖਤ +pdfjs-editor-alt-text-settings-create-model-button-label = ਬਲਦਵੀਂ ਲਿਖਤ ਆਪਣੇ-ਆਪ ਬਣਾਓ +pdfjs-editor-alt-text-settings-create-model-description = ਚਿੱਤਰ ਨਾ ਵੇਖ ਸਕਣ ਵਾਲੇ ਲੋਕਾਂ ਦੀ ਮਦਦ ਜਾਂ ਜਦ ਵੀ ਚਿੱਤਰਾਂ ਨੂੰ ਲੋਡ ਨਹੀਂ ਜਾ ਸਕਦਾ, ਉਸ ਲਈ ਛੋਟਾ ਵੇਰਵਾ ਦਿਓ। +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = ਬਦਲਵੀ ਲਿਖਤ ਲਈ AI ਮਾਡਲ ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = ਤੁਹਾਡੇ ਡਿਵਾਈਸ ਉੱਤੇ ਲੋਕਲ ਹੀ ਚੱਲਦਾ ਹੋਣ ਕਰਕੇ ਤੁਹਾਡਾ ਡਾਟਾ ਪ੍ਰਾਈਵੇਟ ਹੀ ਰਹਿੰਦਾ ਹੈ। ਆਟੋਮੈਟਿਕ ਬਦਲਵੀਂ ਲਿਖਤ ਲਈ ਚਾਹੀਦਾ ਹੈ। +pdfjs-editor-alt-text-settings-delete-model-button = ਹਟਾਓ +pdfjs-editor-alt-text-settings-download-model-button = ਡਾਊਨਲੋਡ +pdfjs-editor-alt-text-settings-downloading-model-button = …ਨੂੰ ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ +pdfjs-editor-alt-text-settings-editor-title = ਬਦਲਵੀਂ ਲਿਖਤ ਐਡੀਟਰ +pdfjs-editor-alt-text-settings-show-dialog-button-label = ਜਦੋਂ ਵਿੱਚ ਚਿੱਤਰ ਜੋੜਿਆ ਜਾਵੇ ਤਾਂ ਫ਼ੌਰਨ ਬਦਲਵੀ ਲਿਖਤ ਸੰਪਾਦਕ ਵੇਖਾਓ +pdfjs-editor-alt-text-settings-show-dialog-description = ਤੁਹਾਡੀ ਮਦਦ ਕਰਦਾ ਹੈ ਕਿ ਤੁਹਾਡੇ ਸਾਰੇ ਚਿੱਤਰਾਂ ਲਈ ਬਦਲਵੀਂ ਲਿਖਤ ਮੌਜੂਦ ਹੋਵੇ। +pdfjs-editor-alt-text-settings-close-button = ਬੰਦ ਕਰੋ + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = ਹਾਈਲਾਈਟ ਨੂੰ ਹਟਾਇਆ ਗਿਆ +pdfjs-editor-undo-bar-message-freetext = ਲਿਖਤ ਨੂੰ ਹਟਾਇਆ ਗਿਆ +pdfjs-editor-undo-bar-message-ink = ਡਰਾਇੰਗ ਨੂੰ ਹਟਾਇਆ ਗਿਆ +pdfjs-editor-undo-bar-message-stamp = ਚਿੱਤਰ ਨੂੰ ਹਟਾਇਆ ਗਿਆ +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } ਵਿਆਖਿਆ ਨੂੰ ਹਟਾਇਆ + *[other] { $count } ਵਿਆਖਿਆਵਾਂ ਨੂੰ ਹਟਾਇਆ + } +pdfjs-editor-undo-bar-undo-button = + .title = ਵਾਪਸ +pdfjs-editor-undo-bar-undo-button-label = ਵਾਪਸ +pdfjs-editor-undo-bar-close-button = + .title = ਬੰਦ ਕਰੋ +pdfjs-editor-undo-bar-close-button-label = ਬੰਦ ਕਰੋ diff --git a/public/pdfjs/web/locale/pl/viewer.ftl b/public/pdfjs/web/locale/pl/viewer.ftl new file mode 100644 index 0000000..07f9416 --- /dev/null +++ b/public/pdfjs/web/locale/pl/viewer.ftl @@ -0,0 +1,518 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Poprzednia strona +pdfjs-previous-button-label = Poprzednia +pdfjs-next-button = + .title = Następna strona +pdfjs-next-button-label = Następna +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Strona +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = z { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } z { $pagesCount }) +pdfjs-zoom-out-button = + .title = Pomniejsz +pdfjs-zoom-out-button-label = Pomniejsz +pdfjs-zoom-in-button = + .title = Powiększ +pdfjs-zoom-in-button-label = Powiększ +pdfjs-zoom-select = + .title = Skala +pdfjs-presentation-mode-button = + .title = Przełącz na tryb prezentacji +pdfjs-presentation-mode-button-label = Tryb prezentacji +pdfjs-open-file-button = + .title = Otwórz plik +pdfjs-open-file-button-label = Otwórz +pdfjs-print-button = + .title = Drukuj +pdfjs-print-button-label = Drukuj +pdfjs-save-button = + .title = Zapisz +pdfjs-save-button-label = Zapisz +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Pobierz +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Pobierz +pdfjs-bookmark-button = + .title = Bieżąca strona (adres do otwarcia na bieżącej stronie) +pdfjs-bookmark-button-label = Bieżąca strona + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Narzędzia +pdfjs-tools-button-label = Narzędzia +pdfjs-first-page-button = + .title = Przejdź do pierwszej strony +pdfjs-first-page-button-label = Przejdź do pierwszej strony +pdfjs-last-page-button = + .title = Przejdź do ostatniej strony +pdfjs-last-page-button-label = Przejdź do ostatniej strony +pdfjs-page-rotate-cw-button = + .title = Obróć zgodnie z ruchem wskazówek zegara +pdfjs-page-rotate-cw-button-label = Obróć zgodnie z ruchem wskazówek zegara +pdfjs-page-rotate-ccw-button = + .title = Obróć przeciwnie do ruchu wskazówek zegara +pdfjs-page-rotate-ccw-button-label = Obróć przeciwnie do ruchu wskazówek zegara +pdfjs-cursor-text-select-tool-button = + .title = Włącz narzędzie zaznaczania tekstu +pdfjs-cursor-text-select-tool-button-label = Narzędzie zaznaczania tekstu +pdfjs-cursor-hand-tool-button = + .title = Włącz narzędzie rączka +pdfjs-cursor-hand-tool-button-label = Narzędzie rączka +pdfjs-scroll-page-button = + .title = Przewijaj strony +pdfjs-scroll-page-button-label = Przewijanie stron +pdfjs-scroll-vertical-button = + .title = Przewijaj dokument w pionie +pdfjs-scroll-vertical-button-label = Przewijanie pionowe +pdfjs-scroll-horizontal-button = + .title = Przewijaj dokument w poziomie +pdfjs-scroll-horizontal-button-label = Przewijanie poziome +pdfjs-scroll-wrapped-button = + .title = Strony dokumentu wyświetlaj i przewijaj w kolumnach +pdfjs-scroll-wrapped-button-label = Widok dwóch stron +pdfjs-spread-none-button = + .title = Nie ustawiaj stron obok siebie +pdfjs-spread-none-button-label = Brak kolumn +pdfjs-spread-odd-button = + .title = Strony nieparzyste ustawiaj na lewo od parzystych +pdfjs-spread-odd-button-label = Nieparzyste po lewej +pdfjs-spread-even-button = + .title = Strony parzyste ustawiaj na lewo od nieparzystych +pdfjs-spread-even-button-label = Parzyste po lewej + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Właściwości dokumentu… +pdfjs-document-properties-button-label = Właściwości dokumentu… +pdfjs-document-properties-file-name = Nazwa pliku: +pdfjs-document-properties-file-size = Rozmiar pliku: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } B) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } B) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } B) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } B) +pdfjs-document-properties-title = Tytuł: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Temat: +pdfjs-document-properties-keywords = Słowa kluczowe: +pdfjs-document-properties-creation-date = Data utworzenia: +pdfjs-document-properties-modification-date = Data modyfikacji: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Utworzony przez: +pdfjs-document-properties-producer = PDF wyprodukowany przez: +pdfjs-document-properties-version = Wersja PDF: +pdfjs-document-properties-page-count = Liczba stron: +pdfjs-document-properties-page-size = Wymiary strony: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = pionowa +pdfjs-document-properties-page-size-orientation-landscape = pozioma +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = US Letter +pdfjs-document-properties-page-size-name-legal = US Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width }×{ $height } { $unit } (orientacja { $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width }×{ $height } { $unit } ({ $name }, orientacja { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Szybki podgląd w Internecie: +pdfjs-document-properties-linearized-yes = tak +pdfjs-document-properties-linearized-no = nie +pdfjs-document-properties-close-button = Zamknij + +## Print + +pdfjs-print-progress-message = Przygotowywanie dokumentu do druku… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Anuluj +pdfjs-printing-not-supported = Ostrzeżenie: drukowanie nie jest w pełni obsługiwane przez tę przeglądarkę. +pdfjs-printing-not-ready = Ostrzeżenie: dokument PDF nie jest całkowicie wczytany, więc nie można go wydrukować. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Przełącz panel boczny +pdfjs-toggle-sidebar-notification-button = + .title = Przełącz panel boczny (dokument zawiera konspekt/załączniki/warstwy) +pdfjs-toggle-sidebar-button-label = Przełącz panel boczny +pdfjs-document-outline-button = + .title = Konspekt dokumentu (podwójne kliknięcie rozwija lub zwija wszystkie pozycje) +pdfjs-document-outline-button-label = Konspekt dokumentu +pdfjs-attachments-button = + .title = Załączniki +pdfjs-attachments-button-label = Załączniki +pdfjs-layers-button = + .title = Warstwy (podwójne kliknięcie przywraca wszystkie warstwy do stanu domyślnego) +pdfjs-layers-button-label = Warstwy +pdfjs-thumbs-button = + .title = Miniatury +pdfjs-thumbs-button-label = Miniatury +pdfjs-current-outline-item-button = + .title = Znajdź bieżący element konspektu +pdfjs-current-outline-item-button-label = Bieżący element konspektu +pdfjs-findbar-button = + .title = Znajdź w dokumencie +pdfjs-findbar-button-label = Znajdź +pdfjs-additional-layers = Dodatkowe warstwy + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = { $page }. strona +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura { $page }. strony + +## Find panel button title and messages + +pdfjs-find-input = + .title = Znajdź + .placeholder = Znajdź w dokumencie… +pdfjs-find-previous-button = + .title = Znajdź poprzednie wystąpienie tekstu +pdfjs-find-previous-button-label = Poprzednie +pdfjs-find-next-button = + .title = Znajdź następne wystąpienie tekstu +pdfjs-find-next-button-label = Następne +pdfjs-find-highlight-checkbox = Wyróżnianie wszystkich +pdfjs-find-match-case-checkbox-label = Rozróżnianie wielkości liter +pdfjs-find-match-diacritics-checkbox-label = Rozróżnianie liter diakrytyzowanych +pdfjs-find-entire-word-checkbox-label = Całe słowa +pdfjs-find-reached-top = Początek dokumentu. Wyszukiwanie od końca. +pdfjs-find-reached-bottom = Koniec dokumentu. Wyszukiwanie od początku. +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current }. z { $total } trafienia + [few] { $current }. z { $total } trafień + *[many] { $current }. z { $total } trafień + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Więcej niż { $limit } trafienie + [few] Więcej niż { $limit } trafienia + *[many] Więcej niż { $limit } trafień + } +pdfjs-find-not-found = Nie znaleziono tekstu + +## Predefined zoom values + +pdfjs-page-scale-width = Szerokość strony +pdfjs-page-scale-fit = Dopasowanie strony +pdfjs-page-scale-auto = Skala automatyczna +pdfjs-page-scale-actual = Rozmiar oryginalny +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = { $page }. strona + +## Loading indicator messages + +pdfjs-loading-error = Podczas wczytywania dokumentu PDF wystąpił błąd. +pdfjs-invalid-file-error = Nieprawidłowy lub uszkodzony plik PDF. +pdfjs-missing-file-error = Brak pliku PDF. +pdfjs-unexpected-response-error = Nieoczekiwana odpowiedź serwera. +pdfjs-rendering-error = Podczas renderowania strony wystąpił błąd. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Przypis: { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Wprowadź hasło, aby otworzyć ten dokument PDF. +pdfjs-password-invalid = Nieprawidłowe hasło. Proszę spróbować ponownie. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Anuluj +pdfjs-web-fonts-disabled = Czcionki sieciowe są wyłączone: nie można użyć osadzonych czcionek PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Tekst +pdfjs-editor-free-text-button-label = Tekst +pdfjs-editor-ink-button = + .title = Rysunek +pdfjs-editor-ink-button-label = Rysunek +pdfjs-editor-stamp-button = + .title = Dodaj lub edytuj obrazy +pdfjs-editor-stamp-button-label = Dodaj lub edytuj obrazy +pdfjs-editor-highlight-button = + .title = Wyróżnij +pdfjs-editor-highlight-button-label = Wyróżnij +pdfjs-highlight-floating-button1 = + .title = Wyróżnij + .aria-label = Wyróżnij +pdfjs-highlight-floating-button-label = Wyróżnij + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Usuń rysunek +pdfjs-editor-remove-freetext-button = + .title = Usuń tekst +pdfjs-editor-remove-stamp-button = + .title = Usuń obraz +pdfjs-editor-remove-highlight-button = + .title = Usuń wyróżnienie + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Kolor +pdfjs-editor-free-text-size-input = Rozmiar +pdfjs-editor-ink-color-input = Kolor +pdfjs-editor-ink-thickness-input = Grubość +pdfjs-editor-ink-opacity-input = Nieprzezroczystość +pdfjs-editor-stamp-add-image-button = + .title = Dodaj obraz +pdfjs-editor-stamp-add-image-button-label = Dodaj obraz +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Grubość +pdfjs-editor-free-highlight-thickness-title = + .title = Zmień grubość podczas wyróżniania elementów innych niż tekst +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Edytor tekstu + .default-content = Zacznij pisać… +pdfjs-free-text = + .aria-label = Edytor tekstu +pdfjs-free-text-default-content = Zacznij pisać… +pdfjs-ink = + .aria-label = Edytor rysunku +pdfjs-ink-canvas = + .aria-label = Obraz utworzony przez użytkownika + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Tekst alternatywny +pdfjs-editor-alt-text-edit-button = + .aria-label = Edytuj tekst alternatywny +pdfjs-editor-alt-text-edit-button-label = Edytuj tekst alternatywny +pdfjs-editor-alt-text-dialog-label = Wybierz opcję +pdfjs-editor-alt-text-dialog-description = Tekst alternatywny pomaga, kiedy ktoś nie może zobaczyć obrazu lub gdy się nie wczytuje. +pdfjs-editor-alt-text-add-description-label = Dodaj opis +pdfjs-editor-alt-text-add-description-description = Staraj się napisać 1-2 zdania opisujące temat, miejsce lub działania. +pdfjs-editor-alt-text-mark-decorative-label = Oznacz jako dekoracyjne +pdfjs-editor-alt-text-mark-decorative-description = Używane w przypadku obrazów ozdobnych, takich jak obramowania lub znaki wodne. +pdfjs-editor-alt-text-cancel-button = Anuluj +pdfjs-editor-alt-text-save-button = Zapisz +pdfjs-editor-alt-text-decorative-tooltip = Oznaczone jako dekoracyjne +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Na przykład: „Młody człowiek siada przy stole, aby zjeść posiłek” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Tekst alternatywny + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Lewy górny róg — zmień rozmiar +pdfjs-editor-resizer-label-top-middle = Górny środkowy — zmień rozmiar +pdfjs-editor-resizer-label-top-right = Prawy górny róg — zmień rozmiar +pdfjs-editor-resizer-label-middle-right = Prawy środkowy — zmień rozmiar +pdfjs-editor-resizer-label-bottom-right = Prawy dolny róg — zmień rozmiar +pdfjs-editor-resizer-label-bottom-middle = Dolny środkowy — zmień rozmiar +pdfjs-editor-resizer-label-bottom-left = Lewy dolny róg — zmień rozmiar +pdfjs-editor-resizer-label-middle-left = Lewy środkowy — zmień rozmiar +pdfjs-editor-resizer-top-left = + .aria-label = Lewy górny róg — zmień rozmiar +pdfjs-editor-resizer-top-middle = + .aria-label = Górny środkowy — zmień rozmiar +pdfjs-editor-resizer-top-right = + .aria-label = Prawy górny róg — zmień rozmiar +pdfjs-editor-resizer-middle-right = + .aria-label = Prawy środkowy — zmień rozmiar +pdfjs-editor-resizer-bottom-right = + .aria-label = Prawy dolny róg — zmień rozmiar +pdfjs-editor-resizer-bottom-middle = + .aria-label = Dolny środkowy — zmień rozmiar +pdfjs-editor-resizer-bottom-left = + .aria-label = Lewy dolny róg — zmień rozmiar +pdfjs-editor-resizer-middle-left = + .aria-label = Lewy środkowy — zmień rozmiar + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Kolor wyróżnienia +pdfjs-editor-colorpicker-button = + .title = Zmień kolor +pdfjs-editor-colorpicker-dropdown = + .aria-label = Wybór kolorów +pdfjs-editor-colorpicker-yellow = + .title = Żółty +pdfjs-editor-colorpicker-green = + .title = Zielony +pdfjs-editor-colorpicker-blue = + .title = Niebieski +pdfjs-editor-colorpicker-pink = + .title = Różowy +pdfjs-editor-colorpicker-red = + .title = Czerwony + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Pokaż wszystkie +pdfjs-editor-highlight-show-all-button = + .title = Pokaż wszystkie + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Edytuj tekst alternatywny (opis obrazu) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Dodaj tekst alternatywny (opis obrazu) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Napisz tutaj opis… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Krótki opis dla osób, które nie widzą obrazu lub kiedy obraz się nie wczytuje. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Ten tekst alternatywny został utworzony automatycznie i może być niepoprawny. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Więcej informacji +pdfjs-editor-new-alt-text-create-automatically-button-label = Automatycznie utwórz tekst alternatywny +pdfjs-editor-new-alt-text-not-now-button = Nie teraz +pdfjs-editor-new-alt-text-error-title = Nie można automatycznie utworzyć tekstu alternatywnego +pdfjs-editor-new-alt-text-error-description = Proszę napisać własny tekst alternatywny lub spróbować ponownie później. +pdfjs-editor-new-alt-text-error-close-button = Zamknij +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Pobieranie modelu SI tekstu alternatywnego ({ $downloadedSize } z { $totalSize } MB) + .aria-valuetext = Pobieranie modelu SI tekstu alternatywnego ({ $downloadedSize } z { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Dodano tekst alternatywny +pdfjs-editor-new-alt-text-added-button-label = Dodano tekst alternatywny +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Brak tekstu alternatywnego +pdfjs-editor-new-alt-text-missing-button-label = Brak tekstu alternatywnego +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Przejrzyj tekst alternatywny +pdfjs-editor-new-alt-text-to-review-button-label = Przejrzyj tekst alternatywny +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Utworzono automatycznie: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Ustawienia tekstu alternatywnego obrazów +pdfjs-image-alt-text-settings-button-label = Ustawienia tekstu alternatywnego obrazów +pdfjs-editor-alt-text-settings-dialog-label = Ustawienia tekstu alternatywnego obrazów +pdfjs-editor-alt-text-settings-automatic-title = Automatyczny tekst alternatywny +pdfjs-editor-alt-text-settings-create-model-button-label = Automatyczne tworzenie tekstu alternatywnego +pdfjs-editor-alt-text-settings-create-model-description = Podpowiada opisy, które mogą pomóc osobom, które nie widzą obrazu lub kiedy obraz się nie wczytuje. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Model SI tekstu alternatywnego ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Działa lokalnie na urządzeniu użytkownika, więc Twoje dane pozostają prywatne. Wymagane do funkcji automatycznego tekstu alternatywnego. +pdfjs-editor-alt-text-settings-delete-model-button = Usuń +pdfjs-editor-alt-text-settings-download-model-button = Pobierz +pdfjs-editor-alt-text-settings-downloading-model-button = Pobieranie… +pdfjs-editor-alt-text-settings-editor-title = Edytor tekstu alternatywnego +pdfjs-editor-alt-text-settings-show-dialog-button-label = Wyświetlanie edytora tekstu alternatywnego od razu po dodaniu obrazu +pdfjs-editor-alt-text-settings-show-dialog-description = Pomaga upewnić się, że wszystkie obrazy mają tekst alternatywny. +pdfjs-editor-alt-text-settings-close-button = Zamknij + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Usunięto wyróżnienie +pdfjs-editor-undo-bar-message-freetext = Usunięto tekst +pdfjs-editor-undo-bar-message-ink = Usunięto rysunek +pdfjs-editor-undo-bar-message-stamp = Usunięto obraz +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] Usunięto przypis + [few] Usunięto { $count } przypisy + *[many] Usunięto { $count } przypisów + } +pdfjs-editor-undo-bar-undo-button = + .title = Cofnij +pdfjs-editor-undo-bar-undo-button-label = Cofnij +pdfjs-editor-undo-bar-close-button = + .title = Zamknij +pdfjs-editor-undo-bar-close-button-label = Zamknij diff --git a/public/pdfjs/web/locale/pt-BR/viewer.ftl b/public/pdfjs/web/locale/pt-BR/viewer.ftl new file mode 100644 index 0000000..7da5201 --- /dev/null +++ b/public/pdfjs/web/locale/pt-BR/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Página anterior +pdfjs-previous-button-label = Anterior +pdfjs-next-button = + .title = Próxima página +pdfjs-next-button-label = Próxima +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Página +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = de { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount }) +pdfjs-zoom-out-button = + .title = Reduzir +pdfjs-zoom-out-button-label = Reduzir +pdfjs-zoom-in-button = + .title = Ampliar +pdfjs-zoom-in-button-label = Ampliar +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Mudar para o modo de apresentação +pdfjs-presentation-mode-button-label = Modo de apresentação +pdfjs-open-file-button = + .title = Abrir arquivo +pdfjs-open-file-button-label = Abrir +pdfjs-print-button = + .title = Imprimir +pdfjs-print-button-label = Imprimir +pdfjs-save-button = + .title = Salvar +pdfjs-save-button-label = Salvar +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Baixar +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Baixar +pdfjs-bookmark-button = + .title = Página atual (ver URL da página atual) +pdfjs-bookmark-button-label = Pagina atual + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Ferramentas +pdfjs-tools-button-label = Ferramentas +pdfjs-first-page-button = + .title = Ir para a primeira página +pdfjs-first-page-button-label = Ir para a primeira página +pdfjs-last-page-button = + .title = Ir para a última página +pdfjs-last-page-button-label = Ir para a última página +pdfjs-page-rotate-cw-button = + .title = Girar no sentido horário +pdfjs-page-rotate-cw-button-label = Girar no sentido horário +pdfjs-page-rotate-ccw-button = + .title = Girar no sentido anti-horário +pdfjs-page-rotate-ccw-button-label = Girar no sentido anti-horário +pdfjs-cursor-text-select-tool-button = + .title = Ativar a ferramenta de seleção de texto +pdfjs-cursor-text-select-tool-button-label = Ferramenta de seleção de texto +pdfjs-cursor-hand-tool-button = + .title = Ativar ferramenta de deslocamento +pdfjs-cursor-hand-tool-button-label = Ferramenta de deslocamento +pdfjs-scroll-page-button = + .title = Usar rolagem de página +pdfjs-scroll-page-button-label = Rolagem de página +pdfjs-scroll-vertical-button = + .title = Usar deslocamento vertical +pdfjs-scroll-vertical-button-label = Deslocamento vertical +pdfjs-scroll-horizontal-button = + .title = Usar deslocamento horizontal +pdfjs-scroll-horizontal-button-label = Deslocamento horizontal +pdfjs-scroll-wrapped-button = + .title = Usar deslocamento contido +pdfjs-scroll-wrapped-button-label = Deslocamento contido +pdfjs-spread-none-button = + .title = Não reagrupar páginas +pdfjs-spread-none-button-label = Não estender +pdfjs-spread-odd-button = + .title = Agrupar páginas começando em páginas com números ímpares +pdfjs-spread-odd-button-label = Estender ímpares +pdfjs-spread-even-button = + .title = Agrupar páginas começando em páginas com números pares +pdfjs-spread-even-button-label = Estender pares + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Propriedades do documento… +pdfjs-document-properties-button-label = Propriedades do documento… +pdfjs-document-properties-file-name = Nome do arquivo: +pdfjs-document-properties-file-size = Tamanho do arquivo: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Título: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Assunto: +pdfjs-document-properties-keywords = Palavras-chave: +pdfjs-document-properties-creation-date = Data da criação: +pdfjs-document-properties-modification-date = Data da modificação: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Criação: +pdfjs-document-properties-producer = Criador do PDF: +pdfjs-document-properties-version = Versão do PDF: +pdfjs-document-properties-page-count = Número de páginas: +pdfjs-document-properties-page-size = Tamanho da página: +pdfjs-document-properties-page-size-unit-inches = pol. +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = retrato +pdfjs-document-properties-page-size-orientation-landscape = paisagem +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Carta +pdfjs-document-properties-page-size-name-legal = Jurídico + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Exibição web rápida: +pdfjs-document-properties-linearized-yes = Sim +pdfjs-document-properties-linearized-no = Não +pdfjs-document-properties-close-button = Fechar + +## Print + +pdfjs-print-progress-message = Preparando documento para impressão… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress } % +pdfjs-print-progress-close-button = Cancelar +pdfjs-printing-not-supported = Aviso: a impressão não é totalmente suportada neste navegador. +pdfjs-printing-not-ready = Aviso: o PDF não está totalmente carregado para impressão. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Exibir/ocultar painel lateral +pdfjs-toggle-sidebar-notification-button = + .title = Exibir/ocultar painel lateral (documento contém estrutura/anexos/camadas) +pdfjs-toggle-sidebar-button-label = Exibir/ocultar painel lateral +pdfjs-document-outline-button = + .title = Mostrar estrutura do documento (duplo-clique expande/recolhe todos os itens) +pdfjs-document-outline-button-label = Estrutura do documento +pdfjs-attachments-button = + .title = Mostrar anexos +pdfjs-attachments-button-label = Anexos +pdfjs-layers-button = + .title = Mostrar camadas (duplo-clique redefine todas as camadas ao estado predefinido) +pdfjs-layers-button-label = Camadas +pdfjs-thumbs-button = + .title = Mostrar miniaturas +pdfjs-thumbs-button-label = Miniaturas +pdfjs-current-outline-item-button = + .title = Encontrar item atual da estrutura +pdfjs-current-outline-item-button-label = Item atual da estrutura +pdfjs-findbar-button = + .title = Procurar no documento +pdfjs-findbar-button-label = Procurar +pdfjs-additional-layers = Camadas adicionais + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Página { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura da página { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Procurar + .placeholder = Procurar no documento… +pdfjs-find-previous-button = + .title = Procurar a ocorrência anterior da frase +pdfjs-find-previous-button-label = Anterior +pdfjs-find-next-button = + .title = Procurar a próxima ocorrência da frase +pdfjs-find-next-button-label = Próxima +pdfjs-find-highlight-checkbox = Destacar tudo +pdfjs-find-match-case-checkbox-label = Diferenciar maiúsculas/minúsculas +pdfjs-find-match-diacritics-checkbox-label = Considerar acentuação +pdfjs-find-entire-word-checkbox-label = Palavras completas +pdfjs-find-reached-top = Início do documento alcançado, continuando do fim +pdfjs-find-reached-bottom = Fim do documento alcançado, continuando do início +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } de { $total } ocorrência + *[other] { $current } de { $total } ocorrências + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Mais de { $limit } ocorrência + *[other] Mais de { $limit } ocorrências + } +pdfjs-find-not-found = Não encontrado + +## Predefined zoom values + +pdfjs-page-scale-width = Largura da página +pdfjs-page-scale-fit = Ajustar à janela +pdfjs-page-scale-auto = Zoom automático +pdfjs-page-scale-actual = Tamanho real +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Página { $page } + +## Loading indicator messages + +pdfjs-loading-error = Ocorreu um erro ao carregar o PDF. +pdfjs-invalid-file-error = Arquivo PDF corrompido ou inválido. +pdfjs-missing-file-error = Arquivo PDF ausente. +pdfjs-unexpected-response-error = Resposta inesperada do servidor. +pdfjs-rendering-error = Ocorreu um erro ao renderizar a página. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anotação { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Forneça a senha para abrir este arquivo PDF. +pdfjs-password-invalid = Senha inválida. Tente novamente. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Cancelar +pdfjs-web-fonts-disabled = As fontes web estão desativadas: não foi possível usar fontes incorporadas do PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Texto +pdfjs-editor-free-text-button-label = Texto +pdfjs-editor-ink-button = + .title = Desenho +pdfjs-editor-ink-button-label = Desenho +pdfjs-editor-stamp-button = + .title = Adicionar ou editar imagens +pdfjs-editor-stamp-button-label = Adicionar ou editar imagens +pdfjs-editor-highlight-button = + .title = Destaque +pdfjs-editor-highlight-button-label = Destaque +pdfjs-highlight-floating-button1 = + .title = Destaque + .aria-label = Destaque +pdfjs-highlight-floating-button-label = Destaque + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Remover desenho +pdfjs-editor-remove-freetext-button = + .title = Remover texto +pdfjs-editor-remove-stamp-button = + .title = Remover imagem +pdfjs-editor-remove-highlight-button = + .title = Remover destaque + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Cor +pdfjs-editor-free-text-size-input = Tamanho +pdfjs-editor-ink-color-input = Cor +pdfjs-editor-ink-thickness-input = Espessura +pdfjs-editor-ink-opacity-input = Opacidade +pdfjs-editor-stamp-add-image-button = + .title = Adicionar imagem +pdfjs-editor-stamp-add-image-button-label = Adicionar imagem +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Espessura +pdfjs-editor-free-highlight-thickness-title = + .title = Mudar espessura ao destacar itens que não são texto +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Comece a digitar… +pdfjs-free-text = + .aria-label = Editor de texto +pdfjs-free-text-default-content = Comece digitando… +pdfjs-ink = + .aria-label = Editor de desenho +pdfjs-ink-canvas = + .aria-label = Imagem criada pelo usuário + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Texto alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Editar texto alternativo +pdfjs-editor-alt-text-edit-button-label = Editar texto alternativo +pdfjs-editor-alt-text-dialog-label = Escolha uma opção +pdfjs-editor-alt-text-dialog-description = O texto alternativo ajuda quando uma imagem não aparece ou não é carregada. +pdfjs-editor-alt-text-add-description-label = Adicionar uma descrição +pdfjs-editor-alt-text-add-description-description = Procure usar uma ou duas frases que descrevam o assunto, cenário ou ação. +pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorativa +pdfjs-editor-alt-text-mark-decorative-description = Isto é usado em imagens ornamentais, como bordas ou marcas d'água. +pdfjs-editor-alt-text-cancel-button = Cancelar +pdfjs-editor-alt-text-save-button = Salvar +pdfjs-editor-alt-text-decorative-tooltip = Marcado como decorativa +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Por exemplo, “Um jovem senta-se à mesa para comer uma refeição” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternativo + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Canto superior esquerdo — redimensionar +pdfjs-editor-resizer-label-top-middle = No centro do topo — redimensionar +pdfjs-editor-resizer-label-top-right = Canto superior direito — redimensionar +pdfjs-editor-resizer-label-middle-right = No meio à direita — redimensionar +pdfjs-editor-resizer-label-bottom-right = Canto inferior direito — redimensionar +pdfjs-editor-resizer-label-bottom-middle = No centro da base — redimensionar +pdfjs-editor-resizer-label-bottom-left = Canto inferior esquerdo — redimensionar +pdfjs-editor-resizer-label-middle-left = No meio à esquerda — redimensionar +pdfjs-editor-resizer-top-left = + .aria-label = Canto superior esquerdo — redimensionar +pdfjs-editor-resizer-top-middle = + .aria-label = No centro do topo — redimensionar +pdfjs-editor-resizer-top-right = + .aria-label = Canto superior direito — redimensionar +pdfjs-editor-resizer-middle-right = + .aria-label = No meio à direita — redimensionar +pdfjs-editor-resizer-bottom-right = + .aria-label = Canto inferior direito — redimensionar +pdfjs-editor-resizer-bottom-middle = + .aria-label = No centro da base — redimensionar +pdfjs-editor-resizer-bottom-left = + .aria-label = Canto inferior esquerdo — redimensionar +pdfjs-editor-resizer-middle-left = + .aria-label = No meio à esquerda — redimensionar + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Cor de destaque +pdfjs-editor-colorpicker-button = + .title = Mudar cor +pdfjs-editor-colorpicker-dropdown = + .aria-label = Opções de cores +pdfjs-editor-colorpicker-yellow = + .title = Amarelo +pdfjs-editor-colorpicker-green = + .title = Verde +pdfjs-editor-colorpicker-blue = + .title = Azul +pdfjs-editor-colorpicker-pink = + .title = Rosa +pdfjs-editor-colorpicker-red = + .title = Vermelho + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Mostrar todos +pdfjs-editor-highlight-show-all-button = + .title = Mostrar todos + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Editar texto alternativo (descrição da imagem) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Adicionar texto alternativo (descrição da imagem) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Escreva sua descrição aqui… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Descrição curta para pessoas que não conseguem ver a imagem ou quando a imagem não é carregada. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Este texto alternativo foi criado automaticamente, pode não estar correto. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Saiba mais +pdfjs-editor-new-alt-text-create-automatically-button-label = Criar texto alternativo automaticamente +pdfjs-editor-new-alt-text-not-now-button = Agora não +pdfjs-editor-new-alt-text-error-title = Não foi possível criar texto alternativo automaticamente +pdfjs-editor-new-alt-text-error-description = Escreva seu próprio texto alternativo ou tente novamente mais tarde. +pdfjs-editor-new-alt-text-error-close-button = Fechar +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Baixando modelo de inteligência artificial de texto alternativo ({ $downloadedSize } de { $totalSize } MB) + .aria-valuetext = Baixando modelo de inteligência artificial de texto alternativo ({ $downloadedSize } de { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Texto alternativo adicionado +pdfjs-editor-new-alt-text-added-button-label = Texto alternativo adicionado +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Falta texto alternativo +pdfjs-editor-new-alt-text-missing-button-label = Falta texto alternativo +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Revisar texto alternativo +pdfjs-editor-new-alt-text-to-review-button-label = Revisar texto alternativo +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Criado automaticamente: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Configurações de texto alternativo de imagens +pdfjs-image-alt-text-settings-button-label = Configurações de texto alternativo de imagens +pdfjs-editor-alt-text-settings-dialog-label = Configurações de texto alternativo de imagens +pdfjs-editor-alt-text-settings-automatic-title = Texto alternativo automático +pdfjs-editor-alt-text-settings-create-model-button-label = Criar texto alternativo automaticamente +pdfjs-editor-alt-text-settings-create-model-description = Sugere uma descrição para ajudar pessoas que não conseguem ver a imagem ou quando a imagem não é carregada. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Modelo de inteligência artificial de texto alternativo ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Funciona localmente no seu dispositivo para que seus dados permaneçam privativos. Necessário para texto alternativo automático. +pdfjs-editor-alt-text-settings-delete-model-button = Excluir +pdfjs-editor-alt-text-settings-download-model-button = Baixar +pdfjs-editor-alt-text-settings-downloading-model-button = Baixando… +pdfjs-editor-alt-text-settings-editor-title = Editor de texto alternativo +pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostrar o editor de texto alternativo imediatamente ao adicionar uma imagem +pdfjs-editor-alt-text-settings-show-dialog-description = Ajuda a assegurar que todas as suas imagens tenham texto alternativo. +pdfjs-editor-alt-text-settings-close-button = Fechar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Destaque removido +pdfjs-editor-undo-bar-message-freetext = Texto removido +pdfjs-editor-undo-bar-message-ink = Desenho removido +pdfjs-editor-undo-bar-message-stamp = Imagem removida +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotação removida + *[other] { $count } anotações removidas + } +pdfjs-editor-undo-bar-undo-button = + .title = Desfazer +pdfjs-editor-undo-bar-undo-button-label = Desfazer +pdfjs-editor-undo-bar-close-button = + .title = Fechar +pdfjs-editor-undo-bar-close-button-label = Fechar diff --git a/public/pdfjs/web/locale/pt-PT/viewer.ftl b/public/pdfjs/web/locale/pt-PT/viewer.ftl new file mode 100644 index 0000000..1829417 --- /dev/null +++ b/public/pdfjs/web/locale/pt-PT/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Página anterior +pdfjs-previous-button-label = Anterior +pdfjs-next-button = + .title = Página seguinte +pdfjs-next-button-label = Seguinte +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Página +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = de { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount }) +pdfjs-zoom-out-button = + .title = Reduzir +pdfjs-zoom-out-button-label = Reduzir +pdfjs-zoom-in-button = + .title = Ampliar +pdfjs-zoom-in-button-label = Ampliar +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Trocar para o modo de apresentação +pdfjs-presentation-mode-button-label = Modo de apresentação +pdfjs-open-file-button = + .title = Abrir ficheiro +pdfjs-open-file-button-label = Abrir +pdfjs-print-button = + .title = Imprimir +pdfjs-print-button-label = Imprimir +pdfjs-save-button = + .title = Guardar +pdfjs-save-button-label = Guardar +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Transferir +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Transferir +pdfjs-bookmark-button = + .title = Página atual (ver URL da página atual) +pdfjs-bookmark-button-label = Pagina atual + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Ferramentas +pdfjs-tools-button-label = Ferramentas +pdfjs-first-page-button = + .title = Ir para a primeira página +pdfjs-first-page-button-label = Ir para a primeira página +pdfjs-last-page-button = + .title = Ir para a última página +pdfjs-last-page-button-label = Ir para a última página +pdfjs-page-rotate-cw-button = + .title = Rodar à direita +pdfjs-page-rotate-cw-button-label = Rodar à direita +pdfjs-page-rotate-ccw-button = + .title = Rodar à esquerda +pdfjs-page-rotate-ccw-button-label = Rodar à esquerda +pdfjs-cursor-text-select-tool-button = + .title = Ativar ferramenta de seleção de texto +pdfjs-cursor-text-select-tool-button-label = Ferramenta de seleção de texto +pdfjs-cursor-hand-tool-button = + .title = Ativar ferramenta de mão +pdfjs-cursor-hand-tool-button-label = Ferramenta de mão +pdfjs-scroll-page-button = + .title = Utilizar deslocamento da página +pdfjs-scroll-page-button-label = Deslocamento da página +pdfjs-scroll-vertical-button = + .title = Utilizar deslocação vertical +pdfjs-scroll-vertical-button-label = Deslocação vertical +pdfjs-scroll-horizontal-button = + .title = Utilizar deslocação horizontal +pdfjs-scroll-horizontal-button-label = Deslocação horizontal +pdfjs-scroll-wrapped-button = + .title = Utilizar deslocação encapsulada +pdfjs-scroll-wrapped-button-label = Deslocação encapsulada +pdfjs-spread-none-button = + .title = Não juntar páginas dispersas +pdfjs-spread-none-button-label = Sem spreads +pdfjs-spread-odd-button = + .title = Juntar páginas dispersas a partir de páginas com números ímpares +pdfjs-spread-odd-button-label = Spreads ímpares +pdfjs-spread-even-button = + .title = Juntar páginas dispersas a partir de páginas com números pares +pdfjs-spread-even-button-label = Spreads pares + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Propriedades do documento… +pdfjs-document-properties-button-label = Propriedades do documento… +pdfjs-document-properties-file-name = Nome do ficheiro: +pdfjs-document-properties-file-size = Tamanho do ficheiro: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Título: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Assunto: +pdfjs-document-properties-keywords = Palavras-chave: +pdfjs-document-properties-creation-date = Data de criação: +pdfjs-document-properties-modification-date = Data de modificação: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Criador: +pdfjs-document-properties-producer = Produtor de PDF: +pdfjs-document-properties-version = Versão do PDF: +pdfjs-document-properties-page-count = N.º de páginas: +pdfjs-document-properties-page-size = Tamanho da página: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = retrato +pdfjs-document-properties-page-size-orientation-landscape = paisagem +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Carta +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Vista rápida web: +pdfjs-document-properties-linearized-yes = Sim +pdfjs-document-properties-linearized-no = Não +pdfjs-document-properties-close-button = Fechar + +## Print + +pdfjs-print-progress-message = A preparar o documento para impressão… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cancelar +pdfjs-printing-not-supported = Aviso: a impressão não é totalmente suportada por este navegador. +pdfjs-printing-not-ready = Aviso: o PDF ainda não está totalmente carregado. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Alternar barra lateral +pdfjs-toggle-sidebar-notification-button = + .title = Alternar barra lateral (o documento contém contornos/anexos/camadas) +pdfjs-toggle-sidebar-button-label = Alternar barra lateral +pdfjs-document-outline-button = + .title = Mostrar esquema do documento (duplo clique para expandir/colapsar todos os itens) +pdfjs-document-outline-button-label = Esquema do documento +pdfjs-attachments-button = + .title = Mostrar anexos +pdfjs-attachments-button-label = Anexos +pdfjs-layers-button = + .title = Mostrar camadas (clique duas vezes para repor todas as camadas para o estado predefinido) +pdfjs-layers-button-label = Camadas +pdfjs-thumbs-button = + .title = Mostrar miniaturas +pdfjs-thumbs-button-label = Miniaturas +pdfjs-current-outline-item-button = + .title = Encontrar o item atualmente destacado +pdfjs-current-outline-item-button-label = Item atualmente destacado +pdfjs-findbar-button = + .title = Localizar em documento +pdfjs-findbar-button-label = Localizar +pdfjs-additional-layers = Camadas adicionais + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Página { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura da página { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Localizar + .placeholder = Localizar em documento… +pdfjs-find-previous-button = + .title = Localizar ocorrência anterior da frase +pdfjs-find-previous-button-label = Anterior +pdfjs-find-next-button = + .title = Localizar ocorrência seguinte da frase +pdfjs-find-next-button-label = Seguinte +pdfjs-find-highlight-checkbox = Destacar tudo +pdfjs-find-match-case-checkbox-label = Correspondência +pdfjs-find-match-diacritics-checkbox-label = Corresponder diacríticos +pdfjs-find-entire-word-checkbox-label = Palavras completas +pdfjs-find-reached-top = Topo do documento atingido, a continuar a partir do fundo +pdfjs-find-reached-bottom = Fim do documento atingido, a continuar a partir do topo +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } de { $total } correspondência + *[other] { $current } de { $total } correspondências + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Mais de { $limit } correspondência + *[other] Mais de { $limit } correspondências + } +pdfjs-find-not-found = Frase não encontrada + +## Predefined zoom values + +pdfjs-page-scale-width = Ajustar à largura +pdfjs-page-scale-fit = Ajustar à página +pdfjs-page-scale-auto = Zoom automático +pdfjs-page-scale-actual = Tamanho real +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Página { $page } + +## Loading indicator messages + +pdfjs-loading-error = Ocorreu um erro ao carregar o PDF. +pdfjs-invalid-file-error = Ficheiro PDF inválido ou danificado. +pdfjs-missing-file-error = Ficheiro PDF inexistente. +pdfjs-unexpected-response-error = Resposta inesperada do servidor. +pdfjs-rendering-error = Ocorreu um erro ao processar a página. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anotação { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Introduza a palavra-passe para abrir este ficheiro PDF. +pdfjs-password-invalid = Palavra-passe inválida. Por favor, tente novamente. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Cancelar +pdfjs-web-fonts-disabled = Os tipos de letra web estão desativados: não é possível utilizar os tipos de letra PDF embutidos. + +## Editing + +pdfjs-editor-free-text-button = + .title = Texto +pdfjs-editor-free-text-button-label = Texto +pdfjs-editor-ink-button = + .title = Desenhar +pdfjs-editor-ink-button-label = Desenhar +pdfjs-editor-stamp-button = + .title = Adicionar ou editar imagens +pdfjs-editor-stamp-button-label = Adicionar ou editar imagens +pdfjs-editor-highlight-button = + .title = Destaque +pdfjs-editor-highlight-button-label = Destaque +pdfjs-highlight-floating-button1 = + .title = Realçar + .aria-label = Realçar +pdfjs-highlight-floating-button-label = Realçar + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Remover desenho +pdfjs-editor-remove-freetext-button = + .title = Remover texto +pdfjs-editor-remove-stamp-button = + .title = Remover imagem +pdfjs-editor-remove-highlight-button = + .title = Remover destaque + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Cor +pdfjs-editor-free-text-size-input = Tamanho +pdfjs-editor-ink-color-input = Cor +pdfjs-editor-ink-thickness-input = Espessura +pdfjs-editor-ink-opacity-input = Opacidade +pdfjs-editor-stamp-add-image-button = + .title = Adicionar imagem +pdfjs-editor-stamp-add-image-button-label = Adicionar imagem +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Espessura +pdfjs-editor-free-highlight-thickness-title = + .title = Alterar espessura quando destacar itens que não sejam texto +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Comece a escrever… +pdfjs-free-text = + .aria-label = Editor de texto +pdfjs-free-text-default-content = Começar a digitar… +pdfjs-ink = + .aria-label = Editor de desenho +pdfjs-ink-canvas = + .aria-label = Imagem criada pelo utilizador + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Texto alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Editar texto alternativo +pdfjs-editor-alt-text-edit-button-label = Editar texto alternativo +pdfjs-editor-alt-text-dialog-label = Escolher uma opção +pdfjs-editor-alt-text-dialog-description = O texto alternativo (texto alternativo) ajuda quando as pessoas não conseguem ver a imagem ou quando a mesma não é carregada. +pdfjs-editor-alt-text-add-description-label = Adicionar uma descrição +pdfjs-editor-alt-text-add-description-description = Aponte para 1-2 frases que descrevam o assunto, definição ou ações. +pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorativa +pdfjs-editor-alt-text-mark-decorative-description = Isto é utilizado para imagens decorativas, tais como limites ou marcas d'água. +pdfjs-editor-alt-text-cancel-button = Cancelar +pdfjs-editor-alt-text-save-button = Guardar +pdfjs-editor-alt-text-decorative-tooltip = Marcada como decorativa +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Por exemplo, “Um jovem senta-se à mesa para comer uma refeição” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternativo + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Canto superior esquerdo — redimensionar +pdfjs-editor-resizer-label-top-middle = Superior ao centro — redimensionar +pdfjs-editor-resizer-label-top-right = Canto superior direito — redimensionar +pdfjs-editor-resizer-label-middle-right = Centro à direita — redimensionar +pdfjs-editor-resizer-label-bottom-right = Canto inferior direito — redimensionar +pdfjs-editor-resizer-label-bottom-middle = Inferior ao centro — redimensionar +pdfjs-editor-resizer-label-bottom-left = Canto inferior esquerdo — redimensionar +pdfjs-editor-resizer-label-middle-left = Centro à esquerda — redimensionar +pdfjs-editor-resizer-top-left = + .aria-label = Canto superior esquerdo — redimensionar +pdfjs-editor-resizer-top-middle = + .aria-label = Superior ao centro — redimensionar +pdfjs-editor-resizer-top-right = + .aria-label = Canto superior direito — redimensionar +pdfjs-editor-resizer-middle-right = + .aria-label = Centro à direita — redimensionar +pdfjs-editor-resizer-bottom-right = + .aria-label = Canto inferior direito — redimensionar +pdfjs-editor-resizer-bottom-middle = + .aria-label = Inferior ao centro — redimensionar +pdfjs-editor-resizer-bottom-left = + .aria-label = Canto inferior esquerdo — redimensionar +pdfjs-editor-resizer-middle-left = + .aria-label = Centro à esquerda — redimensionar + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Cor de destaque +pdfjs-editor-colorpicker-button = + .title = Alterar cor +pdfjs-editor-colorpicker-dropdown = + .aria-label = Escolhas de cor +pdfjs-editor-colorpicker-yellow = + .title = Amarelo +pdfjs-editor-colorpicker-green = + .title = Verde +pdfjs-editor-colorpicker-blue = + .title = Azul +pdfjs-editor-colorpicker-pink = + .title = Rosa +pdfjs-editor-colorpicker-red = + .title = Vermelho + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Mostrar tudo +pdfjs-editor-highlight-show-all-button = + .title = Mostrar tudo + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Editar texto alternativo (descrição da imagem) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Adicionar texto alternativo (descrição da imagem) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Escreva a sua descrição aqui… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Descrição curta para as pessoas que não podem visualizar a imagem ou quando a imagem não carrega. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Este texto alternativo foi criado automaticamente e pode ser impreciso. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Saber mais +pdfjs-editor-new-alt-text-create-automatically-button-label = Criar texto alternativo automaticamente +pdfjs-editor-new-alt-text-not-now-button = Agora não +pdfjs-editor-new-alt-text-error-title = Não foi possível criar o texto alternativo automaticamente +pdfjs-editor-new-alt-text-error-description = Escreva o seu próprio texto alternativo ou tente novamente mais tarde. +pdfjs-editor-new-alt-text-error-close-button = Fechar +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = A transferir o modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) + .aria-valuetext = A transferir o modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Texto alternativo adicionado +pdfjs-editor-new-alt-text-added-button-label = Texto alternativo adicionado +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Texto alternativo em falta +pdfjs-editor-new-alt-text-missing-button-label = Texto alternativo em falta +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Rever texto alternativo +pdfjs-editor-new-alt-text-to-review-button-label = Rever texto alternativo +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Criado automaticamente: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Definições de texto alternativo da imagem +pdfjs-image-alt-text-settings-button-label = Definições de texto alternativo da imagem +pdfjs-editor-alt-text-settings-dialog-label = Definições de texto alternativo das imagens +pdfjs-editor-alt-text-settings-automatic-title = Texto alternativo automático +pdfjs-editor-alt-text-settings-create-model-button-label = Criar texto alternativo automaticamente +pdfjs-editor-alt-text-settings-create-model-description = Sugere descrições para ajudar as pessoas que não podem visualizar a imagem ou quando a imagem não carrega. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Modelo de IA de texto alternativo ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = É executado localmente no seu dispositivo para que os seus dados se mantenham privados. É necessário para o texto alternativo automático. +pdfjs-editor-alt-text-settings-delete-model-button = Eliminar +pdfjs-editor-alt-text-settings-download-model-button = Transferir +pdfjs-editor-alt-text-settings-downloading-model-button = A transferir… +pdfjs-editor-alt-text-settings-editor-title = Editor de texto alternativo +pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostrar editor de texto alternativo imediatamente ao adicionar uma imagem +pdfjs-editor-alt-text-settings-show-dialog-description = Ajuda a garantir que todas as suas imagens tenham um texto alternativo. +pdfjs-editor-alt-text-settings-close-button = Fechar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Destaque removido +pdfjs-editor-undo-bar-message-freetext = Texto removido +pdfjs-editor-undo-bar-message-ink = Desenho removido +pdfjs-editor-undo-bar-message-stamp = Imagem removida +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotação removida + *[other] { $count } anotações removidas + } +pdfjs-editor-undo-bar-undo-button = + .title = Anular +pdfjs-editor-undo-bar-undo-button-label = Anular +pdfjs-editor-undo-bar-close-button = + .title = Fechar +pdfjs-editor-undo-bar-close-button-label = Fechar diff --git a/public/pdfjs/web/locale/rm/viewer.ftl b/public/pdfjs/web/locale/rm/viewer.ftl new file mode 100644 index 0000000..76992da --- /dev/null +++ b/public/pdfjs/web/locale/rm/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pagina precedenta +pdfjs-previous-button-label = Enavos +pdfjs-next-button = + .title = Proxima pagina +pdfjs-next-button-label = Enavant +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pagina +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = da { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } da { $pagesCount }) +pdfjs-zoom-out-button = + .title = Empitschnir +pdfjs-zoom-out-button-label = Empitschnir +pdfjs-zoom-in-button = + .title = Engrondir +pdfjs-zoom-in-button-label = Engrondir +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Midar en il modus da preschentaziun +pdfjs-presentation-mode-button-label = Modus da preschentaziun +pdfjs-open-file-button = + .title = Avrir datoteca +pdfjs-open-file-button-label = Avrir +pdfjs-print-button = + .title = Stampar +pdfjs-print-button-label = Stampar +pdfjs-save-button = + .title = Memorisar +pdfjs-save-button-label = Memorisar +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Telechargiar +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Telechargiar +pdfjs-bookmark-button = + .title = Pagina actuala (mussar l'URL da la pagina actuala) +pdfjs-bookmark-button-label = Pagina actuala + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Utensils +pdfjs-tools-button-label = Utensils +pdfjs-first-page-button = + .title = Siglir a l'emprima pagina +pdfjs-first-page-button-label = Siglir a l'emprima pagina +pdfjs-last-page-button = + .title = Siglir a la davosa pagina +pdfjs-last-page-button-label = Siglir a la davosa pagina +pdfjs-page-rotate-cw-button = + .title = Rotar en direcziun da l'ura +pdfjs-page-rotate-cw-button-label = Rotar en direcziun da l'ura +pdfjs-page-rotate-ccw-button = + .title = Rotar en direcziun cuntraria a l'ura +pdfjs-page-rotate-ccw-button-label = Rotar en direcziun cuntraria a l'ura +pdfjs-cursor-text-select-tool-button = + .title = Activar l'utensil per selecziunar text +pdfjs-cursor-text-select-tool-button-label = Utensil per selecziunar text +pdfjs-cursor-hand-tool-button = + .title = Activar l'utensil da maun +pdfjs-cursor-hand-tool-button-label = Utensil da maun +pdfjs-scroll-page-button = + .title = Utilisar la defilada per pagina +pdfjs-scroll-page-button-label = Defilada per pagina +pdfjs-scroll-vertical-button = + .title = Utilisar il defilar vertical +pdfjs-scroll-vertical-button-label = Defilar vertical +pdfjs-scroll-horizontal-button = + .title = Utilisar il defilar orizontal +pdfjs-scroll-horizontal-button-label = Defilar orizontal +pdfjs-scroll-wrapped-button = + .title = Utilisar il defilar en colonnas +pdfjs-scroll-wrapped-button-label = Defilar en colonnas +pdfjs-spread-none-button = + .title = Betg parallelisar las paginas +pdfjs-spread-none-button-label = Betg parallel +pdfjs-spread-odd-button = + .title = Parallelisar las paginas cun cumenzar cun paginas spèras +pdfjs-spread-odd-button-label = Parallel spèr +pdfjs-spread-even-button = + .title = Parallelisar las paginas cun cumenzar cun paginas pèras +pdfjs-spread-even-button-label = Parallel pèr + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Caracteristicas dal document… +pdfjs-document-properties-button-label = Caracteristicas dal document… +pdfjs-document-properties-file-name = Num da la datoteca: +pdfjs-document-properties-file-size = Grondezza da la datoteca: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Titel: +pdfjs-document-properties-author = Autur: +pdfjs-document-properties-subject = Tema: +pdfjs-document-properties-keywords = Chavazzins: +pdfjs-document-properties-creation-date = Data da creaziun: +pdfjs-document-properties-modification-date = Data da modificaziun: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date } { $time } +pdfjs-document-properties-creator = Creà da: +pdfjs-document-properties-producer = Creà il PDF cun: +pdfjs-document-properties-version = Versiun da PDF: +pdfjs-document-properties-page-count = Dumber da paginas: +pdfjs-document-properties-page-size = Grondezza da la pagina: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = vertical +pdfjs-document-properties-page-size-orientation-landscape = orizontal +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Fast Web View: +pdfjs-document-properties-linearized-yes = Gea +pdfjs-document-properties-linearized-no = Na +pdfjs-document-properties-close-button = Serrar + +## Print + +pdfjs-print-progress-message = Preparar il document per stampar… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Interrumper +pdfjs-printing-not-supported = Attenziun: Il stampar na funcziunescha anc betg dal tut en quest navigatur. +pdfjs-printing-not-ready = Attenziun: Il PDF n'è betg chargià cumplettamain per stampar. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Activar/deactivar la trav laterala +pdfjs-toggle-sidebar-notification-button = + .title = Activar/deactivar la trav laterala (il document cuntegna structura dal document/agiuntas/nivels) +pdfjs-toggle-sidebar-button-label = Activar/deactivar la trav laterala +pdfjs-document-outline-button = + .title = Mussar la structura dal document (cliccar duas giadas per extender/cumprimer tut ils elements) +pdfjs-document-outline-button-label = Structura dal document +pdfjs-attachments-button = + .title = Mussar agiuntas +pdfjs-attachments-button-label = Agiuntas +pdfjs-layers-button = + .title = Mussar ils nivels (cliccar dubel per restaurar il stadi da standard da tut ils nivels) +pdfjs-layers-button-label = Nivels +pdfjs-thumbs-button = + .title = Mussar las miniaturas +pdfjs-thumbs-button-label = Miniaturas +pdfjs-current-outline-item-button = + .title = Tschertgar l'element da structura actual +pdfjs-current-outline-item-button-label = Element da structura actual +pdfjs-findbar-button = + .title = Tschertgar en il document +pdfjs-findbar-button-label = Tschertgar +pdfjs-additional-layers = Nivels supplementars + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pagina { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura da la pagina { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Tschertgar + .placeholder = Tschertgar en il document… +pdfjs-find-previous-button = + .title = Tschertgar la posiziun precedenta da l'expressiun +pdfjs-find-previous-button-label = Enavos +pdfjs-find-next-button = + .title = Tschertgar la proxima posiziun da l'expressiun +pdfjs-find-next-button-label = Enavant +pdfjs-find-highlight-checkbox = Relevar tuts +pdfjs-find-match-case-checkbox-label = Resguardar maiusclas/minusclas +pdfjs-find-match-diacritics-checkbox-label = Resguardar ils segns diacritics +pdfjs-find-entire-word-checkbox-label = Pleds entirs +pdfjs-find-reached-top = Il cumenzament dal document è cuntanschì, la tschertga cuntinuescha a la fin dal document +pdfjs-find-reached-bottom = La fin dal document è cuntanschì, la tschertga cuntinuescha al cumenzament dal document +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } dad { $total } correspundenza + *[other] { $current } da { $total } correspundenzas + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Dapli che { $limit } correspundenza + *[other] Dapli che { $limit } correspundenzas + } +pdfjs-find-not-found = Impussibel da chattar l'expressiun + +## Predefined zoom values + +pdfjs-page-scale-width = Ladezza da la pagina +pdfjs-page-scale-fit = Entira pagina +pdfjs-page-scale-auto = Zoom automatic +pdfjs-page-scale-actual = Grondezza actuala +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Pagina { $page } + +## Loading indicator messages + +pdfjs-loading-error = Ina errur è cumparida cun chargiar il PDF. +pdfjs-invalid-file-error = Datoteca PDF nunvalida u donnegiada. +pdfjs-missing-file-error = Datoteca PDF manconta. +pdfjs-unexpected-response-error = Resposta nunspetgada dal server. +pdfjs-rendering-error = Ina errur è cumparida cun visualisar questa pagina. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Annotaziun da { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Endatescha il pled-clav per avrir questa datoteca da PDF. +pdfjs-password-invalid = Pled-clav nunvalid. Emprova anc ina giada. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Interrumper +pdfjs-web-fonts-disabled = Scrittiras dal web èn deactivadas: impussibel dad utilisar las scrittiras integradas en il PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Text +pdfjs-editor-free-text-button-label = Text +pdfjs-editor-ink-button = + .title = Dissegnar +pdfjs-editor-ink-button-label = Dissegnar +pdfjs-editor-stamp-button = + .title = Agiuntar u modifitgar maletgs +pdfjs-editor-stamp-button-label = Agiuntar u modifitgar maletgs +pdfjs-editor-highlight-button = + .title = Marcar +pdfjs-editor-highlight-button-label = Marcar +pdfjs-highlight-floating-button1 = + .title = Marcar + .aria-label = Marcar +pdfjs-highlight-floating-button-label = Marcar + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Allontanar il dissegn +pdfjs-editor-remove-freetext-button = + .title = Allontanar il text +pdfjs-editor-remove-stamp-button = + .title = Allontanar la grafica +pdfjs-editor-remove-highlight-button = + .title = Allontanar l'emfasa + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Colur +pdfjs-editor-free-text-size-input = Grondezza +pdfjs-editor-ink-color-input = Colur +pdfjs-editor-ink-thickness-input = Grossezza +pdfjs-editor-ink-opacity-input = Opacitad +pdfjs-editor-stamp-add-image-button = + .title = Agiuntar in maletg +pdfjs-editor-stamp-add-image-button-label = Agiuntar in maletg +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Grossezza +pdfjs-editor-free-highlight-thickness-title = + .title = Midar la grossezza cun relevar elements betg textuals +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editur da text + .default-content = Cumenza a tippar… +pdfjs-free-text = + .aria-label = Editur da text +pdfjs-free-text-default-content = Cumenzar a tippar… +pdfjs-ink = + .aria-label = Editur dissegn +pdfjs-ink-canvas = + .aria-label = Maletg creà da l'utilisader + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Text alternativ +pdfjs-editor-alt-text-edit-button = + .aria-label = Modifitgar il text alternativ +pdfjs-editor-alt-text-edit-button-label = Modifitgar il text alternativ +pdfjs-editor-alt-text-dialog-label = Tscherner ina opziun +pdfjs-editor-alt-text-dialog-description = Il text alternativ (alt text) gida en cas che persunas na vesan betg il maletg u sch'i na reussescha betg d'al chargiar. +pdfjs-editor-alt-text-add-description-label = Agiuntar ina descripziun +pdfjs-editor-alt-text-add-description-description = Scriva idealmain 1-2 frasas che descrivan l'object, la situaziun u las acziuns. +pdfjs-editor-alt-text-mark-decorative-label = Marcar sco decorativ +pdfjs-editor-alt-text-mark-decorative-description = Quai vegn duvrà per maletgs ornamentals, sco urs u filigranas. +pdfjs-editor-alt-text-cancel-button = Interrumper +pdfjs-editor-alt-text-save-button = Memorisar +pdfjs-editor-alt-text-decorative-tooltip = Marcà sco decorativ +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Per exempel: «In um giuven sesa a maisa per mangiar in past» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Text alternativ + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Chantun sura a sanestra — redimensiunar +pdfjs-editor-resizer-label-top-middle = Sura amez — redimensiunar +pdfjs-editor-resizer-label-top-right = Chantun sura a dretga — redimensiunar +pdfjs-editor-resizer-label-middle-right = Da vart dretga amez — redimensiunar +pdfjs-editor-resizer-label-bottom-right = Chantun sut a dretga — redimensiunar +pdfjs-editor-resizer-label-bottom-middle = Sutvart amez — redimensiunar +pdfjs-editor-resizer-label-bottom-left = Chantun sut a sanestra — redimensiunar +pdfjs-editor-resizer-label-middle-left = Vart sanestra amez — redimensiunar +pdfjs-editor-resizer-top-left = + .aria-label = Chantun sura a sanestra — redimensiunar +pdfjs-editor-resizer-top-middle = + .aria-label = Sura amez — redimensiunar +pdfjs-editor-resizer-top-right = + .aria-label = Chantun sura a dretga — redimensiunar +pdfjs-editor-resizer-middle-right = + .aria-label = Da vart dretga amez — redimensiunar +pdfjs-editor-resizer-bottom-right = + .aria-label = Chantun sut a dretga — redimensiunar +pdfjs-editor-resizer-bottom-middle = + .aria-label = Sutvart amez — redimensiunar +pdfjs-editor-resizer-bottom-left = + .aria-label = Chantun sut a sanestra — redimensiunar +pdfjs-editor-resizer-middle-left = + .aria-label = Vart sanestra amez — redimensiunar + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Colur per l'emfasa +pdfjs-editor-colorpicker-button = + .title = Midar la colur +pdfjs-editor-colorpicker-dropdown = + .aria-label = Colurs disponiblas +pdfjs-editor-colorpicker-yellow = + .title = Mellen +pdfjs-editor-colorpicker-green = + .title = Verd +pdfjs-editor-colorpicker-blue = + .title = Blau +pdfjs-editor-colorpicker-pink = + .title = Rosa +pdfjs-editor-colorpicker-red = + .title = Cotschen + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Mussar tut +pdfjs-editor-highlight-show-all-button = + .title = Mussar tut + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Modifitgar il text alternativ (descripziun dal maletg) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Agiuntar in text alternativ (descripziun dal maletg) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Scriva qua tia descripziun… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Curta descripziun per persunas che na vesan betg il maletg u per cass en ils quals il maletg na vegn betg chargià. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Quest text alternativ è vegnì creà automaticamain ed è eventualmain nunprecis. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Ulteriuras infurmaziuns +pdfjs-editor-new-alt-text-create-automatically-button-label = Crear automaticamain il text alternativ +pdfjs-editor-new-alt-text-not-now-button = Betg ussa +pdfjs-editor-new-alt-text-error-title = I n’è betg reussì da crear automaticamain il text alternativ +pdfjs-editor-new-alt-text-error-description = Scriva per plaschair tes agen text alternativ u emprova pli tard anc ina giada. +pdfjs-editor-new-alt-text-error-close-button = Serrar +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Telechargiar il model IA da text alternativ ({ $downloadedSize } da { $totalSize } MB) + .aria-valuetext = Telechargiar il model IA da text alternativ ({ $downloadedSize } da { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Agiuntà text alternativ +pdfjs-editor-new-alt-text-added-button-label = Text alternativ agiuntà +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Text alternativ manca +pdfjs-editor-new-alt-text-missing-button-label = Text alternativ manca +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Repassar il text alternativ +pdfjs-editor-new-alt-text-to-review-button-label = Repassar il text alternativ +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Creà automaticamain: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Parameters dal text alternativ da maletgs +pdfjs-image-alt-text-settings-button-label = Parameters dal text alternativ da maletgs +pdfjs-editor-alt-text-settings-dialog-label = Parameters dal text alternativ da maletgs +pdfjs-editor-alt-text-settings-automatic-title = Text alternativ automatic +pdfjs-editor-alt-text-settings-create-model-button-label = Crear automaticamain text alternativ +pdfjs-editor-alt-text-settings-create-model-description = Propona descripziuns per gidar a persunas che na vesan betg il maletg u per cass en ils quals il maletg na vegn betg chargià. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Model IA da text alternativ ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Vegn exequì localmain sin tes apparat per che tias datas restian privatas. Necessari per text alternativ automatic. +pdfjs-editor-alt-text-settings-delete-model-button = Stizzar +pdfjs-editor-alt-text-settings-download-model-button = Telechargiar +pdfjs-editor-alt-text-settings-downloading-model-button = Telechargiar… +pdfjs-editor-alt-text-settings-editor-title = Editur per text alternativ +pdfjs-editor-alt-text-settings-show-dialog-button-label = Mussar l’editur per text alternativ directamain cun agiuntar in maletg +pdfjs-editor-alt-text-settings-show-dialog-description = Ta gida a garantir che tut tes maletgs hajan in text alternativ. +pdfjs-editor-alt-text-settings-close-button = Serrar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Allontanà la marcaziun +pdfjs-editor-undo-bar-message-freetext = Allontanà il text +pdfjs-editor-undo-bar-message-ink = Allontanà il dissegn +pdfjs-editor-undo-bar-message-stamp = Allontanà il maletg +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotaziun allontanada + *[other] { $count } annotaziuns allontanadas + } +pdfjs-editor-undo-bar-undo-button = + .title = Revocar +pdfjs-editor-undo-bar-undo-button-label = Revocar +pdfjs-editor-undo-bar-close-button = + .title = Serrar +pdfjs-editor-undo-bar-close-button-label = Serrar diff --git a/public/pdfjs/web/locale/ro/viewer.ftl b/public/pdfjs/web/locale/ro/viewer.ftl new file mode 100644 index 0000000..7c6f0b6 --- /dev/null +++ b/public/pdfjs/web/locale/ro/viewer.ftl @@ -0,0 +1,251 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pagina precedentă +pdfjs-previous-button-label = Înapoi +pdfjs-next-button = + .title = Pagina următoare +pdfjs-next-button-label = Înainte +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pagina +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = din { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } din { $pagesCount }) +pdfjs-zoom-out-button = + .title = Micșorează +pdfjs-zoom-out-button-label = Micșorează +pdfjs-zoom-in-button = + .title = Mărește +pdfjs-zoom-in-button-label = Mărește +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Comută la modul de prezentare +pdfjs-presentation-mode-button-label = Mod de prezentare +pdfjs-open-file-button = + .title = Deschide un fișier +pdfjs-open-file-button-label = Deschide +pdfjs-print-button = + .title = Tipărește +pdfjs-print-button-label = Tipărește + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Instrumente +pdfjs-tools-button-label = Instrumente +pdfjs-first-page-button = + .title = Mergi la prima pagină +pdfjs-first-page-button-label = Mergi la prima pagină +pdfjs-last-page-button = + .title = Mergi la ultima pagină +pdfjs-last-page-button-label = Mergi la ultima pagină +pdfjs-page-rotate-cw-button = + .title = Rotește în sensul acelor de ceas +pdfjs-page-rotate-cw-button-label = Rotește în sensul acelor de ceas +pdfjs-page-rotate-ccw-button = + .title = Rotește în sens invers al acelor de ceas +pdfjs-page-rotate-ccw-button-label = Rotește în sens invers al acelor de ceas +pdfjs-cursor-text-select-tool-button = + .title = Activează instrumentul de selecție a textului +pdfjs-cursor-text-select-tool-button-label = Instrumentul de selecție a textului +pdfjs-cursor-hand-tool-button = + .title = Activează instrumentul mână +pdfjs-cursor-hand-tool-button-label = Unealta mână +pdfjs-scroll-vertical-button = + .title = Folosește derularea verticală +pdfjs-scroll-vertical-button-label = Derulare verticală +pdfjs-scroll-horizontal-button = + .title = Folosește derularea orizontală +pdfjs-scroll-horizontal-button-label = Derulare orizontală +pdfjs-scroll-wrapped-button = + .title = Folosește derularea încadrată +pdfjs-scroll-wrapped-button-label = Derulare încadrată +pdfjs-spread-none-button = + .title = Nu uni paginile broșate +pdfjs-spread-none-button-label = Fără pagini broșate +pdfjs-spread-odd-button = + .title = Unește paginile broșate începând cu cele impare +pdfjs-spread-odd-button-label = Broșare pagini impare +pdfjs-spread-even-button = + .title = Unește paginile broșate începând cu cele pare +pdfjs-spread-even-button-label = Broșare pagini pare + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Proprietățile documentului… +pdfjs-document-properties-button-label = Proprietățile documentului… +pdfjs-document-properties-file-name = Numele fișierului: +pdfjs-document-properties-file-size = Mărimea fișierului: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } byți) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } byți) +pdfjs-document-properties-title = Titlu: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Subiect: +pdfjs-document-properties-keywords = Cuvinte cheie: +pdfjs-document-properties-creation-date = Data creării: +pdfjs-document-properties-modification-date = Data modificării: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Autor: +pdfjs-document-properties-producer = Producător PDF: +pdfjs-document-properties-version = Versiune PDF: +pdfjs-document-properties-page-count = Număr de pagini: +pdfjs-document-properties-page-size = Mărimea paginii: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = verticală +pdfjs-document-properties-page-size-orientation-landscape = orizontală +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Literă +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Vizualizare web rapidă: +pdfjs-document-properties-linearized-yes = Da +pdfjs-document-properties-linearized-no = Nu +pdfjs-document-properties-close-button = Închide + +## Print + +pdfjs-print-progress-message = Se pregătește documentul pentru tipărire… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Renunță +pdfjs-printing-not-supported = Avertisment: Tipărirea nu este suportată în totalitate de acest browser. +pdfjs-printing-not-ready = Avertisment: PDF-ul nu este încărcat complet pentru tipărire. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Comută bara laterală +pdfjs-toggle-sidebar-button-label = Comută bara laterală +pdfjs-document-outline-button = + .title = Afișează schița documentului (dublu-clic pentru a extinde/restrânge toate elementele) +pdfjs-document-outline-button-label = Schița documentului +pdfjs-attachments-button = + .title = Afișează atașamentele +pdfjs-attachments-button-label = Atașamente +pdfjs-thumbs-button = + .title = Afișează miniaturi +pdfjs-thumbs-button-label = Miniaturi +pdfjs-findbar-button = + .title = Caută în document +pdfjs-findbar-button-label = Caută + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pagina { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura paginii { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Caută + .placeholder = Caută în document… +pdfjs-find-previous-button = + .title = Mergi la apariția anterioară a textului +pdfjs-find-previous-button-label = Înapoi +pdfjs-find-next-button = + .title = Mergi la apariția următoare a textului +pdfjs-find-next-button-label = Înainte +pdfjs-find-highlight-checkbox = Evidențiază toate aparițiile +pdfjs-find-match-case-checkbox-label = Ține cont de majuscule și minuscule +pdfjs-find-entire-word-checkbox-label = Cuvinte întregi +pdfjs-find-reached-top = Am ajuns la începutul documentului, continuă de la sfârșit +pdfjs-find-reached-bottom = Am ajuns la sfârșitul documentului, continuă de la început +pdfjs-find-not-found = Nu s-a găsit textul + +## Predefined zoom values + +pdfjs-page-scale-width = Lățime pagină +pdfjs-page-scale-fit = Potrivire la pagină +pdfjs-page-scale-auto = Zoom automat +pdfjs-page-scale-actual = Mărime reală +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = A intervenit o eroare la încărcarea PDF-ului. +pdfjs-invalid-file-error = Fișier PDF nevalid sau corupt. +pdfjs-missing-file-error = Fișier PDF lipsă. +pdfjs-unexpected-response-error = Răspuns neașteptat de la server. +pdfjs-rendering-error = A intervenit o eroare la randarea paginii. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Adnotare { $type }] + +## Password + +pdfjs-password-label = Introdu parola pentru a deschide acest fișier PDF. +pdfjs-password-invalid = Parolă nevalidă. Te rugăm să încerci din nou. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Renunță +pdfjs-web-fonts-disabled = Fonturile web sunt dezactivate: nu se pot folosi fonturile PDF încorporate. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/ru/viewer.ftl b/public/pdfjs/web/locale/ru/viewer.ftl new file mode 100644 index 0000000..81c2f41 --- /dev/null +++ b/public/pdfjs/web/locale/ru/viewer.ftl @@ -0,0 +1,518 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Предыдущая страница +pdfjs-previous-button-label = Предыдущая +pdfjs-next-button = + .title = Следующая страница +pdfjs-next-button-label = Следующая +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Страница +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = из { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } из { $pagesCount }) +pdfjs-zoom-out-button = + .title = Уменьшить +pdfjs-zoom-out-button-label = Уменьшить +pdfjs-zoom-in-button = + .title = Увеличить +pdfjs-zoom-in-button-label = Увеличить +pdfjs-zoom-select = + .title = Масштаб +pdfjs-presentation-mode-button = + .title = Перейти в режим презентации +pdfjs-presentation-mode-button-label = Режим презентации +pdfjs-open-file-button = + .title = Открыть файл +pdfjs-open-file-button-label = Открыть +pdfjs-print-button = + .title = Печать +pdfjs-print-button-label = Печать +pdfjs-save-button = + .title = Сохранить +pdfjs-save-button-label = Сохранить +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Загрузить +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Загрузить +pdfjs-bookmark-button = + .title = Текущая страница (просмотр URL-адреса с текущей страницы) +pdfjs-bookmark-button-label = Текущая страница + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Инструменты +pdfjs-tools-button-label = Инструменты +pdfjs-first-page-button = + .title = Перейти на первую страницу +pdfjs-first-page-button-label = Перейти на первую страницу +pdfjs-last-page-button = + .title = Перейти на последнюю страницу +pdfjs-last-page-button-label = Перейти на последнюю страницу +pdfjs-page-rotate-cw-button = + .title = Повернуть по часовой стрелке +pdfjs-page-rotate-cw-button-label = Повернуть по часовой стрелке +pdfjs-page-rotate-ccw-button = + .title = Повернуть против часовой стрелки +pdfjs-page-rotate-ccw-button-label = Повернуть против часовой стрелки +pdfjs-cursor-text-select-tool-button = + .title = Включить Инструмент «Выделение текста» +pdfjs-cursor-text-select-tool-button-label = Инструмент «Выделение текста» +pdfjs-cursor-hand-tool-button = + .title = Включить Инструмент «Рука» +pdfjs-cursor-hand-tool-button-label = Инструмент «Рука» +pdfjs-scroll-page-button = + .title = Использовать прокрутку страниц +pdfjs-scroll-page-button-label = Прокрутка страниц +pdfjs-scroll-vertical-button = + .title = Использовать вертикальную прокрутку +pdfjs-scroll-vertical-button-label = Вертикальная прокрутка +pdfjs-scroll-horizontal-button = + .title = Использовать горизонтальную прокрутку +pdfjs-scroll-horizontal-button-label = Горизонтальная прокрутка +pdfjs-scroll-wrapped-button = + .title = Использовать масштабируемую прокрутку +pdfjs-scroll-wrapped-button-label = Масштабируемая прокрутка +pdfjs-spread-none-button = + .title = Не использовать режим разворотов страниц +pdfjs-spread-none-button-label = Без разворотов страниц +pdfjs-spread-odd-button = + .title = Развороты начинаются с нечётных номеров страниц +pdfjs-spread-odd-button-label = Нечётные страницы слева +pdfjs-spread-even-button = + .title = Развороты начинаются с чётных номеров страниц +pdfjs-spread-even-button-label = Чётные страницы слева + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Свойства документа… +pdfjs-document-properties-button-label = Свойства документа… +pdfjs-document-properties-file-name = Имя файла: +pdfjs-document-properties-file-size = Размер файла: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } КБ ({ $b } байт) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } МБ ({ $b } байт) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байт) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } байт) +pdfjs-document-properties-title = Заголовок: +pdfjs-document-properties-author = Автор: +pdfjs-document-properties-subject = Тема: +pdfjs-document-properties-keywords = Ключевые слова: +pdfjs-document-properties-creation-date = Дата создания: +pdfjs-document-properties-modification-date = Дата изменения: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Приложение: +pdfjs-document-properties-producer = Производитель PDF: +pdfjs-document-properties-version = Версия PDF: +pdfjs-document-properties-page-count = Число страниц: +pdfjs-document-properties-page-size = Размер страницы: +pdfjs-document-properties-page-size-unit-inches = дюймов +pdfjs-document-properties-page-size-unit-millimeters = мм +pdfjs-document-properties-page-size-orientation-portrait = книжная +pdfjs-document-properties-page-size-orientation-landscape = альбомная +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Быстрый просмотр в Web: +pdfjs-document-properties-linearized-yes = Да +pdfjs-document-properties-linearized-no = Нет +pdfjs-document-properties-close-button = Закрыть + +## Print + +pdfjs-print-progress-message = Подготовка документа к печати… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Отмена +pdfjs-printing-not-supported = Предупреждение: В этом браузере не полностью поддерживается печать. +pdfjs-printing-not-ready = Предупреждение: PDF не полностью загружен для печати. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Показать/скрыть боковую панель +pdfjs-toggle-sidebar-notification-button = + .title = Показать/скрыть боковую панель (документ имеет содержание/вложения/слои) +pdfjs-toggle-sidebar-button-label = Показать/скрыть боковую панель +pdfjs-document-outline-button = + .title = Показать содержание документа (двойной щелчок, чтобы развернуть/свернуть все элементы) +pdfjs-document-outline-button-label = Содержание документа +pdfjs-attachments-button = + .title = Показать вложения +pdfjs-attachments-button-label = Вложения +pdfjs-layers-button = + .title = Показать слои (дважды щёлкните, чтобы сбросить все слои к состоянию по умолчанию) +pdfjs-layers-button-label = Слои +pdfjs-thumbs-button = + .title = Показать миниатюры +pdfjs-thumbs-button-label = Миниатюры +pdfjs-current-outline-item-button = + .title = Найти текущий элемент структуры +pdfjs-current-outline-item-button-label = Текущий элемент структуры +pdfjs-findbar-button = + .title = Найти в документе +pdfjs-findbar-button-label = Найти +pdfjs-additional-layers = Дополнительные слои + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Страница { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Миниатюра страницы { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Найти + .placeholder = Найти в документе… +pdfjs-find-previous-button = + .title = Найти предыдущее вхождение фразы в текст +pdfjs-find-previous-button-label = Назад +pdfjs-find-next-button = + .title = Найти следующее вхождение фразы в текст +pdfjs-find-next-button-label = Далее +pdfjs-find-highlight-checkbox = Подсветить все +pdfjs-find-match-case-checkbox-label = С учётом регистра +pdfjs-find-match-diacritics-checkbox-label = С учётом диакритических знаков +pdfjs-find-entire-word-checkbox-label = Слова целиком +pdfjs-find-reached-top = Достигнут верх документа, продолжено снизу +pdfjs-find-reached-bottom = Достигнут конец документа, продолжено сверху +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } из { $total } совпадения + [few] { $current } из { $total } совпадений + *[many] { $current } из { $total } совпадений + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Более { $limit } совпадения + [few] Более { $limit } совпадений + *[many] Более { $limit } совпадений + } +pdfjs-find-not-found = Фраза не найдена + +## Predefined zoom values + +pdfjs-page-scale-width = По ширине страницы +pdfjs-page-scale-fit = По размеру страницы +pdfjs-page-scale-auto = Автоматически +pdfjs-page-scale-actual = Реальный размер +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Страница { $page } + +## Loading indicator messages + +pdfjs-loading-error = При загрузке PDF произошла ошибка. +pdfjs-invalid-file-error = Некорректный или повреждённый PDF-файл. +pdfjs-missing-file-error = PDF-файл отсутствует. +pdfjs-unexpected-response-error = Неожиданный ответ сервера. +pdfjs-rendering-error = При создании страницы произошла ошибка. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Аннотация { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Введите пароль, чтобы открыть этот PDF-файл. +pdfjs-password-invalid = Неверный пароль. Пожалуйста, попробуйте снова. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Отмена +pdfjs-web-fonts-disabled = Веб-шрифты отключены: не удалось задействовать встроенные PDF-шрифты. + +## Editing + +pdfjs-editor-free-text-button = + .title = Текст +pdfjs-editor-free-text-button-label = Текст +pdfjs-editor-ink-button = + .title = Рисовать +pdfjs-editor-ink-button-label = Рисовать +pdfjs-editor-stamp-button = + .title = Добавить или изменить изображения +pdfjs-editor-stamp-button-label = Добавить или изменить изображения +pdfjs-editor-highlight-button = + .title = Выделение +pdfjs-editor-highlight-button-label = Выделение +pdfjs-highlight-floating-button1 = + .title = Выделение + .aria-label = Выделение +pdfjs-highlight-floating-button-label = Выделение + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Удалить рисунок +pdfjs-editor-remove-freetext-button = + .title = Удалить текст +pdfjs-editor-remove-stamp-button = + .title = Удалить изображение +pdfjs-editor-remove-highlight-button = + .title = Удалить выделение + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Цвет +pdfjs-editor-free-text-size-input = Размер +pdfjs-editor-ink-color-input = Цвет +pdfjs-editor-ink-thickness-input = Толщина +pdfjs-editor-ink-opacity-input = Прозрачность +pdfjs-editor-stamp-add-image-button = + .title = Добавить изображение +pdfjs-editor-stamp-add-image-button-label = Добавить изображение +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Толщина +pdfjs-editor-free-highlight-thickness-title = + .title = Изменить толщину при выделении элементов, кроме текста +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Текстовый редактор + .default-content = Начните ввод... +pdfjs-free-text = + .aria-label = Текстовый редактор +pdfjs-free-text-default-content = Начните вводить… +pdfjs-ink = + .aria-label = Редактор рисования +pdfjs-ink-canvas = + .aria-label = Созданное пользователем изображение + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Альтернативный текст +pdfjs-editor-alt-text-edit-button = + .aria-label = Изменить альтернативный текст +pdfjs-editor-alt-text-edit-button-label = Изменить альтернативный текст +pdfjs-editor-alt-text-dialog-label = Выберите вариант +pdfjs-editor-alt-text-dialog-description = Альтернативный текст помогает, когда люди не видят изображение или оно не загружается. +pdfjs-editor-alt-text-add-description-label = Добавить описание +pdfjs-editor-alt-text-add-description-description = Старайтесь составлять 1–2 предложения, описывающих предмет, обстановку или действия. +pdfjs-editor-alt-text-mark-decorative-label = Отметить как декоративное +pdfjs-editor-alt-text-mark-decorative-description = Используется для декоративных изображений, таких как рамки или водяные знаки. +pdfjs-editor-alt-text-cancel-button = Отменить +pdfjs-editor-alt-text-save-button = Сохранить +pdfjs-editor-alt-text-decorative-tooltip = Помечен как декоративный +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Например: «Молодой человек садится за стол, чтобы поесть» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Альтернативный текст + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Левый верхний угол — изменить размер +pdfjs-editor-resizer-label-top-middle = Вверху посередине — изменить размер +pdfjs-editor-resizer-label-top-right = Верхний правый угол — изменить размер +pdfjs-editor-resizer-label-middle-right = В центре справа — изменить размер +pdfjs-editor-resizer-label-bottom-right = Нижний правый угол — изменить размер +pdfjs-editor-resizer-label-bottom-middle = Внизу посередине — изменить размер +pdfjs-editor-resizer-label-bottom-left = Нижний левый угол — изменить размер +pdfjs-editor-resizer-label-middle-left = В центре слева — изменить размер +pdfjs-editor-resizer-top-left = + .aria-label = Левый верхний угол — изменить размер +pdfjs-editor-resizer-top-middle = + .aria-label = Вверху посередине — изменить размер +pdfjs-editor-resizer-top-right = + .aria-label = Верхний правый угол — изменить размер +pdfjs-editor-resizer-middle-right = + .aria-label = В центре справа — изменить размер +pdfjs-editor-resizer-bottom-right = + .aria-label = Нижний правый угол — изменить размер +pdfjs-editor-resizer-bottom-middle = + .aria-label = Внизу посередине — изменить размер +pdfjs-editor-resizer-bottom-left = + .aria-label = Нижний левый угол — изменить размер +pdfjs-editor-resizer-middle-left = + .aria-label = В центре слева — изменить размер + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Цвет выделения +pdfjs-editor-colorpicker-button = + .title = Изменить цвет +pdfjs-editor-colorpicker-dropdown = + .aria-label = Выбор цвета +pdfjs-editor-colorpicker-yellow = + .title = Жёлтый +pdfjs-editor-colorpicker-green = + .title = Зелёный +pdfjs-editor-colorpicker-blue = + .title = Синий +pdfjs-editor-colorpicker-pink = + .title = Розовый +pdfjs-editor-colorpicker-red = + .title = Красный + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Показать все +pdfjs-editor-highlight-show-all-button = + .title = Показать все + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Изменить альтернативный текст (описание изображения) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Добавить альтернативный текст (описание изображения) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Напишите здесь своё описание… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Короткое описание для людей, которые не видят изображение, или если изображение не загружается. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Этот альтернативный текст был создан автоматически и может быть неточным. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Подробнее +pdfjs-editor-new-alt-text-create-automatically-button-label = Автоматически создавать альтернативный текст +pdfjs-editor-new-alt-text-not-now-button = Не сейчас +pdfjs-editor-new-alt-text-error-title = Не удалось автоматически создать альтернативный текст +pdfjs-editor-new-alt-text-error-description = Пожалуйста, напишите свой альтернативный текст или попробуйте ещё раз позже. +pdfjs-editor-new-alt-text-error-close-button = Закрыть +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Загрузка модели ИИ для альтернативного текста ({ $downloadedSize } из { $totalSize } МБ) + .aria-valuetext = Загрузка модели ИИ для альтернативного текста ({ $downloadedSize } из { $totalSize } МБ) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Альтернативный текст добавлен +pdfjs-editor-new-alt-text-added-button-label = Альтернативный текст добавлен +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Отсутствует альтернативный текст +pdfjs-editor-new-alt-text-missing-button-label = Отсутствует альтернативный текст +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Оценить альтернативный текст +pdfjs-editor-new-alt-text-to-review-button-label = Оценить альтернативный текст +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Создано автоматически: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Настройки альтернативного текста для изображения +pdfjs-image-alt-text-settings-button-label = Настройки альтернативного текста для изображения +pdfjs-editor-alt-text-settings-dialog-label = Настройки альтернативного текста для изображения +pdfjs-editor-alt-text-settings-automatic-title = Автоматический альтернативный текст +pdfjs-editor-alt-text-settings-create-model-button-label = Автоматически создавать альтернативный текст +pdfjs-editor-alt-text-settings-create-model-description = Предлагает описания, чтобы помочь людям, которые не видят изображение, или если изображение не загружается. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = ИИ-модель альтернативного текста ({ $totalSize } МБ) +pdfjs-editor-alt-text-settings-ai-model-description = Запускается локально на вашем устройстве, поэтому ваши данные остаются конфиденциальными. Требуется для автоматического альтернативного текста. +pdfjs-editor-alt-text-settings-delete-model-button = Удалить +pdfjs-editor-alt-text-settings-download-model-button = Загрузить +pdfjs-editor-alt-text-settings-downloading-model-button = Загрузка… +pdfjs-editor-alt-text-settings-editor-title = Редактор альтернативного текста +pdfjs-editor-alt-text-settings-show-dialog-button-label = Сразу показывать редактор альтернативного текста при добавлении изображения +pdfjs-editor-alt-text-settings-show-dialog-description = Помогает вам убедиться, что все ваши изображения имеют альтернативный текст. +pdfjs-editor-alt-text-settings-close-button = Закрыть + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Выделение удалено +pdfjs-editor-undo-bar-message-freetext = Текст удалён +pdfjs-editor-undo-bar-message-ink = Рисунок удалён +pdfjs-editor-undo-bar-message-stamp = Изображение удалено +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } аннотация удалена + [few] { $count } аннотации удалены + *[many] { $count } аннотаций удалены + } +pdfjs-editor-undo-bar-undo-button = + .title = Отменить +pdfjs-editor-undo-bar-undo-button-label = Отменить +pdfjs-editor-undo-bar-close-button = + .title = Закрыть +pdfjs-editor-undo-bar-close-button-label = Закрыть diff --git a/public/pdfjs/web/locale/sat/viewer.ftl b/public/pdfjs/web/locale/sat/viewer.ftl new file mode 100644 index 0000000..2fbbc12 --- /dev/null +++ b/public/pdfjs/web/locale/sat/viewer.ftl @@ -0,0 +1,325 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = ᱢᱟᱲᱟᱝ ᱥᱟᱦᱴᱟ +pdfjs-previous-button-label = ᱢᱟᱲᱟᱝᱟᱜ +pdfjs-next-button = + .title = ᱤᱱᱟᱹ ᱛᱟᱭᱚᱢ ᱥᱟᱦᱴᱟ +pdfjs-next-button-label = ᱤᱱᱟᱹ ᱛᱟᱭᱚᱢ +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = ᱥᱟᱦᱴᱟ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = ᱨᱮᱭᱟᱜ { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } ᱠᱷᱚᱱ { $pagesCount }) +pdfjs-zoom-out-button = + .title = ᱦᱤᱲᱤᱧ ᱛᱮᱭᱟᱨ +pdfjs-zoom-out-button-label = ᱦᱤᱲᱤᱧ ᱛᱮᱭᱟᱨ +pdfjs-zoom-in-button = + .title = ᱢᱟᱨᱟᱝ ᱛᱮᱭᱟᱨ +pdfjs-zoom-in-button-label = ᱢᱟᱨᱟᱝ ᱛᱮᱭᱟᱨ +pdfjs-zoom-select = + .title = ᱡᱩᱢ +pdfjs-presentation-mode-button = + .title = ᱩᱫᱩᱜ ᱥᱚᱫᱚᱨ ᱚᱵᱚᱥᱛᱟ ᱨᱮ ᱚᱛᱟᱭ ᱢᱮ +pdfjs-presentation-mode-button-label = ᱩᱫᱩᱜ ᱥᱚᱫᱚᱨ ᱚᱵᱚᱥᱛᱟ ᱨᱮ +pdfjs-open-file-button = + .title = ᱨᱮᱫ ᱡᱷᱤᱡᱽ ᱢᱮ +pdfjs-open-file-button-label = ᱡᱷᱤᱡᱽ ᱢᱮ +pdfjs-print-button = + .title = ᱪᱷᱟᱯᱟ +pdfjs-print-button-label = ᱪᱷᱟᱯᱟ +pdfjs-save-button = + .title = ᱥᱟᱺᱪᱟᱣ ᱢᱮ +pdfjs-save-button-label = ᱥᱟᱺᱪᱟᱣ ᱢᱮ +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = ᱰᱟᱣᱩᱱᱞᱚᱰ +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = ᱰᱟᱣᱩᱱᱞᱚᱰ +pdfjs-bookmark-button = + .title = ᱱᱤᱛᱚᱜᱟᱜ ᱥᱟᱦᱴᱟ (ᱱᱤᱛᱚᱜᱟᱜ ᱥᱟᱦᱴᱟ ᱠᱷᱚᱱ URL ᱫᱮᱠᱷᱟᱣ ᱢᱮ) +pdfjs-bookmark-button-label = ᱱᱤᱛᱚᱜᱟᱜ ᱥᱟᱦᱴᱟ + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = ᱦᱟᱹᱛᱤᱭᱟᱹᱨ ᱠᱚ +pdfjs-tools-button-label = ᱦᱟᱹᱛᱤᱭᱟᱹᱨ ᱠᱚ +pdfjs-first-page-button = + .title = ᱯᱩᱭᱞᱩ ᱥᱟᱦᱴᱟ ᱥᱮᱫ ᱪᱟᱞᱟᱜ ᱢᱮ +pdfjs-first-page-button-label = ᱯᱩᱭᱞᱩ ᱥᱟᱦᱴᱟ ᱥᱮᱫ ᱪᱟᱞᱟᱜ ᱢᱮ +pdfjs-last-page-button = + .title = ᱢᱩᱪᱟᱹᱫ ᱥᱟᱦᱴᱟ ᱥᱮᱫ ᱪᱟᱞᱟᱜ ᱢᱮ +pdfjs-last-page-button-label = ᱢᱩᱪᱟᱹᱫ ᱥᱟᱦᱴᱟ ᱥᱮᱫ ᱪᱟᱞᱟᱜ ᱢᱮ +pdfjs-page-rotate-cw-button = + .title = ᱜᱷᱚᱰᱤ ᱦᱤᱥᱟᱹᱵ ᱛᱮ ᱟᱹᱪᱩᱨ +pdfjs-page-rotate-cw-button-label = ᱜᱷᱚᱰᱤ ᱦᱤᱥᱟᱹᱵ ᱛᱮ ᱟᱹᱪᱩᱨ +pdfjs-page-rotate-ccw-button = + .title = ᱜᱷᱚᱰᱤ ᱦᱤᱥᱟᱹᱵ ᱛᱮ ᱩᱞᱴᱟᱹ ᱟᱹᱪᱩᱨ +pdfjs-page-rotate-ccw-button-label = ᱜᱷᱚᱰᱤ ᱦᱤᱥᱟᱹᱵ ᱛᱮ ᱩᱞᱴᱟᱹ ᱟᱹᱪᱩᱨ +pdfjs-cursor-text-select-tool-button = + .title = ᱚᱞ ᱵᱟᱪᱷᱟᱣ ᱦᱟᱹᱛᱤᱭᱟᱨ ᱮᱢ ᱪᱷᱚᱭ ᱢᱮ +pdfjs-cursor-text-select-tool-button-label = ᱚᱞ ᱵᱟᱪᱷᱟᱣ ᱦᱟᱹᱛᱤᱭᱟᱨ +pdfjs-cursor-hand-tool-button = + .title = ᱛᱤ ᱦᱟᱹᱛᱤᱭᱟᱨ ᱮᱢ ᱪᱷᱚᱭ ᱢᱮ +pdfjs-cursor-hand-tool-button-label = ᱛᱤ ᱦᱟᱹᱛᱤᱭᱟᱨ +pdfjs-scroll-page-button = + .title = ᱥᱟᱦᱴᱟ ᱜᱩᱲᱟᱹᱣ ᱵᱮᱵᱷᱟᱨ ᱢᱮ +pdfjs-scroll-page-button-label = ᱥᱟᱦᱴᱟ ᱜᱩᱲᱟᱹᱣ +pdfjs-scroll-vertical-button = + .title = ᱥᱤᱫᱽ ᱜᱩᱲᱟᱹᱣ ᱵᱮᱵᱷᱟᱨ ᱢᱮ +pdfjs-scroll-vertical-button-label = ᱥᱤᱫᱽ ᱜᱩᱲᱟᱹᱣ +pdfjs-scroll-horizontal-button = + .title = ᱜᱤᱛᱤᱡ ᱛᱮ ᱜᱩᱲᱟᱹᱣ ᱵᱮᱵᱷᱟᱨ ᱢᱮ +pdfjs-scroll-horizontal-button-label = ᱜᱤᱛᱤᱡ ᱛᱮ ᱜᱩᱲᱟᱹᱣ +pdfjs-scroll-wrapped-button = + .title = ᱞᱤᱯᱴᱟᱹᱣ ᱜᱩᱰᱨᱟᱹᱣ ᱵᱮᱵᱷᱟᱨ ᱢᱮ +pdfjs-scroll-wrapped-button-label = ᱞᱤᱯᱴᱟᱣ ᱜᱩᱰᱨᱟᱹᱣ +pdfjs-spread-none-button = + .title = ᱟᱞᱚᱢ ᱡᱚᱲᱟᱣ ᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱯᱟᱥᱱᱟᱣᱜᱼᱟ +pdfjs-spread-none-button-label = ᱯᱟᱥᱱᱟᱣ ᱵᱟᱹᱱᱩᱜᱼᱟ +pdfjs-spread-odd-button = + .title = ᱥᱟᱦᱴᱟ ᱯᱟᱥᱱᱟᱣ ᱡᱚᱲᱟᱣ ᱢᱮ ᱡᱟᱦᱟᱸ ᱫᱚ ᱚᱰᱼᱮᱞ ᱥᱟᱦᱴᱟᱠᱚ ᱥᱟᱞᱟᱜ ᱮᱛᱦᱚᱵᱚᱜ ᱠᱟᱱᱟ +pdfjs-spread-odd-button-label = ᱚᱰ ᱯᱟᱥᱱᱟᱣ +pdfjs-spread-even-button = + .title = ᱥᱟᱦᱴᱟ ᱯᱟᱥᱱᱟᱣ ᱡᱚᱲᱟᱣ ᱢᱮ ᱡᱟᱦᱟᱸ ᱫᱚ ᱤᱣᱮᱱᱼᱮᱞ ᱥᱟᱦᱴᱟᱠᱚ ᱥᱟᱞᱟᱜ ᱮᱛᱦᱚᱵᱚᱜ ᱠᱟᱱᱟ +pdfjs-spread-even-button-label = ᱯᱟᱥᱱᱟᱣ ᱤᱣᱮᱱ + +## Document properties dialog + +pdfjs-document-properties-button = + .title = ᱫᱚᱞᱤᱞ ᱜᱩᱱᱠᱚ … +pdfjs-document-properties-button-label = ᱫᱚᱞᱤᱞ ᱜᱩᱱᱠᱚ … +pdfjs-document-properties-file-name = ᱨᱮᱫᱽ ᱧᱩᱛᱩᱢ : +pdfjs-document-properties-file-size = ᱨᱮᱫᱽ ᱢᱟᱯ : +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } ᱵᱟᱭᱤᱴ ᱠᱚ) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } ᱵᱟᱭᱤᱴ ᱠᱚ) +pdfjs-document-properties-title = ᱧᱩᱛᱩᱢ : +pdfjs-document-properties-author = ᱚᱱᱚᱞᱤᱭᱟᱹ : +pdfjs-document-properties-subject = ᱵᱤᱥᱚᱭ : +pdfjs-document-properties-keywords = ᱠᱟᱹᱴᱷᱤ ᱥᱟᱵᱟᱫᱽ : +pdfjs-document-properties-creation-date = ᱛᱮᱭᱟᱨ ᱢᱟᱸᱦᱤᱛ : +pdfjs-document-properties-modification-date = ᱵᱚᱫᱚᱞ ᱦᱚᱪᱚ ᱢᱟᱹᱦᱤᱛ : +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = ᱵᱮᱱᱟᱣᱤᱡ : +pdfjs-document-properties-producer = PDF ᱛᱮᱭᱟᱨ ᱚᱰᱚᱠᱤᱡ : +pdfjs-document-properties-version = PDF ᱵᱷᱟᱹᱨᱥᱚᱱ : +pdfjs-document-properties-page-count = ᱥᱟᱦᱴᱟ ᱞᱮᱠᱷᱟ : +pdfjs-document-properties-page-size = ᱥᱟᱦᱴᱟ ᱢᱟᱯ : +pdfjs-document-properties-page-size-unit-inches = ᱤᱧᱪ +pdfjs-document-properties-page-size-unit-millimeters = ᱢᱤᱢᱤ +pdfjs-document-properties-page-size-orientation-portrait = ᱯᱚᱴᱨᱮᱴ +pdfjs-document-properties-page-size-orientation-landscape = ᱞᱮᱱᱰᱥᱠᱮᱯ +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = ᱪᱤᱴᱷᱤ +pdfjs-document-properties-page-size-name-legal = ᱠᱟᱹᱱᱩᱱᱤ + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = ᱞᱚᱜᱚᱱ ᱣᱮᱵᱽ ᱧᱮᱞ : +pdfjs-document-properties-linearized-yes = ᱦᱚᱭ +pdfjs-document-properties-linearized-no = ᱵᱟᱝ +pdfjs-document-properties-close-button = ᱵᱚᱸᱫᱚᱭ ᱢᱮ + +## Print + +pdfjs-print-progress-message = ᱪᱷᱟᱯᱟ ᱞᱟᱹᱜᱤᱫ ᱫᱚᱞᱤᱞ ᱛᱮᱭᱟᱨᱚᱜ ᱠᱟᱱᱟ … +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = ᱵᱟᱹᱰᱨᱟᱹ +pdfjs-printing-not-supported = ᱦᱚᱥᱤᱭᱟᱨ : ᱪᱷᱟᱯᱟ ᱱᱚᱣᱟ ᱯᱟᱱᱛᱮᱭᱟᱜ ᱫᱟᱨᱟᱭ ᱛᱮ ᱯᱩᱨᱟᱹᱣ ᱵᱟᱭ ᱜᱚᱲᱚᱣᱟᱠᱟᱱᱟ ᱾ +pdfjs-printing-not-ready = ᱦᱩᱥᱤᱭᱟᱹᱨ : ᱪᱷᱟᱯᱟ ᱞᱟᱹᱜᱤᱫ PDF ᱯᱩᱨᱟᱹ ᱵᱟᱭ ᱞᱟᱫᱮ ᱟᱠᱟᱱᱟ ᱾ + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = ᱫᱷᱟᱨᱮᱵᱟᱨ ᱥᱮᱫ ᱩᱪᱟᱹᱲᱚᱜ ᱢᱮ +pdfjs-toggle-sidebar-notification-button = + .title = ᱫᱷᱟᱨᱮᱵᱟᱨ ᱥᱮᱫ ᱩᱪᱟᱹᱲᱚᱜ ᱢᱮ (ᱫᱚᱞᱤᱞ ᱨᱮ ᱟᱣᱴᱞᱟᱭᱤᱢ ᱢᱮᱱᱟᱜᱼᱟ/ᱞᱟᱪᱷᱟᱠᱚ/ᱯᱚᱨᱚᱛᱠᱚ) +pdfjs-toggle-sidebar-button-label = ᱫᱷᱟᱨᱮᱵᱟᱨ ᱥᱮᱫ ᱩᱪᱟᱹᱲᱚᱜ ᱢᱮ +pdfjs-document-outline-button = + .title = ᱫᱚᱞᱚᱞ ᱟᱣᱴᱞᱟᱭᱤᱱ ᱫᱮᱠᱷᱟᱣ ᱢᱮ (ᱡᱷᱚᱛᱚ ᱡᱤᱱᱤᱥᱠᱚ ᱵᱟᱨ ᱡᱮᱠᱷᱟ ᱚᱛᱟ ᱠᱮᱛᱮ ᱡᱷᱟᱹᱞ/ᱦᱩᱰᱤᱧ ᱪᱷᱚᱭ ᱢᱮ) +pdfjs-document-outline-button-label = ᱫᱚᱞᱤᱞ ᱛᱮᱭᱟᱨ ᱛᱮᱫ +pdfjs-attachments-button = + .title = ᱞᱟᱴᱷᱟ ᱥᱮᱞᱮᱫ ᱠᱚ ᱩᱫᱩᱜᱽ ᱢᱮ +pdfjs-attachments-button-label = ᱞᱟᱴᱷᱟ ᱥᱮᱞᱮᱫ ᱠᱚ +pdfjs-layers-button = + .title = ᱯᱚᱨᱚᱛ ᱫᱮᱠᱷᱟᱣ ᱢᱮ (ᱢᱩᱞ ᱡᱟᱭᱜᱟ ᱛᱮ ᱡᱷᱚᱛᱚ ᱯᱚᱨᱚᱛᱠᱚ ᱨᱤᱥᱮᱴ ᱞᱟᱹᱜᱤᱫ ᱵᱟᱨ ᱡᱮᱠᱷᱟ ᱚᱛᱚᱭ ᱢᱮ) +pdfjs-layers-button-label = ᱯᱚᱨᱚᱛᱠᱚ +pdfjs-thumbs-button = + .title = ᱪᱤᱛᱟᱹᱨ ᱟᱦᱞᱟ ᱠᱚ ᱩᱫᱩᱜᱽ ᱢᱮ +pdfjs-thumbs-button-label = ᱪᱤᱛᱟᱹᱨ ᱟᱦᱞᱟ ᱠᱚ +pdfjs-current-outline-item-button = + .title = ᱱᱤᱛᱚᱜᱟᱜ ᱟᱣᱴᱞᱟᱭᱤᱱ ᱡᱟᱱᱤᱥ ᱯᱟᱱᱛᱮ ᱢᱮ +pdfjs-current-outline-item-button-label = ᱱᱤᱛᱚᱜᱟᱜ ᱟᱣᱴᱞᱟᱭᱤᱱ ᱡᱟᱱᱤᱥ +pdfjs-findbar-button = + .title = ᱫᱚᱞᱤᱞ ᱨᱮ ᱯᱟᱱᱛᱮ +pdfjs-findbar-button-label = ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ +pdfjs-additional-layers = ᱵᱟᱹᱲᱛᱤ ᱯᱚᱨᱚᱛᱠᱚ + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = { $page } ᱥᱟᱦᱴᱟ +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page } ᱥᱟᱦᱴᱟ ᱨᱮᱭᱟᱜ ᱪᱤᱛᱟᱹᱨ ᱟᱦᱞᱟ + +## Find panel button title and messages + +pdfjs-find-input = + .title = ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ + .placeholder = ᱫᱚᱞᱤᱞ ᱨᱮ ᱯᱟᱱᱛᱮ ᱢᱮ … +pdfjs-find-previous-button = + .title = ᱟᱭᱟᱛ ᱦᱤᱸᱥ ᱨᱮᱭᱟᱜ ᱯᱟᱹᱦᱤᱞ ᱥᱮᱫᱟᱜ ᱚᱰᱚᱠ ᱧᱟᱢ ᱢᱮ +pdfjs-find-previous-button-label = ᱢᱟᱲᱟᱝᱟᱜ +pdfjs-find-next-button = + .title = ᱟᱭᱟᱛ ᱦᱤᱸᱥ ᱨᱮᱭᱟᱜ ᱤᱱᱟᱹ ᱛᱟᱭᱚᱢ ᱚᱰᱚᱠ ᱧᱟᱢ ᱢᱮ +pdfjs-find-next-button-label = ᱤᱱᱟᱹ ᱛᱟᱭᱚᱢ +pdfjs-find-highlight-checkbox = ᱡᱷᱚᱛᱚ ᱩᱫᱩᱜ ᱨᱟᱠᱟᱵ +pdfjs-find-match-case-checkbox-label = ᱡᱚᱲ ᱠᱟᱛᱷᱟ +pdfjs-find-match-diacritics-checkbox-label = ᱵᱤᱥᱮᱥᱚᱠ ᱠᱚ ᱢᱮᱲᱟᱣ ᱢᱮ +pdfjs-find-entire-word-checkbox-label = ᱡᱷᱚᱛᱚ ᱟᱹᱲᱟᱹᱠᱚ +pdfjs-find-reached-top = ᱫᱚᱞᱤᱞ ᱨᱮᱭᱟᱜ ᱪᱤᱴ ᱨᱮ ᱥᱮᱴᱮᱨ, ᱞᱟᱛᱟᱨ ᱠᱷᱚᱱ ᱞᱮᱛᱟᱲ +pdfjs-find-reached-bottom = ᱫᱚᱞᱤᱞ ᱨᱮᱭᱟᱜ ᱢᱩᱪᱟᱹᱫ ᱨᱮ ᱥᱮᱴᱮᱨ, ᱪᱚᱴ ᱠᱷᱚᱱ ᱞᱮᱛᱟᱲ +pdfjs-find-not-found = ᱛᱚᱯᱚᱞ ᱫᱚᱱᱚᱲ ᱵᱟᱝ ᱧᱟᱢ ᱞᱮᱱᱟ + +## Predefined zoom values + +pdfjs-page-scale-width = ᱥᱟᱦᱴᱟ ᱚᱥᱟᱨ +pdfjs-page-scale-fit = ᱥᱟᱦᱴᱟ ᱠᱷᱟᱯ +pdfjs-page-scale-auto = ᱟᱡᱼᱟᱡ ᱛᱮ ᱦᱩᱰᱤᱧ ᱞᱟᱹᱴᱩ ᱛᱮᱭᱟᱨ +pdfjs-page-scale-actual = ᱴᱷᱤᱠ ᱢᱟᱨᱟᱝ ᱛᱮᱫ +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = { $page } ᱥᱟᱦᱴᱟ + +## Loading indicator messages + +pdfjs-loading-error = PDF ᱞᱟᱫᱮ ᱡᱚᱦᱚᱜ ᱢᱤᱫ ᱵᱷᱩᱞ ᱦᱩᱭ ᱮᱱᱟ ᱾ +pdfjs-invalid-file-error = ᱵᱟᱝ ᱵᱟᱛᱟᱣ ᱟᱨᱵᱟᱝᱠᱷᱟᱱ ᱰᱤᱜᱟᱹᱣ PDF ᱨᱮᱫᱽ ᱾ +pdfjs-missing-file-error = ᱟᱫᱟᱜ PDF ᱨᱮᱫᱽ ᱾ +pdfjs-unexpected-response-error = ᱵᱟᱝᱵᱩᱡᱷ ᱥᱚᱨᱵᱷᱚᱨ ᱛᱮᱞᱟ ᱾ +pdfjs-rendering-error = ᱥᱟᱦᱴᱟ ᱮᱢ ᱡᱚᱦᱚᱠ ᱢᱤᱫ ᱵᱷᱩᱞ ᱦᱩᱭ ᱮᱱᱟ ᱾ + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } ᱢᱚᱱᱛᱚ ᱮᱢ] + +## Password + +pdfjs-password-label = ᱱᱚᱶᱟ PDF ᱨᱮᱫᱽ ᱡᱷᱤᱡᱽ ᱞᱟᱹᱜᱤᱫ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱟᱫᱮᱨ ᱢᱮ ᱾ +pdfjs-password-invalid = ᱵᱷᱩᱞ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱾ ᱫᱟᱭᱟᱠᱟᱛᱮ ᱫᱩᱦᱲᱟᱹ ᱪᱮᱥᱴᱟᱭ ᱢᱮ ᱾ +pdfjs-password-ok-button = ᱴᱷᱤᱠ +pdfjs-password-cancel-button = ᱵᱟᱹᱰᱨᱟᱹ +pdfjs-web-fonts-disabled = ᱣᱮᱵᱽ ᱪᱤᱠᱤ ᱵᱟᱝ ᱦᱩᱭ ᱦᱚᱪᱚ ᱠᱟᱱᱟ : ᱵᱷᱤᱛᱤᱨ ᱛᱷᱟᱯᱚᱱ PDF ᱪᱤᱠᱤ ᱵᱮᱵᱷᱟᱨ ᱵᱟᱝ ᱦᱩᱭ ᱠᱮᱭᱟ ᱾ + +## Editing + +pdfjs-editor-free-text-button = + .title = ᱚᱞ +pdfjs-editor-free-text-button-label = ᱚᱞ +pdfjs-editor-ink-button = + .title = ᱛᱮᱭᱟᱨ +pdfjs-editor-ink-button-label = ᱛᱮᱭᱟᱨ +pdfjs-editor-stamp-button = + .title = ᱪᱤᱛᱟᱹᱨᱠᱚ ᱥᱮᱞᱮᱫ ᱥᱮ ᱥᱟᱯᱲᱟᱣ ᱢᱮ +pdfjs-editor-stamp-button-label = ᱪᱤᱛᱟᱹᱨᱠᱚ ᱥᱮᱞᱮᱫ ᱥᱮ ᱥᱟᱯᱲᱟᱣ ᱢᱮ + +## Remove button for the various kind of editor. + + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = ᱨᱚᱝ +pdfjs-editor-free-text-size-input = ᱢᱟᱯ +pdfjs-editor-ink-color-input = ᱨᱚᱝ +pdfjs-editor-ink-thickness-input = ᱢᱚᱴᱟ +pdfjs-editor-ink-opacity-input = ᱟᱨᱯᱟᱨ +pdfjs-editor-stamp-add-image-button = + .title = ᱪᱤᱛᱟᱹᱨ ᱥᱮᱞᱮᱫ ᱢᱮ +pdfjs-editor-stamp-add-image-button-label = ᱪᱤᱛᱟᱹᱨ ᱥᱮᱞᱮᱫ ᱢᱮ +pdfjs-free-text = + .aria-label = ᱚᱞ ᱥᱟᱯᱲᱟᱣᱤᱭᱟᱹ +pdfjs-free-text-default-content = ᱚᱞ ᱮᱛᱦᱚᱵ ᱢᱮ … +pdfjs-ink = + .aria-label = ᱛᱮᱭᱟᱨ ᱥᱟᱯᱲᱟᱣᱤᱭᱟᱹ +pdfjs-ink-canvas = + .aria-label = ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱛᱮᱭᱟᱨ ᱠᱟᱫ ᱪᱤᱛᱟᱹᱨ + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + diff --git a/public/pdfjs/web/locale/sc/viewer.ftl b/public/pdfjs/web/locale/sc/viewer.ftl new file mode 100644 index 0000000..1137c2b --- /dev/null +++ b/public/pdfjs/web/locale/sc/viewer.ftl @@ -0,0 +1,367 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pàgina anteriore +pdfjs-previous-button-label = S'ischeda chi b'est primu +pdfjs-next-button = + .title = Pàgina imbeniente +pdfjs-next-button-label = Imbeniente +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pàgina +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = de { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount }) +pdfjs-zoom-out-button = + .title = Impitica +pdfjs-zoom-out-button-label = Impitica +pdfjs-zoom-in-button = + .title = Ismànnia +pdfjs-zoom-in-button-label = Ismànnia +pdfjs-zoom-select = + .title = Ismànnia +pdfjs-presentation-mode-button = + .title = Cola a sa modalidade de presentatzione +pdfjs-presentation-mode-button-label = Modalidade de presentatzione +pdfjs-open-file-button = + .title = Aberi s'archìviu +pdfjs-open-file-button-label = Abertu +pdfjs-print-button = + .title = Imprenta +pdfjs-print-button-label = Imprenta +pdfjs-save-button = + .title = Sarva +pdfjs-save-button-label = Sarva +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Iscàrriga +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Iscàrriga +pdfjs-bookmark-button = + .title = Pàgina atuale (ammustra s’URL de sa pàgina atuale) +pdfjs-bookmark-button-label = Pàgina atuale + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Istrumentos +pdfjs-tools-button-label = Istrumentos +pdfjs-first-page-button = + .title = Bae a sa prima pàgina +pdfjs-first-page-button-label = Bae a sa prima pàgina +pdfjs-last-page-button = + .title = Bae a s'ùrtima pàgina +pdfjs-last-page-button-label = Bae a s'ùrtima pàgina +pdfjs-page-rotate-cw-button = + .title = Gira in sensu oràriu +pdfjs-page-rotate-cw-button-label = Gira in sensu oràriu +pdfjs-page-rotate-ccw-button = + .title = Gira in sensu anti-oràriu +pdfjs-page-rotate-ccw-button-label = Gira in sensu anti-oràriu +pdfjs-cursor-text-select-tool-button = + .title = Ativa s'aina de seletzione de testu +pdfjs-cursor-text-select-tool-button-label = Aina de seletzione de testu +pdfjs-cursor-hand-tool-button = + .title = Ativa s'aina de manu +pdfjs-cursor-hand-tool-button-label = Aina de manu +pdfjs-scroll-page-button = + .title = Imprea s'iscurrimentu de pàgina +pdfjs-scroll-page-button-label = Iscurrimentu de pàgina +pdfjs-scroll-vertical-button = + .title = Imprea s'iscurrimentu verticale +pdfjs-scroll-vertical-button-label = Iscurrimentu verticale +pdfjs-scroll-horizontal-button = + .title = Imprea s'iscurrimentu orizontale +pdfjs-scroll-horizontal-button-label = Iscurrimentu orizontale +pdfjs-scroll-wrapped-button = + .title = Imprea s'iscurrimentu continu +pdfjs-scroll-wrapped-button-label = Iscurrimentu continu + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Propiedades de su documentu… +pdfjs-document-properties-button-label = Propiedades de su documentu… +pdfjs-document-properties-file-name = Nòmine de s'archìviu: +pdfjs-document-properties-file-size = Mannària de s'archìviu: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Tìtulu: +pdfjs-document-properties-author = Autoria: +pdfjs-document-properties-subject = Ogetu: +pdfjs-document-properties-keywords = Faeddos crae: +pdfjs-document-properties-creation-date = Data de creatzione: +pdfjs-document-properties-modification-date = Data de modìfica: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Creatzione: +pdfjs-document-properties-producer = Produtore de PDF: +pdfjs-document-properties-version = Versione de PDF: +pdfjs-document-properties-page-count = Contu de pàginas: +pdfjs-document-properties-page-size = Mannària de sa pàgina: +pdfjs-document-properties-page-size-unit-inches = pòddighes +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = verticale +pdfjs-document-properties-page-size-orientation-landscape = orizontale +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Lìtera +pdfjs-document-properties-page-size-name-legal = Legale + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Visualizatzione web lestra: +pdfjs-document-properties-linearized-yes = Eja +pdfjs-document-properties-linearized-no = Nono +pdfjs-document-properties-close-button = Serra + +## Print + +pdfjs-print-progress-message = Aparitzende s'imprenta de su documentu… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Cantzella +pdfjs-printing-not-supported = Atentzione: s'imprenta no est funtzionende de su totu in custu navigadore. +pdfjs-printing-not-ready = Atentzione: su PDF no est istadu carrigadu de su totu pro s'imprenta. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Ativa/disativa sa barra laterale +pdfjs-toggle-sidebar-notification-button = + .title = Ativa/disativa sa barra laterale (su documentu cuntenet un'ischema, alligongiados o livellos) +pdfjs-toggle-sidebar-button-label = Ativa/disativa sa barra laterale +pdfjs-document-outline-button-label = Ischema de su documentu +pdfjs-attachments-button = + .title = Ammustra alligongiados +pdfjs-attachments-button-label = Alliongiados +pdfjs-layers-button = + .title = Ammustra livellos (clic dòpiu pro ripristinare totu is livellos a s'istadu predefinidu) +pdfjs-layers-button-label = Livellos +pdfjs-thumbs-button = + .title = Ammustra miniaturas +pdfjs-thumbs-button-label = Miniaturas +pdfjs-current-outline-item-button = + .title = Agata s'elementu atuale de s'ischema +pdfjs-current-outline-item-button-label = Elementu atuale de s'ischema +pdfjs-findbar-button = + .title = Agata in su documentu +pdfjs-findbar-button-label = Agata +pdfjs-additional-layers = Livellos additzionales + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pàgina { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura de sa pàgina { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Agata + .placeholder = Agata in su documentu… +pdfjs-find-previous-button = + .title = Agata s'ocurrèntzia pretzedente de sa fràsia +pdfjs-find-previous-button-label = S'ischeda chi b'est primu +pdfjs-find-next-button = + .title = Agata s'ocurrèntzia imbeniente de sa fràsia +pdfjs-find-next-button-label = Imbeniente +pdfjs-find-highlight-checkbox = Evidèntzia totu +pdfjs-find-match-case-checkbox-label = Distinghe intre majùsculas e minùsculas +pdfjs-find-match-diacritics-checkbox-label = Respeta is diacrìticos +pdfjs-find-entire-word-checkbox-label = Faeddos intreos +pdfjs-find-reached-top = S'est lòmpidu a su cumintzu de su documentu, si sighit dae su bàsciu +pdfjs-find-reached-bottom = Acabbu de su documentu, si sighit dae s'artu +pdfjs-find-not-found = Testu no agatadu + +## Predefined zoom values + +pdfjs-page-scale-auto = Ingrandimentu automàticu +pdfjs-page-scale-actual = Mannària reale +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Pàgina { $page } + +## Loading indicator messages + +pdfjs-loading-error = Faddina in sa càrriga de su PDF. +pdfjs-invalid-file-error = Archìviu PDF non vàlidu o corrùmpidu. +pdfjs-missing-file-error = Ammancat s'archìviu PDF. +pdfjs-unexpected-response-error = Risposta imprevista de su serbidore. +pdfjs-rendering-error = Faddina in sa visualizatzione de sa pàgina. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } + +## Password + +pdfjs-password-label = Inserta sa crae pro abèrrere custu archìviu PDF. +pdfjs-password-invalid = Sa crae no est curreta. Torra a nche proare. +pdfjs-password-ok-button = Andat bene +pdfjs-password-cancel-button = Cantzella +pdfjs-web-fonts-disabled = Is tipografias web sunt disativadas: is tipografias incrustadas a su PDF non podent èssere impreadas. + +## Editing + +pdfjs-editor-free-text-button = + .title = Testu +pdfjs-editor-free-text-button-label = Testu +pdfjs-editor-ink-button = + .title = Disinnu +pdfjs-editor-ink-button-label = Disinnu +pdfjs-editor-stamp-button = + .title = Agiunghe o modìfica immàgines +pdfjs-editor-stamp-button-label = Agiunghe o modìfica immàgines +pdfjs-editor-highlight-button = + .title = Evidèntzia +pdfjs-editor-highlight-button-label = Evidèntzia +pdfjs-highlight-floating-button1 = + .title = Evidèntzia + .aria-label = Evidèntzia +pdfjs-highlight-floating-button-label = Evidèntzia + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Boga su disinnu +pdfjs-editor-remove-freetext-button = + .title = Boga su testu +pdfjs-editor-remove-stamp-button = + .title = Boga s’immàgine +pdfjs-editor-remove-highlight-button = + .title = Boga s’evidèntzia + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Colore +pdfjs-editor-free-text-size-input = Mannària +pdfjs-editor-ink-color-input = Colore +pdfjs-editor-ink-thickness-input = Grussària +pdfjs-editor-stamp-add-image-button = + .title = Agiunghe un’immàgine +pdfjs-editor-stamp-add-image-button-label = Agiunghe un’immàgine +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Grussària +pdfjs-free-text = + .aria-label = Editore de testu +pdfjs-free-text-default-content = Cumintza a iscrìere… +pdfjs-ink = + .aria-label = Editore de disinnos +pdfjs-ink-canvas = + .aria-label = Immàgine creada dae s’utente + +## Alt-text dialog + +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button-label = Testu alternativu +pdfjs-editor-alt-text-edit-button-label = Modifica su testu alternativu +pdfjs-editor-alt-text-dialog-label = Sèbera un’optzione +pdfjs-editor-alt-text-dialog-description = Su testu alternativu (“alt text”) est ùtile pro persones chi non podent bìdere s’immàgine o cando non benit carrigada. +pdfjs-editor-alt-text-add-description-label = Agiunghe una descritzione +pdfjs-editor-alt-text-cancel-button = Annulla +pdfjs-editor-alt-text-save-button = Sarva + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + + +## Color picker + +pdfjs-editor-colorpicker-button = + .title = Modifica su colore +pdfjs-editor-colorpicker-dropdown = + .aria-label = Colores a disponimentu +pdfjs-editor-colorpicker-yellow = + .title = Grogu +pdfjs-editor-colorpicker-green = + .title = Birde +pdfjs-editor-colorpicker-blue = + .title = Biaitu +pdfjs-editor-colorpicker-pink = + .title = Rosa + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button-label = Mancat su testu alternativu +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button-label = Revisiona su testu alternativu +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Creadu in automàticu: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Cunfiguratzione de su testu alternativu de is immàgines +pdfjs-image-alt-text-settings-button-label = Cunfiguratzione de su testu alternativu de is immàgines +pdfjs-editor-alt-text-settings-dialog-label = Cunfiguratzione de su testu alternativu de is immàgines +pdfjs-editor-alt-text-settings-automatic-title = Testu alternativu automàticu +pdfjs-editor-alt-text-settings-create-model-button-label = Crea testu alternativu in automàticu +pdfjs-editor-alt-text-settings-create-model-description = Cussìgiat descritziones pro agiudare a gente chi non podet bìdere s’immàgine o cando non benit carrigada. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Modellu de IA pro su testu alternativu ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Est esecutadu in locale in manera chi is datos tuos abarrent in privadu. Rechestu pro sa generatzione automàtica de testu alternativu. +pdfjs-editor-alt-text-settings-delete-model-button = Cantzella +pdfjs-editor-alt-text-settings-download-model-button = Iscàrriga +pdfjs-editor-alt-text-settings-downloading-model-button = Iscarrighende… +pdfjs-editor-alt-text-settings-editor-title = Editore de testu alternativu +pdfjs-editor-alt-text-settings-show-dialog-button-label = Mustra deretu s’editore de testu alternativu cando siat agiunta un’immàgine +pdfjs-editor-alt-text-settings-show-dialog-description = T’agiudat a assegurare chi totu is immàgines tuas tèngiant unu testu alternativu. +pdfjs-editor-alt-text-settings-close-button = Serra diff --git a/public/pdfjs/web/locale/scn/viewer.ftl b/public/pdfjs/web/locale/scn/viewer.ftl new file mode 100644 index 0000000..a3c7c03 --- /dev/null +++ b/public/pdfjs/web/locale/scn/viewer.ftl @@ -0,0 +1,74 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-zoom-out-button = + .title = Cchiù nicu +pdfjs-zoom-out-button-label = Cchiù nicu +pdfjs-zoom-in-button = + .title = Cchiù granni +pdfjs-zoom-in-button-label = Cchiù granni + +## Secondary toolbar and context menu + + +## Document properties dialog + + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Vista web lesta: +pdfjs-document-properties-linearized-yes = Se + +## Print + +pdfjs-print-progress-close-button = Sfai + +## Tooltips and alt text for side panel toolbar buttons + + +## Thumbnails panel item (tooltip and alt text for images) + + +## Find panel button title and messages + + +## Predefined zoom values + +pdfjs-page-scale-width = Larghizza dâ pàggina + +## PDF page + + +## Loading indicator messages + + +## Annotations + + +## Password + +pdfjs-password-cancel-button = Sfai + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/sco/viewer.ftl b/public/pdfjs/web/locale/sco/viewer.ftl new file mode 100644 index 0000000..6f71c47 --- /dev/null +++ b/public/pdfjs/web/locale/sco/viewer.ftl @@ -0,0 +1,264 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Page Afore +pdfjs-previous-button-label = Previous +pdfjs-next-button = + .title = Page Efter +pdfjs-next-button-label = Neist +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Page +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = o { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } o { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zoom Oot +pdfjs-zoom-out-button-label = Zoom Oot +pdfjs-zoom-in-button = + .title = Zoom In +pdfjs-zoom-in-button-label = Zoom In +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Flit tae Presentation Mode +pdfjs-presentation-mode-button-label = Presentation Mode +pdfjs-open-file-button = + .title = Open File +pdfjs-open-file-button-label = Open +pdfjs-print-button = + .title = Prent +pdfjs-print-button-label = Prent + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Tools +pdfjs-tools-button-label = Tools +pdfjs-first-page-button = + .title = Gang tae First Page +pdfjs-first-page-button-label = Gang tae First Page +pdfjs-last-page-button = + .title = Gang tae Lest Page +pdfjs-last-page-button-label = Gang tae Lest Page +pdfjs-page-rotate-cw-button = + .title = Rotate Clockwise +pdfjs-page-rotate-cw-button-label = Rotate Clockwise +pdfjs-page-rotate-ccw-button = + .title = Rotate Coonterclockwise +pdfjs-page-rotate-ccw-button-label = Rotate Coonterclockwise +pdfjs-cursor-text-select-tool-button = + .title = Enable Text Walin Tool +pdfjs-cursor-text-select-tool-button-label = Text Walin Tool +pdfjs-cursor-hand-tool-button = + .title = Enable Haun Tool +pdfjs-cursor-hand-tool-button-label = Haun Tool +pdfjs-scroll-vertical-button = + .title = Yaise Vertical Scrollin +pdfjs-scroll-vertical-button-label = Vertical Scrollin +pdfjs-scroll-horizontal-button = + .title = Yaise Horizontal Scrollin +pdfjs-scroll-horizontal-button-label = Horizontal Scrollin +pdfjs-scroll-wrapped-button = + .title = Yaise Wrapped Scrollin +pdfjs-scroll-wrapped-button-label = Wrapped Scrollin +pdfjs-spread-none-button = + .title = Dinnae jyn page spreids +pdfjs-spread-none-button-label = Nae Spreids +pdfjs-spread-odd-button = + .title = Jyn page spreids stertin wi odd-numbered pages +pdfjs-spread-odd-button-label = Odd Spreids +pdfjs-spread-even-button = + .title = Jyn page spreids stertin wi even-numbered pages +pdfjs-spread-even-button-label = Even Spreids + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Document Properties… +pdfjs-document-properties-button-label = Document Properties… +pdfjs-document-properties-file-name = File nemme: +pdfjs-document-properties-file-size = File size: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Title: +pdfjs-document-properties-author = Author: +pdfjs-document-properties-subject = Subjeck: +pdfjs-document-properties-keywords = Keywirds: +pdfjs-document-properties-creation-date = Date o Makkin: +pdfjs-document-properties-modification-date = Date o Chynges: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Makker: +pdfjs-document-properties-producer = PDF Producer: +pdfjs-document-properties-version = PDF Version: +pdfjs-document-properties-page-count = Page Coont: +pdfjs-document-properties-page-size = Page Size: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = portrait +pdfjs-document-properties-page-size-orientation-landscape = landscape +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Fast Wab View: +pdfjs-document-properties-linearized-yes = Aye +pdfjs-document-properties-linearized-no = Naw +pdfjs-document-properties-close-button = Sneck + +## Print + +pdfjs-print-progress-message = Reddin document fur prentin… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Stap +pdfjs-printing-not-supported = Tak tent: Prentin isnae richt supportit by this stravaiger. +pdfjs-printing-not-ready = Tak tent: The PDF isnae richt loadit fur prentin. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Toggle Sidebaur +pdfjs-toggle-sidebar-notification-button = + .title = Toggle Sidebaur (document conteens ootline/attachments/layers) +pdfjs-toggle-sidebar-button-label = Toggle Sidebaur +pdfjs-document-outline-button = + .title = Kythe Document Ootline (double-click fur tae oot-fauld/in-fauld aw items) +pdfjs-document-outline-button-label = Document Ootline +pdfjs-attachments-button = + .title = Kythe Attachments +pdfjs-attachments-button-label = Attachments +pdfjs-layers-button = + .title = Kythe Layers (double-click fur tae reset aw layers tae the staunart state) +pdfjs-layers-button-label = Layers +pdfjs-thumbs-button = + .title = Kythe Thumbnails +pdfjs-thumbs-button-label = Thumbnails +pdfjs-current-outline-item-button = + .title = Find Current Ootline Item +pdfjs-current-outline-item-button-label = Current Ootline Item +pdfjs-findbar-button = + .title = Find in Document +pdfjs-findbar-button-label = Find +pdfjs-additional-layers = Mair Layers + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Page { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Thumbnail o Page { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Find + .placeholder = Find in document… +pdfjs-find-previous-button = + .title = Airt oot the last time this phrase occurred +pdfjs-find-previous-button-label = Previous +pdfjs-find-next-button = + .title = Airt oot the neist time this phrase occurs +pdfjs-find-next-button-label = Neist +pdfjs-find-highlight-checkbox = Highlicht aw +pdfjs-find-match-case-checkbox-label = Match case +pdfjs-find-entire-word-checkbox-label = Hale Wirds +pdfjs-find-reached-top = Raxed tap o document, went on fae the dowp end +pdfjs-find-reached-bottom = Raxed end o document, went on fae the tap +pdfjs-find-not-found = Phrase no fund + +## Predefined zoom values + +pdfjs-page-scale-width = Page Width +pdfjs-page-scale-fit = Page Fit +pdfjs-page-scale-auto = Automatic Zoom +pdfjs-page-scale-actual = Actual Size +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Page { $page } + +## Loading indicator messages + +pdfjs-loading-error = An mishanter tuik place while loadin the PDF. +pdfjs-invalid-file-error = No suithfest or camshauchlet PDF file. +pdfjs-missing-file-error = PDF file tint. +pdfjs-unexpected-response-error = Unexpectit server repone. +pdfjs-rendering-error = A mishanter tuik place while renderin the page. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] + +## Password + +pdfjs-password-label = Inpit the passwird fur tae open this PDF file. +pdfjs-password-invalid = Passwird no suithfest. Gonnae gie it anither shot. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Stap +pdfjs-web-fonts-disabled = Wab fonts are disabled: cannae yaise embeddit PDF fonts. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/si/viewer.ftl b/public/pdfjs/web/locale/si/viewer.ftl new file mode 100644 index 0000000..0481116 --- /dev/null +++ b/public/pdfjs/web/locale/si/viewer.ftl @@ -0,0 +1,271 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = කලින් පිටුව +pdfjs-previous-button-label = කලින් +pdfjs-next-button = + .title = ඊළඟ පිටුව +pdfjs-next-button-label = ඊළඟ +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = පිටුව +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount }) +pdfjs-zoom-out-button = + .title = කුඩාලනය +pdfjs-zoom-out-button-label = කුඩාලනය +pdfjs-zoom-in-button = + .title = විශාලනය +pdfjs-zoom-in-button-label = විශාලනය +pdfjs-zoom-select = + .title = විශාල කරන්න +pdfjs-presentation-mode-button = + .title = සමර්පණ ප්‍රකාරය වෙත මාරුවන්න +pdfjs-presentation-mode-button-label = සමර්පණ ප්‍රකාරය +pdfjs-open-file-button = + .title = ගොනුව අරින්න +pdfjs-open-file-button-label = අරින්න +pdfjs-print-button = + .title = මුද්‍රණය +pdfjs-print-button-label = මුද්‍රණය +pdfjs-save-button = + .title = සුරකින්න +pdfjs-save-button-label = සුරකින්න +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = බාගන්න +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = බාගන්න +pdfjs-bookmark-button-label = පවතින පිටුව + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = මෙවලම් +pdfjs-tools-button-label = මෙවලම් +pdfjs-first-page-button = + .title = මුල් පිටුවට යන්න +pdfjs-first-page-button-label = මුල් පිටුවට යන්න +pdfjs-last-page-button = + .title = අවසන් පිටුවට යන්න +pdfjs-last-page-button-label = අවසන් පිටුවට යන්න +pdfjs-cursor-text-select-tool-button = + .title = පෙළ තේරීමේ මෙවලම සබල කරන්න +pdfjs-cursor-text-select-tool-button-label = පෙළ තේරීමේ මෙවලම +pdfjs-cursor-hand-tool-button = + .title = අත් මෙවලම සබල කරන්න +pdfjs-cursor-hand-tool-button-label = අත් මෙවලම +pdfjs-scroll-page-button = + .title = පිටුව අනුචලනය භාවිතය +pdfjs-scroll-page-button-label = පිටුව අනුචලනය +pdfjs-scroll-vertical-button = + .title = සිරස් අනුචලනය භාවිතය +pdfjs-scroll-vertical-button-label = සිරස් අනුචලනය +pdfjs-scroll-horizontal-button = + .title = තිරස් අනුචලනය භාවිතය +pdfjs-scroll-horizontal-button-label = තිරස් අනුචලනය + +## Document properties dialog + +pdfjs-document-properties-button = + .title = ලේඛනයේ ගුණාංග… +pdfjs-document-properties-button-label = ලේඛනයේ ගුණාංග… +pdfjs-document-properties-file-name = ගොනුවේ නම: +pdfjs-document-properties-file-size = ගොනුවේ ප්‍රමාණය: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = කි.බ. { $size_kb } (බයිට { $size_b }) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = මෙ.බ. { $size_mb } (බයිට { $size_b }) +pdfjs-document-properties-title = සිරැසිය: +pdfjs-document-properties-author = කතෘ: +pdfjs-document-properties-subject = මාතෘකාව: +pdfjs-document-properties-keywords = මූල පද: +pdfjs-document-properties-creation-date = සෑදූ දිනය: +pdfjs-document-properties-modification-date = සංශෝධිත දිනය: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = නිර්මාතෘ: +pdfjs-document-properties-producer = පීඩීඑෆ් සම්පාදක: +pdfjs-document-properties-version = පීඩීඑෆ් අනුවාදය: +pdfjs-document-properties-page-count = පිටු ගණන: +pdfjs-document-properties-page-size = පිටුවේ තරම: +pdfjs-document-properties-page-size-unit-inches = අඟල් +pdfjs-document-properties-page-size-unit-millimeters = මි.මී. +pdfjs-document-properties-page-size-orientation-portrait = සිරස් +pdfjs-document-properties-page-size-orientation-landscape = තිරස් +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width }×{ $height }{ $unit }{ $name }{ $orientation } + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = වේගවත් වියමන දැක්ම: +pdfjs-document-properties-linearized-yes = ඔව් +pdfjs-document-properties-linearized-no = නැහැ +pdfjs-document-properties-close-button = වසන්න + +## Print + +pdfjs-print-progress-message = මුද්‍රණය සඳහා ලේඛනය සූදානම් වෙමින්… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = අවලංගු කරන්න +pdfjs-printing-not-supported = අවවාදයයි: මෙම අතිරික්සුව මුද්‍රණය සඳහා හොඳින් සහාය නොදක්වයි. +pdfjs-printing-not-ready = අවවාදයයි: මුද්‍රණයට පීඩීඑෆ් ගොනුව සම්පූර්ණයෙන් පූරණය වී නැත. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-document-outline-button-label = ලේඛනයේ වටසන +pdfjs-attachments-button = + .title = ඇමුණුම් පෙන්වන්න +pdfjs-attachments-button-label = ඇමුණුම් +pdfjs-layers-button = + .title = ස්තර පෙන්වන්න (සියළු ස්තර පෙරනිමි තත්‍වයට යළි සැකසීමට දෙවරක් ඔබන්න) +pdfjs-layers-button-label = ස්තර +pdfjs-thumbs-button = + .title = සිඟිති රූ පෙන්වන්න +pdfjs-thumbs-button-label = සිඟිති රූ +pdfjs-findbar-button = + .title = ලේඛනයෙහි සොයන්න +pdfjs-findbar-button-label = සොයන්න +pdfjs-additional-layers = අතිරේක ස්තර + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = පිටුව { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = පිටුවේ සිඟිත රූව { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = සොයන්න + .placeholder = ලේඛනයේ සොයන්න… +pdfjs-find-previous-button = + .title = මෙම වැකිකඩ කලින් යෙදුණු ස්ථානය සොයන්න +pdfjs-find-previous-button-label = කලින් +pdfjs-find-next-button = + .title = මෙම වැකිකඩ ඊළඟට යෙදෙන ස්ථානය සොයන්න +pdfjs-find-next-button-label = ඊළඟ +pdfjs-find-highlight-checkbox = සියල්ල උද්දීපනය +pdfjs-find-entire-word-checkbox-label = සමස්ත වචන +pdfjs-find-reached-top = ලේඛනයේ මුදුනට ළඟා විය, පහළ සිට ඉහළට +pdfjs-find-reached-bottom = ලේඛනයේ අවසානයට ළඟා විය, ඉහළ සිට පහළට +pdfjs-find-not-found = වැකිකඩ හමු නොවුණි + +## Predefined zoom values + +pdfjs-page-scale-width = පිටුවේ පළල +pdfjs-page-scale-auto = ස්වයංක්‍රීය විශාලනය +pdfjs-page-scale-actual = සැබෑ ප්‍රමාණය +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = පිටුව { $page } + +## Loading indicator messages + +pdfjs-loading-error = පීඩීඑෆ් පූරණය කිරීමේදී දෝෂයක් සිදු විය. +pdfjs-invalid-file-error = වලංගු නොවන හෝ හානිවූ පීඩීඑෆ් ගොනුවකි. +pdfjs-missing-file-error = මඟහැරුණු පීඩීඑෆ් ගොනුවකි. +pdfjs-unexpected-response-error = අනපේක්‍ෂිත සේවාදායක ප්‍රතිචාරයකි. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } + +## Password + +pdfjs-password-label = මෙම පීඩීඑෆ් ගොනුව විවෘත කිරීමට මුරපදය යොදන්න. +pdfjs-password-invalid = වැරදි මුරපදයකි. නැවත උත්සාහ කරන්න. +pdfjs-password-ok-button = හරි +pdfjs-password-cancel-button = අවලංගු +pdfjs-web-fonts-disabled = වියමන අකුරු අබලයි: පීඩීඑෆ් වෙත කාවැද්දූ රුවකුරු භාවිතා කළ නොහැකිය. + +## Editing + +pdfjs-editor-free-text-button = + .title = පෙළ +pdfjs-editor-free-text-button-label = පෙළ +pdfjs-editor-ink-button = + .title = අඳින්න +pdfjs-editor-ink-button-label = අඳින්න +pdfjs-editor-stamp-button = + .title = රූප සංස්කරණය හෝ එක් කරන්න +pdfjs-editor-stamp-button-label = රූප සංස්කරණය හෝ එක් කරන්න + +## Remove button for the various kind of editor. + + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = වර්ණය +pdfjs-editor-free-text-size-input = තරම +pdfjs-editor-ink-color-input = වර්ණය +pdfjs-editor-ink-thickness-input = ඝණකම +pdfjs-free-text = + .aria-label = වදන් සකසනය +pdfjs-free-text-default-content = ලිවීීම අරඹන්න… + +## Alt-text dialog + +pdfjs-editor-alt-text-mark-decorative-description = මෙය දාර හෝ දිය සලකුණු වැනි අලංකාර රූප සඳහා භාවිතා වේ. + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + diff --git a/public/pdfjs/web/locale/sk/viewer.ftl b/public/pdfjs/web/locale/sk/viewer.ftl new file mode 100644 index 0000000..5cbbb8d --- /dev/null +++ b/public/pdfjs/web/locale/sk/viewer.ftl @@ -0,0 +1,521 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Predchádzajúca strana +pdfjs-previous-button-label = Predchádzajúca +pdfjs-next-button = + .title = Nasledujúca strana +pdfjs-next-button-label = Nasledujúca +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Strana +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = z { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } z { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zmenšiť veľkosť +pdfjs-zoom-out-button-label = Zmenšiť veľkosť +pdfjs-zoom-in-button = + .title = Zväčšiť veľkosť +pdfjs-zoom-in-button-label = Zväčšiť veľkosť +pdfjs-zoom-select = + .title = Nastavenie veľkosti +pdfjs-presentation-mode-button = + .title = Prepnúť na režim prezentácie +pdfjs-presentation-mode-button-label = Režim prezentácie +pdfjs-open-file-button = + .title = Otvoriť súbor +pdfjs-open-file-button-label = Otvoriť +pdfjs-print-button = + .title = Tlačiť +pdfjs-print-button-label = Tlačiť +pdfjs-save-button = + .title = Uložiť +pdfjs-save-button-label = Uložiť +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Stiahnuť +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Stiahnuť +pdfjs-bookmark-button = + .title = Aktuálna stránka (zobraziť adresu URL z aktuálnej stránky) +pdfjs-bookmark-button-label = Aktuálna stránka + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Nástroje +pdfjs-tools-button-label = Nástroje +pdfjs-first-page-button = + .title = Prejsť na prvú stranu +pdfjs-first-page-button-label = Prejsť na prvú stranu +pdfjs-last-page-button = + .title = Prejsť na poslednú stranu +pdfjs-last-page-button-label = Prejsť na poslednú stranu +pdfjs-page-rotate-cw-button = + .title = Otočiť v smere hodinových ručičiek +pdfjs-page-rotate-cw-button-label = Otočiť v smere hodinových ručičiek +pdfjs-page-rotate-ccw-button = + .title = Otočiť proti smeru hodinových ručičiek +pdfjs-page-rotate-ccw-button-label = Otočiť proti smeru hodinových ručičiek +pdfjs-cursor-text-select-tool-button = + .title = Povoliť výber textu +pdfjs-cursor-text-select-tool-button-label = Výber textu +pdfjs-cursor-hand-tool-button = + .title = Povoliť nástroj ruka +pdfjs-cursor-hand-tool-button-label = Nástroj ruka +pdfjs-scroll-page-button = + .title = Použiť rolovanie po stránkach +pdfjs-scroll-page-button-label = Rolovanie po stránkach +pdfjs-scroll-vertical-button = + .title = Používať zvislé posúvanie +pdfjs-scroll-vertical-button-label = Zvislé posúvanie +pdfjs-scroll-horizontal-button = + .title = Používať vodorovné posúvanie +pdfjs-scroll-horizontal-button-label = Vodorovné posúvanie +pdfjs-scroll-wrapped-button = + .title = Použiť postupné posúvanie +pdfjs-scroll-wrapped-button-label = Postupné posúvanie +pdfjs-spread-none-button = + .title = Nezdružovať stránky +pdfjs-spread-none-button-label = Žiadne združovanie +pdfjs-spread-odd-button = + .title = Združí stránky a umiestni nepárne stránky vľavo +pdfjs-spread-odd-button-label = Združiť stránky (nepárne vľavo) +pdfjs-spread-even-button = + .title = Združí stránky a umiestni párne stránky vľavo +pdfjs-spread-even-button-label = Združiť stránky (párne vľavo) + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Vlastnosti dokumentu… +pdfjs-document-properties-button-label = Vlastnosti dokumentu… +pdfjs-document-properties-file-name = Názov súboru: +pdfjs-document-properties-file-size = Veľkosť súboru: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } kB ({ $b } bajtov) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bajtov) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } bajtov) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajtov) +pdfjs-document-properties-title = Názov: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Predmet: +pdfjs-document-properties-keywords = Kľúčové slová: +pdfjs-document-properties-creation-date = Dátum vytvorenia: +pdfjs-document-properties-modification-date = Dátum úpravy: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Aplikácia: +pdfjs-document-properties-producer = Tvorca PDF: +pdfjs-document-properties-version = Verzia PDF: +pdfjs-document-properties-page-count = Počet strán: +pdfjs-document-properties-page-size = Veľkosť stránky: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = na výšku +pdfjs-document-properties-page-size-orientation-landscape = na šírku +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = List +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Rýchle zobrazovanie z webu: +pdfjs-document-properties-linearized-yes = Áno +pdfjs-document-properties-linearized-no = Nie +pdfjs-document-properties-close-button = Zavrieť + +## Print + +pdfjs-print-progress-message = Príprava dokumentu na tlač… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress } % +pdfjs-print-progress-close-button = Zrušiť +pdfjs-printing-not-supported = Upozornenie: tlač nie je v tomto prehliadači plne podporovaná. +pdfjs-printing-not-ready = Upozornenie: súbor PDF nie je plne načítaný pre tlač. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Prepnúť bočný panel +pdfjs-toggle-sidebar-notification-button = + .title = Prepnúť bočný panel (dokument obsahuje osnovu/prílohy/vrstvy) +pdfjs-toggle-sidebar-button-label = Prepnúť bočný panel +pdfjs-document-outline-button = + .title = Zobraziť osnovu dokumentu (dvojitým kliknutím rozbalíte/zbalíte všetky položky) +pdfjs-document-outline-button-label = Osnova dokumentu +pdfjs-attachments-button = + .title = Zobraziť prílohy +pdfjs-attachments-button-label = Prílohy +pdfjs-layers-button = + .title = Zobraziť vrstvy (dvojitým kliknutím uvediete všetky vrstvy do pôvodného stavu) +pdfjs-layers-button-label = Vrstvy +pdfjs-thumbs-button = + .title = Zobraziť miniatúry +pdfjs-thumbs-button-label = Miniatúry +pdfjs-current-outline-item-button = + .title = Nájsť aktuálnu položku v osnove +pdfjs-current-outline-item-button-label = Aktuálna položka v osnove +pdfjs-findbar-button = + .title = Hľadať v dokumente +pdfjs-findbar-button-label = Hľadať +pdfjs-additional-layers = Ďalšie vrstvy + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Strana { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatúra strany { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Hľadať + .placeholder = Hľadať v dokumente… +pdfjs-find-previous-button = + .title = Vyhľadať predchádzajúci výskyt reťazca +pdfjs-find-previous-button-label = Predchádzajúce +pdfjs-find-next-button = + .title = Vyhľadať ďalší výskyt reťazca +pdfjs-find-next-button-label = Ďalšie +pdfjs-find-highlight-checkbox = Zvýrazniť všetky +pdfjs-find-match-case-checkbox-label = Rozlišovať veľkosť písmen +pdfjs-find-match-diacritics-checkbox-label = Rozlišovať diakritiku +pdfjs-find-entire-word-checkbox-label = Celé slová +pdfjs-find-reached-top = Bol dosiahnutý začiatok stránky, pokračuje sa od konca +pdfjs-find-reached-bottom = Bol dosiahnutý koniec stránky, pokračuje sa od začiatku +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] Výskyt { $current } z { $total } + [few] Výskyt { $current } z { $total } + [many] Výskyt { $current } z { $total } + *[other] Výskyt { $current } z { $total } + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Viac ako { $limit } výskyt + [few] Viac ako { $limit } výskyty + [many] Viac ako { $limit } výskytov + *[other] Viac ako { $limit } výskytov + } +pdfjs-find-not-found = Výraz nebol nájdený + +## Predefined zoom values + +pdfjs-page-scale-width = Na šírku strany +pdfjs-page-scale-fit = Na veľkosť strany +pdfjs-page-scale-auto = Automatická veľkosť +pdfjs-page-scale-actual = Skutočná veľkosť +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale } % + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Strana { $page } + +## Loading indicator messages + +pdfjs-loading-error = Počas načítavania dokumentu PDF sa vyskytla chyba. +pdfjs-invalid-file-error = Neplatný alebo poškodený súbor PDF. +pdfjs-missing-file-error = Chýbajúci súbor PDF. +pdfjs-unexpected-response-error = Neočakávaná odpoveď zo servera. +pdfjs-rendering-error = Pri vykresľovaní stránky sa vyskytla chyba. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anotácia typu { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Ak chcete otvoriť tento súbor PDF, zadajte jeho heslo. +pdfjs-password-invalid = Heslo nie je platné. Skúste to znova. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Zrušiť +pdfjs-web-fonts-disabled = Webové písma sú vypnuté: nie je možné použiť písma vložené do súboru PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Text +pdfjs-editor-free-text-button-label = Text +pdfjs-editor-ink-button = + .title = Kresliť +pdfjs-editor-ink-button-label = Kresliť +pdfjs-editor-stamp-button = + .title = Pridať alebo upraviť obrázky +pdfjs-editor-stamp-button-label = Pridať alebo upraviť obrázky +pdfjs-editor-highlight-button = + .title = Zvýrazniť +pdfjs-editor-highlight-button-label = Zvýrazniť +pdfjs-highlight-floating-button1 = + .title = Zvýrazniť + .aria-label = Zvýrazniť +pdfjs-highlight-floating-button-label = Zvýrazniť + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Odstrániť kresbu +pdfjs-editor-remove-freetext-button = + .title = Odstrániť text +pdfjs-editor-remove-stamp-button = + .title = Odstrániť obrázok +pdfjs-editor-remove-highlight-button = + .title = Odstrániť zvýraznenie + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Farba +pdfjs-editor-free-text-size-input = Veľkosť +pdfjs-editor-ink-color-input = Farba +pdfjs-editor-ink-thickness-input = Hrúbka +pdfjs-editor-ink-opacity-input = Priehľadnosť +pdfjs-editor-stamp-add-image-button = + .title = Pridať obrázok +pdfjs-editor-stamp-add-image-button-label = Pridať obrázok +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Hrúbka +pdfjs-editor-free-highlight-thickness-title = + .title = Zmeňte hrúbku pre zvýrazňovanie iných položiek ako textu +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Textový editor + .default-content = Začnite písať… +pdfjs-free-text = + .aria-label = Textový editor +pdfjs-free-text-default-content = Začnite písať… +pdfjs-ink = + .aria-label = Editor kreslenia +pdfjs-ink-canvas = + .aria-label = Obrázok vytvorený používateľom + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alternatívny text +pdfjs-editor-alt-text-edit-button = + .aria-label = Upraviť alternatívny text +pdfjs-editor-alt-text-edit-button-label = Upraviť alternatívny text +pdfjs-editor-alt-text-dialog-label = Vyberte možnosť +pdfjs-editor-alt-text-dialog-description = Alternatívny text (alt text) pomáha, keď ľudia obrázok nevidia alebo sa nenačítava. +pdfjs-editor-alt-text-add-description-label = Pridať popis +pdfjs-editor-alt-text-add-description-description = Zamerajte sa na 1-2 vety, ktoré popisujú predmet, prostredie alebo akcie. +pdfjs-editor-alt-text-mark-decorative-label = Označiť ako dekoratívny +pdfjs-editor-alt-text-mark-decorative-description = Používa sa na ozdobné obrázky, ako sú okraje alebo vodoznaky. +pdfjs-editor-alt-text-cancel-button = Zrušiť +pdfjs-editor-alt-text-save-button = Uložiť +pdfjs-editor-alt-text-decorative-tooltip = Označený ako dekoratívny +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Napríklad: „Mladý muž si sadá za stôl, aby sa najedol“ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternatívny text + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Ľavý horný roh – zmena veľkosti +pdfjs-editor-resizer-label-top-middle = Horný stred – zmena veľkosti +pdfjs-editor-resizer-label-top-right = Pravý horný roh – zmena veľkosti +pdfjs-editor-resizer-label-middle-right = Vpravo uprostred – zmena veľkosti +pdfjs-editor-resizer-label-bottom-right = Pravý dolný roh – zmena veľkosti +pdfjs-editor-resizer-label-bottom-middle = Stred dole – zmena veľkosti +pdfjs-editor-resizer-label-bottom-left = Ľavý dolný roh – zmena veľkosti +pdfjs-editor-resizer-label-middle-left = Vľavo uprostred – zmena veľkosti +pdfjs-editor-resizer-top-left = + .aria-label = Ľavý horný roh – zmena veľkosti +pdfjs-editor-resizer-top-middle = + .aria-label = Horný stred – zmena veľkosti +pdfjs-editor-resizer-top-right = + .aria-label = Pravý horný roh – zmena veľkosti +pdfjs-editor-resizer-middle-right = + .aria-label = Vpravo uprostred – zmena veľkosti +pdfjs-editor-resizer-bottom-right = + .aria-label = Pravý dolný roh – zmena veľkosti +pdfjs-editor-resizer-bottom-middle = + .aria-label = Stred dole – zmena veľkosti +pdfjs-editor-resizer-bottom-left = + .aria-label = Ľavý dolný roh – zmena veľkosti +pdfjs-editor-resizer-middle-left = + .aria-label = Vľavo uprostred – zmena veľkosti + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Farba zvýraznenia +pdfjs-editor-colorpicker-button = + .title = Zmeniť farbu +pdfjs-editor-colorpicker-dropdown = + .aria-label = Výber farieb +pdfjs-editor-colorpicker-yellow = + .title = Žltá +pdfjs-editor-colorpicker-green = + .title = Zelená +pdfjs-editor-colorpicker-blue = + .title = Modrá +pdfjs-editor-colorpicker-pink = + .title = Ružová +pdfjs-editor-colorpicker-red = + .title = Červená + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Zobraziť všetko +pdfjs-editor-highlight-show-all-button = + .title = Zobraziť všetko + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Upraviť alternatívny text (popis obrázka) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Pridať alternatívny text (popis obrázka) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Sem napíšte svoj popis… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Krátky popis pre ľudí, ktorí nevidia obrázok alebo ak sa obrázok nenačíta. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Tento alternatívny text bol vytvorený automaticky a môže byť nepresný. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Ďalšie informácie +pdfjs-editor-new-alt-text-create-automatically-button-label = Automaticky vytvoriť alternatívny text +pdfjs-editor-new-alt-text-not-now-button = Teraz nie +pdfjs-editor-new-alt-text-error-title = Alternatívny text sa nepodarilo vytvoriť automaticky +pdfjs-editor-new-alt-text-error-description = Napíšte svoj vlastný alternatívny text alebo to skúste znova neskôr. +pdfjs-editor-new-alt-text-error-close-button = Zavrieť +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Sťahuje sa model AI pre alternatívne texty ({ $downloadedSize } z { $totalSize } MB) + .aria-valuetext = Sťahuje sa model AI pre alternatívne texty ({ $downloadedSize } z { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternatívny text bol pridaný +pdfjs-editor-new-alt-text-added-button-label = Alternatívny text bol pridaný +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Chýbajúci alternatívny text +pdfjs-editor-new-alt-text-missing-button-label = Chýbajúci alternatívny text +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Skontrolovať alternatívny text +pdfjs-editor-new-alt-text-to-review-button-label = Skontrolovať alternatívny text +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Vytvorené automaticky: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Nastavenia alternatívneho textu obrázka +pdfjs-image-alt-text-settings-button-label = Nastavenia alternatívneho textu obrázka +pdfjs-editor-alt-text-settings-dialog-label = Nastavenia alternatívneho textu obrázka +pdfjs-editor-alt-text-settings-automatic-title = Automatický alternatívny text +pdfjs-editor-alt-text-settings-create-model-button-label = Automaticky vytvoriť alternatívny text +pdfjs-editor-alt-text-settings-create-model-description = Navrhuje popisy, ktoré pomôžu ľuďom, ktorým sa obrázok nezobrazuje alebo ak sa obrázok nenačíta. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Model AI pre alternatívne texty ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Beží lokálne na vašom zariadení, takže vaše dáta zostanú súkromné. Vyžaduje sa pre automatický alternatívny text. +pdfjs-editor-alt-text-settings-delete-model-button = Odstrániť +pdfjs-editor-alt-text-settings-download-model-button = Stiahnuť +pdfjs-editor-alt-text-settings-downloading-model-button = Sťahuje sa… +pdfjs-editor-alt-text-settings-editor-title = Editor alternatívneho textu +pdfjs-editor-alt-text-settings-show-dialog-button-label = Pri pridávaní obrázka ihneď zobraziť editor alternatívneho textu +pdfjs-editor-alt-text-settings-show-dialog-description = Pomáha vám zabezpečiť, aby všetky vaše obrázky mali alternatívny text. +pdfjs-editor-alt-text-settings-close-button = Zavrieť + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Zvýraznenie bolo odstránené +pdfjs-editor-undo-bar-message-freetext = Text bol odstránený +pdfjs-editor-undo-bar-message-ink = Kreslenie bolo odstránené +pdfjs-editor-undo-bar-message-stamp = Obrázok bol odstránený +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotácia odstránená + [few] { $count } anotácie odstránené + [many] { $count } anotácií odstránených + *[other] { $count } anotácií odstránených + } +pdfjs-editor-undo-bar-undo-button = + .title = Späť +pdfjs-editor-undo-bar-undo-button-label = Späť +pdfjs-editor-undo-bar-close-button = + .title = Zavrieť +pdfjs-editor-undo-bar-close-button-label = Zavrieť diff --git a/public/pdfjs/web/locale/skr/viewer.ftl b/public/pdfjs/web/locale/skr/viewer.ftl new file mode 100644 index 0000000..2d0e87f --- /dev/null +++ b/public/pdfjs/web/locale/skr/viewer.ftl @@ -0,0 +1,498 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = پچھلا ورقہ +pdfjs-previous-button-label = پچھلا +pdfjs-next-button = + .title = اڳلا ورقہ +pdfjs-next-button-label = اڳلا +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = ورقہ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount } دا +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } دا { $pagesCount }) +pdfjs-zoom-out-button = + .title = زوم آؤٹ +pdfjs-zoom-out-button-label = زوم آؤٹ +pdfjs-zoom-in-button = + .title = زوم اِن +pdfjs-zoom-in-button-label = زوم اِن +pdfjs-zoom-select = + .title = زوم +pdfjs-presentation-mode-button = + .title = پریزنٹیشن موڈ تے سوئچ کرو +pdfjs-presentation-mode-button-label = پریزنٹیشن موڈ +pdfjs-open-file-button = + .title = فائل کھولو +pdfjs-open-file-button-label = کھولو +pdfjs-print-button = + .title = چھاپو +pdfjs-print-button-label = چھاپو +pdfjs-save-button = + .title = ہتھیکڑا کرو +pdfjs-save-button-label = ہتھیکڑا کرو +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = ڈاؤن لوڈ +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = ڈاؤن لوڈ +pdfjs-bookmark-button = + .title = موجودہ ورقہ (موجودہ ورقے کنوں یوآرایل ݙیکھو) +pdfjs-bookmark-button-label = موجودہ ورقہ + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = اوزار +pdfjs-tools-button-label = اوزار +pdfjs-first-page-button = + .title = پہلے ورقے تے ونڄو +pdfjs-first-page-button-label = پہلے ورقے تے ونڄو +pdfjs-last-page-button = + .title = چھیکڑی ورقے تے ونڄو +pdfjs-last-page-button-label = چھیکڑی ورقے تے ونڄو +pdfjs-page-rotate-cw-button = + .title = گھڑی وانگوں گھماؤ +pdfjs-page-rotate-cw-button-label = گھڑی وانگوں گھماؤ +pdfjs-page-rotate-ccw-button = + .title = گھڑی تے اُپٹھ گھماؤ +pdfjs-page-rotate-ccw-button-label = گھڑی تے اُپٹھ گھماؤ +pdfjs-cursor-text-select-tool-button = + .title = متن منتخب کݨ والا آلہ فعال بݨاؤ +pdfjs-cursor-text-select-tool-button-label = متن منتخب کرݨ والا آلہ +pdfjs-cursor-hand-tool-button = + .title = ہینڈ ٹول فعال بݨاؤ +pdfjs-cursor-hand-tool-button-label = ہینڈ ٹول +pdfjs-scroll-page-button = + .title = پیج سکرولنگ استعمال کرو +pdfjs-scroll-page-button-label = پیج سکرولنگ +pdfjs-scroll-vertical-button = + .title = عمودی سکرولنگ استعمال کرو +pdfjs-scroll-vertical-button-label = عمودی سکرولنگ +pdfjs-scroll-horizontal-button = + .title = افقی سکرولنگ استعمال کرو +pdfjs-scroll-horizontal-button-label = افقی سکرولنگ +pdfjs-scroll-wrapped-button = + .title = ویڑھی ہوئی سکرولنگ استعمال کرو +pdfjs-scroll-wrapped-button-label = وہڑھی ہوئی سکرولنگ +pdfjs-spread-none-button = + .title = پیج سپریڈز وِچ شامل نہ تھیوو۔ +pdfjs-spread-none-button-label = کوئی پولھ کائنی +pdfjs-spread-odd-button = + .title = طاق نمبر والے ورقیاں دے نال شروع تھیوݨ والے پیج سپریڈز وِچ شامل تھیوو۔ +pdfjs-spread-odd-button-label = تاک پھیلاؤ +pdfjs-spread-even-button = + .title = جفت نمر والے ورقیاں نال شروع تھیوݨ والے پیج سپریڈز وِ شامل تھیوو۔ +pdfjs-spread-even-button-label = جفت پھیلاؤ + +## Document properties dialog + +pdfjs-document-properties-button = + .title = دستاویز خواص… +pdfjs-document-properties-button-label = دستاویز خواص … +pdfjs-document-properties-file-name = فائل دا ناں: +pdfjs-document-properties-file-size = فائل دا سائز: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } بائٹاں) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } بائٹاں) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } کے بی ({ $size_b } بائٹس) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } ایم بی ({ $size_b } بائٹس) +pdfjs-document-properties-title = عنوان: +pdfjs-document-properties-author = تخلیق کار: +pdfjs-document-properties-subject = موضوع: +pdfjs-document-properties-keywords = کلیدی الفاظ: +pdfjs-document-properties-creation-date = تخلیق دی تاریخ: +pdfjs-document-properties-modification-date = ترمیم دی تاریخ: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = تخلیق کار: +pdfjs-document-properties-producer = PDF پیدا کار: +pdfjs-document-properties-version = PDF ورژن: +pdfjs-document-properties-page-count = ورقہ شماری: +pdfjs-document-properties-page-size = ورقہ دی سائز: +pdfjs-document-properties-page-size-unit-inches = وِچ +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = عمودی انداز +pdfjs-document-properties-page-size-orientation-landscape = افقى انداز +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = لیٹر +pdfjs-document-properties-page-size-name-legal = قنونی + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = تکھا ویب نظارہ: +pdfjs-document-properties-linearized-yes = جیا +pdfjs-document-properties-linearized-no = کو +pdfjs-document-properties-close-button = بند کرو + +## Print + +pdfjs-print-progress-message = چھاپݨ کیتے دستاویز تیار تھیندے پئے ہن … +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = منسوخ کرو +pdfjs-printing-not-supported = چتاوݨی: چھپائی ایں براؤزر تے پوری طراں معاونت شدہ کائنی۔ +pdfjs-printing-not-ready = چتاوݨی: PDF چھپائی کیتے پوری طراں لوڈ نئیں تھئی۔ + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = سائیڈ بار ٹوگل کرو +pdfjs-toggle-sidebar-notification-button = + .title = سائیڈ بار ٹوگل کرو (دستاویز وِچ آؤٹ لائن/ منسلکات/ پرتاں شامل ہن) +pdfjs-toggle-sidebar-button-label = سائیڈ بار ٹوگل کرو +pdfjs-document-outline-button = + .title = دستاویز دا خاکہ ݙکھاؤ (تمام آئٹمز کوں پھیلاوݨ/سنگوڑݨ کیتے ڈبل کلک کرو) +pdfjs-document-outline-button-label = دستاویز آؤٹ لائن +pdfjs-attachments-button = + .title = نتھیاں ݙکھاؤ +pdfjs-attachments-button-label = منسلکات +pdfjs-layers-button = + .title = پرتاں ݙکھاؤ (تمام پرتاں کوں ڈیفالٹ حالت وِچ دوبارہ ترتیب ݙیوݨ کیتے ڈبل کلک کرو) +pdfjs-layers-button-label = پرتاں +pdfjs-thumbs-button = + .title = تھمبنیل ݙکھاؤ +pdfjs-thumbs-button-label = تھمبنیلز +pdfjs-current-outline-item-button = + .title = موجودہ آؤٹ لائن آئٹم لبھو +pdfjs-current-outline-item-button-label = موجودہ آؤٹ لائن آئٹم +pdfjs-findbar-button = + .title = دستاویز وِچ لبھو +pdfjs-findbar-button-label = لبھو +pdfjs-additional-layers = اضافی پرتاں + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = ورقہ { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = ورقے دا تھمبنیل { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = لبھو + .placeholder = دستاویز وِچ لبھو … +pdfjs-find-previous-button = + .title = فقرے دا پچھلا واقعہ لبھو +pdfjs-find-previous-button-label = پچھلا +pdfjs-find-next-button = + .title = فقرے دا اڳلا واقعہ لبھو +pdfjs-find-next-button-label = اڳلا +pdfjs-find-highlight-checkbox = تمام نشابر کرو +pdfjs-find-match-case-checkbox-label = حروف مشابہ کرو +pdfjs-find-match-diacritics-checkbox-label = ڈائیکرٹکس مشابہ کرو +pdfjs-find-entire-word-checkbox-label = تمام الفاظ +pdfjs-find-reached-top = ورقے دے شروع تے پُج ڳیا، تلوں جاری کیتا ڳیا +pdfjs-find-reached-bottom = ورقے دے پاند تے پُڄ ڳیا، اُتوں شروع کیتا ڳیا +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $total } وِچوں { $current } مشابہ + *[other] { $total } وِچوں { $current } مشابے + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] { $limit } توں ودھ مماثلت۔ + *[other] { $limit } توں ودھ مماثلتاں۔ + } +pdfjs-find-not-found = فقرہ نئیں ملیا + +## Predefined zoom values + +pdfjs-page-scale-width = ورقے دی چوڑائی +pdfjs-page-scale-fit = ورقہ فٹنگ +pdfjs-page-scale-auto = آپوں آپ زوم +pdfjs-page-scale-actual = اصل میچا +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = ورقہ { $page } + +## Loading indicator messages + +pdfjs-loading-error = PDF لوڈ کریندے ویلھے نقص آ ڳیا۔ +pdfjs-invalid-file-error = غلط یا خراب شدہ PDF فائل۔ +pdfjs-missing-file-error = PDF فائل غائب ہے۔ +pdfjs-unexpected-response-error = سرور دا غیر متوقع جواب۔ +pdfjs-rendering-error = ورقہ رینڈر کریندے ویلھے ہک خرابی پیش آڳئی۔ + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } تشریح] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = ایہ PDF فائل کھولݨ کیتے پاس ورڈ درج کرو۔ +pdfjs-password-invalid = غلط پاس ورڈ: براہ مہربانی ولدا کوشش کرو۔ +pdfjs-password-ok-button = ٹھیک ہے +pdfjs-password-cancel-button = منسوخ کرو +pdfjs-web-fonts-disabled = ویب فونٹس غیر فعال ہن: ایمبیڈڈ PDF فونٹس استعمال کرݨ کنوں قاصر ہن + +## Editing + +pdfjs-editor-free-text-button = + .title = متن +pdfjs-editor-free-text-button-label = متن +pdfjs-editor-ink-button = + .title = چھکو +pdfjs-editor-ink-button-label = چھکو +pdfjs-editor-stamp-button = + .title = تصویراں کوں شامل کرو یا ترمیم کرو +pdfjs-editor-stamp-button-label = تصویراں کوں شامل کرو یا ترمیم کرو +pdfjs-editor-highlight-button = + .title = نمایاں کرو +pdfjs-editor-highlight-button-label = نمایاں کرو +pdfjs-highlight-floating-button1 = + .title = نمایاں کرو + .aria-label = نمایاں کرو +pdfjs-highlight-floating-button-label = نمایاں کرو + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = ڈرائینگ ہٹاؤ +pdfjs-editor-remove-freetext-button = + .title = متن ہٹاؤ +pdfjs-editor-remove-stamp-button = + .title = تصویر ہٹاؤ +pdfjs-editor-remove-highlight-button = + .title = نمایاں ہٹاؤ + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = رنگ +pdfjs-editor-free-text-size-input = سائز +pdfjs-editor-ink-color-input = رنگ +pdfjs-editor-ink-thickness-input = ٹھولھ +pdfjs-editor-ink-opacity-input = دھندلاپن +pdfjs-editor-stamp-add-image-button = + .title = تصویر شامل کرو +pdfjs-editor-stamp-add-image-button-label = تصویر شامل کرو +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = مُٹاݨ +pdfjs-editor-free-highlight-thickness-title = + .title = متن توں ان٘ج ٻئے شئیں کوں نمایاں کرݨ ویلے مُٹاݨ کوں بدلو +pdfjs-free-text = + .aria-label = ٹیکسٹ ایڈیٹر +pdfjs-free-text-default-content = ٹائپنگ شروع کرو … +pdfjs-ink = + .aria-label = ڈرا ایڈیٹر +pdfjs-ink-canvas = + .aria-label = صارف دی بݨائی ہوئی تصویر + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alt متن +pdfjs-editor-alt-text-edit-button-label = alt متن وِچ ترمیم کرو +pdfjs-editor-alt-text-dialog-label = ہِک اختیار چُݨو +pdfjs-editor-alt-text-dialog-description = Alt متن (متبادل متن) اِیں ویلے مَدَت کرین٘دا ہِے جہڑیلے لوک تصویر کوں نِھیں ݙیکھ سڳدے یا جہڑیلے اِیہ لوڈ کائنی تِھین٘دا۔ +pdfjs-editor-alt-text-add-description-label = تفصیل شامل کرو +pdfjs-editor-alt-text-add-description-description = 1-2 جملیاں دا مقصد جہڑے موضوع، ترتیب، یا اعمال کوں بیان کرین٘دے ہِن۔ +pdfjs-editor-alt-text-mark-decorative-label = آرائشی طور تے نشان زد کرو +pdfjs-editor-alt-text-mark-decorative-description = اِیہ آرائشی تصویراں کِیتے استعمال تِھین٘دا ہِے، جیویں بارڈر یا واٹر مارکس۔ +pdfjs-editor-alt-text-cancel-button = منسوخ +pdfjs-editor-alt-text-save-button = محفوظ +pdfjs-editor-alt-text-decorative-tooltip = آرائشی دے طور تے نشان زد تِھی ڳِیا +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = مثال دے طور تے، "ہِک جؤان کھاݨاں کھاوݨ کِیتے میز اُتّے ٻیٹھا ہِے" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt متن + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = اُتلی کَھٻّی نُکّڑ — سائز بدلو +pdfjs-editor-resizer-label-top-middle = اُتلا وِچلا — سائز بدلو +pdfjs-editor-resizer-label-top-right = اُتلی سَڄّی نُکَّڑ — سائز بدلو +pdfjs-editor-resizer-label-middle-right = وِچلا سڄّا — سائز بدلو +pdfjs-editor-resizer-label-bottom-right = تلوِیں سَڄّی نُکَّڑ — سائز بدلو +pdfjs-editor-resizer-label-bottom-middle = تلواں وِچلا — سائز بدلو +pdfjs-editor-resizer-label-bottom-left = تلوِیں کَھٻّی نُکّڑ — سائز بدلو +pdfjs-editor-resizer-label-middle-left = وِچلا کَھٻّا — سائز بدلو +pdfjs-editor-resizer-top-left = + .aria-label = اُتلی کَھٻّی نُکّڑ — سائز بدلو +pdfjs-editor-resizer-top-middle = + .aria-label = اُتلا وِچلا — سائز بدلو +pdfjs-editor-resizer-top-right = + .aria-label = اُتلی سَڄّی نُکَّڑ — سائز بدلو +pdfjs-editor-resizer-middle-right = + .aria-label = وِچلا سڄّا — سائز بدلو +pdfjs-editor-resizer-bottom-right = + .aria-label = تلوِیں سَڄّی نُکَّڑ — سائز بدلو +pdfjs-editor-resizer-bottom-middle = + .aria-label = تلواں وِچلا — سائز بدلو +pdfjs-editor-resizer-bottom-left = + .aria-label = تلوِیں کَھٻّی نُکّڑ — سائز بدلو +pdfjs-editor-resizer-middle-left = + .aria-label = وِچلا کَھٻّا — سائز بدلو + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = نشابر رنگ +pdfjs-editor-colorpicker-button = + .title = رنگ بدلو +pdfjs-editor-colorpicker-dropdown = + .aria-label = رنگ اختیارات +pdfjs-editor-colorpicker-yellow = + .title = پیلا +pdfjs-editor-colorpicker-green = + .title = ساوا +pdfjs-editor-colorpicker-blue = + .title = نیلا +pdfjs-editor-colorpicker-pink = + .title = گلابی +pdfjs-editor-colorpicker-red = + .title = لال + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = سارے ݙکھاؤ +pdfjs-editor-highlight-show-all-button = + .title = سارے ݙکھاؤ + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = آلٹ عبارت وچ تبدیلی کرو (تصویر تفصیل) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = آلٹ عبارت شامل کرو (تصویر تفصیل) +pdfjs-editor-new-alt-text-textarea = + .placeholder = اتھ آپݨی وضاحت لکھو۔۔۔ +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = اُنہاں لوکاں کیتے مختصر تفصیل جہڑے تصویر کائنی ݙیکھ سڳدے یا ڄݙݨ تصویر لوڈ کائبی تھیندی۔ +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = آلٹ عبارت خودکار تخلیق تھئی ہے تے غلط تھی سڳدی ہے۔ +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = ٻیا سِکھو +pdfjs-editor-new-alt-text-create-automatically-button-label = آلٹ عبارت خودکار بݨاؤ +pdfjs-editor-new-alt-text-not-now-button = ہݨ کائناں +pdfjs-editor-new-alt-text-error-title = آلٹ عبارت خودکار نہ بݨاؤ +pdfjs-editor-new-alt-text-error-description = سوہݨا، آپݨی آلٹ عبارت لکھو یا ولدا بعد وچ کوشش کرو۔ +pdfjs-editor-new-alt-text-error-close-button = بند کرو +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = آلٹ عبارت اے آئی ماڈل({ $totalSize }ایم بی دے { $downloadedSize }) ڈاؤن لوڈ تھیندا پئے + .aria-valuetext = آلٹ عبارت اے آئی ماڈل({ $totalSize }ایم بی دے { $downloadedSize }) ڈاؤن لوڈ تھیندا پئے +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = آلٹ عبارت شامل تھی ڳئی +pdfjs-editor-new-alt-text-added-button-label = آلٹ عبارت شامل تھی ڳئی +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = متبادل عبارت غائب ہے +pdfjs-editor-new-alt-text-missing-button-label = متبادل عبارت غائب ہے +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = alt متن تے نظرثانی کرو +pdfjs-editor-new-alt-text-to-review-button-label = alt متن تے نظرثانی کرو +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = خودکار تخلیق تھئی: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = تصویر آلٹ عبارت ترتیباں +pdfjs-image-alt-text-settings-button-label = تصویر آلٹ عبارت ترتیباں +pdfjs-editor-alt-text-settings-dialog-label = تصویر آلٹ عبارت ترتیباں +pdfjs-editor-alt-text-settings-automatic-title = خودکار آلٹ عبارت +pdfjs-editor-alt-text-settings-create-model-button-label = آلٹ عبارت خودکار بݨاؤ +pdfjs-editor-alt-text-settings-create-model-description = اُنہاں لوکاں دی مدد کیتے تفصیل تجویز کرو جہڑے تصویر کائنی ݙیکھ سڳدے یا ڄݙݨ تصویر لوڈ کائبی تھیندی۔ +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = آلٹ عبارت اے آئی ماڈل ({ $totalSize } ایم بی) +pdfjs-editor-alt-text-settings-ai-model-description = تہاݙی ڈیوائس تے مقامی طور تے چلدا ہے تاں جو تہاݙا ڈیٹا نجی رہوے۔ خودکار آلٹ عبارت کیتے ضروری ہے۔ +pdfjs-editor-alt-text-settings-delete-model-button = مٹاؤ +pdfjs-editor-alt-text-settings-download-model-button = ڈاؤن لوڈ +pdfjs-editor-alt-text-settings-downloading-model-button = ڈاؤن لوڈ تھیندا پئے … +pdfjs-editor-alt-text-settings-editor-title = متبادل ٹیکسٹ ایڈیٹر +pdfjs-editor-alt-text-settings-show-dialog-button-label = تصویر شامل کرݨ ویلے فوری طور تے آلٹ ٹیکسٹ ایڈیٹر ݙکھاؤ +pdfjs-editor-alt-text-settings-show-dialog-description = ایہ تہاکوں یقینی بݨاوݨ وچ مدد کریندے جو تہاݙیاں ساریاں تصویراں وچ آلٹ عبارت ہے۔ +pdfjs-editor-alt-text-settings-close-button = بند کرو + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-undo-button = + .title = کیتا اݨ کیتا +pdfjs-editor-undo-bar-undo-button-label = کیتا اݨ کیتا +pdfjs-editor-undo-bar-close-button = + .title = بند کرو +pdfjs-editor-undo-bar-close-button-label = بند کرو diff --git a/public/pdfjs/web/locale/sl/viewer.ftl b/public/pdfjs/web/locale/sl/viewer.ftl new file mode 100644 index 0000000..4e004bd --- /dev/null +++ b/public/pdfjs/web/locale/sl/viewer.ftl @@ -0,0 +1,521 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Prejšnja stran +pdfjs-previous-button-label = Nazaj +pdfjs-next-button = + .title = Naslednja stran +pdfjs-next-button-label = Naprej +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Stran +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = od { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } od { $pagesCount }) +pdfjs-zoom-out-button = + .title = Pomanjšaj +pdfjs-zoom-out-button-label = Pomanjšaj +pdfjs-zoom-in-button = + .title = Povečaj +pdfjs-zoom-in-button-label = Povečaj +pdfjs-zoom-select = + .title = Povečava +pdfjs-presentation-mode-button = + .title = Preklopi v način predstavitve +pdfjs-presentation-mode-button-label = Način predstavitve +pdfjs-open-file-button = + .title = Odpri datoteko +pdfjs-open-file-button-label = Odpri +pdfjs-print-button = + .title = Natisni +pdfjs-print-button-label = Natisni +pdfjs-save-button = + .title = Shrani +pdfjs-save-button-label = Shrani +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Prenesi +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Prenesi +pdfjs-bookmark-button = + .title = Trenutna stran (prikaži URL, ki vodi do trenutne strani) +pdfjs-bookmark-button-label = Na trenutno stran + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Orodja +pdfjs-tools-button-label = Orodja +pdfjs-first-page-button = + .title = Pojdi na prvo stran +pdfjs-first-page-button-label = Pojdi na prvo stran +pdfjs-last-page-button = + .title = Pojdi na zadnjo stran +pdfjs-last-page-button-label = Pojdi na zadnjo stran +pdfjs-page-rotate-cw-button = + .title = Zavrti v smeri urnega kazalca +pdfjs-page-rotate-cw-button-label = Zavrti v smeri urnega kazalca +pdfjs-page-rotate-ccw-button = + .title = Zavrti v nasprotni smeri urnega kazalca +pdfjs-page-rotate-ccw-button-label = Zavrti v nasprotni smeri urnega kazalca +pdfjs-cursor-text-select-tool-button = + .title = Omogoči orodje za izbor besedila +pdfjs-cursor-text-select-tool-button-label = Orodje za izbor besedila +pdfjs-cursor-hand-tool-button = + .title = Omogoči roko +pdfjs-cursor-hand-tool-button-label = Roka +pdfjs-scroll-page-button = + .title = Uporabi drsenje po strani +pdfjs-scroll-page-button-label = Drsenje po strani +pdfjs-scroll-vertical-button = + .title = Uporabi navpično drsenje +pdfjs-scroll-vertical-button-label = Navpično drsenje +pdfjs-scroll-horizontal-button = + .title = Uporabi vodoravno drsenje +pdfjs-scroll-horizontal-button-label = Vodoravno drsenje +pdfjs-scroll-wrapped-button = + .title = Uporabi ovito drsenje +pdfjs-scroll-wrapped-button-label = Ovito drsenje +pdfjs-spread-none-button = + .title = Ne združuj razponov strani +pdfjs-spread-none-button-label = Brez razponov +pdfjs-spread-odd-button = + .title = Združuj razpone strani z začetkom pri lihih straneh +pdfjs-spread-odd-button-label = Lihi razponi +pdfjs-spread-even-button = + .title = Združuj razpone strani z začetkom pri sodih straneh +pdfjs-spread-even-button-label = Sodi razponi + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Lastnosti dokumenta … +pdfjs-document-properties-button-label = Lastnosti dokumenta … +pdfjs-document-properties-file-name = Ime datoteke: +pdfjs-document-properties-file-size = Velikost datoteke: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bajtov) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bajtov) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajtov) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajtov) +pdfjs-document-properties-title = Ime: +pdfjs-document-properties-author = Avtor: +pdfjs-document-properties-subject = Tema: +pdfjs-document-properties-keywords = Ključne besede: +pdfjs-document-properties-creation-date = Datum nastanka: +pdfjs-document-properties-modification-date = Datum spremembe: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Ustvaril: +pdfjs-document-properties-producer = Izdelovalec PDF: +pdfjs-document-properties-version = Različica PDF: +pdfjs-document-properties-page-count = Število strani: +pdfjs-document-properties-page-size = Velikost strani: +pdfjs-document-properties-page-size-unit-inches = palcev +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = pokončno +pdfjs-document-properties-page-size-orientation-landscape = ležeče +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Pismo +pdfjs-document-properties-page-size-name-legal = Pravno + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Hitri spletni ogled: +pdfjs-document-properties-linearized-yes = Da +pdfjs-document-properties-linearized-no = Ne +pdfjs-document-properties-close-button = Zapri + +## Print + +pdfjs-print-progress-message = Priprava dokumenta na tiskanje … +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress } % +pdfjs-print-progress-close-button = Prekliči +pdfjs-printing-not-supported = Opozorilo: ta brskalnik ne podpira vseh možnosti tiskanja. +pdfjs-printing-not-ready = Opozorilo: PDF ni v celoti naložen za tiskanje. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Preklopi stransko vrstico +pdfjs-toggle-sidebar-notification-button = + .title = Preklopi stransko vrstico (dokument vsebuje oris/priponke/plasti) +pdfjs-toggle-sidebar-button-label = Preklopi stransko vrstico +pdfjs-document-outline-button = + .title = Prikaži oris dokumenta (dvokliknite za razširitev/strnitev vseh predmetov) +pdfjs-document-outline-button-label = Oris dokumenta +pdfjs-attachments-button = + .title = Prikaži priponke +pdfjs-attachments-button-label = Priponke +pdfjs-layers-button = + .title = Prikaži plasti (dvokliknite za ponastavitev vseh plasti na privzeto stanje) +pdfjs-layers-button-label = Plasti +pdfjs-thumbs-button = + .title = Prikaži sličice +pdfjs-thumbs-button-label = Sličice +pdfjs-current-outline-item-button = + .title = Najdi trenutni predmet orisa +pdfjs-current-outline-item-button-label = Trenutni predmet orisa +pdfjs-findbar-button = + .title = Iskanje po dokumentu +pdfjs-findbar-button-label = Najdi +pdfjs-additional-layers = Dodatne plasti + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Stran { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Sličica strani { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Najdi + .placeholder = Najdi v dokumentu … +pdfjs-find-previous-button = + .title = Najdi prejšnjo ponovitev iskanega +pdfjs-find-previous-button-label = Najdi nazaj +pdfjs-find-next-button = + .title = Najdi naslednjo ponovitev iskanega +pdfjs-find-next-button-label = Najdi naprej +pdfjs-find-highlight-checkbox = Označi vse +pdfjs-find-match-case-checkbox-label = Razlikuj velike/male črke +pdfjs-find-match-diacritics-checkbox-label = Razlikuj diakritične znake +pdfjs-find-entire-word-checkbox-label = Cele besede +pdfjs-find-reached-top = Dosežen začetek dokumenta iz smeri konca +pdfjs-find-reached-bottom = Doseženo konec dokumenta iz smeri začetka +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] Zadetek { $current } od { $total } + [two] Zadetek { $current } od { $total } + [few] Zadetek { $current } od { $total } + *[other] Zadetek { $current } od { $total } + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Več kot { $limit } zadetek + [two] Več kot { $limit } zadetka + [few] Več kot { $limit } zadetki + *[other] Več kot { $limit } zadetkov + } +pdfjs-find-not-found = Iskanega ni mogoče najti + +## Predefined zoom values + +pdfjs-page-scale-width = Širina strani +pdfjs-page-scale-fit = Prilagodi stran +pdfjs-page-scale-auto = Samodejno +pdfjs-page-scale-actual = Dejanska velikost +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale } % + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Stran { $page } + +## Loading indicator messages + +pdfjs-loading-error = Med nalaganjem datoteke PDF je prišlo do napake. +pdfjs-invalid-file-error = Neveljavna ali pokvarjena datoteka PDF. +pdfjs-missing-file-error = Ni datoteke PDF. +pdfjs-unexpected-response-error = Nepričakovan odgovor strežnika. +pdfjs-rendering-error = Med pripravljanjem strani je prišlo do napake! + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Opomba vrste { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Vnesite geslo za odpiranje te datoteke PDF. +pdfjs-password-invalid = Neveljavno geslo. Poskusite znova. +pdfjs-password-ok-button = V redu +pdfjs-password-cancel-button = Prekliči +pdfjs-web-fonts-disabled = Spletne pisave so onemogočene: vgradnih pisav za PDF ni mogoče uporabiti. + +## Editing + +pdfjs-editor-free-text-button = + .title = Besedilo +pdfjs-editor-free-text-button-label = Besedilo +pdfjs-editor-ink-button = + .title = Riši +pdfjs-editor-ink-button-label = Riši +pdfjs-editor-stamp-button = + .title = Dodajanje ali urejanje slik +pdfjs-editor-stamp-button-label = Dodajanje ali urejanje slik +pdfjs-editor-highlight-button = + .title = Označevalnik +pdfjs-editor-highlight-button-label = Označevalnik +pdfjs-highlight-floating-button1 = + .title = Označi + .aria-label = Označi +pdfjs-highlight-floating-button-label = Označi + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Odstrani risbo +pdfjs-editor-remove-freetext-button = + .title = Odstrani besedilo +pdfjs-editor-remove-stamp-button = + .title = Odstrani sliko +pdfjs-editor-remove-highlight-button = + .title = Odstrani označbo + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Barva +pdfjs-editor-free-text-size-input = Velikost +pdfjs-editor-ink-color-input = Barva +pdfjs-editor-ink-thickness-input = Debelina +pdfjs-editor-ink-opacity-input = Neprosojnost +pdfjs-editor-stamp-add-image-button = + .title = Dodaj sliko +pdfjs-editor-stamp-add-image-button-label = Dodaj sliko +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Debelina +pdfjs-editor-free-highlight-thickness-title = + .title = Spremeni debelino pri označevanju nebesedilnih elementov +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Urejevalnik besedila + .default-content = Začnite tipkati … +pdfjs-free-text = + .aria-label = Urejevalnik besedila +pdfjs-free-text-default-content = Začnite tipkati … +pdfjs-ink = + .aria-label = Urejevalnik risanja +pdfjs-ink-canvas = + .aria-label = Uporabnikova slika + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Nadomestno besedilo +pdfjs-editor-alt-text-edit-button = + .aria-label = Uredi nadomestno besedilo +pdfjs-editor-alt-text-edit-button-label = Uredi nadomestno besedilo +pdfjs-editor-alt-text-dialog-label = Izberite možnost +pdfjs-editor-alt-text-dialog-description = Nadomestno besedilo se prikaže tistim, ki ne vidijo slike, ali če se ta ne naloži. +pdfjs-editor-alt-text-add-description-label = Dodaj opis +pdfjs-editor-alt-text-add-description-description = Poskušajte v enem ali dveh stavkih opisati motiv, okolje ali dejanja. +pdfjs-editor-alt-text-mark-decorative-label = Označi kot okrasno +pdfjs-editor-alt-text-mark-decorative-description = Uporablja se za slike, ki služijo samo okrasu, na primer obrobe ali vodne žige. +pdfjs-editor-alt-text-cancel-button = Prekliči +pdfjs-editor-alt-text-save-button = Shrani +pdfjs-editor-alt-text-decorative-tooltip = Označeno kot okrasno +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Na primer: "Mladenič sedi za mizo pri jedi" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Nadomestno besedilo + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Zgornji levi kot – spremeni velikost +pdfjs-editor-resizer-label-top-middle = Zgoraj na sredini – spremeni velikost +pdfjs-editor-resizer-label-top-right = Zgornji desni kot – spremeni velikost +pdfjs-editor-resizer-label-middle-right = Desno na sredini – spremeni velikost +pdfjs-editor-resizer-label-bottom-right = Spodnji desni kot – spremeni velikost +pdfjs-editor-resizer-label-bottom-middle = Spodaj na sredini – spremeni velikost +pdfjs-editor-resizer-label-bottom-left = Spodnji levi kot – spremeni velikost +pdfjs-editor-resizer-label-middle-left = Levo na sredini – spremeni velikost +pdfjs-editor-resizer-top-left = + .aria-label = Zgornji levi kot – spremeni velikost +pdfjs-editor-resizer-top-middle = + .aria-label = Zgoraj na sredini – spremeni velikost +pdfjs-editor-resizer-top-right = + .aria-label = Zgornji desni kot – spremeni velikost +pdfjs-editor-resizer-middle-right = + .aria-label = Desno na sredini – spremeni velikost +pdfjs-editor-resizer-bottom-right = + .aria-label = Spodnji desni kot – spremeni velikost +pdfjs-editor-resizer-bottom-middle = + .aria-label = Spodaj na sredini – spremeni velikost +pdfjs-editor-resizer-bottom-left = + .aria-label = Spodnji levi kot – spremeni velikost +pdfjs-editor-resizer-middle-left = + .aria-label = Levo na sredini – spremeni velikost + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Barva označbe +pdfjs-editor-colorpicker-button = + .title = Spremeni barvo +pdfjs-editor-colorpicker-dropdown = + .aria-label = Izbira barve +pdfjs-editor-colorpicker-yellow = + .title = Rumena +pdfjs-editor-colorpicker-green = + .title = Zelena +pdfjs-editor-colorpicker-blue = + .title = Modra +pdfjs-editor-colorpicker-pink = + .title = Roza +pdfjs-editor-colorpicker-red = + .title = Rdeča + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Prikaži vse +pdfjs-editor-highlight-show-all-button = + .title = Prikaži vse + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Uredi nadomestno besedilo (opis slike) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Dodaj nadomestno besedilo (opis slike) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Tukaj napišite svoj opis … +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Kratek opis za ljudi, ki ne morejo videti slike, ali za primer, ko se slika ne naloži. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = To nadomestno besedilo je bilo ustvarjeno samodejno in je lahko netočno. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Več o tem +pdfjs-editor-new-alt-text-create-automatically-button-label = Samodejno ustvari nadomestno besedilo +pdfjs-editor-new-alt-text-not-now-button = Ne zdaj +pdfjs-editor-new-alt-text-error-title = Nadomestnega besedila ni bilo mogoče samodejno ustvariti +pdfjs-editor-new-alt-text-error-description = Sestavite svoje nadomestno besedilo ali poskusite znova pozneje. +pdfjs-editor-new-alt-text-error-close-button = Zapri +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Prenašanje modela UI za nadomestno besedilo ({ $downloadedSize } od { $totalSize } MB) + .aria-valuetext = Prenašanje modela UI za nadomestno besedilo ({ $downloadedSize } od { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Nadomestno besedilo dodano +pdfjs-editor-new-alt-text-added-button-label = Nadomestno besedilo dodano +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Nadomestno besedilo manjka +pdfjs-editor-new-alt-text-missing-button-label = Nadomestno besedilo manjka +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Oceni nadomestno besedilo +pdfjs-editor-new-alt-text-to-review-button-label = Oceni nadomestno besedilo +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Samodejno ustvarjeno: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Nastavitve nadomestnega besedila slike +pdfjs-image-alt-text-settings-button-label = Nastavitve nadomestnega besedila slike +pdfjs-editor-alt-text-settings-dialog-label = Nastavitve nadomestnega besedila slike +pdfjs-editor-alt-text-settings-automatic-title = Samodejno nadomestno besedilo +pdfjs-editor-alt-text-settings-create-model-button-label = Samodejno ustvari nadomestno besedilo +pdfjs-editor-alt-text-settings-create-model-description = Predlaga opise za pomoč ljudem, ki ne morejo videti slike, ali za primer, ko se slika ne naloži. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Model UI za nadomestno besedilo ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Izvaja se lokalno na vaši napravi, tako da vaši podatki ostajajo zasebni. Zahtevano za samodejno nadomestno besedilo. +pdfjs-editor-alt-text-settings-delete-model-button = Izbriši +pdfjs-editor-alt-text-settings-download-model-button = Prenesi +pdfjs-editor-alt-text-settings-downloading-model-button = Prenašanje ... +pdfjs-editor-alt-text-settings-editor-title = Urejevalnik nadomestnega besedila +pdfjs-editor-alt-text-settings-show-dialog-button-label = Ob dodajanju slike takoj prikaži urejevalnik nadomestnega besedila +pdfjs-editor-alt-text-settings-show-dialog-description = Pomaga vam zagotoviti, da imajo vse vaše slike nadomestno besedilo. +pdfjs-editor-alt-text-settings-close-button = Zapri + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Označba odstranjena +pdfjs-editor-undo-bar-message-freetext = Besedilo odstranjeno +pdfjs-editor-undo-bar-message-ink = Risba odstranjena +pdfjs-editor-undo-bar-message-stamp = Slika odstranjena +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } označba odstranjena + [two] { $count } označbi odstranjeni + [few] { $count } označbe odstranjene + *[other] { $count } označb odstranjenih + } +pdfjs-editor-undo-bar-undo-button = + .title = Razveljavi +pdfjs-editor-undo-bar-undo-button-label = Razveljavi +pdfjs-editor-undo-bar-close-button = + .title = Zapri +pdfjs-editor-undo-bar-close-button-label = Zapri diff --git a/public/pdfjs/web/locale/son/viewer.ftl b/public/pdfjs/web/locale/son/viewer.ftl new file mode 100644 index 0000000..fa4f6b1 --- /dev/null +++ b/public/pdfjs/web/locale/son/viewer.ftl @@ -0,0 +1,206 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Moo bisante +pdfjs-previous-button-label = Bisante +pdfjs-next-button = + .title = Jinehere moo +pdfjs-next-button-label = Jine +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Moo +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount } ra +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } ka hun { $pagesCount }) ra +pdfjs-zoom-out-button = + .title = Nakasandi +pdfjs-zoom-out-button-label = Nakasandi +pdfjs-zoom-in-button = + .title = Bebbeerandi +pdfjs-zoom-in-button-label = Bebbeerandi +pdfjs-zoom-select = + .title = Bebbeerandi +pdfjs-presentation-mode-button = + .title = Bere cebeyan alhaali +pdfjs-presentation-mode-button-label = Cebeyan alhaali +pdfjs-open-file-button = + .title = Tuku feeri +pdfjs-open-file-button-label = Feeri +pdfjs-print-button = + .title = Kar +pdfjs-print-button-label = Kar + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Goyjinawey +pdfjs-tools-button-label = Goyjinawey +pdfjs-first-page-button = + .title = Koy moo jinaa ga +pdfjs-first-page-button-label = Koy moo jinaa ga +pdfjs-last-page-button = + .title = Koy moo koraa ga +pdfjs-last-page-button-label = Koy moo koraa ga +pdfjs-page-rotate-cw-button = + .title = Kuubi kanbe guma here +pdfjs-page-rotate-cw-button-label = Kuubi kanbe guma here +pdfjs-page-rotate-ccw-button = + .title = Kuubi kanbe wowa here +pdfjs-page-rotate-ccw-button-label = Kuubi kanbe wowa here + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Takadda mayrawey… +pdfjs-document-properties-button-label = Takadda mayrawey… +pdfjs-document-properties-file-name = Tuku maa: +pdfjs-document-properties-file-size = Tuku adadu: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = KB { $size_kb } (cebsu-ize { $size_b }) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = MB { $size_mb } (cebsu-ize { $size_b }) +pdfjs-document-properties-title = Tiiramaa: +pdfjs-document-properties-author = Hantumkaw: +pdfjs-document-properties-subject = Dalil: +pdfjs-document-properties-keywords = Kufalkalimawey: +pdfjs-document-properties-creation-date = Teeyan han: +pdfjs-document-properties-modification-date = Barmayan han: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Teekaw: +pdfjs-document-properties-producer = PDF berandikaw: +pdfjs-document-properties-version = PDF dumi: +pdfjs-document-properties-page-count = Moo hinna: + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + + +## + +pdfjs-document-properties-close-button = Daabu + +## Print + +pdfjs-print-progress-message = Goo ma takaddaa soolu k'a kar se… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Naŋ +pdfjs-printing-not-supported = Yaamar: Karyan ši tee ka timme nda ceecikaa woo. +pdfjs-printing-not-ready = Yaamar: PDF ši zunbu ka timme karyan še. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Kanjari ceraw zuu +pdfjs-toggle-sidebar-button-label = Kanjari ceraw zuu +pdfjs-document-outline-button = + .title = Takaddaa korfur alhaaloo cebe (naagu cee hinka ka haya-izey kul hayandi/kankamandi) +pdfjs-document-outline-button-label = Takadda filla-boŋ +pdfjs-attachments-button = + .title = Hangarey cebe +pdfjs-attachments-button-label = Hangarey +pdfjs-thumbs-button = + .title = Kabeboy biyey cebe +pdfjs-thumbs-button-label = Kabeboy biyey +pdfjs-findbar-button = + .title = Ceeci takaddaa ra +pdfjs-findbar-button-label = Ceeci + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = { $page } moo +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Kabeboy bii { $page } moo še + +## Find panel button title and messages + +pdfjs-find-input = + .title = Ceeci + .placeholder = Ceeci takaddaa ra… +pdfjs-find-previous-button = + .title = Kalimaɲaŋoo bangayri bisantaa ceeci +pdfjs-find-previous-button-label = Bisante +pdfjs-find-next-button = + .title = Kalimaɲaŋoo hiino bangayroo ceeci +pdfjs-find-next-button-label = Jine +pdfjs-find-highlight-checkbox = Ikul šilbay +pdfjs-find-match-case-checkbox-label = Harfu-beeriyan hawgay +pdfjs-find-reached-top = A too moŋoo boŋoo, koy jine ka šinitin nda cewoo +pdfjs-find-reached-bottom = A too moɲoo cewoo, koy jine šintioo ga +pdfjs-find-not-found = Kalimaɲaa mana duwandi + +## Predefined zoom values + +pdfjs-page-scale-width = Mooo hayyan +pdfjs-page-scale-fit = Moo sawayan +pdfjs-page-scale-auto = Boŋše azzaati barmayyan +pdfjs-page-scale-actual = Adadu cimi +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Firka bangay kaŋ PDF goo ma zumandi. +pdfjs-invalid-file-error = PDF tuku laala wala laybante. +pdfjs-missing-file-error = PDF tuku kumante. +pdfjs-unexpected-response-error = Manti feršikaw tuuruyan maatante. +pdfjs-rendering-error = Firka bangay kaŋ moɲoo goo ma willandi. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = { $type } maasa-caw] + +## Password + +pdfjs-password-label = Šennikufal dam ka PDF tukoo woo feeri. +pdfjs-password-invalid = Šennikufal laalo. Ceeci koyne taare. +pdfjs-password-ok-button = Ayyo +pdfjs-password-cancel-button = Naŋ +pdfjs-web-fonts-disabled = Interneti šigirawey kay: ši hin ka goy nda PDF šigira hurantey. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/sq/viewer.ftl b/public/pdfjs/web/locale/sq/viewer.ftl new file mode 100644 index 0000000..2b1c91a --- /dev/null +++ b/public/pdfjs/web/locale/sq/viewer.ftl @@ -0,0 +1,506 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Faqja e Mëparshme +pdfjs-previous-button-label = E mëparshmja +pdfjs-next-button = + .title = Faqja Pasuese +pdfjs-next-button-label = Pasuesja +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Faqe +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = nga { $pagesCount } gjithsej +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } nga { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zvogëlojeni +pdfjs-zoom-out-button-label = Zvogëlojeni +pdfjs-zoom-in-button = + .title = Zmadhojeni +pdfjs-zoom-in-button-label = Zmadhojini +pdfjs-zoom-select = + .title = Zmadhim/Zvogëlim +pdfjs-presentation-mode-button = + .title = Kalo te Mënyra Paraqitje +pdfjs-presentation-mode-button-label = Mënyra Paraqitje +pdfjs-open-file-button = + .title = Hapni Kartelë +pdfjs-open-file-button-label = Hape +pdfjs-print-button = + .title = Shtypje +pdfjs-print-button-label = Shtype +pdfjs-save-button = + .title = Ruaje +pdfjs-save-button-label = Ruaje +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Shkarkojeni +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Shkarkoje +pdfjs-bookmark-button = + .title = Faqja e Tanishme (Shihni URL nga Faqja e Tanishme) +pdfjs-bookmark-button-label = Faqja e Tanishme + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Mjete +pdfjs-tools-button-label = Mjete +pdfjs-first-page-button = + .title = Kaloni te Faqja e Parë +pdfjs-first-page-button-label = Kaloni te Faqja e Parë +pdfjs-last-page-button = + .title = Kaloni te Faqja e Fundit +pdfjs-last-page-button-label = Kaloni te Faqja e Fundit +pdfjs-page-rotate-cw-button = + .title = Rrotullojeni Në Kahun Orar +pdfjs-page-rotate-cw-button-label = Rrotulloje Në Kahun Orar +pdfjs-page-rotate-ccw-button = + .title = Rrotullojeni Në Kahun Kundërorar +pdfjs-page-rotate-ccw-button-label = Rrotulloje Në Kahun Kundërorar +pdfjs-cursor-text-select-tool-button = + .title = Aktivizo Mjet Përzgjedhjeje Teksti +pdfjs-cursor-text-select-tool-button-label = Mjet Përzgjedhjeje Teksti +pdfjs-cursor-hand-tool-button = + .title = Aktivizo Mjetin Dorë +pdfjs-cursor-hand-tool-button-label = Mjeti Dorë +pdfjs-scroll-page-button = + .title = Përdor Rrëshqitje Në Faqe +pdfjs-scroll-page-button-label = Rrëshqitje Në Faqe +pdfjs-scroll-vertical-button = + .title = Përdor Rrëshqitje Vertikale +pdfjs-scroll-vertical-button-label = Rrëshqitje Vertikale +pdfjs-scroll-horizontal-button = + .title = Përdor Rrëshqitje Horizontale +pdfjs-scroll-horizontal-button-label = Rrëshqitje Horizontale +pdfjs-scroll-wrapped-button = + .title = Përdor Rrëshqitje Me Mbështjellje +pdfjs-scroll-wrapped-button-label = Rrëshqitje Me Mbështjellje + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Veti Dokumenti… +pdfjs-document-properties-button-label = Veti Dokumenti… +pdfjs-document-properties-file-name = Emër kartele: +pdfjs-document-properties-file-size = Madhësi kartele: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bajte) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bajte) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajte) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajte) +pdfjs-document-properties-title = Titull: +pdfjs-document-properties-author = Autor: +pdfjs-document-properties-subject = Subjekt: +pdfjs-document-properties-keywords = Fjalëkyçe: +pdfjs-document-properties-creation-date = Datë Krijimi: +pdfjs-document-properties-modification-date = Datë Ndryshimi: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Krijues: +pdfjs-document-properties-producer = Prodhues PDF-je: +pdfjs-document-properties-version = Version PDF-je: +pdfjs-document-properties-page-count = Numër Faqesh: +pdfjs-document-properties-page-size = Madhësi Faqeje: +pdfjs-document-properties-page-size-unit-inches = inç +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = portret +pdfjs-document-properties-page-size-orientation-landscape = së gjeri +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Parje e Shpjetë në Web: +pdfjs-document-properties-linearized-yes = Po +pdfjs-document-properties-linearized-no = Jo +pdfjs-document-properties-close-button = Mbylleni + +## Print + +pdfjs-print-progress-message = Po përgatitet dokumenti për shtypje… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Anuloje +pdfjs-printing-not-supported = Kujdes: Shtypja s’mbulohet plotësisht nga ky shfletues. +pdfjs-printing-not-ready = Kujdes: PDF-ja s’është ngarkuar plotësisht që ta shtypni. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Shfaqni/Fshihni Anështyllën +pdfjs-toggle-sidebar-notification-button = + .title = Hap/Mbyll Anështylë (dokumenti përmban përvijim/nashkëngjitje/shtresa) +pdfjs-toggle-sidebar-button-label = Shfaq/Fshih Anështyllën +pdfjs-document-outline-button = + .title = Shfaqni Përvijim Dokumenti (dyklikoni që të shfaqen/fshihen krejt elementët) +pdfjs-document-outline-button-label = Përvijim Dokumenti +pdfjs-attachments-button = + .title = Shfaqni Bashkëngjitje +pdfjs-attachments-button-label = Bashkëngjitje +pdfjs-layers-button = + .title = Shfaq Shtresa (dyklikoni që të rikthehen krejt shtresat në gjendjen e tyre parazgjedhje) +pdfjs-layers-button-label = Shtresa +pdfjs-thumbs-button = + .title = Shfaqni Miniatura +pdfjs-thumbs-button-label = Miniatura +pdfjs-current-outline-item-button = + .title = Gjej Objektin e Tanishëm të Përvijuar +pdfjs-current-outline-item-button-label = Objekt i Tanishëm i Përvijuar +pdfjs-findbar-button = + .title = Gjeni në Dokument +pdfjs-findbar-button-label = Gjej +pdfjs-additional-layers = Shtresa Shtesë + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Faqja { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniaturë e Faqes { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Gjej + .placeholder = Gjeni në dokument… +pdfjs-find-previous-button = + .title = Gjeni hasjen e mëparshme të togfjalëshit +pdfjs-find-previous-button-label = E mëparshmja +pdfjs-find-next-button = + .title = Gjeni hasjen pasuese të togfjalëshit +pdfjs-find-next-button-label = Pasuesja +pdfjs-find-highlight-checkbox = Theksoji të tëra +pdfjs-find-match-case-checkbox-label = Siç Është Shkruar +pdfjs-find-match-diacritics-checkbox-label = Me Përputhje Me Shenjat Diakritike +pdfjs-find-entire-word-checkbox-label = Fjalë të Plota +pdfjs-find-reached-top = U mbërrit në krye të dokumentit, vazhduar prej fundit +pdfjs-find-reached-bottom = U mbërrit në fund të dokumentit, vazhduar prej kreut +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } nga { $total } përputhje + *[other] { $current } nga { $total } përputhje + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Më tepër se { $limit } përputhje + *[other] Më tepër se { $limit } përputhje + } +pdfjs-find-not-found = Togfjalësh që s’gjendet + +## Predefined zoom values + +pdfjs-page-scale-width = Gjerësi Faqeje +pdfjs-page-scale-fit = Sa Nxë Faqja +pdfjs-page-scale-auto = Zoom i Vetvetishëm +pdfjs-page-scale-actual = Madhësia Faktike +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Faqja { $page } + +## Loading indicator messages + +pdfjs-loading-error = Ndodhi një gabim gjatë ngarkimit të PDF-së. +pdfjs-invalid-file-error = Kartelë PDF e pavlefshme ose e dëmtuar. +pdfjs-missing-file-error = Kartelë PDF që mungon. +pdfjs-unexpected-response-error = Përgjigje shërbyesi e papritur. +pdfjs-rendering-error = Ndodhi një gabim gjatë riprodhimit të faqes. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Nënvizim { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Jepni fjalëkalimin që të hapet kjo kartelë PDF. +pdfjs-password-invalid = Fjalëkalim i pavlefshëm. Ju lutemi, riprovoni. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Anuloje +pdfjs-web-fonts-disabled = Shkronjat Web janë të çaktivizuara: s’arrihet të përdoren shkronja të trupëzuara në PDF. + +## Editing + +pdfjs-editor-free-text-button = + .title = Tekst +pdfjs-editor-free-text-button-label = Tekst +pdfjs-editor-ink-button = + .title = Vizatoni +pdfjs-editor-ink-button-label = Vizatoni +pdfjs-editor-stamp-button = + .title = Shtoni ose përpunoni figura +pdfjs-editor-stamp-button-label = Shtoni ose përpunoni figura +pdfjs-editor-highlight-button = + .title = Theksim +pdfjs-editor-highlight-button-label = Theksoje +pdfjs-highlight-floating-button1 = + .title = Theksim + .aria-label = Theksim +pdfjs-highlight-floating-button-label = Theksim + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Hiq vizatim +pdfjs-editor-remove-freetext-button = + .title = Hiq tekst +pdfjs-editor-remove-stamp-button = + .title = Hiq figurë +pdfjs-editor-remove-highlight-button = + .title = Hiqe theksimin + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Ngjyrë +pdfjs-editor-free-text-size-input = Madhësi +pdfjs-editor-ink-color-input = Ngjyrë +pdfjs-editor-ink-thickness-input = Trashësi +pdfjs-editor-ink-opacity-input = Patejdukshmëri +pdfjs-editor-stamp-add-image-button = + .title = Shtoni figurë +pdfjs-editor-stamp-add-image-button-label = Shtoni figurë +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Trashësi +pdfjs-editor-free-highlight-thickness-title = + .title = Ndryshoni trashësinë kur theksoni objekte tjetër nga tekst +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Përpunues Tekstesh + .default-content = Filloni të shtypni… +pdfjs-free-text = + .aria-label = Përpunues Tekstesh +pdfjs-free-text-default-content = Filloni të shtypni… +pdfjs-ink = + .aria-label = Përpunues Vizatimesh +pdfjs-ink-canvas = + .aria-label = Figurë e krijuar nga përdoruesi + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Tekst alternativ +pdfjs-editor-alt-text-edit-button = + .aria-label = Përpunoni tekst alternativ +pdfjs-editor-alt-text-edit-button-label = Përpunoni tekst alternativ +pdfjs-editor-alt-text-dialog-label = Zgjidhni një mundësi +pdfjs-editor-alt-text-dialog-description = Teksti alt (tekst alternativ) vjen në ndihmë kur njerëzit s’mund të shohin figurën, ose kur ajo nuk ngarkohet. +pdfjs-editor-alt-text-add-description-label = Shtoni një përshkrim +pdfjs-editor-alt-text-add-description-description = Synoni për 1-2 togfjalësha që përshkruajnë subjektin, rrethanat apo veprimet. +pdfjs-editor-alt-text-mark-decorative-label = Vëri shenjë si dekorative +pdfjs-editor-alt-text-mark-decorative-description = Kjo përdoret për figura zbukuruese, fjala vjen, anë, ose watermark-e. +pdfjs-editor-alt-text-cancel-button = Anuloje +pdfjs-editor-alt-text-save-button = Ruaje +pdfjs-editor-alt-text-decorative-tooltip = Iu vu shenjë si dekorative +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Për shembull, “Një djalosh ulet në një tryezë të hajë” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Tekst alternativ + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Cepi i sipërm majtas — ripërmasojeni +pdfjs-editor-resizer-label-top-middle = Mesi i pjesës sipër — ripërmasojeni +pdfjs-editor-resizer-label-top-right = Cepi i sipërm djathtas — ripërmasojeni +pdfjs-editor-resizer-label-middle-right = Djathtas në mes — ripërmasojeni +pdfjs-editor-resizer-label-bottom-right = Cepi i poshtëm djathtas — ripërmasojeni +pdfjs-editor-resizer-label-bottom-middle = Mesi i pjesës poshtë — ripërmasojeni +pdfjs-editor-resizer-label-bottom-left = Cepi i poshtëm — ripërmasojeni +pdfjs-editor-resizer-label-middle-left = Majtas në mes — ripërmasojeni +pdfjs-editor-resizer-top-left = + .aria-label = Cepi i sipërm majtas — ripërmasojeni +pdfjs-editor-resizer-top-middle = + .aria-label = Mesi i pjesës sipër — ripërmasojeni +pdfjs-editor-resizer-top-right = + .aria-label = Cepi i sipërm djathtas — ripërmasojeni +pdfjs-editor-resizer-middle-right = + .aria-label = Djathtas në mes — ripërmasojeni +pdfjs-editor-resizer-bottom-right = + .aria-label = Cepi i poshtëm djathtas — ripërmasojeni +pdfjs-editor-resizer-bottom-middle = + .aria-label = Mesi i pjesës poshtë — ripërmasojeni +pdfjs-editor-resizer-bottom-left = + .aria-label = Cepi i poshtëm — ripërmasojeni +pdfjs-editor-resizer-middle-left = + .aria-label = Majtas në mes — ripërmasojeni + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Ngjyrë theksimi +pdfjs-editor-colorpicker-button = + .title = Ndryshoni ngjyrë +pdfjs-editor-colorpicker-dropdown = + .aria-label = Zgjedhje ngjyre +pdfjs-editor-colorpicker-yellow = + .title = E verdhë +pdfjs-editor-colorpicker-green = + .title = E gjelbër +pdfjs-editor-colorpicker-blue = + .title = Blu +pdfjs-editor-colorpicker-pink = + .title = Rozë +pdfjs-editor-colorpicker-red = + .title = E kuqe + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Shfaqi krejt +pdfjs-editor-highlight-show-all-button = + .title = Shfaqi krejt + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Përpunoni tekst alternativ (përshkrim figure) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Shtoni tekst alternativ (përshkrim figure) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Shkruani këtu përshkrimin tuaj… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Përshkrim i shkurtër për persona që s’munden të shohin figurën, ose për kur figura nuk ngarkohet dot. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Ky tekst alternativ qe krijuar automatikisht dhe mund të jetë i pasaktë. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Mësoni më tepër +pdfjs-editor-new-alt-text-create-automatically-button-label = Krijo automatikisht tekst alternativ +pdfjs-editor-new-alt-text-not-now-button = Jo tani +pdfjs-editor-new-alt-text-error-title = S’u krijua dot automatikisht tekst alternativ +pdfjs-editor-new-alt-text-error-description = Ju lutemi, shkruani tekstin tuaj alternativ, ose riprovoni më vonë. +pdfjs-editor-new-alt-text-error-close-button = Mbylle +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Po shkarkohet model IA teksti alternativ ({ $downloadedSize } nga { $totalSize } MB) + .aria-valuetext = Po shkarkohet model IA teksti alternativ ({ $downloadedSize } nga { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = U shtua tekst alternativ +pdfjs-editor-new-alt-text-added-button-label = U shtua tekst alternativ +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Mungon tekst alternativ +pdfjs-editor-new-alt-text-missing-button-label = Mungon tekst alternativ +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Shqyrtoni tekst alternativ +pdfjs-editor-new-alt-text-to-review-button-label = Shqyrtoni tekst alternativ +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Krijuar automatikisht: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Rregullime teksti alternativ figure +pdfjs-image-alt-text-settings-button-label = Rregullime teksti alternativ figure +pdfjs-editor-alt-text-settings-dialog-label = Rregullime teksti alternativ figure +pdfjs-editor-alt-text-settings-automatic-title = Tekst alternativ i automatizuar +pdfjs-editor-alt-text-settings-create-model-button-label = Krijo automatikisht tekst alternativ +pdfjs-editor-alt-text-settings-create-model-description = Sugjeron përshkrime, për të ndihmuar persona që s’munden të shohin figurën, ose për kur figura nuk ngarkohet dot. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Model IA teksti alternativ ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Xhiron lokalisht në pajisjen tuaj, pra të dhënat tuaja mbeten private. E domosdoshme për tekst të automatizuar alternativ. +pdfjs-editor-alt-text-settings-delete-model-button = Fshije +pdfjs-editor-alt-text-settings-download-model-button = Shkarkoje +pdfjs-editor-alt-text-settings-downloading-model-button = Po shkarkohet… +pdfjs-editor-alt-text-settings-editor-title = Përpunues teksti alternativ +pdfjs-editor-alt-text-settings-show-dialog-button-label = Shfaq menjëherë përpunues teksti alternativ, kur shtohet një figurë +pdfjs-editor-alt-text-settings-show-dialog-description = Ju ndihmon të siguroheni se krejt figurat tuaja kanë tekst alternativ. +pdfjs-editor-alt-text-settings-close-button = Mbylle + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = U hoq theksimi +pdfjs-editor-undo-bar-message-freetext = U hoq tekst +pdfjs-editor-undo-bar-message-ink = U hoq vizatim +pdfjs-editor-undo-bar-message-stamp = U hoq figurë +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] U hoq { $count } shënim + *[other] U hoqën { $count } shënime + } +pdfjs-editor-undo-bar-undo-button = + .title = Zhbëje +pdfjs-editor-undo-bar-undo-button-label = Zhbëje +pdfjs-editor-undo-bar-close-button = + .title = Mbylle +pdfjs-editor-undo-bar-close-button-label = Mbylle diff --git a/public/pdfjs/web/locale/sr/viewer.ftl b/public/pdfjs/web/locale/sr/viewer.ftl new file mode 100644 index 0000000..e125dfb --- /dev/null +++ b/public/pdfjs/web/locale/sr/viewer.ftl @@ -0,0 +1,421 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Претходна страница +pdfjs-previous-button-label = Претходна +pdfjs-next-button = + .title = Следећа страница +pdfjs-next-button-label = Следећа +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Страница +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = од { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } од { $pagesCount }) +pdfjs-zoom-out-button = + .title = Умањи +pdfjs-zoom-out-button-label = Умањи +pdfjs-zoom-in-button = + .title = Увеличај +pdfjs-zoom-in-button-label = Увеличај +pdfjs-zoom-select = + .title = Увеличавање +pdfjs-presentation-mode-button = + .title = Промени на приказ у режиму презентације +pdfjs-presentation-mode-button-label = Режим презентације +pdfjs-open-file-button = + .title = Отвори датотеку +pdfjs-open-file-button-label = Отвори +pdfjs-print-button = + .title = Штампај +pdfjs-print-button-label = Штампај +pdfjs-save-button = + .title = Сачувај +pdfjs-save-button-label = Сачувај +pdfjs-bookmark-button = + .title = Тренутна страница (погледајте URL са тренутне странице) +pdfjs-bookmark-button-label = Тренутна страница + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Алатке +pdfjs-tools-button-label = Алатке +pdfjs-first-page-button = + .title = Иди на прву страницу +pdfjs-first-page-button-label = Иди на прву страницу +pdfjs-last-page-button = + .title = Иди на последњу страницу +pdfjs-last-page-button-label = Иди на последњу страницу +pdfjs-page-rotate-cw-button = + .title = Ротирај у смеру казаљке на сату +pdfjs-page-rotate-cw-button-label = Ротирај у смеру казаљке на сату +pdfjs-page-rotate-ccw-button = + .title = Ротирај у смеру супротном од казаљке на сату +pdfjs-page-rotate-ccw-button-label = Ротирај у смеру супротном од казаљке на сату +pdfjs-cursor-text-select-tool-button = + .title = Омогући алат за селектовање текста +pdfjs-cursor-text-select-tool-button-label = Алат за селектовање текста +pdfjs-cursor-hand-tool-button = + .title = Омогући алат за померање +pdfjs-cursor-hand-tool-button-label = Алат за померање +pdfjs-scroll-page-button = + .title = Користи скроловање по омоту +pdfjs-scroll-page-button-label = Скроловање странице +pdfjs-scroll-vertical-button = + .title = Користи вертикално скроловање +pdfjs-scroll-vertical-button-label = Вертикално скроловање +pdfjs-scroll-horizontal-button = + .title = Користи хоризонтално скроловање +pdfjs-scroll-horizontal-button-label = Хоризонтално скроловање +pdfjs-scroll-wrapped-button = + .title = Користи скроловање по омоту +pdfjs-scroll-wrapped-button-label = Скроловање по омоту +pdfjs-spread-none-button = + .title = Немој спајати ширења страница +pdfjs-spread-none-button-label = Без распростирања +pdfjs-spread-odd-button = + .title = Споји ширења страница које почињу непарним бројем +pdfjs-spread-odd-button-label = Непарна распростирања +pdfjs-spread-even-button = + .title = Споји ширења страница које почињу парним бројем +pdfjs-spread-even-button-label = Парна распростирања + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Параметри документа… +pdfjs-document-properties-button-label = Параметри документа… +pdfjs-document-properties-file-name = Име датотеке: +pdfjs-document-properties-file-size = Величина датотеке: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } B) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } B) +pdfjs-document-properties-title = Наслов: +pdfjs-document-properties-author = Аутор: +pdfjs-document-properties-subject = Тема: +pdfjs-document-properties-keywords = Кључне речи: +pdfjs-document-properties-creation-date = Датум креирања: +pdfjs-document-properties-modification-date = Датум модификације: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Стваралац: +pdfjs-document-properties-producer = PDF произвођач: +pdfjs-document-properties-version = PDF верзија: +pdfjs-document-properties-page-count = Број страница: +pdfjs-document-properties-page-size = Величина странице: +pdfjs-document-properties-page-size-unit-inches = ин +pdfjs-document-properties-page-size-unit-millimeters = мм +pdfjs-document-properties-page-size-orientation-portrait = усправно +pdfjs-document-properties-page-size-orientation-landscape = водоравно +pdfjs-document-properties-page-size-name-a-three = А3 +pdfjs-document-properties-page-size-name-a-four = А4 +pdfjs-document-properties-page-size-name-letter = Слово +pdfjs-document-properties-page-size-name-legal = Права + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Брз веб приказ: +pdfjs-document-properties-linearized-yes = Да +pdfjs-document-properties-linearized-no = Не +pdfjs-document-properties-close-button = Затвори + +## Print + +pdfjs-print-progress-message = Припремам документ за штампање… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Откажи +pdfjs-printing-not-supported = Упозорење: Штампање није у потпуности подржано у овом прегледачу. +pdfjs-printing-not-ready = Упозорење: PDF није у потпуности учитан за штампу. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Прикажи/сакриј бочни панел +pdfjs-toggle-sidebar-notification-button = + .title = Прикажи/сакриј бочни панел (документ садржи контуру/прилоге/слојеве) +pdfjs-toggle-sidebar-button-label = Прикажи/сакриј бочни панел +pdfjs-document-outline-button = + .title = Прикажи структуру документа (двоструким кликом проширујете/скупљате све ставке) +pdfjs-document-outline-button-label = Контура документа +pdfjs-attachments-button = + .title = Прикажи прилоге +pdfjs-attachments-button-label = Прилози +pdfjs-layers-button = + .title = Прикажи слојеве (дупли клик за враћање свих слојева у подразумевано стање) +pdfjs-layers-button-label = Слојеви +pdfjs-thumbs-button = + .title = Прикажи сличице +pdfjs-thumbs-button-label = Сличице +pdfjs-current-outline-item-button = + .title = Пронађите тренутни елемент структуре +pdfjs-current-outline-item-button-label = Тренутна контура +pdfjs-findbar-button = + .title = Пронађи у документу +pdfjs-findbar-button-label = Пронађи +pdfjs-additional-layers = Додатни слојеви + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Страница { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Сличица од странице { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Пронађи + .placeholder = Пронађи у документу… +pdfjs-find-previous-button = + .title = Пронађи претходно појављивање фразе +pdfjs-find-previous-button-label = Претходна +pdfjs-find-next-button = + .title = Пронађи следеће појављивање фразе +pdfjs-find-next-button-label = Следећа +pdfjs-find-highlight-checkbox = Истакнути све +pdfjs-find-match-case-checkbox-label = Подударања +pdfjs-find-match-diacritics-checkbox-label = Дијакритика +pdfjs-find-entire-word-checkbox-label = Целе речи +pdfjs-find-reached-top = Достигнут врх документа, наставио са дна +pdfjs-find-reached-bottom = Достигнуто дно документа, наставио са врха +pdfjs-find-not-found = Фраза није пронађена + +## Predefined zoom values + +pdfjs-page-scale-width = Ширина странице +pdfjs-page-scale-fit = Прилагоди страницу +pdfjs-page-scale-auto = Аутоматско увеличавање +pdfjs-page-scale-actual = Стварна величина +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Страница { $page } + +## Loading indicator messages + +pdfjs-loading-error = Дошло је до грешке приликом учитавања PDF-а. +pdfjs-invalid-file-error = PDF датотека је неважећа или је оштећена. +pdfjs-missing-file-error = Недостаје PDF датотека. +pdfjs-unexpected-response-error = Неочекиван одговор од сервера. +pdfjs-rendering-error = Дошло је до грешке приликом рендеровања ове странице. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } коментар] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Унесите лозинку да бисте отворили овај PDF докуменат. +pdfjs-password-invalid = Неисправна лозинка. Покушајте поново. +pdfjs-password-ok-button = У реду +pdfjs-password-cancel-button = Откажи +pdfjs-web-fonts-disabled = Веб фонтови су онемогућени: не могу користити уграђене PDF фонтове. + +## Editing + +pdfjs-editor-free-text-button = + .title = Текст +pdfjs-editor-free-text-button-label = Текст +pdfjs-editor-ink-button = + .title = Цртај +pdfjs-editor-ink-button-label = Цртај +pdfjs-editor-stamp-button = + .title = Додај или уреди слике +pdfjs-editor-stamp-button-label = Додај или уреди слике +pdfjs-editor-highlight-button = + .title = Означи +pdfjs-editor-highlight-button-label = Означи +pdfjs-highlight-floating-button1 = + .title = Означи + .aria-label = Означи +pdfjs-highlight-floating-button-label = Означи + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Уклони цртеж +pdfjs-editor-remove-freetext-button = + .title = Уклони текст +pdfjs-editor-remove-stamp-button = + .title = Уклони слику +pdfjs-editor-remove-highlight-button = + .title = Уклони ознаку + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Боја +pdfjs-editor-free-text-size-input = Величина +pdfjs-editor-ink-color-input = Боја +pdfjs-editor-ink-thickness-input = Дебљина +pdfjs-editor-ink-opacity-input = Опацитет +pdfjs-editor-stamp-add-image-button = + .title = Додај слику +pdfjs-editor-stamp-add-image-button-label = Додај слику +pdfjs-editor-free-highlight-thickness-title = + .title = Промени дебљину при означавању других ставки сем текста +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Уређивач текста + .default-content = Почни куцати… +pdfjs-free-text = + .aria-label = Уређивач текста +pdfjs-free-text-default-content = Почни куцање… +pdfjs-ink = + .aria-label = Уређивач цртежа +pdfjs-ink-canvas = + .aria-label = Кориснички направљена слика + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Алтернативни текст +pdfjs-editor-alt-text-edit-button = + .aria-label = Уреди алтернативни текст +pdfjs-editor-alt-text-edit-button-label = Уреди алтернативни текст +pdfjs-editor-alt-text-dialog-label = Одабери опцију +pdfjs-editor-alt-text-dialog-description = Алтернативни текст помаже слепим и слабовидим особама или када се слика не учита. +pdfjs-editor-alt-text-add-description-label = Додај опис +pdfjs-editor-alt-text-add-description-description = Сажмите у 1-2 реченице које описују предмет, окружење или радње. +pdfjs-editor-alt-text-mark-decorative-label = Означи као украсно +pdfjs-editor-alt-text-mark-decorative-description = Ово је за украсне слике, као што су ивице или водени печати. +pdfjs-editor-alt-text-cancel-button = Откажи +pdfjs-editor-alt-text-save-button = Сачувај +pdfjs-editor-alt-text-decorative-tooltip = Означено као украсно +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = На пример: „Младић седа за сто да једе“ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Алтернативни текст + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Горњи леви угао — промени величину +pdfjs-editor-resizer-label-top-middle = Средина горе — промени величину +pdfjs-editor-resizer-label-top-right = Горњи десни угао — промени величину +pdfjs-editor-resizer-label-middle-right = Средина десно — промени величину +pdfjs-editor-resizer-label-bottom-right = Доњи десни угао — промени величину +pdfjs-editor-resizer-label-bottom-middle = Средина доле — промени величину +pdfjs-editor-resizer-label-bottom-left = Доњи леви угао — промени величину +pdfjs-editor-resizer-label-middle-left = Средина лево — промени величину +pdfjs-editor-resizer-top-left = + .aria-label = Горњи леви угао — промени величину +pdfjs-editor-resizer-top-middle = + .aria-label = Средина горе — промени величину +pdfjs-editor-resizer-top-right = + .aria-label = Горњи десни угао — промени величину +pdfjs-editor-resizer-middle-right = + .aria-label = Средина десно — промени величину +pdfjs-editor-resizer-bottom-right = + .aria-label = Доњи десни угао — промени величину +pdfjs-editor-resizer-bottom-middle = + .aria-label = Средина доле — промени величину +pdfjs-editor-resizer-bottom-left = + .aria-label = Доњи леви угао — промени величину +pdfjs-editor-resizer-middle-left = + .aria-label = Средина лево — промени величину + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Боја означавања +pdfjs-editor-colorpicker-button = + .title = Промени боју +pdfjs-editor-colorpicker-dropdown = + .aria-label = Избор боја +pdfjs-editor-colorpicker-yellow = + .title = Жута +pdfjs-editor-colorpicker-green = + .title = Зелена +pdfjs-editor-colorpicker-blue = + .title = Плава +pdfjs-editor-colorpicker-pink = + .title = Розе +pdfjs-editor-colorpicker-red = + .title = Црвена + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Прикажи све +pdfjs-editor-highlight-show-all-button = + .title = Прикажи све + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Уреди алтернативни текст (опис слике) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Додај алтернативни текст (опис слике) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Напиши опис овде… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Кратак опис за слепе и слабовиде људе или када се слика не успе учитати. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Овај алтернативни текст је направљен аутоматски и може бити нетачан. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Сазнајте више +pdfjs-editor-new-alt-text-create-automatically-button-label = Прави алтернативни текст аутоматски +pdfjs-editor-new-alt-text-not-now-button = Не сада + +## Image alt-text settings + diff --git a/public/pdfjs/web/locale/sv-SE/viewer.ftl b/public/pdfjs/web/locale/sv-SE/viewer.ftl new file mode 100644 index 0000000..6c4c610 --- /dev/null +++ b/public/pdfjs/web/locale/sv-SE/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Föregående sida +pdfjs-previous-button-label = Föregående +pdfjs-next-button = + .title = Nästa sida +pdfjs-next-button-label = Nästa +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Sida +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = av { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } av { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zooma ut +pdfjs-zoom-out-button-label = Zooma ut +pdfjs-zoom-in-button = + .title = Zooma in +pdfjs-zoom-in-button-label = Zooma in +pdfjs-zoom-select = + .title = Zoom +pdfjs-presentation-mode-button = + .title = Byt till presentationsläge +pdfjs-presentation-mode-button-label = Presentationsläge +pdfjs-open-file-button = + .title = Öppna fil +pdfjs-open-file-button-label = Öppna +pdfjs-print-button = + .title = Skriv ut +pdfjs-print-button-label = Skriv ut +pdfjs-save-button = + .title = Spara +pdfjs-save-button-label = Spara +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Hämta +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Hämta +pdfjs-bookmark-button = + .title = Aktuell sida (Visa URL från aktuell sida) +pdfjs-bookmark-button-label = Aktuell sida + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Verktyg +pdfjs-tools-button-label = Verktyg +pdfjs-first-page-button = + .title = Gå till första sidan +pdfjs-first-page-button-label = Gå till första sidan +pdfjs-last-page-button = + .title = Gå till sista sidan +pdfjs-last-page-button-label = Gå till sista sidan +pdfjs-page-rotate-cw-button = + .title = Rotera medurs +pdfjs-page-rotate-cw-button-label = Rotera medurs +pdfjs-page-rotate-ccw-button = + .title = Rotera moturs +pdfjs-page-rotate-ccw-button-label = Rotera moturs +pdfjs-cursor-text-select-tool-button = + .title = Aktivera textmarkeringsverktyg +pdfjs-cursor-text-select-tool-button-label = Textmarkeringsverktyg +pdfjs-cursor-hand-tool-button = + .title = Aktivera handverktyg +pdfjs-cursor-hand-tool-button-label = Handverktyg +pdfjs-scroll-page-button = + .title = Använd sidrullning +pdfjs-scroll-page-button-label = Sidrullning +pdfjs-scroll-vertical-button = + .title = Använd vertikal rullning +pdfjs-scroll-vertical-button-label = Vertikal rullning +pdfjs-scroll-horizontal-button = + .title = Använd horisontell rullning +pdfjs-scroll-horizontal-button-label = Horisontell rullning +pdfjs-scroll-wrapped-button = + .title = Använd överlappande rullning +pdfjs-scroll-wrapped-button-label = Överlappande rullning +pdfjs-spread-none-button = + .title = Visa enkelsidor +pdfjs-spread-none-button-label = Enkelsidor +pdfjs-spread-odd-button = + .title = Visa uppslag med olika sidnummer till vänster +pdfjs-spread-odd-button-label = Uppslag med framsida +pdfjs-spread-even-button = + .title = Visa uppslag med lika sidnummer till vänster +pdfjs-spread-even-button-label = Uppslag utan framsida + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Dokumentegenskaper… +pdfjs-document-properties-button-label = Dokumentegenskaper… +pdfjs-document-properties-file-name = Filnamn: +pdfjs-document-properties-file-size = Filstorlek: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } kB ({ $b } byte) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } byte) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } byte) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } byte) +pdfjs-document-properties-title = Titel: +pdfjs-document-properties-author = Författare: +pdfjs-document-properties-subject = Ämne: +pdfjs-document-properties-keywords = Nyckelord: +pdfjs-document-properties-creation-date = Skapades: +pdfjs-document-properties-modification-date = Ändrades: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Skapare: +pdfjs-document-properties-producer = PDF-producent: +pdfjs-document-properties-version = PDF-version: +pdfjs-document-properties-page-count = Sidantal: +pdfjs-document-properties-page-size = Pappersstorlek: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = porträtt +pdfjs-document-properties-page-size-orientation-landscape = landskap +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Snabb webbvisning: +pdfjs-document-properties-linearized-yes = Ja +pdfjs-document-properties-linearized-no = Nej +pdfjs-document-properties-close-button = Stäng + +## Print + +pdfjs-print-progress-message = Förbereder sidor för utskrift… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Avbryt +pdfjs-printing-not-supported = Varning: Utskrifter stöds inte helt av den här webbläsaren. +pdfjs-printing-not-ready = Varning: PDF:en är inte klar för utskrift. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Visa/dölj sidofält +pdfjs-toggle-sidebar-notification-button = + .title = Växla sidofält (dokumentet innehåller dokumentstruktur/bilagor/lager) +pdfjs-toggle-sidebar-button-label = Visa/dölj sidofält +pdfjs-document-outline-button = + .title = Visa dokumentdisposition (dubbelklicka för att expandera/komprimera alla objekt) +pdfjs-document-outline-button-label = Dokumentöversikt +pdfjs-attachments-button = + .title = Visa Bilagor +pdfjs-attachments-button-label = Bilagor +pdfjs-layers-button = + .title = Visa lager (dubbelklicka för att återställa alla lager till standardläge) +pdfjs-layers-button-label = Lager +pdfjs-thumbs-button = + .title = Visa miniatyrer +pdfjs-thumbs-button-label = Miniatyrer +pdfjs-current-outline-item-button = + .title = Hitta aktuellt dispositionsobjekt +pdfjs-current-outline-item-button-label = Aktuellt dispositionsobjekt +pdfjs-findbar-button = + .title = Sök i dokument +pdfjs-findbar-button-label = Sök +pdfjs-additional-layers = Ytterligare lager + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Sida { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatyr av sida { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Sök + .placeholder = Sök i dokument… +pdfjs-find-previous-button = + .title = Hitta föregående förekomst av frasen +pdfjs-find-previous-button-label = Föregående +pdfjs-find-next-button = + .title = Hitta nästa förekomst av frasen +pdfjs-find-next-button-label = Nästa +pdfjs-find-highlight-checkbox = Markera alla +pdfjs-find-match-case-checkbox-label = Matcha versal/gemen +pdfjs-find-match-diacritics-checkbox-label = Matcha diakritiska tecken +pdfjs-find-entire-word-checkbox-label = Hela ord +pdfjs-find-reached-top = Nådde början av dokumentet, började från slutet +pdfjs-find-reached-bottom = Nådde slutet på dokumentet, började från början +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } av { $total } match + *[other] { $current } av { $total } matchningar + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Mer än { $limit } matchning + *[other] Fler än { $limit } matchningar + } +pdfjs-find-not-found = Frasen hittades inte + +## Predefined zoom values + +pdfjs-page-scale-width = Sidbredd +pdfjs-page-scale-fit = Anpassa sida +pdfjs-page-scale-auto = Automatisk zoom +pdfjs-page-scale-actual = Verklig storlek +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Sida { $page } + +## Loading indicator messages + +pdfjs-loading-error = Ett fel uppstod vid laddning av PDF-filen. +pdfjs-invalid-file-error = Ogiltig eller korrupt PDF-fil. +pdfjs-missing-file-error = Saknad PDF-fil. +pdfjs-unexpected-response-error = Oväntat svar från servern. +pdfjs-rendering-error = Ett fel uppstod vid visning av sidan. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date } { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type }-annotering] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Skriv in lösenordet för att öppna PDF-filen. +pdfjs-password-invalid = Ogiltigt lösenord. Försök igen. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Avbryt +pdfjs-web-fonts-disabled = Webbtypsnitt är inaktiverade: kan inte använda inbäddade PDF-typsnitt. + +## Editing + +pdfjs-editor-free-text-button = + .title = Text +pdfjs-editor-free-text-button-label = Text +pdfjs-editor-ink-button = + .title = Rita +pdfjs-editor-ink-button-label = Rita +pdfjs-editor-stamp-button = + .title = Lägg till eller redigera bilder +pdfjs-editor-stamp-button-label = Lägg till eller redigera bilder +pdfjs-editor-highlight-button = + .title = Markera +pdfjs-editor-highlight-button-label = Markera +pdfjs-highlight-floating-button1 = + .title = Markera + .aria-label = Markera +pdfjs-highlight-floating-button-label = Markera + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Ta bort ritning +pdfjs-editor-remove-freetext-button = + .title = Ta bort text +pdfjs-editor-remove-stamp-button = + .title = Ta bort bild +pdfjs-editor-remove-highlight-button = + .title = Ta bort markering + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Färg +pdfjs-editor-free-text-size-input = Storlek +pdfjs-editor-ink-color-input = Färg +pdfjs-editor-ink-thickness-input = Tjocklek +pdfjs-editor-ink-opacity-input = Opacitet +pdfjs-editor-stamp-add-image-button = + .title = Lägg till bild +pdfjs-editor-stamp-add-image-button-label = Lägg till bild +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Tjocklek +pdfjs-editor-free-highlight-thickness-title = + .title = Ändra tjocklek när du markerar andra objekt än text +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Textredigerare + .default-content = Börja skriva… +pdfjs-free-text = + .aria-label = Textredigerare +pdfjs-free-text-default-content = Börja skriva… +pdfjs-ink = + .aria-label = Ritredigerare +pdfjs-ink-canvas = + .aria-label = Användarskapad bild + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alternativ text +pdfjs-editor-alt-text-edit-button = + .aria-label = Redigera alternativ text +pdfjs-editor-alt-text-edit-button-label = Redigera alternativ text +pdfjs-editor-alt-text-dialog-label = Välj ett alternativ +pdfjs-editor-alt-text-dialog-description = Alt text (alternativ text) hjälper till när människor inte kan se bilden eller när den inte laddas. +pdfjs-editor-alt-text-add-description-label = Lägg till en beskrivning +pdfjs-editor-alt-text-add-description-description = Sikta på 1-2 meningar som beskriver ämnet, miljön eller handlingen. +pdfjs-editor-alt-text-mark-decorative-label = Markera som dekorativ +pdfjs-editor-alt-text-mark-decorative-description = Detta används för dekorativa bilder, som kanter eller vattenstämplar. +pdfjs-editor-alt-text-cancel-button = Avbryt +pdfjs-editor-alt-text-save-button = Spara +pdfjs-editor-alt-text-decorative-tooltip = Märkt som dekorativ +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Till exempel, "En ung man sätter sig vid ett bord för att äta en måltid" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternativ text + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Det övre vänstra hörnet — ändra storlek +pdfjs-editor-resizer-label-top-middle = Överst i mitten — ändra storlek +pdfjs-editor-resizer-label-top-right = Det övre högra hörnet — ändra storlek +pdfjs-editor-resizer-label-middle-right = Mitten höger — ändra storlek +pdfjs-editor-resizer-label-bottom-right = Nedre högra hörnet — ändra storlek +pdfjs-editor-resizer-label-bottom-middle = Nedre mitten — ändra storlek +pdfjs-editor-resizer-label-bottom-left = Nedre vänstra hörnet — ändra storlek +pdfjs-editor-resizer-label-middle-left = Mitten till vänster — ändra storlek +pdfjs-editor-resizer-top-left = + .aria-label = Det övre vänstra hörnet — ändra storlek +pdfjs-editor-resizer-top-middle = + .aria-label = Överst i mitten — ändra storlek +pdfjs-editor-resizer-top-right = + .aria-label = Det övre högra hörnet — ändra storlek +pdfjs-editor-resizer-middle-right = + .aria-label = Mitten höger — ändra storlek +pdfjs-editor-resizer-bottom-right = + .aria-label = Nedre högra hörnet — ändra storlek +pdfjs-editor-resizer-bottom-middle = + .aria-label = Nedre mitten — ändra storlek +pdfjs-editor-resizer-bottom-left = + .aria-label = Nedre vänstra hörnet — ändra storlek +pdfjs-editor-resizer-middle-left = + .aria-label = Mitten till vänster — ändra storlek + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Markeringsfärg +pdfjs-editor-colorpicker-button = + .title = Ändra färg +pdfjs-editor-colorpicker-dropdown = + .aria-label = Färgval +pdfjs-editor-colorpicker-yellow = + .title = Gul +pdfjs-editor-colorpicker-green = + .title = Grön +pdfjs-editor-colorpicker-blue = + .title = Blå +pdfjs-editor-colorpicker-pink = + .title = Rosa +pdfjs-editor-colorpicker-red = + .title = Röd + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Visa alla +pdfjs-editor-highlight-show-all-button = + .title = Visa alla + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Redigera alternativ text (bildbeskrivning) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Lägg till alternativ text (bildbeskrivning) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Skriv din beskrivning här… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Kort beskrivning för personer som inte kan se bilden eller när bilden inte laddas. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Denna alternativa text skapades automatiskt och kan vara felaktig. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Läs mer +pdfjs-editor-new-alt-text-create-automatically-button-label = Skapa alternativ text automatiskt +pdfjs-editor-new-alt-text-not-now-button = Inte nu +pdfjs-editor-new-alt-text-error-title = Det gick inte att skapa alternativ text automatiskt +pdfjs-editor-new-alt-text-error-description = Skriv din egna alternativa text eller försök igen senare. +pdfjs-editor-new-alt-text-error-close-button = Stäng +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Hämtar AI-modell med alternativ text ({ $downloadedSize } av { $totalSize } MB) + .aria-valuetext = Hämtar AI-modell med alternativ text ({ $downloadedSize } av { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternativ text tillagd +pdfjs-editor-new-alt-text-added-button-label = Alternativ text tillagd +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Saknar alternativ text +pdfjs-editor-new-alt-text-missing-button-label = Saknar alternativ text +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Granska alternativ text +pdfjs-editor-new-alt-text-to-review-button-label = Granska alternativ text +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Skapas automatiskt: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Alternativ textinställningar för bild +pdfjs-image-alt-text-settings-button-label = Alternativ textinställningar för bild +pdfjs-editor-alt-text-settings-dialog-label = Alternativ textinställningar för bild +pdfjs-editor-alt-text-settings-automatic-title = Automatisk alternativ text +pdfjs-editor-alt-text-settings-create-model-button-label = Skapa alternativ text automatiskt +pdfjs-editor-alt-text-settings-create-model-description = Föreslår beskrivningar för att hjälpa personer som inte kan se bilden eller när bilden inte laddas. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = AI-modell för alternativ text ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Körs lokalt på din enhet så att din data förblir privat. Krävs för automatisk alternativ text. +pdfjs-editor-alt-text-settings-delete-model-button = Ta bort +pdfjs-editor-alt-text-settings-download-model-button = Hämta +pdfjs-editor-alt-text-settings-downloading-model-button = Hämtar… +pdfjs-editor-alt-text-settings-editor-title = Alternativ textredigerare +pdfjs-editor-alt-text-settings-show-dialog-button-label = Visa alternativ textredigerare direkt när du lägger till en bild +pdfjs-editor-alt-text-settings-show-dialog-description = Hjälper dig att se till att alla dina bilder har alternativ text. +pdfjs-editor-alt-text-settings-close-button = Stäng + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Markering borttagen +pdfjs-editor-undo-bar-message-freetext = Text borttagen +pdfjs-editor-undo-bar-message-ink = Ritning borttagen +pdfjs-editor-undo-bar-message-stamp = Bild borttagen +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anteckning har tagits bort + *[other] { $count } anteckningar har tagits bort + } +pdfjs-editor-undo-bar-undo-button = + .title = Ångra +pdfjs-editor-undo-bar-undo-button-label = Ångra +pdfjs-editor-undo-bar-close-button = + .title = Stäng +pdfjs-editor-undo-bar-close-button-label = Stäng diff --git a/public/pdfjs/web/locale/szl/viewer.ftl b/public/pdfjs/web/locale/szl/viewer.ftl new file mode 100644 index 0000000..cbf166e --- /dev/null +++ b/public/pdfjs/web/locale/szl/viewer.ftl @@ -0,0 +1,257 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Piyrwyjszo strōna +pdfjs-previous-button-label = Piyrwyjszo +pdfjs-next-button = + .title = Nastympno strōna +pdfjs-next-button-label = Dalij +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Strōna +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = ze { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } ze { $pagesCount }) +pdfjs-zoom-out-button = + .title = Zmyńsz +pdfjs-zoom-out-button-label = Zmyńsz +pdfjs-zoom-in-button = + .title = Zwiynksz +pdfjs-zoom-in-button-label = Zwiynksz +pdfjs-zoom-select = + .title = Srogość +pdfjs-presentation-mode-button = + .title = Przełōncz na tryb prezyntacyje +pdfjs-presentation-mode-button-label = Tryb prezyntacyje +pdfjs-open-file-button = + .title = Ôdewrzij zbiōr +pdfjs-open-file-button-label = Ôdewrzij +pdfjs-print-button = + .title = Durkuj +pdfjs-print-button-label = Durkuj + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Noczynia +pdfjs-tools-button-label = Noczynia +pdfjs-first-page-button = + .title = Idź ku piyrszyj strōnie +pdfjs-first-page-button-label = Idź ku piyrszyj strōnie +pdfjs-last-page-button = + .title = Idź ku ôstatnij strōnie +pdfjs-last-page-button-label = Idź ku ôstatnij strōnie +pdfjs-page-rotate-cw-button = + .title = Zwyrtnij w prawo +pdfjs-page-rotate-cw-button-label = Zwyrtnij w prawo +pdfjs-page-rotate-ccw-button = + .title = Zwyrtnij w lewo +pdfjs-page-rotate-ccw-button-label = Zwyrtnij w lewo +pdfjs-cursor-text-select-tool-button = + .title = Załōncz noczynie ôbiyranio tekstu +pdfjs-cursor-text-select-tool-button-label = Noczynie ôbiyranio tekstu +pdfjs-cursor-hand-tool-button = + .title = Załōncz noczynie rōnczka +pdfjs-cursor-hand-tool-button-label = Noczynie rōnczka +pdfjs-scroll-vertical-button = + .title = Używej piōnowego przewijanio +pdfjs-scroll-vertical-button-label = Piōnowe przewijanie +pdfjs-scroll-horizontal-button = + .title = Używej poziōmego przewijanio +pdfjs-scroll-horizontal-button-label = Poziōme przewijanie +pdfjs-scroll-wrapped-button = + .title = Używej szichtowego przewijanio +pdfjs-scroll-wrapped-button-label = Szichtowe przewijanie +pdfjs-spread-none-button = + .title = Niy dowej strōn w widoku po dwie +pdfjs-spread-none-button-label = Po jednyj strōnie +pdfjs-spread-odd-button = + .title = Pokoż strōny po dwie; niyporziste po lewyj +pdfjs-spread-odd-button-label = Niyporziste po lewyj +pdfjs-spread-even-button = + .title = Pokoż strōny po dwie; porziste po lewyj +pdfjs-spread-even-button-label = Porziste po lewyj + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Włosności dokumyntu… +pdfjs-document-properties-button-label = Włosności dokumyntu… +pdfjs-document-properties-file-name = Miano zbioru: +pdfjs-document-properties-file-size = Srogość zbioru: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } B) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } B) +pdfjs-document-properties-title = Tytuł: +pdfjs-document-properties-author = Autōr: +pdfjs-document-properties-subject = Tymat: +pdfjs-document-properties-keywords = Kluczowe słowa: +pdfjs-document-properties-creation-date = Data zrychtowanio: +pdfjs-document-properties-modification-date = Data zmiany: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Zrychtowane ôd: +pdfjs-document-properties-producer = PDF ôd: +pdfjs-document-properties-version = Wersyjo PDF: +pdfjs-document-properties-page-count = Wielość strōn: +pdfjs-document-properties-page-size = Srogość strōny: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = piōnowo +pdfjs-document-properties-page-size-orientation-landscape = poziōmo +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Gibki necowy podglōnd: +pdfjs-document-properties-linearized-yes = Ja +pdfjs-document-properties-linearized-no = Niy +pdfjs-document-properties-close-button = Zawrzij + +## Print + +pdfjs-print-progress-message = Rychtowanie dokumyntu do durku… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Pociep +pdfjs-printing-not-supported = Pozōr: Ta przeglōndarka niy cołkiym ôbsuguje durk. +pdfjs-printing-not-ready = Pozōr: Tyn PDF niy ma za tela zaladowany do durku. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Przełōncz posek na rancie +pdfjs-toggle-sidebar-notification-button = + .title = Przełōncz posek na rancie (dokumynt mo struktura/przidowki/warstwy) +pdfjs-toggle-sidebar-button-label = Przełōncz posek na rancie +pdfjs-document-outline-button = + .title = Pokoż struktura dokumyntu (tuplowane klikniyncie rozszyrzo/swijo wszyskie elymynta) +pdfjs-document-outline-button-label = Struktura dokumyntu +pdfjs-attachments-button = + .title = Pokoż przidowki +pdfjs-attachments-button-label = Przidowki +pdfjs-layers-button = + .title = Pokoż warstwy (tuplowane klikniyncie resetuje wszyskie warstwy do bazowego stanu) +pdfjs-layers-button-label = Warstwy +pdfjs-thumbs-button = + .title = Pokoż miniatury +pdfjs-thumbs-button-label = Miniatury +pdfjs-findbar-button = + .title = Znojdź w dokumyncie +pdfjs-findbar-button-label = Znojdź +pdfjs-additional-layers = Nadbytnie warstwy + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Strōna { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Miniatura strōny { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Znojdź + .placeholder = Znojdź w dokumyncie… +pdfjs-find-previous-button = + .title = Znojdź piyrwyjsze pokozanie sie tyj frazy +pdfjs-find-previous-button-label = Piyrwyjszo +pdfjs-find-next-button = + .title = Znojdź nastympne pokozanie sie tyj frazy +pdfjs-find-next-button-label = Dalij +pdfjs-find-highlight-checkbox = Zaznacz wszysko +pdfjs-find-match-case-checkbox-label = Poznowej srogość liter +pdfjs-find-entire-word-checkbox-label = Cołke słowa +pdfjs-find-reached-top = Doszło do samego wiyrchu strōny, dalij ôd spodku +pdfjs-find-reached-bottom = Doszło do samego spodku strōny, dalij ôd wiyrchu +pdfjs-find-not-found = Fraza niy znaleziōno + +## Predefined zoom values + +pdfjs-page-scale-width = Szyrzka strōny +pdfjs-page-scale-fit = Napasowanie strōny +pdfjs-page-scale-auto = Autōmatyczno srogość +pdfjs-page-scale-actual = Aktualno srogość +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Przi ladowaniu PDFa pokozoł sie feler. +pdfjs-invalid-file-error = Zły abo felerny zbiōr PDF. +pdfjs-missing-file-error = Chybio zbioru PDF. +pdfjs-unexpected-response-error = Niyôczekowano ôdpowiydź serwera. +pdfjs-rendering-error = Przi renderowaniu strōny pokozoł sie feler. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Anotacyjo typu { $type }] + +## Password + +pdfjs-password-label = Wkludź hasło, coby ôdewrzić tyn zbiōr PDF. +pdfjs-password-invalid = Hasło je złe. Sprōbuj jeszcze roz. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Pociep +pdfjs-web-fonts-disabled = Necowe fōnty sōm zastawiōne: niy idzie użyć wkludzōnych fōntōw PDF. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/ta/viewer.ftl b/public/pdfjs/web/locale/ta/viewer.ftl new file mode 100644 index 0000000..82cf197 --- /dev/null +++ b/public/pdfjs/web/locale/ta/viewer.ftl @@ -0,0 +1,223 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = முந்தைய பக்கம் +pdfjs-previous-button-label = முந்தையது +pdfjs-next-button = + .title = அடுத்த பக்கம் +pdfjs-next-button-label = அடுத்து +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = பக்கம் +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount } இல் +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = { $pagesCount }) இல் ({ $pageNumber } +pdfjs-zoom-out-button = + .title = சிறிதாக்கு +pdfjs-zoom-out-button-label = சிறிதாக்கு +pdfjs-zoom-in-button = + .title = பெரிதாக்கு +pdfjs-zoom-in-button-label = பெரிதாக்கு +pdfjs-zoom-select = + .title = பெரிதாக்கு +pdfjs-presentation-mode-button = + .title = விளக்ககாட்சி பயன்முறைக்கு மாறு +pdfjs-presentation-mode-button-label = விளக்ககாட்சி பயன்முறை +pdfjs-open-file-button = + .title = கோப்பினை திற +pdfjs-open-file-button-label = திற +pdfjs-print-button = + .title = அச்சிடு +pdfjs-print-button-label = அச்சிடு + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = கருவிகள் +pdfjs-tools-button-label = கருவிகள் +pdfjs-first-page-button = + .title = முதல் பக்கத்திற்கு செல்லவும் +pdfjs-first-page-button-label = முதல் பக்கத்திற்கு செல்லவும் +pdfjs-last-page-button = + .title = கடைசி பக்கத்திற்கு செல்லவும் +pdfjs-last-page-button-label = கடைசி பக்கத்திற்கு செல்லவும் +pdfjs-page-rotate-cw-button = + .title = வலஞ்சுழியாக சுழற்று +pdfjs-page-rotate-cw-button-label = வலஞ்சுழியாக சுழற்று +pdfjs-page-rotate-ccw-button = + .title = இடஞ்சுழியாக சுழற்று +pdfjs-page-rotate-ccw-button-label = இடஞ்சுழியாக சுழற்று +pdfjs-cursor-text-select-tool-button = + .title = உரைத் தெரிவு கருவியைச் செயல்படுத்து +pdfjs-cursor-text-select-tool-button-label = உரைத் தெரிவு கருவி +pdfjs-cursor-hand-tool-button = + .title = கைக் கருவிக்ச் செயற்படுத்து +pdfjs-cursor-hand-tool-button-label = கைக்குருவி + +## Document properties dialog + +pdfjs-document-properties-button = + .title = ஆவண பண்புகள்... +pdfjs-document-properties-button-label = ஆவண பண்புகள்... +pdfjs-document-properties-file-name = கோப்பு பெயர்: +pdfjs-document-properties-file-size = கோப்பின் அளவு: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } கிபை ({ $size_b } பைட்டுகள்) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } மெபை ({ $size_b } பைட்டுகள்) +pdfjs-document-properties-title = தலைப்பு: +pdfjs-document-properties-author = எழுதியவர் +pdfjs-document-properties-subject = பொருள்: +pdfjs-document-properties-keywords = முக்கிய வார்த்தைகள்: +pdfjs-document-properties-creation-date = படைத்த தேதி : +pdfjs-document-properties-modification-date = திருத்திய தேதி: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = உருவாக்குபவர்: +pdfjs-document-properties-producer = பிடிஎஃப் தயாரிப்பாளர்: +pdfjs-document-properties-version = PDF பதிப்பு: +pdfjs-document-properties-page-count = பக்க எண்ணிக்கை: +pdfjs-document-properties-page-size = பக்க அளவு: +pdfjs-document-properties-page-size-unit-inches = இதில் +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = நிலைபதிப்பு +pdfjs-document-properties-page-size-orientation-landscape = நிலைபரப்பு +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = கடிதம் +pdfjs-document-properties-page-size-name-legal = சட்டபூர்வ + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +pdfjs-document-properties-close-button = மூடுக + +## Print + +pdfjs-print-progress-message = அச்சிடுவதற்கான ஆவணம் தயாராகிறது... +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = ரத்து +pdfjs-printing-not-supported = எச்சரிக்கை: இந்த உலாவி அச்சிடுதலை முழுமையாக ஆதரிக்கவில்லை. +pdfjs-printing-not-ready = எச்சரிக்கை: PDF அச்சிட முழுவதுமாக ஏற்றப்படவில்லை. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = பக்கப் பட்டியை நிலைமாற்று +pdfjs-toggle-sidebar-button-label = பக்கப் பட்டியை நிலைமாற்று +pdfjs-document-outline-button = + .title = ஆவண அடக்கத்தைக் காட்டு (இருமுறைச் சொடுக்கி அனைத்து உறுப்பிடிகளையும் விரி/சேர்) +pdfjs-document-outline-button-label = ஆவண வெளிவரை +pdfjs-attachments-button = + .title = இணைப்புகளை காண்பி +pdfjs-attachments-button-label = இணைப்புகள் +pdfjs-thumbs-button = + .title = சிறுபடங்களைக் காண்பி +pdfjs-thumbs-button-label = சிறுபடங்கள் +pdfjs-findbar-button = + .title = ஆவணத்தில் கண்டறி +pdfjs-findbar-button-label = தேடு + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = பக்கம் { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = பக்கத்தின் சிறுபடம் { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = கண்டுபிடி + .placeholder = ஆவணத்தில் கண்டறி… +pdfjs-find-previous-button = + .title = இந்த சொற்றொடரின் முந்தைய நிகழ்வை தேடு +pdfjs-find-previous-button-label = முந்தையது +pdfjs-find-next-button = + .title = இந்த சொற்றொடரின் அடுத்த நிகழ்வை தேடு +pdfjs-find-next-button-label = அடுத்து +pdfjs-find-highlight-checkbox = அனைத்தையும் தனிப்படுத்து +pdfjs-find-match-case-checkbox-label = பேரெழுத்தாக்கத்தை உணர் +pdfjs-find-reached-top = ஆவணத்தின் மேல் பகுதியை அடைந்தது, அடிப்பக்கத்திலிருந்து தொடர்ந்தது +pdfjs-find-reached-bottom = ஆவணத்தின் முடிவை அடைந்தது, மேலிருந்து தொடர்ந்தது +pdfjs-find-not-found = சொற்றொடர் காணவில்லை + +## Predefined zoom values + +pdfjs-page-scale-width = பக்க அகலம் +pdfjs-page-scale-fit = பக்கப் பொருத்தம் +pdfjs-page-scale-auto = தானியக்க பெரிதாக்கல் +pdfjs-page-scale-actual = உண்மையான அளவு +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = PDF ஐ ஏற்றும் போது ஒரு பிழை ஏற்பட்டது. +pdfjs-invalid-file-error = செல்லுபடியாகாத அல்லது சிதைந்த PDF கோப்பு. +pdfjs-missing-file-error = PDF கோப்பு காணவில்லை. +pdfjs-unexpected-response-error = சேவகன் பதில் எதிர்பாரதது. +pdfjs-rendering-error = இந்தப் பக்கத்தை காட்சிப்படுத்தும் போது ஒரு பிழை ஏற்பட்டது. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } விளக்கம்] + +## Password + +pdfjs-password-label = இந்த PDF கோப்பை திறக்க கடவுச்சொல்லை உள்ளிடவும். +pdfjs-password-invalid = செல்லுபடியாகாத கடவுச்சொல், தயை செய்து மீண்டும் முயற்சி செய்க. +pdfjs-password-ok-button = சரி +pdfjs-password-cancel-button = ரத்து +pdfjs-web-fonts-disabled = வலை எழுத்துருக்கள் முடக்கப்பட்டுள்ளன: உட்பொதிக்கப்பட்ட PDF எழுத்துருக்களைப் பயன்படுத்த முடியவில்லை. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/te/viewer.ftl b/public/pdfjs/web/locale/te/viewer.ftl new file mode 100644 index 0000000..94dc2b8 --- /dev/null +++ b/public/pdfjs/web/locale/te/viewer.ftl @@ -0,0 +1,239 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = మునుపటి పేజీ +pdfjs-previous-button-label = క్రితం +pdfjs-next-button = + .title = తరువాత పేజీ +pdfjs-next-button-label = తరువాత +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = పేజీ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = మొత్తం { $pagesCount } లో +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = (మొత్తం { $pagesCount } లో { $pageNumber }వది) +pdfjs-zoom-out-button = + .title = జూమ్ తగ్గించు +pdfjs-zoom-out-button-label = జూమ్ తగ్గించు +pdfjs-zoom-in-button = + .title = జూమ్ చేయి +pdfjs-zoom-in-button-label = జూమ్ చేయి +pdfjs-zoom-select = + .title = జూమ్ +pdfjs-presentation-mode-button = + .title = ప్రదర్శనా రీతికి మారు +pdfjs-presentation-mode-button-label = ప్రదర్శనా రీతి +pdfjs-open-file-button = + .title = ఫైల్ తెరువు +pdfjs-open-file-button-label = తెరువు +pdfjs-print-button = + .title = ముద్రించు +pdfjs-print-button-label = ముద్రించు + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = పనిముట్లు +pdfjs-tools-button-label = పనిముట్లు +pdfjs-first-page-button = + .title = మొదటి పేజీకి వెళ్ళు +pdfjs-first-page-button-label = మొదటి పేజీకి వెళ్ళు +pdfjs-last-page-button = + .title = చివరి పేజీకి వెళ్ళు +pdfjs-last-page-button-label = చివరి పేజీకి వెళ్ళు +pdfjs-page-rotate-cw-button = + .title = సవ్యదిశలో తిప్పు +pdfjs-page-rotate-cw-button-label = సవ్యదిశలో తిప్పు +pdfjs-page-rotate-ccw-button = + .title = అపసవ్యదిశలో తిప్పు +pdfjs-page-rotate-ccw-button-label = అపసవ్యదిశలో తిప్పు +pdfjs-cursor-text-select-tool-button = + .title = టెక్స్ట్ ఎంపిక సాధనాన్ని ప్రారంభించండి +pdfjs-cursor-text-select-tool-button-label = టెక్స్ట్ ఎంపిక సాధనం +pdfjs-cursor-hand-tool-button = + .title = చేతి సాధనం చేతనించు +pdfjs-cursor-hand-tool-button-label = చేతి సాధనం +pdfjs-scroll-vertical-button-label = నిలువు స్క్రోలింగు + +## Document properties dialog + +pdfjs-document-properties-button = + .title = పత్రము లక్షణాలు... +pdfjs-document-properties-button-label = పత్రము లక్షణాలు... +pdfjs-document-properties-file-name = దస్త్రం పేరు: +pdfjs-document-properties-file-size = దస్త్రం పరిమాణం: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = శీర్షిక: +pdfjs-document-properties-author = మూలకర్త: +pdfjs-document-properties-subject = విషయం: +pdfjs-document-properties-keywords = కీ పదాలు: +pdfjs-document-properties-creation-date = సృష్టించిన తేదీ: +pdfjs-document-properties-modification-date = సవరించిన తేదీ: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = సృష్టికర్త: +pdfjs-document-properties-producer = PDF ఉత్పాదకి: +pdfjs-document-properties-version = PDF వర్షన్: +pdfjs-document-properties-page-count = పేజీల సంఖ్య: +pdfjs-document-properties-page-size = కాగితం పరిమాణం: +pdfjs-document-properties-page-size-unit-inches = లో +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = నిలువుచిత్రం +pdfjs-document-properties-page-size-orientation-landscape = అడ్డచిత్రం +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = లేఖ +pdfjs-document-properties-page-size-name-legal = చట్టపరమైన + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +pdfjs-document-properties-linearized-yes = అవును +pdfjs-document-properties-linearized-no = కాదు +pdfjs-document-properties-close-button = మూసివేయి + +## Print + +pdfjs-print-progress-message = ముద్రించడానికి పత్రము సిద్ధమవుతున్నది… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = రద్దుచేయి +pdfjs-printing-not-supported = హెచ్చరిక: ఈ విహారిణి చేత ముద్రణ పూర్తిగా తోడ్పాటు లేదు. +pdfjs-printing-not-ready = హెచ్చరిక: ముద్రణ కొరకు ఈ PDF పూర్తిగా లోడవలేదు. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = పక్కపట్టీ మార్చు +pdfjs-toggle-sidebar-button-label = పక్కపట్టీ మార్చు +pdfjs-document-outline-button = + .title = పత్రము రూపము చూపించు (డబుల్ క్లిక్ చేసి అన్ని అంశాలను విస్తరించు/కూల్చు) +pdfjs-document-outline-button-label = పత్రము అవుట్‌లైన్ +pdfjs-attachments-button = + .title = అనుబంధాలు చూపు +pdfjs-attachments-button-label = అనుబంధాలు +pdfjs-layers-button-label = పొరలు +pdfjs-thumbs-button = + .title = థంబ్‌నైల్స్ చూపు +pdfjs-thumbs-button-label = థంబ్‌నైల్స్ +pdfjs-findbar-button = + .title = పత్రములో కనుగొనుము +pdfjs-findbar-button-label = కనుగొను +pdfjs-additional-layers = అదనపు పొరలు + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = పేజీ { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page } పేజీ నఖచిత్రం + +## Find panel button title and messages + +pdfjs-find-input = + .title = కనుగొను + .placeholder = పత్రములో కనుగొను… +pdfjs-find-previous-button = + .title = పదం యొక్క ముందు సంభవాన్ని కనుగొను +pdfjs-find-previous-button-label = మునుపటి +pdfjs-find-next-button = + .title = పదం యొక్క తర్వాతి సంభవాన్ని కనుగొను +pdfjs-find-next-button-label = తరువాత +pdfjs-find-highlight-checkbox = అన్నిటిని ఉద్దీపనం చేయుము +pdfjs-find-match-case-checkbox-label = అక్షరముల తేడాతో పోల్చు +pdfjs-find-entire-word-checkbox-label = పూర్తి పదాలు +pdfjs-find-reached-top = పేజీ పైకి చేరుకున్నది, క్రింది నుండి కొనసాగించండి +pdfjs-find-reached-bottom = పేజీ చివరకు చేరుకున్నది, పైనుండి కొనసాగించండి +pdfjs-find-not-found = పదబంధం కనబడలేదు + +## Predefined zoom values + +pdfjs-page-scale-width = పేజీ వెడల్పు +pdfjs-page-scale-fit = పేజీ అమర్పు +pdfjs-page-scale-auto = స్వయంచాలక జూమ్ +pdfjs-page-scale-actual = యథార్ధ పరిమాణం +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = PDF లోడవుచున్నప్పుడు ఒక దోషం ఎదురైంది. +pdfjs-invalid-file-error = చెల్లని లేదా పాడైన PDF ఫైలు. +pdfjs-missing-file-error = దొరకని PDF ఫైలు. +pdfjs-unexpected-response-error = అనుకోని సర్వర్ స్పందన. +pdfjs-rendering-error = పేజీను రెండర్ చేయుటలో ఒక దోషం ఎదురైంది. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } టీకా] + +## Password + +pdfjs-password-label = ఈ PDF ఫైల్ తెరుచుటకు సంకేతపదం ప్రవేశపెట్టుము. +pdfjs-password-invalid = సంకేతపదం చెల్లదు. దయచేసి మళ్ళీ ప్రయత్నించండి. +pdfjs-password-ok-button = సరే +pdfjs-password-cancel-button = రద్దుచేయి +pdfjs-web-fonts-disabled = వెబ్ ఫాంట్లు అచేతనించబడెను: ఎంబెడెడ్ PDF ఫాంట్లు ఉపయోగించలేక పోయింది. + +## Editing + +# Editor Parameters +pdfjs-editor-free-text-color-input = రంగు +pdfjs-editor-free-text-size-input = పరిమాణం +pdfjs-editor-ink-color-input = రంగు +pdfjs-editor-ink-thickness-input = మందం +pdfjs-editor-ink-opacity-input = అకిరణ్యత + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/tg/viewer.ftl b/public/pdfjs/web/locale/tg/viewer.ftl new file mode 100644 index 0000000..b39fee5 --- /dev/null +++ b/public/pdfjs/web/locale/tg/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Саҳифаи қаблӣ +pdfjs-previous-button-label = Қаблӣ +pdfjs-next-button = + .title = Саҳифаи навбатӣ +pdfjs-next-button-label = Навбатӣ +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Саҳифа +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = аз { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } аз { $pagesCount }) +pdfjs-zoom-out-button = + .title = Хурд кардан +pdfjs-zoom-out-button-label = Хурд кардан +pdfjs-zoom-in-button = + .title = Калон кардан +pdfjs-zoom-in-button-label = Калон кардан +pdfjs-zoom-select = + .title = Танзими андоза +pdfjs-presentation-mode-button = + .title = Гузариш ба реҷаи тақдим +pdfjs-presentation-mode-button-label = Реҷаи тақдим +pdfjs-open-file-button = + .title = Кушодани файл +pdfjs-open-file-button-label = Кушодан +pdfjs-print-button = + .title = Чоп кардан +pdfjs-print-button-label = Чоп кардан +pdfjs-save-button = + .title = Нигоҳ доштан +pdfjs-save-button-label = Нигоҳ доштан +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Боргирӣ кардан +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Боргирӣ кардан +pdfjs-bookmark-button = + .title = Саҳифаи ҷорӣ (Дидани нишонии URL аз саҳифаи ҷорӣ) +pdfjs-bookmark-button-label = Саҳифаи ҷорӣ + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Абзорҳо +pdfjs-tools-button-label = Абзорҳо +pdfjs-first-page-button = + .title = Ба саҳифаи аввал гузаред +pdfjs-first-page-button-label = Ба саҳифаи аввал гузаред +pdfjs-last-page-button = + .title = Ба саҳифаи охирин гузаред +pdfjs-last-page-button-label = Ба саҳифаи охирин гузаред +pdfjs-page-rotate-cw-button = + .title = Ба самти ҳаракати ақрабаки соат давр задан +pdfjs-page-rotate-cw-button-label = Ба самти ҳаракати ақрабаки соат давр задан +pdfjs-page-rotate-ccw-button = + .title = Ба муқобили самти ҳаракати ақрабаки соат давр задан +pdfjs-page-rotate-ccw-button-label = Ба муқобили самти ҳаракати ақрабаки соат давр задан +pdfjs-cursor-text-select-tool-button = + .title = Фаъол кардани «Абзори интихоби матн» +pdfjs-cursor-text-select-tool-button-label = Абзори интихоби матн +pdfjs-cursor-hand-tool-button = + .title = Фаъол кардани «Абзори даст» +pdfjs-cursor-hand-tool-button-label = Абзори даст +pdfjs-scroll-page-button = + .title = Истифодаи варақзанӣ +pdfjs-scroll-page-button-label = Варақзанӣ +pdfjs-scroll-vertical-button = + .title = Истифодаи варақзании амудӣ +pdfjs-scroll-vertical-button-label = Варақзании амудӣ +pdfjs-scroll-horizontal-button = + .title = Истифодаи варақзании уфуқӣ +pdfjs-scroll-horizontal-button-label = Варақзании уфуқӣ +pdfjs-scroll-wrapped-button = + .title = Истифодаи варақзании миқёсбандӣ +pdfjs-scroll-wrapped-button-label = Варақзании миқёсбандӣ +pdfjs-spread-none-button = + .title = Густариши саҳифаҳо истифода бурда нашавад +pdfjs-spread-none-button-label = Бе густурдани саҳифаҳо +pdfjs-spread-odd-button = + .title = Густариши саҳифаҳо аз саҳифаҳо бо рақамҳои тоқ оғоз карда мешавад +pdfjs-spread-odd-button-label = Саҳифаҳои тоқ аз тарафи чап +pdfjs-spread-even-button = + .title = Густариши саҳифаҳо аз саҳифаҳо бо рақамҳои ҷуфт оғоз карда мешавад +pdfjs-spread-even-button-label = Саҳифаҳои ҷуфт аз тарафи чап + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Хусусиятҳои ҳуҷҷат… +pdfjs-document-properties-button-label = Хусусиятҳои ҳуҷҷат… +pdfjs-document-properties-file-name = Номи файл: +pdfjs-document-properties-file-size = Андозаи файл: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } КБ ({ $b } байт) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } МБ ({ $b } байт) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байт) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } байт) +pdfjs-document-properties-title = Сарлавҳа: +pdfjs-document-properties-author = Муаллиф: +pdfjs-document-properties-subject = Мавзуъ: +pdfjs-document-properties-keywords = Калимаҳои калидӣ: +pdfjs-document-properties-creation-date = Санаи эҷод: +pdfjs-document-properties-modification-date = Санаи тағйирот: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Эҷодкунанда: +pdfjs-document-properties-producer = Таҳиякунандаи «PDF»: +pdfjs-document-properties-version = Версияи «PDF»: +pdfjs-document-properties-page-count = Шумораи саҳифаҳо: +pdfjs-document-properties-page-size = Андозаи саҳифа: +pdfjs-document-properties-page-size-unit-inches = дюйм +pdfjs-document-properties-page-size-unit-millimeters = мм +pdfjs-document-properties-page-size-orientation-portrait = амудӣ +pdfjs-document-properties-page-size-orientation-landscape = уфуқӣ +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Мактуб +pdfjs-document-properties-page-size-name-legal = Ҳуқуқӣ + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Намоиши тез дар Интернет: +pdfjs-document-properties-linearized-yes = Ҳа +pdfjs-document-properties-linearized-no = Не +pdfjs-document-properties-close-button = Пӯшидан + +## Print + +pdfjs-print-progress-message = Омодасозии ҳуҷҷат барои чоп… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Бекор кардан +pdfjs-printing-not-supported = Диққат: Чопкунӣ аз тарафи ин браузер ба таври пурра дастгирӣ намешавад. +pdfjs-printing-not-ready = Диққат: Файли «PDF» барои чопкунӣ пурра бор карда нашуд. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Фаъол кардани навори ҷонибӣ +pdfjs-toggle-sidebar-notification-button = + .title = Фаъол кардани навори ҷонибӣ (ҳуҷҷат дорои сохтор/замимаҳо/қабатҳо мебошад) +pdfjs-toggle-sidebar-button-label = Фаъол кардани навори ҷонибӣ +pdfjs-document-outline-button = + .title = Намоиш додани сохтори ҳуҷҷат (барои баркушодан/пеҷондани ҳамаи унсурҳо дубора зер кунед) +pdfjs-document-outline-button-label = Сохтори ҳуҷҷат +pdfjs-attachments-button = + .title = Намоиш додани замимаҳо +pdfjs-attachments-button-label = Замимаҳо +pdfjs-layers-button = + .title = Намоиш додани қабатҳо (барои барқарор кардани ҳамаи қабатҳо ба вазъияти пешфарз дубора зер кунед) +pdfjs-layers-button-label = Қабатҳо +pdfjs-thumbs-button = + .title = Намоиш додани тасвирчаҳо +pdfjs-thumbs-button-label = Тасвирчаҳо +pdfjs-current-outline-item-button = + .title = Ёфтани унсури сохтори ҷорӣ +pdfjs-current-outline-item-button-label = Унсури сохтори ҷорӣ +pdfjs-findbar-button = + .title = Ёфтан дар ҳуҷҷат +pdfjs-findbar-button-label = Ёфтан +pdfjs-additional-layers = Қабатҳои иловагӣ + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Саҳифаи { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Тасвирчаи саҳифаи { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Ёфтан + .placeholder = Ёфтан дар ҳуҷҷат… +pdfjs-find-previous-button = + .title = Ҷустуҷӯи мавриди қаблии ибораи пешниҳодшуда +pdfjs-find-previous-button-label = Қаблӣ +pdfjs-find-next-button = + .title = Ҷустуҷӯи мавриди навбатии ибораи пешниҳодшуда +pdfjs-find-next-button-label = Навбатӣ +pdfjs-find-highlight-checkbox = Ҳамаашро бо ранг ҷудо кардан +pdfjs-find-match-case-checkbox-label = Бо дарназардошти ҳарфҳои хурду калон +pdfjs-find-match-diacritics-checkbox-label = Бо дарназардошти аломатҳои диакритикӣ +pdfjs-find-entire-word-checkbox-label = Калимаҳои пурра +pdfjs-find-reached-top = Ба болои ҳуҷҷат расид, аз поён идома ёфт +pdfjs-find-reached-bottom = Ба поёни ҳуҷҷат расид, аз боло идома ёфт +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } аз { $total } мувофиқат + *[other] { $current } аз { $total } мувофиқат + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Зиёда аз { $limit } мувофиқат + *[other] Зиёда аз { $limit } мувофиқат + } +pdfjs-find-not-found = Ибора ёфт нашуд + +## Predefined zoom values + +pdfjs-page-scale-width = Аз рӯи паҳнои саҳифа +pdfjs-page-scale-fit = Аз рӯи андозаи саҳифа +pdfjs-page-scale-auto = Андозаи худкор +pdfjs-page-scale-actual = Андозаи воқеӣ +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Саҳифаи { $page } + +## Loading indicator messages + +pdfjs-loading-error = Ҳангоми боркунии «PDF» хато ба миён омад. +pdfjs-invalid-file-error = Файли «PDF» нодуруст ё вайроншуда мебошад. +pdfjs-missing-file-error = Файли «PDF» ғоиб аст. +pdfjs-unexpected-response-error = Ҷавоби ногаҳон аз сервер. +pdfjs-rendering-error = Ҳангоми шаклсозии саҳифа хато ба миён омад. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Ҳошиянависӣ - { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Барои кушодани ин файли «PDF» ниҳонвожаро ворид кунед. +pdfjs-password-invalid = Ниҳонвожаи нодуруст. Лутфан, аз нав кӯшиш кунед. +pdfjs-password-ok-button = ХУБ +pdfjs-password-cancel-button = Бекор кардан +pdfjs-web-fonts-disabled = Шрифтҳои интернетӣ ғайрифаъоланд: истифодаи шрифтҳои дарунсохти «PDF» ғайриимкон аст. + +## Editing + +pdfjs-editor-free-text-button = + .title = Матн +pdfjs-editor-free-text-button-label = Матн +pdfjs-editor-ink-button = + .title = Расмкашӣ +pdfjs-editor-ink-button-label = Расмкашӣ +pdfjs-editor-stamp-button = + .title = Илова ё таҳрир кардани тасвирҳо +pdfjs-editor-stamp-button-label = Илова ё таҳрир кардани тасвирҳо +pdfjs-editor-highlight-button = + .title = Ҷудокунӣ +pdfjs-editor-highlight-button-label = Ҷудокунӣ +pdfjs-highlight-floating-button1 = + .title = Ҷудокунӣ + .aria-label = Ҷудокунӣ +pdfjs-highlight-floating-button-label = Ҷудокунӣ + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Тоза кардани нақша +pdfjs-editor-remove-freetext-button = + .title = Тоза кардани матн +pdfjs-editor-remove-stamp-button = + .title = Тоза кардани тасвир +pdfjs-editor-remove-highlight-button = + .title = Тоза кардани ҷудокунӣ + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Ранг +pdfjs-editor-free-text-size-input = Андоза +pdfjs-editor-ink-color-input = Ранг +pdfjs-editor-ink-thickness-input = Ғафсӣ +pdfjs-editor-ink-opacity-input = Шаффофӣ +pdfjs-editor-stamp-add-image-button = + .title = Илова кардани тасвир +pdfjs-editor-stamp-add-image-button-label = Илова кардани тасвир +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Ғафсӣ +pdfjs-editor-free-highlight-thickness-title = + .title = Иваз кардани ғафсӣ ҳангоми ҷудокунии унсурҳо ба ғайр аз матн +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Муҳаррири матн + .default-content = Матнро ворид кунед… +pdfjs-free-text = + .aria-label = Муҳаррири матн +pdfjs-free-text-default-content = Нависед… +pdfjs-ink = + .aria-label = Муҳаррири расмкашӣ +pdfjs-ink-canvas = + .aria-label = Тасвири эҷодкардаи корбар + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Матни иловагӣ +pdfjs-editor-alt-text-edit-button = + .aria-label = Таҳрир кардани матни ивазкунанда +pdfjs-editor-alt-text-edit-button-label = Таҳрир кардани матни иловагӣ +pdfjs-editor-alt-text-dialog-label = Имконеро интихоб намоед +pdfjs-editor-alt-text-dialog-description = Вақте ки одамон тасвирро дида наметавонанд ё вақте ки тасвир бор карда намешавад, матни иловагӣ (Alt text) кумак мерасонад. +pdfjs-editor-alt-text-add-description-label = Илова кардани тавсиф +pdfjs-editor-alt-text-add-description-description = Кӯшиш кунед, ки 1-2 ҷумлаеро нависед, ки ба мавзӯъ, танзим ё амалҳо тавзеҳ медиҳад. +pdfjs-editor-alt-text-mark-decorative-label = Гузоштан ҳамчун матни ороишӣ +pdfjs-editor-alt-text-mark-decorative-description = Ин барои тасвирҳои ороишӣ, ба монанди марзҳо ё аломатҳои обӣ, истифода мешавад. +pdfjs-editor-alt-text-cancel-button = Бекор кардан +pdfjs-editor-alt-text-save-button = Нигоҳ доштан +pdfjs-editor-alt-text-decorative-tooltip = Ҳамчун матни ороишӣ гузошта шуд +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Барои мисол, «Ман забони тоҷикиро дӯст медорам» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Матни ивазкунанда + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Кунҷи чапи боло — тағйир додани андоза +pdfjs-editor-resizer-label-top-middle = Канори миёнаи боло — тағйир додани андоза +pdfjs-editor-resizer-label-top-right = Кунҷи рости боло — тағйир додани андоза +pdfjs-editor-resizer-label-middle-right = Канори миёнаи рост — тағйир додани андоза +pdfjs-editor-resizer-label-bottom-right = Кунҷи рости поён — тағйир додани андоза +pdfjs-editor-resizer-label-bottom-middle = Канори миёнаи поён — тағйир додани андоза +pdfjs-editor-resizer-label-bottom-left = Кунҷи чапи поён — тағйир додани андоза +pdfjs-editor-resizer-label-middle-left = Канори миёнаи чап — тағйир додани андоза +pdfjs-editor-resizer-top-left = + .aria-label = Кунҷи чапи боло — тағйир додани андоза +pdfjs-editor-resizer-top-middle = + .aria-label = Канори миёнаи боло — тағйир додани андоза +pdfjs-editor-resizer-top-right = + .aria-label = Кунҷи рости боло — тағйир додани андоза +pdfjs-editor-resizer-middle-right = + .aria-label = Канори миёнаи рост — тағйир додани андоза +pdfjs-editor-resizer-bottom-right = + .aria-label = Кунҷи рости поён — тағйир додани андоза +pdfjs-editor-resizer-bottom-middle = + .aria-label = Канори миёнаи поён — тағйир додани андоза +pdfjs-editor-resizer-bottom-left = + .aria-label = Кунҷи чапи поён — тағйир додани андоза +pdfjs-editor-resizer-middle-left = + .aria-label = Канори миёнаи чап — тағйир додани андоза + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Ранги ҷудокунӣ +pdfjs-editor-colorpicker-button = + .title = Иваз кардани ранг +pdfjs-editor-colorpicker-dropdown = + .aria-label = Интихоби ранг +pdfjs-editor-colorpicker-yellow = + .title = Зард +pdfjs-editor-colorpicker-green = + .title = Сабз +pdfjs-editor-colorpicker-blue = + .title = Кабуд +pdfjs-editor-colorpicker-pink = + .title = Гулобӣ +pdfjs-editor-colorpicker-red = + .title = Сурх + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Ҳамаро намоиш додан +pdfjs-editor-highlight-show-all-button = + .title = Ҳамаро намоиш додан + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Таҳрир кардани матни иловагӣ (тафсири тасвир) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Илова кардани матни иловагӣ (тафсири тасвир) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Тафсири худро дар ин ҷо нависед… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Тавсифи мухтасар барои одамоне, ки аксҳоро дида наметавонанд ё вақте ки аксҳо кушода намешаванд. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Ин матни ивазкунанда ба таври худкор сохта шудааст ва шояд нодуруст бошад. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Маълумоти бештар +pdfjs-editor-new-alt-text-create-automatically-button-label = Ба таври худкор эҷод кардани матни иловагӣ +pdfjs-editor-new-alt-text-not-now-button = Ҳоло не +pdfjs-editor-new-alt-text-error-title = Матни иловагӣ ба таври худкор эҷод карда нашуд +pdfjs-editor-new-alt-text-error-description = Лутфан, матни иловагии худро ворид кунед ё баъдтар аз нав кӯшиш кунед. +pdfjs-editor-new-alt-text-error-close-button = Пӯшидан +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Боргирии модели зеҳни сунъӣ (AI) барои матни ивазкунанда ({ $downloadedSize } аз { $totalSize } МБ) + .aria-valuetext = Боргирии модели зеҳни сунъӣ (AI) барои матни ивазкунанда ({ $downloadedSize } аз { $totalSize } МБ) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Матни иловагӣ илова карда шуд +pdfjs-editor-new-alt-text-added-button-label = Матни иловагӣ илова карда шуд +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Матни иловагӣ вуҷуд надорад +pdfjs-editor-new-alt-text-missing-button-label = Матни иловагӣ вуҷуд надорад +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Бознигарӣ кардани матни иловагӣ +pdfjs-editor-new-alt-text-to-review-button-label = Бознигарӣ кардани матни иловагӣ +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Ба таври худкор сохта шудааст: «{ $generatedAltText }» + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Танзимоти матни иловагии тасвир +pdfjs-image-alt-text-settings-button-label = Танзимоти матни иловагии тасвир +pdfjs-editor-alt-text-settings-dialog-label = Танзимоти матни иловагии тасвир +pdfjs-editor-alt-text-settings-automatic-title = Матни иловагии худкор +pdfjs-editor-alt-text-settings-create-model-button-label = Ба таври худкор эҷод кардани матни иловагӣ +pdfjs-editor-alt-text-settings-create-model-description = Ин имкон барои расонидани кумак ба одамоне, ки аксҳоро дида наметавонанд ё вақте ки аксҳо кушода намешаванд, тавсифи аксҳоро пешниҳод мекунад. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Модели зеҳни сунъӣ «AI» барои матни ивазкунанда ({ $totalSize } МБ) +pdfjs-editor-alt-text-settings-ai-model-description = Дар дастгоҳи шумо ба таври маҳаллӣ кор мекунад, бинобар ин махфияти маълумоти шахсии шумо нигоҳ дошта мешавад. Барои матни ивазкунандаи худкор лозим аст. +pdfjs-editor-alt-text-settings-delete-model-button = Нест кардан +pdfjs-editor-alt-text-settings-download-model-button = Боргирӣ кардан +pdfjs-editor-alt-text-settings-downloading-model-button = Дар ҳоли боргирӣ… +pdfjs-editor-alt-text-settings-editor-title = Муҳаррири матни иловагӣ +pdfjs-editor-alt-text-settings-show-dialog-button-label = Дарҳол нишон додани муҳаррири матни ивазкунанда ҳангоми иловакунии тасвир +pdfjs-editor-alt-text-settings-show-dialog-description = Ба шумо кумак мекунад, ки боварӣ ҳосил кунед, ки ҳамаи тасвирҳои шумо дорои матни ивазкунанда мебошанд. +pdfjs-editor-alt-text-settings-close-button = Пӯшидан + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Ҷудосозӣ тоза карда шуд +pdfjs-editor-undo-bar-message-freetext = Матн тоза карда шуд +pdfjs-editor-undo-bar-message-ink = Расм тоза карда шуд +pdfjs-editor-undo-bar-message-stamp = Тасвир тоза карда шуд +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } ҳошиянависӣ тоза карда шуд + *[other] { $count } ҳошиянависӣ тоза карда шуданд + } +pdfjs-editor-undo-bar-undo-button = + .title = Бекор кардан +pdfjs-editor-undo-bar-undo-button-label = Бекор кардан +pdfjs-editor-undo-bar-close-button = + .title = Пӯшидан +pdfjs-editor-undo-bar-close-button-label = Пӯшидан diff --git a/public/pdfjs/web/locale/th/viewer.ftl b/public/pdfjs/web/locale/th/viewer.ftl new file mode 100644 index 0000000..cba15f9 --- /dev/null +++ b/public/pdfjs/web/locale/th/viewer.ftl @@ -0,0 +1,503 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = หน้าก่อนหน้า +pdfjs-previous-button-label = ก่อนหน้า +pdfjs-next-button = + .title = หน้าถัดไป +pdfjs-next-button-label = ถัดไป +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = หน้า +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = จาก { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } จาก { $pagesCount }) +pdfjs-zoom-out-button = + .title = ซูมออก +pdfjs-zoom-out-button-label = ซูมออก +pdfjs-zoom-in-button = + .title = ซูมเข้า +pdfjs-zoom-in-button-label = ซูมเข้า +pdfjs-zoom-select = + .title = ซูม +pdfjs-presentation-mode-button = + .title = สลับเป็นโหมดการนำเสนอ +pdfjs-presentation-mode-button-label = โหมดการนำเสนอ +pdfjs-open-file-button = + .title = เปิดไฟล์ +pdfjs-open-file-button-label = เปิด +pdfjs-print-button = + .title = พิมพ์ +pdfjs-print-button-label = พิมพ์ +pdfjs-save-button = + .title = บันทึก +pdfjs-save-button-label = บันทึก +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = ดาวน์โหลด +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = ดาวน์โหลด +pdfjs-bookmark-button = + .title = หน้าปัจจุบัน (ดู URL จากหน้าปัจจุบัน) +pdfjs-bookmark-button-label = หน้าปัจจุบัน + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = เครื่องมือ +pdfjs-tools-button-label = เครื่องมือ +pdfjs-first-page-button = + .title = ไปยังหน้าแรก +pdfjs-first-page-button-label = ไปยังหน้าแรก +pdfjs-last-page-button = + .title = ไปยังหน้าสุดท้าย +pdfjs-last-page-button-label = ไปยังหน้าสุดท้าย +pdfjs-page-rotate-cw-button = + .title = หมุนตามเข็มนาฬิกา +pdfjs-page-rotate-cw-button-label = หมุนตามเข็มนาฬิกา +pdfjs-page-rotate-ccw-button = + .title = หมุนทวนเข็มนาฬิกา +pdfjs-page-rotate-ccw-button-label = หมุนทวนเข็มนาฬิกา +pdfjs-cursor-text-select-tool-button = + .title = เปิดใช้งานเครื่องมือการเลือกข้อความ +pdfjs-cursor-text-select-tool-button-label = เครื่องมือการเลือกข้อความ +pdfjs-cursor-hand-tool-button = + .title = เปิดใช้งานเครื่องมือมือ +pdfjs-cursor-hand-tool-button-label = เครื่องมือมือ +pdfjs-scroll-page-button = + .title = ใช้การเลื่อนหน้า +pdfjs-scroll-page-button-label = การเลื่อนหน้า +pdfjs-scroll-vertical-button = + .title = ใช้การเลื่อนแนวตั้ง +pdfjs-scroll-vertical-button-label = การเลื่อนแนวตั้ง +pdfjs-scroll-horizontal-button = + .title = ใช้การเลื่อนแนวนอน +pdfjs-scroll-horizontal-button-label = การเลื่อนแนวนอน +pdfjs-scroll-wrapped-button = + .title = ใช้การเลื่อนแบบคลุม +pdfjs-scroll-wrapped-button-label = เลื่อนแบบคลุม +pdfjs-spread-none-button = + .title = ไม่ต้องรวมการกระจายหน้า +pdfjs-spread-none-button-label = ไม่กระจาย +pdfjs-spread-odd-button = + .title = รวมการกระจายหน้าเริ่มจากหน้าคี่ +pdfjs-spread-odd-button-label = กระจายอย่างเหลือเศษ +pdfjs-spread-even-button = + .title = รวมการกระจายหน้าเริ่มจากหน้าคู่ +pdfjs-spread-even-button-label = กระจายอย่างเท่าเทียม + +## Document properties dialog + +pdfjs-document-properties-button = + .title = คุณสมบัติเอกสาร… +pdfjs-document-properties-button-label = คุณสมบัติเอกสาร… +pdfjs-document-properties-file-name = ชื่อไฟล์: +pdfjs-document-properties-file-size = ขนาดไฟล์: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } ไบต์) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } ไบต์) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } ไบต์) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } ไบต์) +pdfjs-document-properties-title = ชื่อเรื่อง: +pdfjs-document-properties-author = ผู้สร้าง: +pdfjs-document-properties-subject = ชื่อเรื่อง: +pdfjs-document-properties-keywords = คำสำคัญ: +pdfjs-document-properties-creation-date = วันที่สร้าง: +pdfjs-document-properties-modification-date = วันที่แก้ไข: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = ผู้สร้าง: +pdfjs-document-properties-producer = ผู้ผลิต PDF: +pdfjs-document-properties-version = รุ่น PDF: +pdfjs-document-properties-page-count = จำนวนหน้า: +pdfjs-document-properties-page-size = ขนาดหน้า: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = แนวตั้ง +pdfjs-document-properties-page-size-orientation-landscape = แนวนอน +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = จดหมาย +pdfjs-document-properties-page-size-name-legal = ข้อกฎหมาย + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = มุมมองเว็บแบบรวดเร็ว: +pdfjs-document-properties-linearized-yes = ใช่ +pdfjs-document-properties-linearized-no = ไม่ +pdfjs-document-properties-close-button = ปิด + +## Print + +pdfjs-print-progress-message = กำลังเตรียมเอกสารสำหรับการพิมพ์… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = ยกเลิก +pdfjs-printing-not-supported = คำเตือน: เบราว์เซอร์นี้ไม่ได้สนับสนุนการพิมพ์อย่างเต็มที่ +pdfjs-printing-not-ready = คำเตือน: PDF ไม่ได้รับการโหลดอย่างเต็มที่สำหรับการพิมพ์ + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = เปิด/ปิดแถบข้าง +pdfjs-toggle-sidebar-notification-button = + .title = เปิด/ปิดแถบข้าง (เอกสารมีเค้าร่าง/ไฟล์แนบ/เลเยอร์) +pdfjs-toggle-sidebar-button-label = เปิด/ปิดแถบข้าง +pdfjs-document-outline-button = + .title = แสดงเค้าร่างเอกสาร (คลิกสองครั้งเพื่อขยาย/ยุบรายการทั้งหมด) +pdfjs-document-outline-button-label = เค้าร่างเอกสาร +pdfjs-attachments-button = + .title = แสดงไฟล์แนบ +pdfjs-attachments-button-label = ไฟล์แนบ +pdfjs-layers-button = + .title = แสดงเลเยอร์ (คลิกสองครั้งเพื่อรีเซ็ตเลเยอร์ทั้งหมดเป็นสถานะเริ่มต้น) +pdfjs-layers-button-label = เลเยอร์ +pdfjs-thumbs-button = + .title = แสดงภาพขนาดย่อ +pdfjs-thumbs-button-label = ภาพขนาดย่อ +pdfjs-current-outline-item-button = + .title = ค้นหารายการเค้าร่างปัจจุบัน +pdfjs-current-outline-item-button-label = รายการเค้าร่างปัจจุบัน +pdfjs-findbar-button = + .title = ค้นหาในเอกสาร +pdfjs-findbar-button-label = ค้นหา +pdfjs-additional-layers = เลเยอร์เพิ่มเติม + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = หน้า { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = ภาพขนาดย่อของหน้า { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = ค้นหา + .placeholder = ค้นหาในเอกสาร… +pdfjs-find-previous-button = + .title = หาตำแหน่งก่อนหน้าของวลี +pdfjs-find-previous-button-label = ก่อนหน้า +pdfjs-find-next-button = + .title = หาตำแหน่งถัดไปของวลี +pdfjs-find-next-button-label = ถัดไป +pdfjs-find-highlight-checkbox = เน้นสีทั้งหมด +pdfjs-find-match-case-checkbox-label = ตัวพิมพ์ใหญ่เล็กตรงกัน +pdfjs-find-match-diacritics-checkbox-label = เครื่องหมายกำกับการออกเสียงตรงกัน +pdfjs-find-entire-word-checkbox-label = ทั้งคำ +pdfjs-find-reached-top = ค้นหาถึงจุดเริ่มต้นของหน้า เริ่มค้นต่อจากด้านล่าง +pdfjs-find-reached-bottom = ค้นหาถึงจุดสิ้นสุดหน้า เริ่มค้นต่อจากด้านบน +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = { $current } จาก { $total } รายการที่ตรงกัน +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = มากกว่า { $limit } รายการที่ตรงกัน +pdfjs-find-not-found = ไม่พบวลี + +## Predefined zoom values + +pdfjs-page-scale-width = ความกว้างหน้า +pdfjs-page-scale-fit = พอดีหน้า +pdfjs-page-scale-auto = ซูมอัตโนมัติ +pdfjs-page-scale-actual = ขนาดจริง +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = หน้า { $page } + +## Loading indicator messages + +pdfjs-loading-error = เกิดข้อผิดพลาดขณะโหลด PDF +pdfjs-invalid-file-error = ไฟล์ PDF ไม่ถูกต้องหรือเสียหาย +pdfjs-missing-file-error = ไฟล์ PDF หายไป +pdfjs-unexpected-response-error = การตอบสนองของเซิร์ฟเวอร์ที่ไม่คาดคิด +pdfjs-rendering-error = เกิดข้อผิดพลาดขณะเรนเดอร์หน้า + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [คำอธิบายประกอบ { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = ป้อนรหัสผ่านเพื่อเปิดไฟล์ PDF นี้ +pdfjs-password-invalid = รหัสผ่านไม่ถูกต้อง โปรดลองอีกครั้ง +pdfjs-password-ok-button = ตกลง +pdfjs-password-cancel-button = ยกเลิก +pdfjs-web-fonts-disabled = แบบอักษรเว็บถูกปิดใช้งาน: ไม่สามารถใช้แบบอักษร PDF ฝังตัว + +## Editing + +pdfjs-editor-free-text-button = + .title = ข้อความ +pdfjs-editor-free-text-button-label = ข้อความ +pdfjs-editor-ink-button = + .title = รูปวาด +pdfjs-editor-ink-button-label = รูปวาด +pdfjs-editor-stamp-button = + .title = เพิ่มหรือแก้ไขภาพ +pdfjs-editor-stamp-button-label = เพิ่มหรือแก้ไขภาพ +pdfjs-editor-highlight-button = + .title = เน้น +pdfjs-editor-highlight-button-label = เน้น +pdfjs-highlight-floating-button1 = + .title = เน้นสี + .aria-label = เน้นสี +pdfjs-highlight-floating-button-label = เน้นสี + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = เอาภาพวาดออก +pdfjs-editor-remove-freetext-button = + .title = เอาข้อความออก +pdfjs-editor-remove-stamp-button = + .title = เอาภาพออก +pdfjs-editor-remove-highlight-button = + .title = เอาการเน้นสีออก + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = สี +pdfjs-editor-free-text-size-input = ขนาด +pdfjs-editor-ink-color-input = สี +pdfjs-editor-ink-thickness-input = ความหนา +pdfjs-editor-ink-opacity-input = ความทึบ +pdfjs-editor-stamp-add-image-button = + .title = เพิ่มภาพ +pdfjs-editor-stamp-add-image-button-label = เพิ่มภาพ +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = ความหนา +pdfjs-editor-free-highlight-thickness-title = + .title = เปลี่ยนความหนาเมื่อเน้นรายการอื่นๆ ที่ไม่ใช่ข้อความ +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = ตัวแก้ไขข้อความ + .default-content = เริ่มพิมพ์ได้เลย… +pdfjs-free-text = + .aria-label = ตัวแก้ไขข้อความ +pdfjs-free-text-default-content = เริ่มพิมพ์… +pdfjs-ink = + .aria-label = ตัวแก้ไขรูปวาด +pdfjs-ink-canvas = + .aria-label = ภาพที่ผู้ใช้สร้างขึ้น + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = ข้อความทดแทน +pdfjs-editor-alt-text-edit-button = + .aria-label = แก้ไขข้อความทดแทน +pdfjs-editor-alt-text-edit-button-label = แก้ไขข้อความทดแทน +pdfjs-editor-alt-text-dialog-label = เลือกตัวเลือก +pdfjs-editor-alt-text-dialog-description = ข้อความทดแทนสามารถช่วยเหลือได้เมื่อผู้ใช้มองไม่เห็นภาพ หรือภาพไม่โหลด +pdfjs-editor-alt-text-add-description-label = เพิ่มคำอธิบาย +pdfjs-editor-alt-text-add-description-description = แนะนำให้ใช้ 1-2 ประโยคซึ่งอธิบายหัวเรื่อง ฉาก หรือการกระทำ +pdfjs-editor-alt-text-mark-decorative-label = ทำเครื่องหมายเป็นสิ่งตกแต่ง +pdfjs-editor-alt-text-mark-decorative-description = สิ่งนี้ใช้สำหรับภาพที่เป็นสิ่งประดับ เช่น ขอบ หรือลายน้ำ +pdfjs-editor-alt-text-cancel-button = ยกเลิก +pdfjs-editor-alt-text-save-button = บันทึก +pdfjs-editor-alt-text-decorative-tooltip = ทำเครื่องหมายเป็นสิ่งตกแต่งแล้ว +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = ตัวอย่างเช่น “ชายหนุ่มคนหนึ่งนั่งลงที่โต๊ะเพื่อรับประทานอาหารมื้อหนึ่ง” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = ข้อความทดแทน + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = มุมซ้ายบน — ปรับขนาด +pdfjs-editor-resizer-label-top-middle = ตรงกลางด้านบน — ปรับขนาด +pdfjs-editor-resizer-label-top-right = มุมขวาบน — ปรับขนาด +pdfjs-editor-resizer-label-middle-right = ตรงกลางด้านขวา — ปรับขนาด +pdfjs-editor-resizer-label-bottom-right = มุมขวาล่าง — ปรับขนาด +pdfjs-editor-resizer-label-bottom-middle = ตรงกลางด้านล่าง — ปรับขนาด +pdfjs-editor-resizer-label-bottom-left = มุมซ้ายล่าง — ปรับขนาด +pdfjs-editor-resizer-label-middle-left = ตรงกลางด้านซ้าย — ปรับขนาด +pdfjs-editor-resizer-top-left = + .aria-label = มุมซ้ายบน — ปรับขนาด +pdfjs-editor-resizer-top-middle = + .aria-label = ตรงกลางด้านบน — ปรับขนาด +pdfjs-editor-resizer-top-right = + .aria-label = มุมขวาบน — ปรับขนาด +pdfjs-editor-resizer-middle-right = + .aria-label = ตรงกลางด้านขวา — ปรับขนาด +pdfjs-editor-resizer-bottom-right = + .aria-label = มุมขวาล่าง — ปรับขนาด +pdfjs-editor-resizer-bottom-middle = + .aria-label = ตรงกลางด้านล่าง — ปรับขนาด +pdfjs-editor-resizer-bottom-left = + .aria-label = มุมซ้ายล่าง — ปรับขนาด +pdfjs-editor-resizer-middle-left = + .aria-label = ตรงกลางด้านซ้าย — ปรับขนาด + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = สีเน้น +pdfjs-editor-colorpicker-button = + .title = เปลี่ยนสี +pdfjs-editor-colorpicker-dropdown = + .aria-label = ทางเลือกสี +pdfjs-editor-colorpicker-yellow = + .title = เหลือง +pdfjs-editor-colorpicker-green = + .title = เขียว +pdfjs-editor-colorpicker-blue = + .title = น้ำเงิน +pdfjs-editor-colorpicker-pink = + .title = ชมพู +pdfjs-editor-colorpicker-red = + .title = แดง + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = แสดงทั้งหมด +pdfjs-editor-highlight-show-all-button = + .title = แสดงทั้งหมด + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = แก้ไขข้อความทดแทน (คำอธิบายภาพ) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = เพิ่มข้อความทดแทน (คำอธิบายภาพ) +pdfjs-editor-new-alt-text-textarea = + .placeholder = เขียนคำอธิบายของคุณที่นี่… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = คำอธิบายสั้นๆ สำหรับผู้ที่ไม่สามารถมองเห็นภาพหรือเมื่อภาพไม่โหลด +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = ข้อความทดแทนนี้ถูกสร้างขึ้นโดยอัตโนมัติและอาจไม่ถูกต้อง +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = เรียนรู้เพิ่มเติม +pdfjs-editor-new-alt-text-create-automatically-button-label = สร้างข้อความทดแทนโดยอัตโนมัติ +pdfjs-editor-new-alt-text-not-now-button = ไม่ใช่ตอนนี้ +pdfjs-editor-new-alt-text-error-title = ไม่สามารถสร้างข้อความทดแทนโดยอัตโนมัติได้ +pdfjs-editor-new-alt-text-error-description = กรุณาเขียนข้อความทดแทนด้วยตัวเองหรือลองใหม่อีกครั้งในภายหลัง +pdfjs-editor-new-alt-text-error-close-button = ปิด +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = กำลังดาวน์โหลดโมเดล AI สำหรับข้อความทดแทน ({ $downloadedSize } จาก { $totalSize } MB) + .aria-valuetext = กำลังดาวน์โหลดโมเดล AI สำหรับข้อความทดแทน ({ $downloadedSize } จาก { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = เพิ่มข้อความทดแทนแล้ว +pdfjs-editor-new-alt-text-added-button-label = เพิ่มข้อความทดแทนแล้ว +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = ขาดข้อความทดแทน +pdfjs-editor-new-alt-text-missing-button-label = ขาดข้อความทดแทน +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = ตรวจสอบข้อความทดแทน +pdfjs-editor-new-alt-text-to-review-button-label = ตรวจสอบข้อความทดแทน +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = สร้างขึ้นโดยอัตโนมัติ: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = ตั้งค่าข้อความทดแทนภาพ +pdfjs-image-alt-text-settings-button-label = ตั้งค่าข้อความทดแทนภาพ +pdfjs-editor-alt-text-settings-dialog-label = ตั้งค่าข้อความทดแทนภาพ +pdfjs-editor-alt-text-settings-automatic-title = การทดแทนด้วยข้อความอัตโนมัติ +pdfjs-editor-alt-text-settings-create-model-button-label = สร้างข้อความทดแทนอัตโนมัติ +pdfjs-editor-alt-text-settings-create-model-description = แนะนำคำอธิบายเพื่อช่วยเหลือผู้ที่ไม่สามารถมองเห็นภาพหรือเมื่อภาพไม่โหลด +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = โมเดล AI สำหรับข้อความทดแทน ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = ทำงานในเครื่องของคุณเพื่อให้ข้อมูลของคุณเป็นส่วนตัว จำเป็นสำหรับข้อความทดแทนอัตโนมัติ +pdfjs-editor-alt-text-settings-delete-model-button = ลบ +pdfjs-editor-alt-text-settings-download-model-button = ดาวน์โหลด +pdfjs-editor-alt-text-settings-downloading-model-button = กำลังดาวน์โหลด… +pdfjs-editor-alt-text-settings-editor-title = ตัวแก้ไขข้อความทดแทน +pdfjs-editor-alt-text-settings-show-dialog-button-label = แสดงตัวแก้ไขข้อความทดแทนทันทีเมื่อเพิ่มภาพ +pdfjs-editor-alt-text-settings-show-dialog-description = ช่วยให้คุณแน่ใจว่าภาพทั้งหมดของคุณมีข้อความทดแทน +pdfjs-editor-alt-text-settings-close-button = ปิด + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = เอาการเน้นสีออกแล้ว +pdfjs-editor-undo-bar-message-freetext = เอาข้อความออกแล้ว +pdfjs-editor-undo-bar-message-ink = เอาภาพวาดออกแล้ว +pdfjs-editor-undo-bar-message-stamp = เอาภาพออกแล้ว +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = เอาคำอธิบายประกอบ { $count } รายการออกแล้ว +pdfjs-editor-undo-bar-undo-button = + .title = เลิกทำ +pdfjs-editor-undo-bar-undo-button-label = เลิกทำ +pdfjs-editor-undo-bar-close-button = + .title = ปิด +pdfjs-editor-undo-bar-close-button-label = ปิด diff --git a/public/pdfjs/web/locale/tl/viewer.ftl b/public/pdfjs/web/locale/tl/viewer.ftl new file mode 100644 index 0000000..faa0009 --- /dev/null +++ b/public/pdfjs/web/locale/tl/viewer.ftl @@ -0,0 +1,257 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Naunang Pahina +pdfjs-previous-button-label = Nakaraan +pdfjs-next-button = + .title = Sunod na Pahina +pdfjs-next-button-label = Sunod +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Pahina +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = ng { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } ng { $pagesCount }) +pdfjs-zoom-out-button = + .title = Paliitin +pdfjs-zoom-out-button-label = Paliitin +pdfjs-zoom-in-button = + .title = Palakihin +pdfjs-zoom-in-button-label = Palakihin +pdfjs-zoom-select = + .title = Mag-zoom +pdfjs-presentation-mode-button = + .title = Lumipat sa Presentation Mode +pdfjs-presentation-mode-button-label = Presentation Mode +pdfjs-open-file-button = + .title = Magbukas ng file +pdfjs-open-file-button-label = Buksan +pdfjs-print-button = + .title = i-Print +pdfjs-print-button-label = i-Print + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Mga Kagamitan +pdfjs-tools-button-label = Mga Kagamitan +pdfjs-first-page-button = + .title = Pumunta sa Unang Pahina +pdfjs-first-page-button-label = Pumunta sa Unang Pahina +pdfjs-last-page-button = + .title = Pumunta sa Huling Pahina +pdfjs-last-page-button-label = Pumunta sa Huling Pahina +pdfjs-page-rotate-cw-button = + .title = Paikutin Pakanan +pdfjs-page-rotate-cw-button-label = Paikutin Pakanan +pdfjs-page-rotate-ccw-button = + .title = Paikutin Pakaliwa +pdfjs-page-rotate-ccw-button-label = Paikutin Pakaliwa +pdfjs-cursor-text-select-tool-button = + .title = I-enable ang Text Selection Tool +pdfjs-cursor-text-select-tool-button-label = Text Selection Tool +pdfjs-cursor-hand-tool-button = + .title = I-enable ang Hand Tool +pdfjs-cursor-hand-tool-button-label = Hand Tool +pdfjs-scroll-vertical-button = + .title = Gumamit ng Vertical Scrolling +pdfjs-scroll-vertical-button-label = Vertical Scrolling +pdfjs-scroll-horizontal-button = + .title = Gumamit ng Horizontal Scrolling +pdfjs-scroll-horizontal-button-label = Horizontal Scrolling +pdfjs-scroll-wrapped-button = + .title = Gumamit ng Wrapped Scrolling +pdfjs-scroll-wrapped-button-label = Wrapped Scrolling +pdfjs-spread-none-button = + .title = Huwag pagsamahin ang mga page spread +pdfjs-spread-none-button-label = No Spreads +pdfjs-spread-odd-button = + .title = Join page spreads starting with odd-numbered pages +pdfjs-spread-odd-button-label = Mga Odd Spread +pdfjs-spread-even-button = + .title = Pagsamahin ang mga page spread na nagsisimula sa mga even-numbered na pahina +pdfjs-spread-even-button-label = Mga Even Spread + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Mga Katangian ng Dokumento… +pdfjs-document-properties-button-label = Mga Katangian ng Dokumento… +pdfjs-document-properties-file-name = File name: +pdfjs-document-properties-file-size = File size: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Pamagat: +pdfjs-document-properties-author = May-akda: +pdfjs-document-properties-subject = Paksa: +pdfjs-document-properties-keywords = Mga keyword: +pdfjs-document-properties-creation-date = Petsa ng Pagkakagawa: +pdfjs-document-properties-modification-date = Petsa ng Pagkakabago: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Tagalikha: +pdfjs-document-properties-producer = PDF Producer: +pdfjs-document-properties-version = PDF Version: +pdfjs-document-properties-page-count = Bilang ng Pahina: +pdfjs-document-properties-page-size = Laki ng Pahina: +pdfjs-document-properties-page-size-unit-inches = pulgada +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = patayo +pdfjs-document-properties-page-size-orientation-landscape = pahiga +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Fast Web View: +pdfjs-document-properties-linearized-yes = Oo +pdfjs-document-properties-linearized-no = Hindi +pdfjs-document-properties-close-button = Isara + +## Print + +pdfjs-print-progress-message = Inihahanda ang dokumento para sa pag-print… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Kanselahin +pdfjs-printing-not-supported = Babala: Hindi pa ganap na suportado ang pag-print sa browser na ito. +pdfjs-printing-not-ready = Babala: Hindi ganap na nabuksan ang PDF para sa pag-print. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Ipakita/Itago ang Sidebar +pdfjs-toggle-sidebar-notification-button = + .title = Ipakita/Itago ang Sidebar (nagtataglay ang dokumento ng balangkas/mga attachment/mga layer) +pdfjs-toggle-sidebar-button-label = Ipakita/Itago ang Sidebar +pdfjs-document-outline-button = + .title = Ipakita ang Document Outline (mag-double-click para i-expand/collapse ang laman) +pdfjs-document-outline-button-label = Balangkas ng Dokumento +pdfjs-attachments-button = + .title = Ipakita ang mga Attachment +pdfjs-attachments-button-label = Mga attachment +pdfjs-layers-button = + .title = Ipakita ang mga Layer (mag-double click para mareset ang lahat ng layer sa orihinal na estado) +pdfjs-layers-button-label = Mga layer +pdfjs-thumbs-button = + .title = Ipakita ang mga Thumbnail +pdfjs-thumbs-button-label = Mga thumbnail +pdfjs-findbar-button = + .title = Hanapin sa Dokumento +pdfjs-findbar-button-label = Hanapin +pdfjs-additional-layers = Mga Karagdagang Layer + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Pahina { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Thumbnail ng Pahina { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Hanapin + .placeholder = Hanapin sa dokumento… +pdfjs-find-previous-button = + .title = Hanapin ang nakaraang pangyayari ng parirala +pdfjs-find-previous-button-label = Nakaraan +pdfjs-find-next-button = + .title = Hanapin ang susunod na pangyayari ng parirala +pdfjs-find-next-button-label = Susunod +pdfjs-find-highlight-checkbox = I-highlight lahat +pdfjs-find-match-case-checkbox-label = Itugma ang case +pdfjs-find-entire-word-checkbox-label = Buong salita +pdfjs-find-reached-top = Naabot na ang tuktok ng dokumento, ipinagpatuloy mula sa ilalim +pdfjs-find-reached-bottom = Naabot na ang dulo ng dokumento, ipinagpatuloy mula sa tuktok +pdfjs-find-not-found = Hindi natagpuan ang parirala + +## Predefined zoom values + +pdfjs-page-scale-width = Lapad ng Pahina +pdfjs-page-scale-fit = Pagkasyahin ang Pahina +pdfjs-page-scale-auto = Automatic Zoom +pdfjs-page-scale-actual = Totoong sukat +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Nagkaproblema habang niloload ang PDF. +pdfjs-invalid-file-error = Di-wasto o sira ang PDF file. +pdfjs-missing-file-error = Nawawalang PDF file. +pdfjs-unexpected-response-error = Hindi inaasahang tugon ng server. +pdfjs-rendering-error = Nagkaproblema habang nirerender ang pahina. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] + +## Password + +pdfjs-password-label = Ipasok ang password upang buksan ang PDF file na ito. +pdfjs-password-invalid = Maling password. Subukan uli. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Kanselahin +pdfjs-web-fonts-disabled = Naka-disable ang mga Web font: hindi kayang gamitin ang mga naka-embed na PDF font. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/tr/viewer.ftl b/public/pdfjs/web/locale/tr/viewer.ftl new file mode 100644 index 0000000..b1b7cbf --- /dev/null +++ b/public/pdfjs/web/locale/tr/viewer.ftl @@ -0,0 +1,515 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Önceki sayfa +pdfjs-previous-button-label = Önceki +pdfjs-next-button = + .title = Sonraki sayfa +pdfjs-next-button-label = Sonraki +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Sayfa +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = / { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount }) +pdfjs-zoom-out-button = + .title = Uzaklaştır +pdfjs-zoom-out-button-label = Uzaklaştır +pdfjs-zoom-in-button = + .title = Yakınlaştır +pdfjs-zoom-in-button-label = Yakınlaştır +pdfjs-zoom-select = + .title = Yakınlaştırma +pdfjs-presentation-mode-button = + .title = Sunum moduna geç +pdfjs-presentation-mode-button-label = Sunum modu +pdfjs-open-file-button = + .title = Dosya aç +pdfjs-open-file-button-label = Aç +pdfjs-print-button = + .title = Yazdır +pdfjs-print-button-label = Yazdır +pdfjs-save-button = + .title = Kaydet +pdfjs-save-button-label = Kaydet +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = İndir +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = İndir +pdfjs-bookmark-button = + .title = Geçerli sayfa (geçerli sayfanın adresini görüntüle) +pdfjs-bookmark-button-label = Geçerli sayfa + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Araçlar +pdfjs-tools-button-label = Araçlar +pdfjs-first-page-button = + .title = İlk sayfaya git +pdfjs-first-page-button-label = İlk sayfaya git +pdfjs-last-page-button = + .title = Son sayfaya git +pdfjs-last-page-button-label = Son sayfaya git +pdfjs-page-rotate-cw-button = + .title = Saat yönünde döndür +pdfjs-page-rotate-cw-button-label = Saat yönünde döndür +pdfjs-page-rotate-ccw-button = + .title = Saat yönünün tersine döndür +pdfjs-page-rotate-ccw-button-label = Saat yönünün tersine döndür +pdfjs-cursor-text-select-tool-button = + .title = Metin seçme aracını etkinleştir +pdfjs-cursor-text-select-tool-button-label = Metin seçme aracı +pdfjs-cursor-hand-tool-button = + .title = El aracını etkinleştir +pdfjs-cursor-hand-tool-button-label = El aracı +pdfjs-scroll-page-button = + .title = Sayfa kaydırmayı kullan +pdfjs-scroll-page-button-label = Sayfa kaydırma +pdfjs-scroll-vertical-button = + .title = Dikey kaydırmayı kullan +pdfjs-scroll-vertical-button-label = Dikey kaydırma +pdfjs-scroll-horizontal-button = + .title = Yatay kaydırmayı kullan +pdfjs-scroll-horizontal-button-label = Yatay kaydırma +pdfjs-scroll-wrapped-button = + .title = Yan yana kaydırmayı kullan +pdfjs-scroll-wrapped-button-label = Yan yana kaydırma +pdfjs-spread-none-button = + .title = Yan yana sayfaları birleştirme +pdfjs-spread-none-button-label = Birleştirme +pdfjs-spread-odd-button = + .title = Yan yana sayfaları tek numaralı sayfalardan başlayarak birleştir +pdfjs-spread-odd-button-label = Tek numaralı +pdfjs-spread-even-button = + .title = Yan yana sayfaları çift numaralı sayfalardan başlayarak birleştir +pdfjs-spread-even-button-label = Çift numaralı + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Belge özellikleri… +pdfjs-document-properties-button-label = Belge özellikleri… +pdfjs-document-properties-file-name = Dosya adı: +pdfjs-document-properties-file-size = Dosya boyutu: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bayt) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bayt) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bayt) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bayt) +pdfjs-document-properties-title = Başlık: +pdfjs-document-properties-author = Yazar: +pdfjs-document-properties-subject = Konu: +pdfjs-document-properties-keywords = Anahtar kelimeler: +pdfjs-document-properties-creation-date = Oluşturma tarihi: +pdfjs-document-properties-modification-date = Değiştirme tarihi: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date } { $time } +pdfjs-document-properties-creator = Oluşturan: +pdfjs-document-properties-producer = PDF üreticisi: +pdfjs-document-properties-version = PDF sürümü: +pdfjs-document-properties-page-count = Sayfa sayısı: +pdfjs-document-properties-page-size = Sayfa boyutu: +pdfjs-document-properties-page-size-unit-inches = inç +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = dikey +pdfjs-document-properties-page-size-orientation-landscape = yatay +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Hızlı web görünümü: +pdfjs-document-properties-linearized-yes = Evet +pdfjs-document-properties-linearized-no = Hayır +pdfjs-document-properties-close-button = Kapat + +## Print + +pdfjs-print-progress-message = Belge yazdırılmaya hazırlanıyor… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = %{ $progress } +pdfjs-print-progress-close-button = İptal +pdfjs-printing-not-supported = Uyarı: Yazdırma bu tarayıcı tarafından tam olarak desteklenmemektedir. +pdfjs-printing-not-ready = Uyarı: PDF tamamen yüklenmedi ve yazdırmaya hazır değil. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Kenar çubuğunu aç/kapat +pdfjs-toggle-sidebar-notification-button = + .title = Kenar çubuğunu aç/kapat (Belge ana hat/ekler/katmanlar içeriyor) +pdfjs-toggle-sidebar-button-label = Kenar çubuğunu aç/kapat +pdfjs-document-outline-button = + .title = Belge ana hatlarını göster (Tüm öğeleri genişletmek/daraltmak için çift tıklayın) +pdfjs-document-outline-button-label = Belge ana hatları +pdfjs-attachments-button = + .title = Ekleri göster +pdfjs-attachments-button-label = Ekler +pdfjs-layers-button = + .title = Katmanları göster (tüm katmanları varsayılan duruma sıfırlamak için çift tıklayın) +pdfjs-layers-button-label = Katmanlar +pdfjs-thumbs-button = + .title = Küçük resimleri göster +pdfjs-thumbs-button-label = Küçük resimler +pdfjs-current-outline-item-button = + .title = Mevcut ana hat öğesini bul +pdfjs-current-outline-item-button-label = Mevcut ana hat öğesi +pdfjs-findbar-button = + .title = Belgede bul +pdfjs-findbar-button-label = Bul +pdfjs-additional-layers = Ek katmanlar + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Sayfa { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page }. sayfanın küçük hâli + +## Find panel button title and messages + +pdfjs-find-input = + .title = Bul + .placeholder = Belgede bul… +pdfjs-find-previous-button = + .title = Önceki eşleşmeyi bul +pdfjs-find-previous-button-label = Önceki +pdfjs-find-next-button = + .title = Sonraki eşleşmeyi bul +pdfjs-find-next-button-label = Sonraki +pdfjs-find-highlight-checkbox = Tümünü vurgula +pdfjs-find-match-case-checkbox-label = Büyük-küçük harfe duyarlı +pdfjs-find-match-diacritics-checkbox-label = Fonetik işaretleri bul +pdfjs-find-entire-word-checkbox-label = Tam sözcükler +pdfjs-find-reached-top = Belgenin başına ulaşıldı, sonundan devam edildi +pdfjs-find-reached-bottom = Belgenin sonuna ulaşıldı, başından devam edildi +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $total } eşleşmeden { $current }. eşleşme + *[other] { $total } eşleşmeden { $current }. eşleşme + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] { $limit } eşleşmeden fazla + *[other] { $limit } eşleşmeden fazla + } +pdfjs-find-not-found = Eşleşme bulunamadı + +## Predefined zoom values + +pdfjs-page-scale-width = Sayfa genişliği +pdfjs-page-scale-fit = Sayfayı sığdır +pdfjs-page-scale-auto = Otomatik yakınlaştır +pdfjs-page-scale-actual = Gerçek boyut +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = %{ $scale } + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Sayfa { $page } + +## Loading indicator messages + +pdfjs-loading-error = PDF yüklenirken bir hata oluştu. +pdfjs-invalid-file-error = Geçersiz veya bozulmuş PDF dosyası. +pdfjs-missing-file-error = PDF dosyası eksik. +pdfjs-unexpected-response-error = Beklenmeyen sunucu yanıtı. +pdfjs-rendering-error = Sayfa yorumlanırken bir hata oluştu. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date } { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } işareti] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Bu PDF dosyasını açmak için parolasını yazın. +pdfjs-password-invalid = Geçersiz parola. Lütfen yeniden deneyin. +pdfjs-password-ok-button = Tamam +pdfjs-password-cancel-button = İptal +pdfjs-web-fonts-disabled = Web fontları devre dışı: Gömülü PDF fontları kullanılamıyor. + +## Editing + +pdfjs-editor-free-text-button = + .title = Metin +pdfjs-editor-free-text-button-label = Metin +pdfjs-editor-ink-button = + .title = Çiz +pdfjs-editor-ink-button-label = Çiz +pdfjs-editor-stamp-button = + .title = Resim ekle veya düzenle +pdfjs-editor-stamp-button-label = Resim ekle veya düzenle +pdfjs-editor-highlight-button = + .title = Vurgula +pdfjs-editor-highlight-button-label = Vurgula +pdfjs-highlight-floating-button1 = + .title = Vurgula + .aria-label = Vurgula +pdfjs-highlight-floating-button-label = Vurgula + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Çizimi kaldır +pdfjs-editor-remove-freetext-button = + .title = Metni kaldır +pdfjs-editor-remove-stamp-button = + .title = Resmi kaldır +pdfjs-editor-remove-highlight-button = + .title = Vurgulamayı kaldır + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Renk +pdfjs-editor-free-text-size-input = Boyut +pdfjs-editor-ink-color-input = Renk +pdfjs-editor-ink-thickness-input = Kalınlık +pdfjs-editor-ink-opacity-input = Saydamlık +pdfjs-editor-stamp-add-image-button = + .title = Resim ekle +pdfjs-editor-stamp-add-image-button-label = Resim ekle +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Kalınlık +pdfjs-editor-free-highlight-thickness-title = + .title = Metin dışındaki öğeleri vurgularken kalınlığı değiştir +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Metin düzenleyicisi + .default-content = Yazmaya başlayın… +pdfjs-free-text = + .aria-label = Metin düzenleyicisi +pdfjs-free-text-default-content = Yazmaya başlayın… +pdfjs-ink = + .aria-label = Çizim düzenleyicisi +pdfjs-ink-canvas = + .aria-label = Kullanıcı tarafından oluşturulan resim + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Alternatif metin +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternatif metni düzenle +pdfjs-editor-alt-text-edit-button-label = Alternatif metni düzenle +pdfjs-editor-alt-text-dialog-label = Bir seçenek seçin +pdfjs-editor-alt-text-dialog-description = Alternatif metin, insanlar resmi göremediğinde veya resim yüklenmediğinde işe yarar. +pdfjs-editor-alt-text-add-description-label = Açıklama ekle +pdfjs-editor-alt-text-add-description-description = Konuyu, ortamı veya eylemleri tanımlayan bir iki cümle yazmaya çalışın. +pdfjs-editor-alt-text-mark-decorative-label = Dekoratif olarak işaretle +pdfjs-editor-alt-text-mark-decorative-description = Kenarlıklar veya filigranlar gibi dekoratif resimler için kullanılır. +pdfjs-editor-alt-text-cancel-button = Vazgeç +pdfjs-editor-alt-text-save-button = Kaydet +pdfjs-editor-alt-text-decorative-tooltip = Dekoratif olarak işaretlendi +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Örneğin, “Genç bir adam yemek yemek için masaya oturuyor” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternatif metin + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Sol üst köşe — yeniden boyutlandır +pdfjs-editor-resizer-label-top-middle = Üst orta — yeniden boyutlandır +pdfjs-editor-resizer-label-top-right = Sağ üst köşe — yeniden boyutlandır +pdfjs-editor-resizer-label-middle-right = Orta sağ — yeniden boyutlandır +pdfjs-editor-resizer-label-bottom-right = Sağ alt köşe — yeniden boyutlandır +pdfjs-editor-resizer-label-bottom-middle = Alt orta — yeniden boyutlandır +pdfjs-editor-resizer-label-bottom-left = Sol alt köşe — yeniden boyutlandır +pdfjs-editor-resizer-label-middle-left = Orta sol — yeniden boyutlandır +pdfjs-editor-resizer-top-left = + .aria-label = Sol üst köşe — yeniden boyutlandır +pdfjs-editor-resizer-top-middle = + .aria-label = Üst orta — yeniden boyutlandır +pdfjs-editor-resizer-top-right = + .aria-label = Sağ üst köşe — yeniden boyutlandır +pdfjs-editor-resizer-middle-right = + .aria-label = Orta sağ — yeniden boyutlandır +pdfjs-editor-resizer-bottom-right = + .aria-label = Sağ alt köşe — yeniden boyutlandır +pdfjs-editor-resizer-bottom-middle = + .aria-label = Alt orta — yeniden boyutlandır +pdfjs-editor-resizer-bottom-left = + .aria-label = Sol alt köşe — yeniden boyutlandır +pdfjs-editor-resizer-middle-left = + .aria-label = Orta sol — yeniden boyutlandır + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Vurgu rengi +pdfjs-editor-colorpicker-button = + .title = Rengi değiştir +pdfjs-editor-colorpicker-dropdown = + .aria-label = Renk seçenekleri +pdfjs-editor-colorpicker-yellow = + .title = Sarı +pdfjs-editor-colorpicker-green = + .title = Yeşil +pdfjs-editor-colorpicker-blue = + .title = Mavi +pdfjs-editor-colorpicker-pink = + .title = Pembe +pdfjs-editor-colorpicker-red = + .title = Kırmızı + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Tümünü göster +pdfjs-editor-highlight-show-all-button = + .title = Tümünü göster + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Alt metni düzenle (resim açıklaması) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Alt metin ekle (resim açıklaması) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Açıklamanızı buraya yazın… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Görme engelli kişilere gösterilecek veya resmin yüklenemediği durumlarda gösterilecek kısa açıklama. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Bu alt metin otomatik olarak oluşturulmuştur ve hatalı olabilir. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Daha fazla bilgi alın +pdfjs-editor-new-alt-text-create-automatically-button-label = Otomatik olarak alt metin oluştur +pdfjs-editor-new-alt-text-not-now-button = Şimdi değil +pdfjs-editor-new-alt-text-error-title = Alt metin otomatik olarak oluşturulamadı +pdfjs-editor-new-alt-text-error-description = Lütfen kendi alt metninizi yazın veya daha sonra yeniden deneyin. +pdfjs-editor-new-alt-text-error-close-button = Kapat +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Alt metin yapay zekâ modeli indiriliyor ({ $downloadedSize } / { $totalSize } MB) + .aria-valuetext = Alt metin yapay zekâ modeli indiriliyor ({ $downloadedSize } / { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternatif metin eklendi +pdfjs-editor-new-alt-text-added-button-label = Alt metin eklendi +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Alternatif metin eksik +pdfjs-editor-new-alt-text-missing-button-label = Alt metin eksik +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternatif metni incele +pdfjs-editor-new-alt-text-to-review-button-label = Alt metni incele +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Otomatik olarak oluşturuldu: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Resim alt metni ayarları +pdfjs-image-alt-text-settings-button-label = Resim alt metni ayarları +pdfjs-editor-alt-text-settings-dialog-label = Resim alt metni ayarları +pdfjs-editor-alt-text-settings-automatic-title = Otomatik alt metin +pdfjs-editor-alt-text-settings-create-model-button-label = Otomatik olarak alt metin oluştur +pdfjs-editor-alt-text-settings-create-model-description = Görme engelli kişilere gösterilecek veya resmin yüklenemediği durumlarda gösterilecek açıklamalar önerir. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Alt metin yapay zekâ modeli ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Verilerinizin gizli kalması için cihazınızda yerel olarak çalışır. Otomatik alt metin için gereklidir. +pdfjs-editor-alt-text-settings-delete-model-button = Sil +pdfjs-editor-alt-text-settings-download-model-button = İndir +pdfjs-editor-alt-text-settings-downloading-model-button = İndiriliyor… +pdfjs-editor-alt-text-settings-editor-title = Alt metin düzenleyicisi +pdfjs-editor-alt-text-settings-show-dialog-button-label = Resim eklerken alt metin düzenleyicisini hemen göster +pdfjs-editor-alt-text-settings-show-dialog-description = Tüm resimlerinizin alt metne sahip olduğundan emin olmanızı sağlar. +pdfjs-editor-alt-text-settings-close-button = Kapat + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Vurgulama silindi +pdfjs-editor-undo-bar-message-freetext = Metin silindi +pdfjs-editor-undo-bar-message-ink = Çizim silindi +pdfjs-editor-undo-bar-message-stamp = Görsel silindi +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } ek açıklama silindi + *[other] { $count } ek açıklama silindi + } +pdfjs-editor-undo-bar-undo-button = + .title = Geri al +pdfjs-editor-undo-bar-undo-button-label = Geri al +pdfjs-editor-undo-bar-close-button = + .title = Kapat +pdfjs-editor-undo-bar-close-button-label = Kapat diff --git a/public/pdfjs/web/locale/trs/viewer.ftl b/public/pdfjs/web/locale/trs/viewer.ftl new file mode 100644 index 0000000..aba3c72 --- /dev/null +++ b/public/pdfjs/web/locale/trs/viewer.ftl @@ -0,0 +1,197 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Pajinâ gunâj rukùu +pdfjs-previous-button-label = Sa gachin +pdfjs-next-button = + .title = Pajinâ 'na' ñaan +pdfjs-next-button-label = Ne' ñaan +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Ñanj +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = si'iaj { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount }) +pdfjs-zoom-out-button = + .title = Nagi'iaj li' +pdfjs-zoom-out-button-label = Nagi'iaj li' +pdfjs-zoom-in-button = + .title = Nagi'iaj niko' +pdfjs-zoom-in-button-label = Nagi'iaj niko' +pdfjs-zoom-select = + .title = dàj nìko ma'an +pdfjs-presentation-mode-button = + .title = Naduno' daj ga ma +pdfjs-presentation-mode-button-label = Daj gà ma +pdfjs-open-file-button = + .title = Na'nïn' chrû ñanj +pdfjs-open-file-button-label = Na'nïn +pdfjs-print-button = + .title = Nari' ña du'ua +pdfjs-print-button-label = Nari' ñadu'ua + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Rasun +pdfjs-tools-button-label = Nej rasùun +pdfjs-first-page-button = + .title = gun' riña pajina asiniin +pdfjs-first-page-button-label = Gun' riña pajina asiniin +pdfjs-last-page-button = + .title = Gun' riña pajina rukù ni'in +pdfjs-last-page-button-label = Gun' riña pajina rukù ni'inj +pdfjs-page-rotate-cw-button = + .title = Tanikaj ne' huat +pdfjs-page-rotate-cw-button-label = Tanikaj ne' huat +pdfjs-page-rotate-ccw-button = + .title = Tanikaj ne' chînt' +pdfjs-page-rotate-ccw-button-label = Tanikaj ne' chint +pdfjs-cursor-text-select-tool-button = + .title = Dugi'iaj sun' sa ganahui texto +pdfjs-cursor-text-select-tool-button-label = Nej rasun arajsun' da' nahui' texto +pdfjs-cursor-hand-tool-button = + .title = Nachrun' nej rasun +pdfjs-cursor-hand-tool-button-label = Sa rajsun ro'o' +pdfjs-scroll-vertical-button = + .title = Garasun' dukuán runūu +pdfjs-scroll-vertical-button-label = Dukuán runūu +pdfjs-scroll-horizontal-button = + .title = Garasun' dukuán nikin' nahui +pdfjs-scroll-horizontal-button-label = Dukuán nikin' nahui +pdfjs-scroll-wrapped-button = + .title = Garasun' sa nachree +pdfjs-scroll-wrapped-button-label = Sa nachree +pdfjs-spread-none-button = + .title = Si nagi'iaj nugun'un' nej pagina hua ninin +pdfjs-spread-none-button-label = Ni'io daj hua pagina +pdfjs-spread-odd-button = + .title = Nagi'iaj nugua'ant nej pajina +pdfjs-spread-odd-button-label = Ni'io' daj hua libro gurin +pdfjs-spread-even-button = + .title = Nakāj dugui' ngà nej pajinâ ayi'ì ngà da' hùi hùi +pdfjs-spread-even-button-label = Nahuin nìko nej + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Nej sa nikāj ñanj… +pdfjs-document-properties-button-label = Nej sa nikāj ñanj… +pdfjs-document-properties-file-name = Si yugui archîbo: +pdfjs-document-properties-file-size = Dàj yachìj archîbo: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Si yugui: +pdfjs-document-properties-author = Sí girirà: +pdfjs-document-properties-subject = Dugui': +pdfjs-document-properties-keywords = Nej nuguan' huìi: +pdfjs-document-properties-creation-date = Gui gurugui' man: +pdfjs-document-properties-modification-date = Nuguan' nahuin nakà: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Guiri ro' +pdfjs-document-properties-producer = Sa ri PDF: +pdfjs-document-properties-version = PDF Version: +pdfjs-document-properties-page-count = Si Guendâ Pâjina: +pdfjs-document-properties-page-size = Dàj yachìj pâjina: +pdfjs-document-properties-page-size-unit-inches = riña +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = nadu'ua +pdfjs-document-properties-page-size-orientation-landscape = dàj huaj +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Da'ngà'a +pdfjs-document-properties-page-size-name-legal = Nuguan' a'nï'ïn + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Nanèt chre ni'iajt riña Web: +pdfjs-document-properties-linearized-yes = Ga'ue +pdfjs-document-properties-linearized-no = Si ga'ue +pdfjs-document-properties-close-button = Narán + +## Print + +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Duyichin' + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Nadunā barrâ nù yi'nïn +pdfjs-toggle-sidebar-button-label = Nadunā barrâ nù yi'nïn +pdfjs-findbar-button-label = Narì' + +## Thumbnails panel item (tooltip and alt text for images) + + +## Find panel button title and messages + +pdfjs-find-previous-button-label = Sa gachîn +pdfjs-find-next-button-label = Ne' ñaan +pdfjs-find-highlight-checkbox = Daran' sa ña'an +pdfjs-find-match-case-checkbox-label = Match case +pdfjs-find-not-found = Nu narì'ij nugua'anj + +## Predefined zoom values + +pdfjs-page-scale-actual = Dàj yàchi akuan' nín +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + + +## Annotations + + +## Password + +pdfjs-password-ok-button = Ga'ue +pdfjs-password-cancel-button = Duyichin' + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/uk/viewer.ftl b/public/pdfjs/web/locale/uk/viewer.ftl new file mode 100644 index 0000000..dd54727 --- /dev/null +++ b/public/pdfjs/web/locale/uk/viewer.ftl @@ -0,0 +1,518 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Попередня сторінка +pdfjs-previous-button-label = Попередня +pdfjs-next-button = + .title = Наступна сторінка +pdfjs-next-button-label = Наступна +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Сторінка +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = із { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } із { $pagesCount }) +pdfjs-zoom-out-button = + .title = Зменшити +pdfjs-zoom-out-button-label = Зменшити +pdfjs-zoom-in-button = + .title = Збільшити +pdfjs-zoom-in-button-label = Збільшити +pdfjs-zoom-select = + .title = Масштаб +pdfjs-presentation-mode-button = + .title = Перейти в режим презентації +pdfjs-presentation-mode-button-label = Режим презентації +pdfjs-open-file-button = + .title = Відкрити файл +pdfjs-open-file-button-label = Відкрити +pdfjs-print-button = + .title = Друк +pdfjs-print-button-label = Друк +pdfjs-save-button = + .title = Зберегти +pdfjs-save-button-label = Зберегти +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Завантажити +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Завантажити +pdfjs-bookmark-button = + .title = Поточна сторінка (перегляд URL-адреси з поточної сторінки) +pdfjs-bookmark-button-label = Поточна сторінка + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Інструменти +pdfjs-tools-button-label = Інструменти +pdfjs-first-page-button = + .title = На першу сторінку +pdfjs-first-page-button-label = На першу сторінку +pdfjs-last-page-button = + .title = На останню сторінку +pdfjs-last-page-button-label = На останню сторінку +pdfjs-page-rotate-cw-button = + .title = Повернути за годинниковою стрілкою +pdfjs-page-rotate-cw-button-label = Повернути за годинниковою стрілкою +pdfjs-page-rotate-ccw-button = + .title = Повернути проти годинникової стрілки +pdfjs-page-rotate-ccw-button-label = Повернути проти годинникової стрілки +pdfjs-cursor-text-select-tool-button = + .title = Увімкнути інструмент вибору тексту +pdfjs-cursor-text-select-tool-button-label = Інструмент вибору тексту +pdfjs-cursor-hand-tool-button = + .title = Увімкнути інструмент "Рука" +pdfjs-cursor-hand-tool-button-label = Інструмент "Рука" +pdfjs-scroll-page-button = + .title = Використовувати прокручування сторінки +pdfjs-scroll-page-button-label = Прокручування сторінки +pdfjs-scroll-vertical-button = + .title = Використовувати вертикальне прокручування +pdfjs-scroll-vertical-button-label = Вертикальне прокручування +pdfjs-scroll-horizontal-button = + .title = Використовувати горизонтальне прокручування +pdfjs-scroll-horizontal-button-label = Горизонтальне прокручування +pdfjs-scroll-wrapped-button = + .title = Використовувати масштабоване прокручування +pdfjs-scroll-wrapped-button-label = Масштабоване прокручування +pdfjs-spread-none-button = + .title = Не використовувати розгорнуті сторінки +pdfjs-spread-none-button-label = Без розгорнутих сторінок +pdfjs-spread-odd-button = + .title = Розгорнуті сторінки починаються з непарних номерів +pdfjs-spread-odd-button-label = Непарні сторінки зліва +pdfjs-spread-even-button = + .title = Розгорнуті сторінки починаються з парних номерів +pdfjs-spread-even-button-label = Парні сторінки зліва + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Властивості документа… +pdfjs-document-properties-button-label = Властивості документа… +pdfjs-document-properties-file-name = Назва файлу: +pdfjs-document-properties-file-size = Розмір файлу: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } кБ ({ $b } байтів) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } МБ ({ $b } байтів) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } кБ ({ $size_b } байтів) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } байтів) +pdfjs-document-properties-title = Заголовок: +pdfjs-document-properties-author = Автор: +pdfjs-document-properties-subject = Тема: +pdfjs-document-properties-keywords = Ключові слова: +pdfjs-document-properties-creation-date = Дата створення: +pdfjs-document-properties-modification-date = Дата зміни: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Створено: +pdfjs-document-properties-producer = Виробник PDF: +pdfjs-document-properties-version = Версія PDF: +pdfjs-document-properties-page-count = Кількість сторінок: +pdfjs-document-properties-page-size = Розмір сторінки: +pdfjs-document-properties-page-size-unit-inches = дюймів +pdfjs-document-properties-page-size-unit-millimeters = мм +pdfjs-document-properties-page-size-orientation-portrait = книжкова +pdfjs-document-properties-page-size-orientation-landscape = альбомна +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Швидкий перегляд в Інтернеті: +pdfjs-document-properties-linearized-yes = Так +pdfjs-document-properties-linearized-no = Ні +pdfjs-document-properties-close-button = Закрити + +## Print + +pdfjs-print-progress-message = Підготовка документу до друку… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Скасувати +pdfjs-printing-not-supported = Попередження: Цей браузер не повністю підтримує друк. +pdfjs-printing-not-ready = Попередження: PDF не повністю завантажений для друку. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Бічна панель +pdfjs-toggle-sidebar-notification-button = + .title = Перемкнути бічну панель (документ містить ескіз/вкладення/шари) +pdfjs-toggle-sidebar-button-label = Перемкнути бічну панель +pdfjs-document-outline-button = + .title = Показати схему документу (подвійний клік для розгортання/згортання елементів) +pdfjs-document-outline-button-label = Схема документа +pdfjs-attachments-button = + .title = Показати вкладення +pdfjs-attachments-button-label = Вкладення +pdfjs-layers-button = + .title = Показати шари (двічі клацніть, щоб скинути всі шари до типового стану) +pdfjs-layers-button-label = Шари +pdfjs-thumbs-button = + .title = Показати мініатюри +pdfjs-thumbs-button-label = Мініатюри +pdfjs-current-outline-item-button = + .title = Знайти поточний елемент змісту +pdfjs-current-outline-item-button-label = Поточний елемент змісту +pdfjs-findbar-button = + .title = Знайти в документі +pdfjs-findbar-button-label = Знайти +pdfjs-additional-layers = Додаткові шари + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Сторінка { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Ескіз сторінки { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Знайти + .placeholder = Знайти в документі… +pdfjs-find-previous-button = + .title = Знайти попереднє входження фрази +pdfjs-find-previous-button-label = Попереднє +pdfjs-find-next-button = + .title = Знайти наступне входження фрази +pdfjs-find-next-button-label = Наступне +pdfjs-find-highlight-checkbox = Підсвітити все +pdfjs-find-match-case-checkbox-label = З урахуванням регістру +pdfjs-find-match-diacritics-checkbox-label = Відповідність діакритичних знаків +pdfjs-find-entire-word-checkbox-label = Цілі слова +pdfjs-find-reached-top = Досягнуто початку документу, продовжено з кінця +pdfjs-find-reached-bottom = Досягнуто кінця документу, продовжено з початку +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] { $current } збіг з { $total } + [few] { $current } збіги з { $total } + *[many] { $current } збігів з { $total } + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Понад { $limit } збіг + [few] Понад { $limit } збіги + *[many] Понад { $limit } збігів + } +pdfjs-find-not-found = Фразу не знайдено + +## Predefined zoom values + +pdfjs-page-scale-width = За шириною +pdfjs-page-scale-fit = Вмістити +pdfjs-page-scale-auto = Автомасштаб +pdfjs-page-scale-actual = Дійсний розмір +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Сторінка { $page } + +## Loading indicator messages + +pdfjs-loading-error = Під час завантаження PDF сталася помилка. +pdfjs-invalid-file-error = Недійсний або пошкоджений PDF-файл. +pdfjs-missing-file-error = Відсутній PDF-файл. +pdfjs-unexpected-response-error = Неочікувана відповідь сервера. +pdfjs-rendering-error = Під час виведення сторінки сталася помилка. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type }-анотація] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Введіть пароль для відкриття цього PDF-файлу. +pdfjs-password-invalid = Неправильний пароль. Спробуйте ще раз. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Скасувати +pdfjs-web-fonts-disabled = Вебшрифти вимкнено: неможливо використати вбудовані у PDF шрифти. + +## Editing + +pdfjs-editor-free-text-button = + .title = Текст +pdfjs-editor-free-text-button-label = Текст +pdfjs-editor-ink-button = + .title = Малювати +pdfjs-editor-ink-button-label = Малювати +pdfjs-editor-stamp-button = + .title = Додати чи редагувати зображення +pdfjs-editor-stamp-button-label = Додати чи редагувати зображення +pdfjs-editor-highlight-button = + .title = Підсвітити +pdfjs-editor-highlight-button-label = Підсвітити +pdfjs-highlight-floating-button1 = + .title = Підсвітити + .aria-label = Підсвітити +pdfjs-highlight-floating-button-label = Підсвітити + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Вилучити малюнок +pdfjs-editor-remove-freetext-button = + .title = Вилучити текст +pdfjs-editor-remove-stamp-button = + .title = Вилучити зображення +pdfjs-editor-remove-highlight-button = + .title = Вилучити підсвічування + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Колір +pdfjs-editor-free-text-size-input = Розмір +pdfjs-editor-ink-color-input = Колір +pdfjs-editor-ink-thickness-input = Товщина +pdfjs-editor-ink-opacity-input = Прозорість +pdfjs-editor-stamp-add-image-button = + .title = Додати зображення +pdfjs-editor-stamp-add-image-button-label = Додати зображення +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Товщина +pdfjs-editor-free-highlight-thickness-title = + .title = Змінюйте товщину під час підсвічування елементів, крім тексту +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Текстовий редактор + .default-content = Напишіть щось… +pdfjs-free-text = + .aria-label = Текстовий редактор +pdfjs-free-text-default-content = Почніть вводити… +pdfjs-ink = + .aria-label = Графічний редактор +pdfjs-ink-canvas = + .aria-label = Зображення, створене користувачем + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Альтернативний текст +pdfjs-editor-alt-text-edit-button = + .aria-label = Редагувати альтернативний текст +pdfjs-editor-alt-text-edit-button-label = Змінити альтернативний текст +pdfjs-editor-alt-text-dialog-label = Вибрати варіант +pdfjs-editor-alt-text-dialog-description = Альтернативний текст допомагає, коли зображення не видно або коли воно не завантажується. +pdfjs-editor-alt-text-add-description-label = Додати опис +pdfjs-editor-alt-text-add-description-description = Намагайтеся створити 1-2 речення, які описують тему, обставини або дії. +pdfjs-editor-alt-text-mark-decorative-label = Позначити декоративним +pdfjs-editor-alt-text-mark-decorative-description = Використовується для декоративних зображень, наприклад рамок або водяних знаків. +pdfjs-editor-alt-text-cancel-button = Скасувати +pdfjs-editor-alt-text-save-button = Зберегти +pdfjs-editor-alt-text-decorative-tooltip = Позначено декоративним +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Наприклад, “Молодий чоловік сідає за стіл їсти” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Альтернативний текст + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Верхній лівий кут – зміна розміру +pdfjs-editor-resizer-label-top-middle = Вгорі посередині – зміна розміру +pdfjs-editor-resizer-label-top-right = Верхній правий кут – зміна розміру +pdfjs-editor-resizer-label-middle-right = Праворуч посередині – зміна розміру +pdfjs-editor-resizer-label-bottom-right = Нижній правий кут – зміна розміру +pdfjs-editor-resizer-label-bottom-middle = Внизу посередині – зміна розміру +pdfjs-editor-resizer-label-bottom-left = Нижній лівий кут – зміна розміру +pdfjs-editor-resizer-label-middle-left = Ліворуч посередині – зміна розміру +pdfjs-editor-resizer-top-left = + .aria-label = Верхній лівий кут – зміна розміру +pdfjs-editor-resizer-top-middle = + .aria-label = Вгорі посередині – зміна розміру +pdfjs-editor-resizer-top-right = + .aria-label = Верхній правий кут – зміна розміру +pdfjs-editor-resizer-middle-right = + .aria-label = Праворуч посередині – зміна розміру +pdfjs-editor-resizer-bottom-right = + .aria-label = Нижній правий кут – зміна розміру +pdfjs-editor-resizer-bottom-middle = + .aria-label = Внизу посередині – зміна розміру +pdfjs-editor-resizer-bottom-left = + .aria-label = Нижній лівий кут – зміна розміру +pdfjs-editor-resizer-middle-left = + .aria-label = Ліворуч посередині – зміна розміру + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Колір підсвічування +pdfjs-editor-colorpicker-button = + .title = Змінити колір +pdfjs-editor-colorpicker-dropdown = + .aria-label = Вибір кольору +pdfjs-editor-colorpicker-yellow = + .title = Жовтий +pdfjs-editor-colorpicker-green = + .title = Зелений +pdfjs-editor-colorpicker-blue = + .title = Блакитний +pdfjs-editor-colorpicker-pink = + .title = Рожевий +pdfjs-editor-colorpicker-red = + .title = Червоний + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Показати все +pdfjs-editor-highlight-show-all-button = + .title = Показати все + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Редагувати альтернативний текст (опис зображення) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Додати альтернативний текст (опис зображення) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Напишіть свій опис тут… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Короткий опис для людей, які не бачать зображення, або якщо зображення не завантажується. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Цей альтернативний текст створено автоматично, тому він може бути неточним. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Докладніше +pdfjs-editor-new-alt-text-create-automatically-button-label = Автоматично створювати альтернативний текст +pdfjs-editor-new-alt-text-not-now-button = Не зараз +pdfjs-editor-new-alt-text-error-title = Не вдалося автоматично створити альтернативний текст +pdfjs-editor-new-alt-text-error-description = Напишіть власний альтернативний текст або повторіть спробу пізніше. +pdfjs-editor-new-alt-text-error-close-button = Закрити +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Завантаження моделі ШІ для альтернативного тексту ({ $downloadedSize } з { $totalSize } МБ) + .aria-valuetext = Завантаження моделі ШІ для альтернативного тексту ({ $downloadedSize } з { $totalSize } МБ) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Альтернативний текст додано +pdfjs-editor-new-alt-text-added-button-label = Альтернативний текст додано +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Відсутній альтернативний текст +pdfjs-editor-new-alt-text-missing-button-label = Відсутній альтернативний текст +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Переглянути альтернативний текст +pdfjs-editor-new-alt-text-to-review-button-label = Переглянути альтернативний текст +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Створено автоматично: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Налаштування альтернативного тексту зображення +pdfjs-image-alt-text-settings-button-label = Налаштування альтернативного тексту зображення +pdfjs-editor-alt-text-settings-dialog-label = Налаштування альтернативного тексту зображення +pdfjs-editor-alt-text-settings-automatic-title = Автоматичний альтернативний текст +pdfjs-editor-alt-text-settings-create-model-button-label = Автоматично створювати альтернативний текст +pdfjs-editor-alt-text-settings-create-model-description = Пропонує описи, щоб допомогти людям, які не бачать зображення, або якщо зображення не завантажується. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Модель ШІ для альтернативного тексту ({ $totalSize } МБ) +pdfjs-editor-alt-text-settings-ai-model-description = Працює локально на вашому пристрої, тому приватність ваших даних захищена. Призначена для автоматичного створення альтернативного тексту. +pdfjs-editor-alt-text-settings-delete-model-button = Видалити +pdfjs-editor-alt-text-settings-download-model-button = Завантажити +pdfjs-editor-alt-text-settings-downloading-model-button = Завантаження… +pdfjs-editor-alt-text-settings-editor-title = Редактор альтернативного тексту +pdfjs-editor-alt-text-settings-show-dialog-button-label = Показувати редактор альтернативного тексту під час додавання зображення +pdfjs-editor-alt-text-settings-show-dialog-description = Допомагає переконатися, що всі ваші зображення мають альтернативний текст. +pdfjs-editor-alt-text-settings-close-button = Закрити + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Підсвічення вилучено +pdfjs-editor-undo-bar-message-freetext = Текст вилучено +pdfjs-editor-undo-bar-message-ink = Малюнок вилучено +pdfjs-editor-undo-bar-message-stamp = Зображення вилучено +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } анотацію вилучено + [few] { $count } анотації вилучено + *[many] { $count } анотацій вилучено + } +pdfjs-editor-undo-bar-undo-button = + .title = Повернути +pdfjs-editor-undo-bar-undo-button-label = Повернути +pdfjs-editor-undo-bar-close-button = + .title = Закрити +pdfjs-editor-undo-bar-close-button-label = Закрити diff --git a/public/pdfjs/web/locale/ur/viewer.ftl b/public/pdfjs/web/locale/ur/viewer.ftl new file mode 100644 index 0000000..c15f157 --- /dev/null +++ b/public/pdfjs/web/locale/ur/viewer.ftl @@ -0,0 +1,248 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = پچھلا صفحہ +pdfjs-previous-button-label = پچھلا +pdfjs-next-button = + .title = اگلا صفحہ +pdfjs-next-button-label = آگے +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = صفحہ +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = { $pagesCount } کا +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } کا { $pagesCount }) +pdfjs-zoom-out-button = + .title = باہر زوم کریں +pdfjs-zoom-out-button-label = باہر زوم کریں +pdfjs-zoom-in-button = + .title = اندر زوم کریں +pdfjs-zoom-in-button-label = اندر زوم کریں +pdfjs-zoom-select = + .title = زوم +pdfjs-presentation-mode-button = + .title = پیشکش موڈ میں چلے جائیں +pdfjs-presentation-mode-button-label = پیشکش موڈ +pdfjs-open-file-button = + .title = مسل کھولیں +pdfjs-open-file-button-label = کھولیں +pdfjs-print-button = + .title = چھاپیں +pdfjs-print-button-label = چھاپیں + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = آلات +pdfjs-tools-button-label = آلات +pdfjs-first-page-button = + .title = پہلے صفحہ پر جائیں +pdfjs-first-page-button-label = پہلے صفحہ پر جائیں +pdfjs-last-page-button = + .title = آخری صفحہ پر جائیں +pdfjs-last-page-button-label = آخری صفحہ پر جائیں +pdfjs-page-rotate-cw-button = + .title = گھڑی وار گھمائیں +pdfjs-page-rotate-cw-button-label = گھڑی وار گھمائیں +pdfjs-page-rotate-ccw-button = + .title = ضد گھڑی وار گھمائیں +pdfjs-page-rotate-ccw-button-label = ضد گھڑی وار گھمائیں +pdfjs-cursor-text-select-tool-button = + .title = متن کے انتخاب کے ٹول کو فعال بناے +pdfjs-cursor-text-select-tool-button-label = متن کے انتخاب کا آلہ +pdfjs-cursor-hand-tool-button = + .title = ہینڈ ٹول کو فعال بناییں +pdfjs-cursor-hand-tool-button-label = ہاتھ کا آلہ +pdfjs-scroll-vertical-button = + .title = عمودی اسکرولنگ کا استعمال کریں +pdfjs-scroll-vertical-button-label = عمودی اسکرولنگ +pdfjs-scroll-horizontal-button = + .title = افقی سکرولنگ کا استعمال کریں +pdfjs-scroll-horizontal-button-label = افقی سکرولنگ +pdfjs-spread-none-button = + .title = صفحہ پھیلانے میں شامل نہ ہوں +pdfjs-spread-none-button-label = کوئی پھیلاؤ نہیں +pdfjs-spread-odd-button-label = تاک پھیلاؤ +pdfjs-spread-even-button-label = جفت پھیلاؤ + +## Document properties dialog + +pdfjs-document-properties-button = + .title = دستاویز خواص… +pdfjs-document-properties-button-label = دستاویز خواص… +pdfjs-document-properties-file-name = نام مسل: +pdfjs-document-properties-file-size = مسل سائز: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = عنوان: +pdfjs-document-properties-author = تخلیق کار: +pdfjs-document-properties-subject = موضوع: +pdfjs-document-properties-keywords = کلیدی الفاظ: +pdfjs-document-properties-creation-date = تخلیق کی تاریخ: +pdfjs-document-properties-modification-date = ترمیم کی تاریخ: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }، { $time } +pdfjs-document-properties-creator = تخلیق کار: +pdfjs-document-properties-producer = PDF پیدا کار: +pdfjs-document-properties-version = PDF ورژن: +pdfjs-document-properties-page-count = صفحہ شمار: +pdfjs-document-properties-page-size = صفہ کی لمبائ: +pdfjs-document-properties-page-size-unit-inches = میں +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = عمودی انداز +pdfjs-document-properties-page-size-orientation-landscape = افقى انداز +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = خط +pdfjs-document-properties-page-size-name-legal = قانونی + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } { $name } { $orientation } + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = تیز ویب دیکھیں: +pdfjs-document-properties-linearized-yes = ہاں +pdfjs-document-properties-linearized-no = نہیں +pdfjs-document-properties-close-button = بند کریں + +## Print + +pdfjs-print-progress-message = چھاپنے کرنے کے لیے دستاویز تیار کیے جا رھے ھیں +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = *{ $progress }%* +pdfjs-print-progress-close-button = منسوخ کریں +pdfjs-printing-not-supported = تنبیہ:چھاپنا اس براؤزر پر پوری طرح معاونت شدہ نہیں ہے۔ +pdfjs-printing-not-ready = تنبیہ: PDF چھپائی کے لیے پوری طرح لوڈ نہیں ہوئی۔ + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = سلائیڈ ٹوگل کریں +pdfjs-toggle-sidebar-button-label = سلائیڈ ٹوگل کریں +pdfjs-document-outline-button = + .title = دستاویز کی سرخیاں دکھایں (تمام اشیاء وسیع / غائب کرنے کے لیے ڈبل کلک کریں) +pdfjs-document-outline-button-label = دستاویز آؤٹ لائن +pdfjs-attachments-button = + .title = منسلکات دکھائیں +pdfjs-attachments-button-label = منسلکات +pdfjs-thumbs-button = + .title = تھمبنیل دکھائیں +pdfjs-thumbs-button-label = مجمل +pdfjs-findbar-button = + .title = دستاویز میں ڈھونڈیں +pdfjs-findbar-button-label = ڈھونڈیں + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = صفحہ { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = صفحے کا مجمل { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = ڈھونڈیں + .placeholder = دستاویز… میں ڈھونڈیں +pdfjs-find-previous-button = + .title = فقرے کا پچھلا وقوع ڈھونڈیں +pdfjs-find-previous-button-label = پچھلا +pdfjs-find-next-button = + .title = فقرے کا اگلہ وقوع ڈھونڈیں +pdfjs-find-next-button-label = آگے +pdfjs-find-highlight-checkbox = تمام نمایاں کریں +pdfjs-find-match-case-checkbox-label = حروف مشابہ کریں +pdfjs-find-entire-word-checkbox-label = تمام الفاظ +pdfjs-find-reached-top = صفحہ کے شروع پر پہنچ گیا، نیچے سے جاری کیا +pdfjs-find-reached-bottom = صفحہ کے اختتام پر پہنچ گیا، اوپر سے جاری کیا +pdfjs-find-not-found = فقرا نہیں ملا + +## Predefined zoom values + +pdfjs-page-scale-width = صفحہ چوڑائی +pdfjs-page-scale-fit = صفحہ فٹنگ +pdfjs-page-scale-auto = خودکار زوم +pdfjs-page-scale-actual = اصل سائز +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = صفحہ { $page } + +## Loading indicator messages + +pdfjs-loading-error = PDF لوڈ کرتے وقت نقص آ گیا۔ +pdfjs-invalid-file-error = ناجائز یا خراب PDF مسل +pdfjs-missing-file-error = PDF مسل غائب ہے۔ +pdfjs-unexpected-response-error = غیرمتوقع پیش کار جواب +pdfjs-rendering-error = صفحہ بناتے ہوئے نقص آ گیا۔ + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }.{ $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } نوٹ] + +## Password + +pdfjs-password-label = PDF مسل کھولنے کے لیے پاس ورڈ داخل کریں. +pdfjs-password-invalid = ناجائز پاس ورڈ. براےؑ کرم دوبارہ کوشش کریں. +pdfjs-password-ok-button = ٹھیک ہے +pdfjs-password-cancel-button = منسوخ کریں +pdfjs-web-fonts-disabled = ویب فانٹ نا اہل ہیں: شامل PDF فانٹ استعمال کرنے میں ناکام۔ + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/uz/viewer.ftl b/public/pdfjs/web/locale/uz/viewer.ftl new file mode 100644 index 0000000..fb82f22 --- /dev/null +++ b/public/pdfjs/web/locale/uz/viewer.ftl @@ -0,0 +1,187 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Oldingi sahifa +pdfjs-previous-button-label = Oldingi +pdfjs-next-button = + .title = Keyingi sahifa +pdfjs-next-button-label = Keyingi +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = /{ $pagesCount } +pdfjs-zoom-out-button = + .title = Kichiklashtirish +pdfjs-zoom-out-button-label = Kichiklashtirish +pdfjs-zoom-in-button = + .title = Kattalashtirish +pdfjs-zoom-in-button-label = Kattalashtirish +pdfjs-zoom-select = + .title = Masshtab +pdfjs-presentation-mode-button = + .title = Namoyish usuliga oʻtish +pdfjs-presentation-mode-button-label = Namoyish usuli +pdfjs-open-file-button = + .title = Faylni ochish +pdfjs-open-file-button-label = Ochish +pdfjs-print-button = + .title = Chop qilish +pdfjs-print-button-label = Chop qilish + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Vositalar +pdfjs-tools-button-label = Vositalar +pdfjs-first-page-button = + .title = Birinchi sahifaga oʻtish +pdfjs-first-page-button-label = Birinchi sahifaga oʻtish +pdfjs-last-page-button = + .title = Soʻnggi sahifaga oʻtish +pdfjs-last-page-button-label = Soʻnggi sahifaga oʻtish +pdfjs-page-rotate-cw-button = + .title = Soat yoʻnalishi boʻyicha burish +pdfjs-page-rotate-cw-button-label = Soat yoʻnalishi boʻyicha burish +pdfjs-page-rotate-ccw-button = + .title = Soat yoʻnalishiga qarshi burish +pdfjs-page-rotate-ccw-button-label = Soat yoʻnalishiga qarshi burish + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Hujjat xossalari +pdfjs-document-properties-button-label = Hujjat xossalari +pdfjs-document-properties-file-name = Fayl nomi: +pdfjs-document-properties-file-size = Fayl hajmi: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes) +pdfjs-document-properties-title = Nomi: +pdfjs-document-properties-author = Muallifi: +pdfjs-document-properties-subject = Mavzusi: +pdfjs-document-properties-keywords = Kalit so‘zlar +pdfjs-document-properties-creation-date = Yaratilgan sanasi: +pdfjs-document-properties-modification-date = O‘zgartirilgan sanasi +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Yaratuvchi: +pdfjs-document-properties-producer = PDF ishlab chiqaruvchi: +pdfjs-document-properties-version = PDF versiyasi: +pdfjs-document-properties-page-count = Sahifa soni: + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + + +## + +pdfjs-document-properties-close-button = Yopish + +## Print + +pdfjs-printing-not-supported = Diqqat: chop qilish bruzer tomonidan toʻliq qoʻllab-quvvatlanmaydi. +pdfjs-printing-not-ready = Diqqat: PDF fayl chop qilish uchun toʻliq yuklanmadi. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Yon panelni yoqib/oʻchirib qoʻyish +pdfjs-toggle-sidebar-button-label = Yon panelni yoqib/oʻchirib qoʻyish +pdfjs-document-outline-button-label = Hujjat tuzilishi +pdfjs-attachments-button = + .title = Ilovalarni ko‘rsatish +pdfjs-attachments-button-label = Ilovalar +pdfjs-thumbs-button = + .title = Nishonchalarni koʻrsatish +pdfjs-thumbs-button-label = Nishoncha +pdfjs-findbar-button = + .title = Hujjat ichidan topish + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = { $page } sahifa +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = { $page } sahifa nishonchasi + +## Find panel button title and messages + +pdfjs-find-previous-button = + .title = Soʻzlardagi oldingi hodisani topish +pdfjs-find-previous-button-label = Oldingi +pdfjs-find-next-button = + .title = Iboradagi keyingi hodisani topish +pdfjs-find-next-button-label = Keyingi +pdfjs-find-highlight-checkbox = Barchasini ajratib koʻrsatish +pdfjs-find-match-case-checkbox-label = Katta-kichik harflarni farqlash +pdfjs-find-reached-top = Hujjatning boshigacha yetib keldik, pastdan davom ettiriladi +pdfjs-find-reached-bottom = Hujjatning oxiriga yetib kelindi, yuqoridan davom ettirladi +pdfjs-find-not-found = Soʻzlar topilmadi + +## Predefined zoom values + +pdfjs-page-scale-width = Sahifa eni +pdfjs-page-scale-fit = Sahifani moslashtirish +pdfjs-page-scale-auto = Avtomatik masshtab +pdfjs-page-scale-actual = Haqiqiy hajmi +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = PDF yuklanayotganda xato yuz berdi. +pdfjs-invalid-file-error = Xato yoki buzuq PDF fayli. +pdfjs-missing-file-error = PDF fayl kerak. +pdfjs-unexpected-response-error = Kutilmagan server javobi. +pdfjs-rendering-error = Sahifa renderlanayotganda xato yuz berdi. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Annotation] + +## Password + +pdfjs-password-label = PDF faylni ochish uchun parolni kiriting. +pdfjs-password-invalid = Parol - notoʻgʻri. Qaytadan urinib koʻring. +pdfjs-password-ok-button = OK +pdfjs-web-fonts-disabled = Veb shriftlar oʻchirilgan: ichki PDF shriftlardan foydalanib boʻlmmaydi. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/vi/viewer.ftl b/public/pdfjs/web/locale/vi/viewer.ftl new file mode 100644 index 0000000..af1291f --- /dev/null +++ b/public/pdfjs/web/locale/vi/viewer.ftl @@ -0,0 +1,503 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Trang trước +pdfjs-previous-button-label = Trước +pdfjs-next-button = + .title = Trang Sau +pdfjs-next-button-label = Tiếp +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Trang +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = trên { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } trên { $pagesCount }) +pdfjs-zoom-out-button = + .title = Thu nhỏ +pdfjs-zoom-out-button-label = Thu nhỏ +pdfjs-zoom-in-button = + .title = Phóng to +pdfjs-zoom-in-button-label = Phóng to +pdfjs-zoom-select = + .title = Thu phóng +pdfjs-presentation-mode-button = + .title = Chuyển sang chế độ trình chiếu +pdfjs-presentation-mode-button-label = Chế độ trình chiếu +pdfjs-open-file-button = + .title = Mở tập tin +pdfjs-open-file-button-label = Mở tập tin +pdfjs-print-button = + .title = In +pdfjs-print-button-label = In +pdfjs-save-button = + .title = Lưu +pdfjs-save-button-label = Lưu +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Tải xuống +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Tải xuống +pdfjs-bookmark-button = + .title = Trang hiện tại (xem URL từ trang hiện tại) +pdfjs-bookmark-button-label = Trang hiện tại + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Công cụ +pdfjs-tools-button-label = Công cụ +pdfjs-first-page-button = + .title = Về trang đầu +pdfjs-first-page-button-label = Về trang đầu +pdfjs-last-page-button = + .title = Đến trang cuối +pdfjs-last-page-button-label = Đến trang cuối +pdfjs-page-rotate-cw-button = + .title = Xoay theo chiều kim đồng hồ +pdfjs-page-rotate-cw-button-label = Xoay theo chiều kim đồng hồ +pdfjs-page-rotate-ccw-button = + .title = Xoay ngược chiều kim đồng hồ +pdfjs-page-rotate-ccw-button-label = Xoay ngược chiều kim đồng hồ +pdfjs-cursor-text-select-tool-button = + .title = Kích hoạt công cụ chọn vùng văn bản +pdfjs-cursor-text-select-tool-button-label = Công cụ chọn vùng văn bản +pdfjs-cursor-hand-tool-button = + .title = Kích hoạt công cụ con trỏ +pdfjs-cursor-hand-tool-button-label = Công cụ con trỏ +pdfjs-scroll-page-button = + .title = Sử dụng cuộn trang hiện tại +pdfjs-scroll-page-button-label = Cuộn trang hiện tại +pdfjs-scroll-vertical-button = + .title = Sử dụng cuộn dọc +pdfjs-scroll-vertical-button-label = Cuộn dọc +pdfjs-scroll-horizontal-button = + .title = Sử dụng cuộn ngang +pdfjs-scroll-horizontal-button-label = Cuộn ngang +pdfjs-scroll-wrapped-button = + .title = Sử dụng cuộn ngắt dòng +pdfjs-scroll-wrapped-button-label = Cuộn ngắt dòng +pdfjs-spread-none-button = + .title = Không nối rộng trang +pdfjs-spread-none-button-label = Không có phân cách +pdfjs-spread-odd-button = + .title = Nối trang bài bắt đầu với các trang được đánh số lẻ +pdfjs-spread-odd-button-label = Phân cách theo số lẻ +pdfjs-spread-even-button = + .title = Nối trang bài bắt đầu với các trang được đánh số chẵn +pdfjs-spread-even-button-label = Phân cách theo số chẵn + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Thuộc tính của tài liệu… +pdfjs-document-properties-button-label = Thuộc tính của tài liệu… +pdfjs-document-properties-file-name = Tên tập tin: +pdfjs-document-properties-file-size = Kích thước: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } byte) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } byte) +pdfjs-document-properties-title = Tiêu đề: +pdfjs-document-properties-author = Tác giả: +pdfjs-document-properties-subject = Chủ đề: +pdfjs-document-properties-keywords = Từ khóa: +pdfjs-document-properties-creation-date = Ngày tạo: +pdfjs-document-properties-modification-date = Ngày sửa đổi: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Người tạo: +pdfjs-document-properties-producer = Phần mềm tạo PDF: +pdfjs-document-properties-version = Phiên bản PDF: +pdfjs-document-properties-page-count = Tổng số trang: +pdfjs-document-properties-page-size = Kích thước trang: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = khổ dọc +pdfjs-document-properties-page-size-orientation-landscape = khổ ngang +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Thư +pdfjs-document-properties-page-size-name-legal = Pháp lý + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = Xem nhanh trên web: +pdfjs-document-properties-linearized-yes = Có +pdfjs-document-properties-linearized-no = Không +pdfjs-document-properties-close-button = Ðóng + +## Print + +pdfjs-print-progress-message = Chuẩn bị trang để in… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Hủy bỏ +pdfjs-printing-not-supported = Cảnh báo: In ấn không được hỗ trợ đầy đủ ở trình duyệt này. +pdfjs-printing-not-ready = Cảnh báo: PDF chưa được tải hết để in. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Bật/Tắt thanh lề +pdfjs-toggle-sidebar-notification-button = + .title = Bật tắt thanh lề (tài liệu bao gồm bản phác thảo/tập tin đính kèm/lớp) +pdfjs-toggle-sidebar-button-label = Bật/Tắt thanh lề +pdfjs-document-outline-button = + .title = Hiển thị tài liệu phác thảo (nhấp đúp vào để mở rộng/thu gọn tất cả các mục) +pdfjs-document-outline-button-label = Bản phác tài liệu +pdfjs-attachments-button = + .title = Hiện nội dung đính kèm +pdfjs-attachments-button-label = Nội dung đính kèm +pdfjs-layers-button = + .title = Hiển thị các lớp (nhấp đúp để đặt lại tất cả các lớp về trạng thái mặc định) +pdfjs-layers-button-label = Lớp +pdfjs-thumbs-button = + .title = Hiển thị ảnh thu nhỏ +pdfjs-thumbs-button-label = Ảnh thu nhỏ +pdfjs-current-outline-item-button = + .title = Tìm mục phác thảo hiện tại +pdfjs-current-outline-item-button-label = Mục phác thảo hiện tại +pdfjs-findbar-button = + .title = Tìm trong tài liệu +pdfjs-findbar-button-label = Tìm +pdfjs-additional-layers = Các lớp bổ sung + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Trang { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Ảnh thu nhỏ của trang { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Tìm + .placeholder = Tìm trong tài liệu… +pdfjs-find-previous-button = + .title = Tìm cụm từ ở phần trước +pdfjs-find-previous-button-label = Trước +pdfjs-find-next-button = + .title = Tìm cụm từ ở phần sau +pdfjs-find-next-button-label = Tiếp +pdfjs-find-highlight-checkbox = Đánh dấu tất cả +pdfjs-find-match-case-checkbox-label = Phân biệt hoa, thường +pdfjs-find-match-diacritics-checkbox-label = Khớp dấu phụ +pdfjs-find-entire-word-checkbox-label = Toàn bộ từ +pdfjs-find-reached-top = Đã đến phần đầu tài liệu, quay trở lại từ cuối +pdfjs-find-reached-bottom = Đã đến phần cuối của tài liệu, quay trở lại từ đầu +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = { $current } trên { $total } kết quả +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = Tìm thấy hơn { $limit } kết quả +pdfjs-find-not-found = Không tìm thấy cụm từ này + +## Predefined zoom values + +pdfjs-page-scale-width = Vừa chiều rộng +pdfjs-page-scale-fit = Vừa chiều cao +pdfjs-page-scale-auto = Tự động chọn kích thước +pdfjs-page-scale-actual = Kích thước thực +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = Trang { $page } + +## Loading indicator messages + +pdfjs-loading-error = Lỗi khi tải tài liệu PDF. +pdfjs-invalid-file-error = Tập tin PDF hỏng hoặc không hợp lệ. +pdfjs-missing-file-error = Thiếu tập tin PDF. +pdfjs-unexpected-response-error = Máy chủ có phản hồi lạ. +pdfjs-rendering-error = Lỗi khi hiển thị trang. + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }, { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Chú thích] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = Nhập mật khẩu để mở tập tin PDF này. +pdfjs-password-invalid = Mật khẩu không đúng. Vui lòng thử lại. +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Hủy bỏ +pdfjs-web-fonts-disabled = Phông chữ Web bị vô hiệu hóa: không thể sử dụng các phông chữ PDF được nhúng. + +## Editing + +pdfjs-editor-free-text-button = + .title = Văn bản +pdfjs-editor-free-text-button-label = Văn bản +pdfjs-editor-ink-button = + .title = Vẽ +pdfjs-editor-ink-button-label = Vẽ +pdfjs-editor-stamp-button = + .title = Thêm hoặc chỉnh sửa hình ảnh +pdfjs-editor-stamp-button-label = Thêm hoặc chỉnh sửa hình ảnh +pdfjs-editor-highlight-button = + .title = Đánh dấu +pdfjs-editor-highlight-button-label = Đánh dấu +pdfjs-highlight-floating-button1 = + .title = Đánh dấu + .aria-label = Đánh dấu +pdfjs-highlight-floating-button-label = Đánh dấu + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Xóa bản vẽ +pdfjs-editor-remove-freetext-button = + .title = Xóa văn bản +pdfjs-editor-remove-stamp-button = + .title = Xóa ảnh +pdfjs-editor-remove-highlight-button = + .title = Xóa phần đánh dấu + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = Màu +pdfjs-editor-free-text-size-input = Kích cỡ +pdfjs-editor-ink-color-input = Màu +pdfjs-editor-ink-thickness-input = Độ dày +pdfjs-editor-ink-opacity-input = Độ mờ +pdfjs-editor-stamp-add-image-button = + .title = Thêm hình ảnh +pdfjs-editor-stamp-add-image-button-label = Thêm hình ảnh +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Độ dày +pdfjs-editor-free-highlight-thickness-title = + .title = Thay đổi độ dày khi đánh dấu các mục không phải là văn bản +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Trình chỉnh sửa văn bản + .default-content = Bắt đầu nhập… +pdfjs-free-text = + .aria-label = Trình sửa văn bản +pdfjs-free-text-default-content = Bắt đầu nhập… +pdfjs-ink = + .aria-label = Trình sửa nét vẽ +pdfjs-ink-canvas = + .aria-label = Hình ảnh do người dùng tạo + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = Văn bản thay thế +pdfjs-editor-alt-text-edit-button = + .aria-label = Chỉnh sửa văn bản thay thế +pdfjs-editor-alt-text-edit-button-label = Chỉnh sửa văn bản thay thế +pdfjs-editor-alt-text-dialog-label = Chọn một lựa chọn +pdfjs-editor-alt-text-dialog-description = Văn bản thay thế sẽ hữu ích khi mọi người không thể thấy hình ảnh hoặc khi hình ảnh không tải. +pdfjs-editor-alt-text-add-description-label = Thêm một mô tả +pdfjs-editor-alt-text-add-description-description = Hãy nhắm tới 1-2 câu mô tả chủ đề, bối cảnh hoặc hành động. +pdfjs-editor-alt-text-mark-decorative-label = Đánh dấu là trang trí +pdfjs-editor-alt-text-mark-decorative-description = Điều này được sử dụng cho các hình ảnh trang trí, như đường viền hoặc watermark. +pdfjs-editor-alt-text-cancel-button = Hủy bỏ +pdfjs-editor-alt-text-save-button = Lưu +pdfjs-editor-alt-text-decorative-tooltip = Đã đánh dấu là trang trí +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Ví dụ: “Một thanh niên ngồi xuống bàn để thưởng thức một bữa ăn” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Văn bản thay thế + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = Trên cùng bên trái — thay đổi kích thước +pdfjs-editor-resizer-label-top-middle = Trên cùng ở giữa — thay đổi kích thước +pdfjs-editor-resizer-label-top-right = Trên cùng bên phải — thay đổi kích thước +pdfjs-editor-resizer-label-middle-right = Ở giữa bên phải — thay đổi kích thước +pdfjs-editor-resizer-label-bottom-right = Dưới cùng bên phải — thay đổi kích thước +pdfjs-editor-resizer-label-bottom-middle = Ở giữa dưới cùng — thay đổi kích thước +pdfjs-editor-resizer-label-bottom-left = Góc dưới bên trái — thay đổi kích thước +pdfjs-editor-resizer-label-middle-left = Ở giữa bên trái — thay đổi kích thước +pdfjs-editor-resizer-top-left = + .aria-label = Trên cùng bên trái — thay đổi kích thước +pdfjs-editor-resizer-top-middle = + .aria-label = Trên cùng ở giữa — thay đổi kích thước +pdfjs-editor-resizer-top-right = + .aria-label = Trên cùng bên phải — thay đổi kích thước +pdfjs-editor-resizer-middle-right = + .aria-label = Ở giữa bên phải — thay đổi kích thước +pdfjs-editor-resizer-bottom-right = + .aria-label = Dưới cùng bên phải — thay đổi kích thước +pdfjs-editor-resizer-bottom-middle = + .aria-label = Ở giữa dưới cùng — thay đổi kích thước +pdfjs-editor-resizer-bottom-left = + .aria-label = Góc dưới bên trái — thay đổi kích thước +pdfjs-editor-resizer-middle-left = + .aria-label = Ở giữa bên trái — thay đổi kích thước + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Màu đánh dấu +pdfjs-editor-colorpicker-button = + .title = Thay đổi màu +pdfjs-editor-colorpicker-dropdown = + .aria-label = Lựa chọn màu sắc +pdfjs-editor-colorpicker-yellow = + .title = Vàng +pdfjs-editor-colorpicker-green = + .title = Xanh lục +pdfjs-editor-colorpicker-blue = + .title = Xanh dương +pdfjs-editor-colorpicker-pink = + .title = Hồng +pdfjs-editor-colorpicker-red = + .title = Đỏ + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Hiện tất cả +pdfjs-editor-highlight-show-all-button = + .title = Hiện tất cả + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Chỉnh sửa văn bản thay thế (mô tả hình ảnh) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Thêm văn bản thay thế (mô tả hình ảnh) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Viết mô tả của bạn ở đây… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Mô tả ngắn gọn dành cho người không xem được ảnh hoặc khi không thể tải ảnh. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Văn bản thay thế này được tạo tự động và có thể không chính xác. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Tìm hiểu thêm +pdfjs-editor-new-alt-text-create-automatically-button-label = Tạo văn bản thay thế tự động +pdfjs-editor-new-alt-text-not-now-button = Không phải bây giờ +pdfjs-editor-new-alt-text-error-title = Không thể tạo tự động văn bản thay thế +pdfjs-editor-new-alt-text-error-description = Vui lòng viết văn bản thay thế của riêng bạn hoặc thử lại sau. +pdfjs-editor-new-alt-text-error-close-button = Đóng +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Đang tải xuống mô hình AI văn bản thay thế ({ $downloadedSize } trong số { $totalSize } MB) + .aria-valuetext = Đang tải xuống mô hình AI văn bản thay thế ({ $downloadedSize } trong số { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Đã thêm văn bản thay thế +pdfjs-editor-new-alt-text-added-button-label = Đã thêm văn bản thay thế +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Thiếu văn bản thay thế +pdfjs-editor-new-alt-text-missing-button-label = Thiếu văn bản thay thế +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Xem lại văn bản thay thế +pdfjs-editor-new-alt-text-to-review-button-label = Xem lại văn bản thay thế +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Được tạo tự động: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Cài đặt văn bản thay thế của hình ảnh +pdfjs-image-alt-text-settings-button-label = Cài đặt văn bản thay thế của hình ảnh +pdfjs-editor-alt-text-settings-dialog-label = Cài đặt văn bản thay thế của hình ảnh +pdfjs-editor-alt-text-settings-automatic-title = Văn bản thay thế tự động +pdfjs-editor-alt-text-settings-create-model-button-label = Tạo văn bản thay thế tự động +pdfjs-editor-alt-text-settings-create-model-description = Đề xuất mô tả giúp ích cho những người không xem được ảnh hoặc khi không thể tải ảnh. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Mô hình AI văn bản khác ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Chạy cục bộ trên thiết bị của bạn để dữ liệu của bạn luôn ở chế độ riêng tư. Bắt buộc đối với văn bản thay thế tự động. +pdfjs-editor-alt-text-settings-delete-model-button = Xóa +pdfjs-editor-alt-text-settings-download-model-button = Tải xuống +pdfjs-editor-alt-text-settings-downloading-model-button = Đang tải xuống… +pdfjs-editor-alt-text-settings-editor-title = Trình soạn thảo văn bản thay thế +pdfjs-editor-alt-text-settings-show-dialog-button-label = Hiển thị ngay trình soạn thảo văn bản thay thế khi thêm hình ảnh +pdfjs-editor-alt-text-settings-show-dialog-description = Giúp bạn đảm bảo tất cả hình ảnh của bạn đều có văn bản thay thế. +pdfjs-editor-alt-text-settings-close-button = Đóng + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Đã xóa đánh dấu +pdfjs-editor-undo-bar-message-freetext = Đã xóa văn bản +pdfjs-editor-undo-bar-message-ink = Đã xóa bản vẽ +pdfjs-editor-undo-bar-message-stamp = Đã xóa hình ảnh +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = { $count } chú thích đã bị xóa +pdfjs-editor-undo-bar-undo-button = + .title = Hoàn tác +pdfjs-editor-undo-bar-undo-button-label = Hoàn tác +pdfjs-editor-undo-bar-close-button = + .title = Đóng +pdfjs-editor-undo-bar-close-button-label = Đóng diff --git a/public/pdfjs/web/locale/wo/viewer.ftl b/public/pdfjs/web/locale/wo/viewer.ftl new file mode 100644 index 0000000..d66c459 --- /dev/null +++ b/public/pdfjs/web/locale/wo/viewer.ftl @@ -0,0 +1,127 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Xët wi jiitu +pdfjs-previous-button-label = Bi jiitu +pdfjs-next-button = + .title = Xët wi ci topp +pdfjs-next-button-label = Bi ci topp +pdfjs-zoom-out-button = + .title = Wàññi +pdfjs-zoom-out-button-label = Wàññi +pdfjs-zoom-in-button = + .title = Yaatal +pdfjs-zoom-in-button-label = Yaatal +pdfjs-zoom-select = + .title = Yambalaŋ +pdfjs-presentation-mode-button = + .title = Wañarñil ci anamu wone +pdfjs-presentation-mode-button-label = Anamu Wone +pdfjs-open-file-button = + .title = Ubbi benn dencukaay +pdfjs-open-file-button-label = Ubbi +pdfjs-print-button = + .title = Móol +pdfjs-print-button-label = Móol + +## Secondary toolbar and context menu + + +## Document properties dialog + +pdfjs-document-properties-title = Bopp: + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + + +## + + +## Print + +pdfjs-printing-not-supported = Artu: Joowkat bii nanguwul lool mool. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-thumbs-button = + .title = Wone nataal yu ndaw yi +pdfjs-thumbs-button-label = Nataal yu ndaw yi +pdfjs-findbar-button = + .title = Gis ci biir jukki bi +pdfjs-findbar-button-label = Wut + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Xët { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Wiñet bu xët { $page } + +## Find panel button title and messages + +pdfjs-find-previous-button = + .title = Seet beneen kaddu bu ni mel te jiitu +pdfjs-find-previous-button-label = Bi jiitu +pdfjs-find-next-button = + .title = Seet beneen kaddu bu ni mel +pdfjs-find-next-button-label = Bi ci topp +pdfjs-find-highlight-checkbox = Melaxal lépp +pdfjs-find-match-case-checkbox-label = Sàmm jëmmalin wi +pdfjs-find-reached-top = Jot nañu ndorteel xët wi, kontine dale ko ci suuf +pdfjs-find-reached-bottom = Jot nañu jeexitalu xët wi, kontine ci ndorte +pdfjs-find-not-found = Gisiñu kaddu gi + +## Predefined zoom values + +pdfjs-page-scale-width = Yaatuwaay bu mët +pdfjs-page-scale-fit = Xët lëmm +pdfjs-page-scale-auto = Yambalaŋ ci saa si +pdfjs-page-scale-actual = Dayo bi am + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Am na njumte ci yebum dencukaay PDF bi. +pdfjs-invalid-file-error = Dencukaay PDF bi baaxul walla mu sankar. +pdfjs-rendering-error = Am njumte bu am bi xët bi di wonewu. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [Karmat { $type }] + +## Password + +pdfjs-password-ok-button = OK +pdfjs-password-cancel-button = Neenal + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/xh/viewer.ftl b/public/pdfjs/web/locale/xh/viewer.ftl new file mode 100644 index 0000000..0798887 --- /dev/null +++ b/public/pdfjs/web/locale/xh/viewer.ftl @@ -0,0 +1,212 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = Iphepha langaphambili +pdfjs-previous-button-label = Okwangaphambili +pdfjs-next-button = + .title = Iphepha elilandelayo +pdfjs-next-button-label = Okulandelayo +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = Iphepha +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = kwali- { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } kwali { $pagesCount }) +pdfjs-zoom-out-button = + .title = Bhekelisela Kudana +pdfjs-zoom-out-button-label = Bhekelisela Kudana +pdfjs-zoom-in-button = + .title = Sondeza Kufuphi +pdfjs-zoom-in-button-label = Sondeza Kufuphi +pdfjs-zoom-select = + .title = Yandisa / Nciphisa +pdfjs-presentation-mode-button = + .title = Tshintshela kwimo yonikezelo +pdfjs-presentation-mode-button-label = Imo yonikezelo +pdfjs-open-file-button = + .title = Vula Ifayile +pdfjs-open-file-button-label = Vula +pdfjs-print-button = + .title = Printa +pdfjs-print-button-label = Printa + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = Izixhobo zemiyalelo +pdfjs-tools-button-label = Izixhobo zemiyalelo +pdfjs-first-page-button = + .title = Yiya kwiphepha lokuqala +pdfjs-first-page-button-label = Yiya kwiphepha lokuqala +pdfjs-last-page-button = + .title = Yiya kwiphepha lokugqibela +pdfjs-last-page-button-label = Yiya kwiphepha lokugqibela +pdfjs-page-rotate-cw-button = + .title = Jikelisa ngasekunene +pdfjs-page-rotate-cw-button-label = Jikelisa ngasekunene +pdfjs-page-rotate-ccw-button = + .title = Jikelisa ngasekhohlo +pdfjs-page-rotate-ccw-button-label = Jikelisa ngasekhohlo +pdfjs-cursor-text-select-tool-button = + .title = Vumela iSixhobo sokuKhetha iTeksti +pdfjs-cursor-text-select-tool-button-label = ISixhobo sokuKhetha iTeksti +pdfjs-cursor-hand-tool-button = + .title = Yenza iSixhobo seSandla siSebenze +pdfjs-cursor-hand-tool-button-label = ISixhobo seSandla + +## Document properties dialog + +pdfjs-document-properties-button = + .title = Iipropati zoxwebhu… +pdfjs-document-properties-button-label = Iipropati zoxwebhu… +pdfjs-document-properties-file-name = Igama lefayile: +pdfjs-document-properties-file-size = Isayizi yefayile: +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB (iibhayiti{ $size_b }) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB (iibhayithi{ $size_b }) +pdfjs-document-properties-title = Umxholo: +pdfjs-document-properties-author = Umbhali: +pdfjs-document-properties-subject = Umbandela: +pdfjs-document-properties-keywords = Amagama aphambili: +pdfjs-document-properties-creation-date = Umhla wokwenziwa kwayo: +pdfjs-document-properties-modification-date = Umhla wokulungiswa kwayo: +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = Umntu oyenzileyo: +pdfjs-document-properties-producer = Umvelisi we-PDF: +pdfjs-document-properties-version = Uhlelo lwe-PDF: +pdfjs-document-properties-page-count = Inani lamaphepha: + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + + +## + +pdfjs-document-properties-close-button = Vala + +## Print + +pdfjs-print-progress-message = Ilungisa uxwebhu ukuze iprinte… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = Rhoxisa +pdfjs-printing-not-supported = Isilumkiso: Ukuprinta akuxhaswa ngokupheleleyo yile bhrawuza. +pdfjs-printing-not-ready = Isilumkiso: IPDF ayihlohlwanga ngokupheleleyo ukwenzela ukuprinta. + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = Togola ngebha eseCaleni +pdfjs-toggle-sidebar-button-label = Togola ngebha eseCaleni +pdfjs-document-outline-button = + .title = Bonisa uLwandlalo loXwebhu (cofa kabini ukuze wandise/diliza zonke izinto) +pdfjs-document-outline-button-label = Isishwankathelo soxwebhu +pdfjs-attachments-button = + .title = Bonisa iziqhotyoshelwa +pdfjs-attachments-button-label = Iziqhoboshelo +pdfjs-thumbs-button = + .title = Bonisa ukrobiso kumfanekiso +pdfjs-thumbs-button-label = Ukrobiso kumfanekiso +pdfjs-findbar-button = + .title = Fumana kuXwebhu +pdfjs-findbar-button-label = Fumana + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = Iphepha { $page } +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = Ukrobiso kumfanekiso wephepha { $page } + +## Find panel button title and messages + +pdfjs-find-input = + .title = Fumana + .placeholder = Fumana kuXwebhu… +pdfjs-find-previous-button = + .title = Fumanisa isenzeko sangaphambili sebinzana lamagama +pdfjs-find-previous-button-label = Okwangaphambili +pdfjs-find-next-button = + .title = Fumanisa isenzeko esilandelayo sebinzana lamagama +pdfjs-find-next-button-label = Okulandelayo +pdfjs-find-highlight-checkbox = Qaqambisa konke +pdfjs-find-match-case-checkbox-label = Tshatisa ngobukhulu bukanobumba +pdfjs-find-reached-top = Ufike ngaphezulu ephepheni, kusukwa ngezantsi +pdfjs-find-reached-bottom = Ufike ekupheleni kwephepha, kusukwa ngaphezulu +pdfjs-find-not-found = Ibinzana alifunyenwanga + +## Predefined zoom values + +pdfjs-page-scale-width = Ububanzi bephepha +pdfjs-page-scale-fit = Ukulinganiswa kwephepha +pdfjs-page-scale-auto = Ukwandisa/Ukunciphisa Ngokwayo +pdfjs-page-scale-actual = Ubungakanani bokwenene +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + + +## Loading indicator messages + +pdfjs-loading-error = Imposiso yenzekile xa kulayishwa i-PDF. +pdfjs-invalid-file-error = Ifayile ye-PDF engeyiyo okanye eyonakalisiweyo. +pdfjs-missing-file-error = Ifayile ye-PDF edukileyo. +pdfjs-unexpected-response-error = Impendulo yeseva engalindelekanga. +pdfjs-rendering-error = Imposiso yenzekile xa bekunikezelwa iphepha. + +## Annotations + +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } Ubhalo-nqaku] + +## Password + +pdfjs-password-label = Faka ipasiwedi ukuze uvule le fayile yePDF. +pdfjs-password-invalid = Ipasiwedi ayisebenzi. Nceda uzame kwakhona. +pdfjs-password-ok-button = KULUNGILE +pdfjs-password-cancel-button = Rhoxisa +pdfjs-web-fonts-disabled = Iifonti zewebhu ziqhwalelisiwe: ayikwazi ukusebenzisa iifonti ze-PDF ezincanyathelisiweyo. + +## Editing + + +## Alt-text dialog + + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + diff --git a/public/pdfjs/web/locale/zh-CN/viewer.ftl b/public/pdfjs/web/locale/zh-CN/viewer.ftl new file mode 100644 index 0000000..8fe9a6a --- /dev/null +++ b/public/pdfjs/web/locale/zh-CN/viewer.ftl @@ -0,0 +1,503 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = 上一页 +pdfjs-previous-button-label = 上一页 +pdfjs-next-button = + .title = 下一页 +pdfjs-next-button-label = 下一页 +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = 页面 +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = / { $pagesCount } +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount }) +pdfjs-zoom-out-button = + .title = 缩小 +pdfjs-zoom-out-button-label = 缩小 +pdfjs-zoom-in-button = + .title = 放大 +pdfjs-zoom-in-button-label = 放大 +pdfjs-zoom-select = + .title = 缩放 +pdfjs-presentation-mode-button = + .title = 切换到演示模式 +pdfjs-presentation-mode-button-label = 演示模式 +pdfjs-open-file-button = + .title = 打开文件 +pdfjs-open-file-button-label = 打开 +pdfjs-print-button = + .title = 打印 +pdfjs-print-button-label = 打印 +pdfjs-save-button = + .title = 保存 +pdfjs-save-button-label = 保存 +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = 下载 +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = 下载 +pdfjs-bookmark-button = + .title = 当前页面(在当前页面查看 URL) +pdfjs-bookmark-button-label = 当前页面 + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = 工具 +pdfjs-tools-button-label = 工具 +pdfjs-first-page-button = + .title = 转到第一页 +pdfjs-first-page-button-label = 转到第一页 +pdfjs-last-page-button = + .title = 转到最后一页 +pdfjs-last-page-button-label = 转到最后一页 +pdfjs-page-rotate-cw-button = + .title = 顺时针旋转 +pdfjs-page-rotate-cw-button-label = 顺时针旋转 +pdfjs-page-rotate-ccw-button = + .title = 逆时针旋转 +pdfjs-page-rotate-ccw-button-label = 逆时针旋转 +pdfjs-cursor-text-select-tool-button = + .title = 启用文本选择工具 +pdfjs-cursor-text-select-tool-button-label = 文本选择工具 +pdfjs-cursor-hand-tool-button = + .title = 启用手形工具 +pdfjs-cursor-hand-tool-button-label = 手形工具 +pdfjs-scroll-page-button = + .title = 使用页面滚动 +pdfjs-scroll-page-button-label = 页面滚动 +pdfjs-scroll-vertical-button = + .title = 使用垂直滚动 +pdfjs-scroll-vertical-button-label = 垂直滚动 +pdfjs-scroll-horizontal-button = + .title = 使用水平滚动 +pdfjs-scroll-horizontal-button-label = 水平滚动 +pdfjs-scroll-wrapped-button = + .title = 使用平铺滚动 +pdfjs-scroll-wrapped-button-label = 平铺滚动 +pdfjs-spread-none-button = + .title = 不加入衔接页 +pdfjs-spread-none-button-label = 单页视图 +pdfjs-spread-odd-button = + .title = 加入衔接页使奇数页作为起始页 +pdfjs-spread-odd-button-label = 双页视图 +pdfjs-spread-even-button = + .title = 加入衔接页使偶数页作为起始页 +pdfjs-spread-even-button-label = 书籍视图 + +## Document properties dialog + +pdfjs-document-properties-button = + .title = 文档属性… +pdfjs-document-properties-button-label = 文档属性… +pdfjs-document-properties-file-name = 文件名: +pdfjs-document-properties-file-size = 文件大小: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB({ $b } 字节) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB({ $b } 字节) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } 字节) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } 字节) +pdfjs-document-properties-title = 标题: +pdfjs-document-properties-author = 作者: +pdfjs-document-properties-subject = 主题: +pdfjs-document-properties-keywords = 关键词: +pdfjs-document-properties-creation-date = 创建日期: +pdfjs-document-properties-modification-date = 修改日期: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date }, { $time } +pdfjs-document-properties-creator = 创建者: +pdfjs-document-properties-producer = PDF 生成器: +pdfjs-document-properties-version = PDF 版本: +pdfjs-document-properties-page-count = 页数: +pdfjs-document-properties-page-size = 页面大小: +pdfjs-document-properties-page-size-unit-inches = 英寸 +pdfjs-document-properties-page-size-unit-millimeters = 毫米 +pdfjs-document-properties-page-size-orientation-portrait = 纵向 +pdfjs-document-properties-page-size-orientation-landscape = 横向 +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit }({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit }({ $name },{ $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = 快速 Web 视图: +pdfjs-document-properties-linearized-yes = 是 +pdfjs-document-properties-linearized-no = 否 +pdfjs-document-properties-close-button = 关闭 + +## Print + +pdfjs-print-progress-message = 正在准备打印文档… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = 取消 +pdfjs-printing-not-supported = 警告:此浏览器尚未完整支持打印功能。 +pdfjs-printing-not-ready = 警告:此 PDF 未完成加载,无法打印。 + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = 切换侧栏 +pdfjs-toggle-sidebar-notification-button = + .title = 切换侧栏(文档所含的大纲/附件/图层) +pdfjs-toggle-sidebar-button-label = 切换侧栏 +pdfjs-document-outline-button = + .title = 显示文档大纲(双击展开/折叠所有项) +pdfjs-document-outline-button-label = 文档大纲 +pdfjs-attachments-button = + .title = 显示附件 +pdfjs-attachments-button-label = 附件 +pdfjs-layers-button = + .title = 显示图层(双击即可将所有图层重置为默认状态) +pdfjs-layers-button-label = 图层 +pdfjs-thumbs-button = + .title = 显示缩略图 +pdfjs-thumbs-button-label = 缩略图 +pdfjs-current-outline-item-button = + .title = 查找当前大纲项目 +pdfjs-current-outline-item-button-label = 当前大纲项目 +pdfjs-findbar-button = + .title = 在文档中查找 +pdfjs-findbar-button-label = 查找 +pdfjs-additional-layers = 其他图层 + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = 第 { $page } 页 +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = 页面 { $page } 的缩略图 + +## Find panel button title and messages + +pdfjs-find-input = + .title = 查找 + .placeholder = 在文档中查找… +pdfjs-find-previous-button = + .title = 查找词语上一次出现的位置 +pdfjs-find-previous-button-label = 上一页 +pdfjs-find-next-button = + .title = 查找词语后一次出现的位置 +pdfjs-find-next-button-label = 下一页 +pdfjs-find-highlight-checkbox = 全部高亮显示 +pdfjs-find-match-case-checkbox-label = 区分大小写 +pdfjs-find-match-diacritics-checkbox-label = 匹配变音符号 +pdfjs-find-entire-word-checkbox-label = 全词匹配 +pdfjs-find-reached-top = 到达文档开头,从末尾继续 +pdfjs-find-reached-bottom = 到达文档末尾,从开头继续 +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = 第 { $current } 项,共找到 { $total } 个匹配项 +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = 匹配超过 { $limit } 项 +pdfjs-find-not-found = 找不到指定词语 + +## Predefined zoom values + +pdfjs-page-scale-width = 适合页宽 +pdfjs-page-scale-fit = 适合页面 +pdfjs-page-scale-auto = 自动缩放 +pdfjs-page-scale-actual = 实际大小 +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = 第 { $page } 页 + +## Loading indicator messages + +pdfjs-loading-error = 加载 PDF 时发生错误。 +pdfjs-invalid-file-error = 无效或损坏的 PDF 文件。 +pdfjs-missing-file-error = 缺少 PDF 文件。 +pdfjs-unexpected-response-error = 意外的服务器响应。 +pdfjs-rendering-error = 渲染页面时发生错误。 + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date },{ $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } 注释] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = 输入密码以打开此 PDF 文件。 +pdfjs-password-invalid = 密码无效。请重试。 +pdfjs-password-ok-button = 确定 +pdfjs-password-cancel-button = 取消 +pdfjs-web-fonts-disabled = Web 字体已被禁用:无法使用嵌入的 PDF 字体。 + +## Editing + +pdfjs-editor-free-text-button = + .title = 文本 +pdfjs-editor-free-text-button-label = 文本 +pdfjs-editor-ink-button = + .title = 绘图 +pdfjs-editor-ink-button-label = 绘图 +pdfjs-editor-stamp-button = + .title = 添加或编辑图像 +pdfjs-editor-stamp-button-label = 添加或编辑图像 +pdfjs-editor-highlight-button = + .title = 高亮 +pdfjs-editor-highlight-button-label = 高亮 +pdfjs-highlight-floating-button1 = + .title = 高亮 + .aria-label = 高亮 +pdfjs-highlight-floating-button-label = 高亮 + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = 移除绘图 +pdfjs-editor-remove-freetext-button = + .title = 移除文本 +pdfjs-editor-remove-stamp-button = + .title = 移除图像 +pdfjs-editor-remove-highlight-button = + .title = 移除高亮 + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = 颜色 +pdfjs-editor-free-text-size-input = 字号 +pdfjs-editor-ink-color-input = 颜色 +pdfjs-editor-ink-thickness-input = 粗细 +pdfjs-editor-ink-opacity-input = 不透明度 +pdfjs-editor-stamp-add-image-button = + .title = 添加图像 +pdfjs-editor-stamp-add-image-button-label = 添加图像 +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = 粗细 +pdfjs-editor-free-highlight-thickness-title = + .title = 更改高亮粗细(用于文本以外项目) +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = 文本编辑器 + .default-content = 在此键入… +pdfjs-free-text = + .aria-label = 文本编辑器 +pdfjs-free-text-default-content = 开始输入… +pdfjs-ink = + .aria-label = 绘图编辑器 +pdfjs-ink-canvas = + .aria-label = 用户创建图像 + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = 替换文字 +pdfjs-editor-alt-text-edit-button = + .aria-label = 编辑替换文字 +pdfjs-editor-alt-text-edit-button-label = 编辑替换文字 +pdfjs-editor-alt-text-dialog-label = 选择一项 +pdfjs-editor-alt-text-dialog-description = 替换文字可在用户无法看到或加载图像时,描述其内容。 +pdfjs-editor-alt-text-add-description-label = 添加描述 +pdfjs-editor-alt-text-add-description-description = 用一两个句子,描述主题、背景或动作。 +pdfjs-editor-alt-text-mark-decorative-label = 标记为装饰 +pdfjs-editor-alt-text-mark-decorative-description = 用于装饰的图像,例如边框和水印。 +pdfjs-editor-alt-text-cancel-button = 取消 +pdfjs-editor-alt-text-save-button = 保存 +pdfjs-editor-alt-text-decorative-tooltip = 已标记为装饰 +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = 例如:一个少年坐到桌前,准备吃饭 +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = 替换文字 + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = 调整尺寸 - 左上角 +pdfjs-editor-resizer-label-top-middle = 调整尺寸 - 顶部中间 +pdfjs-editor-resizer-label-top-right = 调整尺寸 - 右上角 +pdfjs-editor-resizer-label-middle-right = 调整尺寸 - 右侧中间 +pdfjs-editor-resizer-label-bottom-right = 调整尺寸 - 右下角 +pdfjs-editor-resizer-label-bottom-middle = 调整大小 - 底部中间 +pdfjs-editor-resizer-label-bottom-left = 调整尺寸 - 左下角 +pdfjs-editor-resizer-label-middle-left = 调整尺寸 - 左侧中间 +pdfjs-editor-resizer-top-left = + .aria-label = 调整尺寸 - 左上角 +pdfjs-editor-resizer-top-middle = + .aria-label = 调整尺寸 - 顶部中间 +pdfjs-editor-resizer-top-right = + .aria-label = 调整尺寸 - 右上角 +pdfjs-editor-resizer-middle-right = + .aria-label = 调整尺寸 - 右侧中间 +pdfjs-editor-resizer-bottom-right = + .aria-label = 调整尺寸 - 右下角 +pdfjs-editor-resizer-bottom-middle = + .aria-label = 调整大小 - 底部中间 +pdfjs-editor-resizer-bottom-left = + .aria-label = 调整尺寸 - 左下角 +pdfjs-editor-resizer-middle-left = + .aria-label = 调整尺寸 - 左侧中间 + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = 高亮色 +pdfjs-editor-colorpicker-button = + .title = 更改颜色 +pdfjs-editor-colorpicker-dropdown = + .aria-label = 颜色选择 +pdfjs-editor-colorpicker-yellow = + .title = 黄色 +pdfjs-editor-colorpicker-green = + .title = 绿色 +pdfjs-editor-colorpicker-blue = + .title = 蓝色 +pdfjs-editor-colorpicker-pink = + .title = 粉色 +pdfjs-editor-colorpicker-red = + .title = 红色 + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = 显示全部 +pdfjs-editor-highlight-show-all-button = + .title = 显示全部 + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = 编辑替换文字(图像描述) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = 添加替换文字(图像描述) +pdfjs-editor-new-alt-text-textarea = + .placeholder = 请在此处撰写描述… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = 向无法看到或加载图像的用户提供的简短描述。 +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = 此段替换文字为自动创建,有可能不准确。 +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = 详细了解 +pdfjs-editor-new-alt-text-create-automatically-button-label = 自动创建替换文字 +pdfjs-editor-new-alt-text-not-now-button = 暂时不要 +pdfjs-editor-new-alt-text-error-title = 无法自动创建替换文字 +pdfjs-editor-new-alt-text-error-description = 请自行撰写替换文字,或稍后再试。 +pdfjs-editor-new-alt-text-error-close-button = 关闭 +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = 正在下载提供替换文字的 AI 模型({ $downloadedSize }/{ $totalSize } MB) + .aria-valuetext = 正在下载提供替换文字的 AI 模型({ $downloadedSize }/{ $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = 已添加替换文字 +pdfjs-editor-new-alt-text-added-button-label = 已添加替换文字 +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = 缺少替换文字 +pdfjs-editor-new-alt-text-missing-button-label = 缺少替换文字 +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = 检查替换文字 +pdfjs-editor-new-alt-text-to-review-button-label = 检查替换文字 +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = [自动创建] { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = 图像替换文字设置 +pdfjs-image-alt-text-settings-button-label = 图像替换文字设置 +pdfjs-editor-alt-text-settings-dialog-label = 图像替换文字设置 +pdfjs-editor-alt-text-settings-automatic-title = 自动创建替换文字 +pdfjs-editor-alt-text-settings-create-model-button-label = 自动创建替换文字 +pdfjs-editor-alt-text-settings-create-model-description = 向无法看到或加载图像的用户提供描述。 +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = 提供替换文字的 AI 模型({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = 在您的设备本地运行,可使数据保持私密。自动创建替换文字需要使用此模型。 +pdfjs-editor-alt-text-settings-delete-model-button = 删除 +pdfjs-editor-alt-text-settings-download-model-button = 下载 +pdfjs-editor-alt-text-settings-downloading-model-button = 正在下载… +pdfjs-editor-alt-text-settings-editor-title = 替换文字编辑器 +pdfjs-editor-alt-text-settings-show-dialog-button-label = 添加图像后立即显示替换文字编辑器 +pdfjs-editor-alt-text-settings-show-dialog-description = 帮助确保所有图像均拥有替换文字。 +pdfjs-editor-alt-text-settings-close-button = 关闭 + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = 已移除高亮 +pdfjs-editor-undo-bar-message-freetext = 已移除文本 +pdfjs-editor-undo-bar-message-ink = 已移除绘图 +pdfjs-editor-undo-bar-message-stamp = 已移除图像 +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = 已移除 { $count } 条注释 +pdfjs-editor-undo-bar-undo-button = + .title = 撤销 +pdfjs-editor-undo-bar-undo-button-label = 撤销 +pdfjs-editor-undo-bar-close-button = + .title = 关闭 +pdfjs-editor-undo-bar-close-button-label = 关闭 diff --git a/public/pdfjs/web/locale/zh-TW/viewer.ftl b/public/pdfjs/web/locale/zh-TW/viewer.ftl new file mode 100644 index 0000000..bc4b7ef --- /dev/null +++ b/public/pdfjs/web/locale/zh-TW/viewer.ftl @@ -0,0 +1,503 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +## Main toolbar buttons (tooltips and alt text for images) + +pdfjs-previous-button = + .title = 上一頁 +pdfjs-previous-button-label = 上一頁 +pdfjs-next-button = + .title = 下一頁 +pdfjs-next-button-label = 下一頁 +# .title: Tooltip for the pageNumber input. +pdfjs-page-input = + .title = 第 +# Variables: +# $pagesCount (Number) - the total number of pages in the document +# This string follows an input field with the number of the page currently displayed. +pdfjs-of-pages = 頁,共 { $pagesCount } 頁 +# Variables: +# $pageNumber (Number) - the currently visible page +# $pagesCount (Number) - the total number of pages in the document +pdfjs-page-of-pages = (第 { $pageNumber } 頁,共 { $pagesCount } 頁) +pdfjs-zoom-out-button = + .title = 縮小 +pdfjs-zoom-out-button-label = 縮小 +pdfjs-zoom-in-button = + .title = 放大 +pdfjs-zoom-in-button-label = 放大 +pdfjs-zoom-select = + .title = 縮放 +pdfjs-presentation-mode-button = + .title = 切換至簡報模式 +pdfjs-presentation-mode-button-label = 簡報模式 +pdfjs-open-file-button = + .title = 開啟檔案 +pdfjs-open-file-button-label = 開啟 +pdfjs-print-button = + .title = 列印 +pdfjs-print-button-label = 列印 +pdfjs-save-button = + .title = 儲存 +pdfjs-save-button-label = 儲存 +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = 下載 +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = 下載 +pdfjs-bookmark-button = + .title = 目前頁面(含目前檢視頁面的網址) +pdfjs-bookmark-button-label = 目前頁面 + +## Secondary toolbar and context menu + +pdfjs-tools-button = + .title = 工具 +pdfjs-tools-button-label = 工具 +pdfjs-first-page-button = + .title = 跳到第一頁 +pdfjs-first-page-button-label = 跳到第一頁 +pdfjs-last-page-button = + .title = 跳到最後一頁 +pdfjs-last-page-button-label = 跳到最後一頁 +pdfjs-page-rotate-cw-button = + .title = 順時針旋轉 +pdfjs-page-rotate-cw-button-label = 順時針旋轉 +pdfjs-page-rotate-ccw-button = + .title = 逆時針旋轉 +pdfjs-page-rotate-ccw-button-label = 逆時針旋轉 +pdfjs-cursor-text-select-tool-button = + .title = 開啟文字選擇工具 +pdfjs-cursor-text-select-tool-button-label = 文字選擇工具 +pdfjs-cursor-hand-tool-button = + .title = 開啟頁面移動工具 +pdfjs-cursor-hand-tool-button-label = 頁面移動工具 +pdfjs-scroll-page-button = + .title = 使用單頁捲動版面 +pdfjs-scroll-page-button-label = 單頁捲動 +pdfjs-scroll-vertical-button = + .title = 使用垂直捲動版面 +pdfjs-scroll-vertical-button-label = 垂直捲動 +pdfjs-scroll-horizontal-button = + .title = 使用水平捲動版面 +pdfjs-scroll-horizontal-button-label = 水平捲動 +pdfjs-scroll-wrapped-button = + .title = 使用多頁捲動版面 +pdfjs-scroll-wrapped-button-label = 多頁捲動 +pdfjs-spread-none-button = + .title = 不要進行跨頁顯示 +pdfjs-spread-none-button-label = 不跨頁 +pdfjs-spread-odd-button = + .title = 從奇數頁開始跨頁 +pdfjs-spread-odd-button-label = 奇數跨頁 +pdfjs-spread-even-button = + .title = 從偶數頁開始跨頁 +pdfjs-spread-even-button-label = 偶數跨頁 + +## Document properties dialog + +pdfjs-document-properties-button = + .title = 文件內容… +pdfjs-document-properties-button-label = 文件內容… +pdfjs-document-properties-file-name = 檔案名稱: +pdfjs-document-properties-file-size = 檔案大小: +# Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB({ $b } 位元組) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB({ $b } 位元組) +# Variables: +# $size_kb (Number) - the PDF file size in kilobytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-kb = { $size_kb } KB({ $size_b } 位元組) +# Variables: +# $size_mb (Number) - the PDF file size in megabytes +# $size_b (Number) - the PDF file size in bytes +pdfjs-document-properties-mb = { $size_mb } MB({ $size_b } 位元組) +pdfjs-document-properties-title = 標題: +pdfjs-document-properties-author = 作者: +pdfjs-document-properties-subject = 主旨: +pdfjs-document-properties-keywords = 關鍵字: +pdfjs-document-properties-creation-date = 建立日期: +pdfjs-document-properties-modification-date = 修改日期: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: +# $date (Date) - the creation/modification date of the PDF file +# $time (Time) - the creation/modification time of the PDF file +pdfjs-document-properties-date-string = { $date } { $time } +pdfjs-document-properties-creator = 建立者: +pdfjs-document-properties-producer = PDF 產生器: +pdfjs-document-properties-version = PDF 版本: +pdfjs-document-properties-page-count = 頁數: +pdfjs-document-properties-page-size = 頁面大小: +pdfjs-document-properties-page-size-unit-inches = in +pdfjs-document-properties-page-size-unit-millimeters = mm +pdfjs-document-properties-page-size-orientation-portrait = 垂直 +pdfjs-document-properties-page-size-orientation-landscape = 水平 +pdfjs-document-properties-page-size-name-a-three = A3 +pdfjs-document-properties-page-size-name-a-four = A4 +pdfjs-document-properties-page-size-name-letter = Letter +pdfjs-document-properties-page-size-name-legal = Legal + +## Variables: +## $width (Number) - the width of the (current) page +## $height (Number) - the height of the (current) page +## $unit (String) - the unit of measurement of the (current) page +## $name (String) - the name of the (current) page +## $orientation (String) - the orientation of the (current) page + +pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit }({ $orientation }) +pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit }({ $name },{ $orientation }) + +## + +# The linearization status of the document; usually called "Fast Web View" in +# English locales of Adobe software. +pdfjs-document-properties-linearized = 快速 Web 檢視: +pdfjs-document-properties-linearized-yes = 是 +pdfjs-document-properties-linearized-no = 否 +pdfjs-document-properties-close-button = 關閉 + +## Print + +pdfjs-print-progress-message = 正在準備列印文件… +# Variables: +# $progress (Number) - percent value +pdfjs-print-progress-percent = { $progress }% +pdfjs-print-progress-close-button = 取消 +pdfjs-printing-not-supported = 警告: 此瀏覽器未完整支援列印功能。 +pdfjs-printing-not-ready = 警告: 此 PDF 未完成下載以供列印。 + +## Tooltips and alt text for side panel toolbar buttons + +pdfjs-toggle-sidebar-button = + .title = 切換側邊欄 +pdfjs-toggle-sidebar-notification-button = + .title = 切換側邊欄(包含大綱、附件、圖層的文件) +pdfjs-toggle-sidebar-button-label = 切換側邊欄 +pdfjs-document-outline-button = + .title = 顯示文件大綱(雙擊展開/摺疊所有項目) +pdfjs-document-outline-button-label = 文件大綱 +pdfjs-attachments-button = + .title = 顯示附件 +pdfjs-attachments-button-label = 附件 +pdfjs-layers-button = + .title = 顯示圖層(滑鼠雙擊即可將所有圖層重設為預設狀態) +pdfjs-layers-button-label = 圖層 +pdfjs-thumbs-button = + .title = 顯示縮圖 +pdfjs-thumbs-button-label = 縮圖 +pdfjs-current-outline-item-button = + .title = 尋找目前的大綱項目 +pdfjs-current-outline-item-button-label = 目前的大綱項目 +pdfjs-findbar-button = + .title = 在文件中尋找 +pdfjs-findbar-button-label = 尋找 +pdfjs-additional-layers = 其他圖層 + +## Thumbnails panel item (tooltip and alt text for images) + +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-title = + .title = 第 { $page } 頁 +# Variables: +# $page (Number) - the page number +pdfjs-thumb-page-canvas = + .aria-label = 第 { $page } 頁的縮圖 + +## Find panel button title and messages + +pdfjs-find-input = + .title = 尋找 + .placeholder = 在文件中搜尋… +pdfjs-find-previous-button = + .title = 尋找文字前次出現的位置 +pdfjs-find-previous-button-label = 上一個 +pdfjs-find-next-button = + .title = 尋找文字下次出現的位置 +pdfjs-find-next-button-label = 下一個 +pdfjs-find-highlight-checkbox = 強調全部 +pdfjs-find-match-case-checkbox-label = 區分大小寫 +pdfjs-find-match-diacritics-checkbox-label = 符合變音符號 +pdfjs-find-entire-word-checkbox-label = 符合整個字 +pdfjs-find-reached-top = 已搜尋至文件頂端,自底端繼續搜尋 +pdfjs-find-reached-bottom = 已搜尋至文件底端,自頂端繼續搜尋 +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = 第 { $current } 筆符合,共符合 { $total } 筆 +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = 符合超過 { $limit } 項 +pdfjs-find-not-found = 找不到指定文字 + +## Predefined zoom values + +pdfjs-page-scale-width = 頁面寬度 +pdfjs-page-scale-fit = 縮放至頁面大小 +pdfjs-page-scale-auto = 自動縮放 +pdfjs-page-scale-actual = 實際大小 +# Variables: +# $scale (Number) - percent value for page scale +pdfjs-page-scale-percent = { $scale }% + +## PDF page + +# Variables: +# $page (Number) - the page number +pdfjs-page-landmark = + .aria-label = 第 { $page } 頁 + +## Loading indicator messages + +pdfjs-loading-error = 載入 PDF 時發生錯誤。 +pdfjs-invalid-file-error = 無效或毀損的 PDF 檔案。 +pdfjs-missing-file-error = 找不到 PDF 檔案。 +pdfjs-unexpected-response-error = 伺服器回應未預期的內容。 +pdfjs-rendering-error = 描繪頁面時發生錯誤。 + +## Annotations + +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date } { $time } +# .alt: This is used as a tooltip. +# Variables: +# $type (String) - an annotation type from a list defined in the PDF spec +# (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +pdfjs-text-annotation-type = + .alt = [{ $type } 註解] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } + +## Password + +pdfjs-password-label = 請輸入用來開啟此 PDF 檔案的密碼。 +pdfjs-password-invalid = 密碼不正確,請再試一次。 +pdfjs-password-ok-button = 確定 +pdfjs-password-cancel-button = 取消 +pdfjs-web-fonts-disabled = 已停用網路字型 (Web fonts): 無法使用 PDF 內嵌字型。 + +## Editing + +pdfjs-editor-free-text-button = + .title = 文字 +pdfjs-editor-free-text-button-label = 文字 +pdfjs-editor-ink-button = + .title = 繪圖 +pdfjs-editor-ink-button-label = 繪圖 +pdfjs-editor-stamp-button = + .title = 新增或編輯圖片 +pdfjs-editor-stamp-button-label = 新增或編輯圖片 +pdfjs-editor-highlight-button = + .title = 強調 +pdfjs-editor-highlight-button-label = 強調 +pdfjs-highlight-floating-button1 = + .title = 強調 + .aria-label = 強調 +pdfjs-highlight-floating-button-label = 強調 + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = 移除繪圖 +pdfjs-editor-remove-freetext-button = + .title = 移除文字 +pdfjs-editor-remove-stamp-button = + .title = 移除圖片 +pdfjs-editor-remove-highlight-button = + .title = 移除強調範圍 + +## + +# Editor Parameters +pdfjs-editor-free-text-color-input = 色彩 +pdfjs-editor-free-text-size-input = 大小 +pdfjs-editor-ink-color-input = 色彩 +pdfjs-editor-ink-thickness-input = 線條粗細 +pdfjs-editor-ink-opacity-input = 透​明度 +pdfjs-editor-stamp-add-image-button = + .title = 新增圖片 +pdfjs-editor-stamp-add-image-button-label = 新增圖片 +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = 線條粗細 +pdfjs-editor-free-highlight-thickness-title = + .title = 更改強調文字以外的項目時的線條粗細 +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = 文字編輯器 + .default-content = 請打字… +pdfjs-free-text = + .aria-label = 文本編輯器 +pdfjs-free-text-default-content = 在此打字… +pdfjs-ink = + .aria-label = 圖形編輯器 +pdfjs-ink-canvas = + .aria-label = 使用者建立的圖片 + +## Alt-text dialog + +pdfjs-editor-alt-text-button-label = 替代文字 +pdfjs-editor-alt-text-edit-button = + .aria-label = 編輯替代文字 +pdfjs-editor-alt-text-edit-button-label = 編輯替代文字 +pdfjs-editor-alt-text-dialog-label = 挑選一種 +pdfjs-editor-alt-text-dialog-description = 替代文字可協助盲人,或於圖片無法載入時提供說明。 +pdfjs-editor-alt-text-add-description-label = 新增描述 +pdfjs-editor-alt-text-add-description-description = 用 1-2 句文字描述主題、背景或動作。 +pdfjs-editor-alt-text-mark-decorative-label = 標示為裝飾性內容 +pdfjs-editor-alt-text-mark-decorative-description = 這是裝飾性圖片,例如邊框或浮水印。 +pdfjs-editor-alt-text-cancel-button = 取消 +pdfjs-editor-alt-text-save-button = 儲存 +pdfjs-editor-alt-text-decorative-tooltip = 已標示為裝飾性內容 +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = 例如:「有一位年輕男人坐在桌子前面吃飯」 +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = 替代文字 + +## Editor resizers +## This is used in an aria label to help to understand the role of the resizer. + +pdfjs-editor-resizer-label-top-left = 左上角 — 調整大小 +pdfjs-editor-resizer-label-top-middle = 頂部中間 — 調整大小 +pdfjs-editor-resizer-label-top-right = 右上角 — 調整大小 +pdfjs-editor-resizer-label-middle-right = 中間右方 — 調整大小 +pdfjs-editor-resizer-label-bottom-right = 右下角 — 調整大小 +pdfjs-editor-resizer-label-bottom-middle = 底部中間 — 調整大小 +pdfjs-editor-resizer-label-bottom-left = 左下角 — 調整大小 +pdfjs-editor-resizer-label-middle-left = 中間左方 — 調整大小 +pdfjs-editor-resizer-top-left = + .aria-label = 左上角 — 調整大小 +pdfjs-editor-resizer-top-middle = + .aria-label = 頂部中間 — 調整大小 +pdfjs-editor-resizer-top-right = + .aria-label = 右上角 — 調整大小 +pdfjs-editor-resizer-middle-right = + .aria-label = 中間右方 — 調整大小 +pdfjs-editor-resizer-bottom-right = + .aria-label = 右下角 — 調整大小 +pdfjs-editor-resizer-bottom-middle = + .aria-label = 底部中間 — 調整大小 +pdfjs-editor-resizer-bottom-left = + .aria-label = 左下角 — 調整大小 +pdfjs-editor-resizer-middle-left = + .aria-label = 中間左方 — 調整大小 + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = 強調色彩 +pdfjs-editor-colorpicker-button = + .title = 更改色彩 +pdfjs-editor-colorpicker-dropdown = + .aria-label = 色彩選項 +pdfjs-editor-colorpicker-yellow = + .title = 黃色 +pdfjs-editor-colorpicker-green = + .title = 綠色 +pdfjs-editor-colorpicker-blue = + .title = 藍色 +pdfjs-editor-colorpicker-pink = + .title = 粉紅色 +pdfjs-editor-colorpicker-red = + .title = 紅色 + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = 顯示全部 +pdfjs-editor-highlight-show-all-button = + .title = 顯示全部 + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = 編輯替代文字(圖片描述) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = 新增替代文字(圖片描述) +pdfjs-editor-new-alt-text-textarea = + .placeholder = 在此寫下您的描述文字… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = 為看不到圖片的讀者,或圖片無法載入時顯示的簡短描述。 +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = 此替代文字是自動產生的,可能不夠精確。 +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = 更多資訊 +pdfjs-editor-new-alt-text-create-automatically-button-label = 自動產生替代文字 +pdfjs-editor-new-alt-text-not-now-button = 暫時不要 +pdfjs-editor-new-alt-text-error-title = 無法自動產生替代文字 +pdfjs-editor-new-alt-text-error-description = 請自行填寫替代文字,或稍後再試一次。 +pdfjs-editor-new-alt-text-error-close-button = 關閉 +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = 正在下載替代文字 AI 模型({ $downloadedSize } / { $totalSize } MB) + .aria-valuetext = 正在下載替代文字 AI 模型({ $downloadedSize } / { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = 已新增替代文字 +pdfjs-editor-new-alt-text-added-button-label = 已新增替代文字 +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = 缺少替代文字 +pdfjs-editor-new-alt-text-missing-button-label = 缺少替代文字 +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = 確認替代文字 +pdfjs-editor-new-alt-text-to-review-button-label = 確認替代文字 +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = 自動產生:{ $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = 圖片替代文字設定 +pdfjs-image-alt-text-settings-button-label = 圖片替代文字設定 +pdfjs-editor-alt-text-settings-dialog-label = 圖片替代文字設定 +pdfjs-editor-alt-text-settings-automatic-title = 自動化替代文字 +pdfjs-editor-alt-text-settings-create-model-button-label = 自動產生替代文字 +pdfjs-editor-alt-text-settings-create-model-description = 為您建議圖片描述,幫助看不到圖片的讀者,或於圖片無法載入時顯示。 +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = 替代文字 AI 模型({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = 在您的本機裝置上運作,以確保您的資料隱私。必須下載此模型才可以自動產生替代文字。 +pdfjs-editor-alt-text-settings-delete-model-button = 刪除 +pdfjs-editor-alt-text-settings-download-model-button = 下載 +pdfjs-editor-alt-text-settings-downloading-model-button = 下載中… +pdfjs-editor-alt-text-settings-editor-title = 替代文字編輯器 +pdfjs-editor-alt-text-settings-show-dialog-button-label = 新增圖片後立即顯示替代文字編輯器 +pdfjs-editor-alt-text-settings-show-dialog-description = 幫助您確保所有圖片都有替代文字。 +pdfjs-editor-alt-text-settings-close-button = 關閉 + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = 已移除強調 +pdfjs-editor-undo-bar-message-freetext = 已移除文字 +pdfjs-editor-undo-bar-message-ink = 已移除繪圖 +pdfjs-editor-undo-bar-message-stamp = 已移除圖片 +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = 已移除 { $count } 筆註解 +pdfjs-editor-undo-bar-undo-button = + .title = 還原 +pdfjs-editor-undo-bar-undo-button-label = 還原 +pdfjs-editor-undo-bar-close-button = + .title = 關閉 +pdfjs-editor-undo-bar-close-button-label = 關閉 diff --git a/public/pdfjs/web/standard_fonts/FoxitDingbats.pfb b/public/pdfjs/web/standard_fonts/FoxitDingbats.pfb new file mode 100644 index 0000000000000000000000000000000000000000..30d52963e281dcd7e6dd70555340fc987d3568ef GIT binary patch literal 29513 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 DiyeX$ literal 0 HcmV?d00001 diff --git a/public/pdfjs/web/standard_fonts/FoxitFixedBold.pfb b/public/pdfjs/web/standard_fonts/FoxitFixedBold.pfb new file mode 100644 index 0000000000000000000000000000000000000000..cf8e24aee5962ebcf7e4d58d932cc3321f3cdd38 GIT binary patch literal 18055 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 aP{6{BWwy2LU5CmZYCDT$C?nq``+oq(ZGoi# literal 0 HcmV?d00001 diff --git a/public/pdfjs/web/standard_fonts/FoxitFixedBoldItalic.pfb b/public/pdfjs/web/standard_fonts/FoxitFixedBoldItalic.pfb new file mode 100644 index 0000000000000000000000000000000000000000..d2880017c25716417f837a1c26044a0342fa3f9a GIT binary patch literal 19151 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 M96+?GEz$me0Iy=aZvX%Q literal 0 HcmV?d00001 diff --git a/public/pdfjs/web/standard_fonts/FoxitFixedItalic.pfb b/public/pdfjs/web/standard_fonts/FoxitFixedItalic.pfb new file mode 100644 index 0000000000000000000000000000000000000000..d71697d4b638737a5545dd0cb28cba8f613e4eec GIT binary patch literal 18746 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 literal 0 HcmV?d00001 diff --git a/public/pdfjs/web/standard_fonts/FoxitSerifBold.pfb b/public/pdfjs/web/standard_fonts/FoxitSerifBold.pfb new file mode 100644 index 0000000000000000000000000000000000000000..ff7c6ddecf6b689d93823c4c2a8c41e6e058de46 GIT binary patch literal 19395 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 literal 0 HcmV?d00001 diff --git a/public/pdfjs/web/standard_fonts/LICENSE_FOXIT b/public/pdfjs/web/standard_fonts/LICENSE_FOXIT new file mode 100644 index 0000000..8b4ed6d --- /dev/null +++ b/public/pdfjs/web/standard_fonts/LICENSE_FOXIT @@ -0,0 +1,27 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/public/pdfjs/web/standard_fonts/LICENSE_LIBERATION b/public/pdfjs/web/standard_fonts/LICENSE_LIBERATION new file mode 100644 index 0000000..aba73e8 --- /dev/null +++ b/public/pdfjs/web/standard_fonts/LICENSE_LIBERATION @@ -0,0 +1,102 @@ +Digitized data copyright (c) 2010 Google Corporation + with Reserved Font Arimo, Tinos and Cousine. +Copyright (c) 2012 Red Hat, Inc. + with Reserved Font Name Liberation. + +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 + +PREAMBLE The goals of the Open Font License (OFL) are to stimulate +worldwide development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to provide +a free and open framework in which fonts may be shared and improved in +partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. +The fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + + + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. +This may include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components +as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting ? in part or in whole ? +any of the components of the Original Version, by changing formats or +by porting the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical writer +or other person who contributed to the Font Software. + + +PERMISSION & CONDITIONS + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components,in + Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, + redistributed and/or sold with any software, provided that each copy + contains the above copyright notice and this license. These can be + included either as stand-alone text files, human-readable headers or + in the appropriate machine-readable metadata fields within text or + binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font + Name(s) unless explicit written permission is granted by the + corresponding Copyright Holder. This restriction only applies to the + primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font + Software shall not be used to promote, endorse or advertise any + Modified Version, except to acknowledge the contribution(s) of the + Copyright Holder(s) and the Author(s) or with their explicit written + permission. + +5) The Font Software, modified or unmodified, in part or in whole, must + be distributed entirely under this license, and must not be distributed + under any other license. The requirement for fonts to remain under + this license does not apply to any document created using the Font + Software. + + + +TERMINATION +This license becomes null and void if any of the above conditions are not met. + + + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER +DEALINGS IN THE FONT SOFTWARE. + diff --git a/public/pdfjs/web/standard_fonts/LiberationSans-Bold.ttf b/public/pdfjs/web/standard_fonts/LiberationSans-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ee2371540417ffbc652745b059effa260430179f GIT binary patch literal 137052 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

    w}oosc!DqhwrRva;dq-kkc$`L>esvI>80 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

    sZ)<`1D=v@%Ct``s?2HicnE%( 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

    C6m+A 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%0AxaXreK_|l;-0=a=dw_(;$9Q}a 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 Y(69aAo~sa8j~?;H6%7CW#+m&80ZRF%Gynhq literal 0 HcmV?d00001 diff --git a/public/pdfjs/web/standard_fonts/LiberationSans-Italic.ttf b/public/pdfjs/web/standard_fonts/LiberationSans-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0cf61263494608cbf1fccecce606bc01a247d1e4 GIT binary patch literal 162036 zcmcG$30RcZ**|=r^E|V%%rNZx48tHe1A`2!0`sVVgBz%zfU>B#FQ5?PmWsQgMq`ZC zXrfu0#ndq|saYCLlQ+#;O|vwMso0pl>07Nfv2Ff|!1p`PfH7UJ@BOap`;Y$SJkQye z`@YY8-{%a(h$tLiGG$j()J&Lv|2I0~D?dhT!kDu13L3)C5}&^Z?`dPItELqHb?v9b z7wCyhzZo;7cGTU)8^$616w;CE&iRLB%UL2O-dkobnb|HMz3+CS;FpNBU(a6AX`T6X z_0vR2sGqy$w$EGA@xI-U4gfmMyqO*C6hj8o*M;)td5c%g9rECN^NF0thzD8c&zaeJ zJ@1J))L)GB{P~CoxvbxU_fEX0&R^2Ga&G>)<)}M`$oTN0Im?%B%-*<;c>X&?5h;t? zX3ym3de9e}jP%Er%v{;7`=#Lxls|y-)}=F-%*l(3XeU1Q8j=3d_O_1B2YcS@Bt8Y{ z5nr`0pVR*KlO;2W;!a`0tR&`+h5h-`%*rXvrsBU*GNuKe({cS>B0RfyBI|v>_ilZc zK9M--0Z{^FFxt|-i8NY&ruY56MfxskrARep;EO*l^6XgnpS>4SpTq*Q%0k9+xJ}a ze&00a(Ur2NTZoQa_#J8=u33+KR^t3Jm5CDk37|;D1pYgEQSP82a!3DlT_Ww(OeGuJ zN^c=g3xA@&NJ^xY@-fO-`gfvCJ&-5LA~FFXSAOp;sNCEe-~jn>PbPm2IU&@J{8aH=*L|2`{%$;-~)b)B6$RT z$dC2U#Q0aEjDo!P56s;zJZFk@y#F3)+cjTe?0;A1cRcd9ig$TY|5eFDugR@65_yt< zJm9!|xW8W>PkYe*BX}PJlwh6bpiYhU7_|UyU>oLVBOYP?FB5ZmD^RCc zyPH6s6hNJSu?DvS{XC{E>VE_22Y&>t0qRUb`(oa5{~bVg)?3#Ac?Q>_9_oBF>ERXzu=p`8x{>l@&A!E=J|ZwcQF%n3^v zTzf(Dg6;&(3c4BW>p)*7VO$e{ZNQzt2L1$_ittT9@R;DeXy8v6_YF}e#`B1_ipFdI ziz=lAs?;|0|3iD3Ue|j1`_Z;abLu+n0gpBHf2=;Ek7CXQeTcOcaj5ITK{=<%?*CfM zz2=<&U&`?9`rk^11C)!8ooHmM*2g-ja@1Y856X;->Dzm)uT$(T90JbI?DppdIO2|3mVue#|A!mhPqkvG$r7G+n-@|2NVl@ZS4W z#`|cTs8dr&W8|j(MVjBz3hf9Qt=1vh5?fU`33Sk4YYTgoK#{Q5;rJ^3S{Db{!ojS-d`A-zC=0$$B+}i%T2j}h9 zIm7%eQ|AnIYG=@DF=v|hQ1@*5jjoZrx>;y@8J*HaqaD%KE{f1+A-%Kz5sW2E?xF>9 zCJm9khHP1^%Aa!0`u^vnQ~lo{U6nPGkP938@7JtFI1jifr-a-A|EPOJ|4!|3$ThJq z+znZlO53#$(^TzQ)bTBi(tLqEGLO=vt7Op1sH39)9O_Z!Ai@+$OE&2rXdirqxyHUk zPQm*+U;q8uBWjx|YCpD!JxJZ3rw;P1bPas^3rZQ}mwyfgJ&7`)=LG_gm*0RtRAAp} zL0i8Dgbex)VK4IR1GaKSL8yHe44;8~J{;_XO3T{7YH~EtTJ;I_z5|Kq)XASgY%2;Av*oeIE$* z6bMC}be#(2FDXX5pT5_eq_?miZNy&b!Se#$hwOmn42$B!`tL{FcwnU1R}p&E=MU*V z_5DMhB7evJ&2XVA2&2i0J$g>G2~3;`wr4*}zVLeR~(?AI)j zH?TS&m>-~5fxq&t^bdZNo|4?OjQeN#Pr3e`Y;q)7ZjoQMMFYnk~!LWV^@qSPD+kRXf*{HI$Mn1VHur7nTXExP@44Qyy}#*wxA$;w zckj|Ky1t0G`0d4iT>SgRzg_(L;-4;lb@7Xff4F$@;`xgYUR-zaj*F`<+Ac<1v|Q9( zlzV!6-s?Hqb7xOpPfpL!o{XL#qMqcQsGi6R|L4LVF7#X|yHIc;{qwP(SAMR1Uh?_y z&kH`!{XFyYw9k`2kNI44{_FEs&tE?Or}KY2|LOVPo`3iJ>*xRL{LAN`KmXkMpPzs5 zeAW5!=f|DTI`2B~JfCjaXW6UUr&1Fv#{XL%ZvDSi^FX~4&7#?`FXzx)nn&|#0WG9O zw3wE_qFhRC)K1H2IdxDc-9{_uc3MfRXf>^&wR8vlgw}z7)`QbF(k9rUTWBk7qwUm9 zJ7_2EqC06f?V-D9FWpW1=pNcnKcxe7knW{JbeQg=`{@WBr3dIidWasTN9a-d89hdi z(-ZV09iyk{=X9L>bb_9yXXqq7OV80S=oCFqFVKth68#d~dz#MBEA%URm0qL&qSxuy z^aj02Z_(TI4!ujip|f<3-lN~r`}6^QNFUMf=wteXKBdp-Jbg~Trwi0W7wHf51zn;q z>5uf^^cDSy{)hfdm+1=qg|5;y`YU}+-_UjX8~vUBLEqBxvo+YqEmc){omDyMdv$Is@ zU}y0fEn8cuV4s9s?yM_<1yth&p&93}mDr3Z@VWB@?J2j4TvR5%5GrQ8fH8 zu@pz~kfe!}M9GjqHcBBorILfvXb7cK1`Q=AxhRve;G4*%VU$B2%B4KYrvfS@SYkAS zim3$h*GmeGq){{)UWIb1pfTj5N*YV!Xgp1ziBv_^G>InD6!<%8X(~;lI;w}iV>->C zMrxvFYN44JkD$V>puoF8YllHuM?qT;gSs98Wjzks5|s56XiHGm2~gG3psVLVSucR9 zKvO&t^e(tzK92^q9{~p}OBR1 zn1y|^m??ZDF9a8@fu~6Vk8GrS1Ql>258@Uc&O^D0e!(}=X;#Rs+`$ugBEN_4=TTTs zGmqf0;HhqK)(-GffRA>nJVf9n!954*uWTH=KW%Ixt7359uqkX38&9N^*Von7Oqo2X zx@zKt@#Dr;`o>h0myI4ZQt_6S6pt8QR9KLoH!RzomFY?!lIBRYr`VEXBErn3(2yXb zL9f$lWQj9!mD?*?tp2nXzno_G`7*`3eI_Di-V)K`w<4_d}D+ zuW^{|*6zO%hGV~S8RNJmX68VQ)?xk|iNp%5RCjlU-CEJz(mnH3f7dL#)okxR5ggpz zUXB^2>N-C|{0lqc{S`aw{pObWZ1})nD<+TghfkVb=jV-?D*KK^^{w!(N@7zUtD zNCnFO`((mIU^+0THk%muj#CQF!i&FaQeEKPO0(jhCdKWn_wyE!a%M0kvR0&Y4W`^I z-eSjUj+;`~?Ux;ut@d)v@s64Pu34a`g<>h}W`F44<8AhCOPICDjXol;6`ihZU10TV z()?Oc>ed3FLQ$j}Us#0DzXyUV@n|3|%wjFFqZ(09xxKt);QxyGF=)}6>GJ!Wfu*mh z^DAYT4Q1v4S(Km1cB9117A(;Mv67SO{BC=@Kf*pLKsSgG-6~%&rA{pph%NU=jP_H@ z?155#cR9*}*vq>GZHX?3y6ltco+nR#&xu@X{4+vg)|VmLA2k|ul~&$e*E-jq+!Egk zPMd43i?{iedMt3gy>3puAT-QSMh}|92gCC7(KU7Brr5_#YN*4i+UnI5kszv*9p(R2 zjlC{DPz^}VuXpIJbv$0GM@BOutQB~$k1EEuU+2&RW=yymD@b)zv9*rH(_n6N!=GU- zpHnuFPrU!AQoKlx(Z0djwBi-@jP}Lbz|`s|)5Vc&9cUQE^^VsFl_L;;AeZqOgr@T ziU2=%pHLLR%ku@>cH1jkyX{lziq+hZAa}&C7HwN-92-|ND$@mFHtK|(ZJTsLVcVuO z)ID#8d9baf?rF~W=$29SCsL7C_q-K*g&NC6tcVm@tl~w~G#M{?HGll`3Q?DuDytFd z`|MMU)VM$<#4(zEiU;D%gZVh(sN#A`*o&OXWy-73?yy4zfC|65EZ;f#f< znGSt&tKY|&tlY!S^|I5w%-qY`zGuq!>~GcS$zNBbCts^@Ca=4ChY^* zuWIPB`Lc4k`f~f_uFG1(mzR=~zxYE%vgr>@`9noi^2MHtX z>#03Y@uN@qpW^GDWJjOyKf&FPw>{2Hk5@f@VkZ8=Y98>@@^enC@cDcOALw_+6*( zx_VdtU0UU?1V_cu-TvMD%C z6=JpyOvaR6ozy>xE0gjHE0jr&^oq09%sMf{U7??toKj(!5IceUCe%&%c*0i`zMY^s zFoDI4PtB}|8J}paI57UP@qAoGL2_k_x4kmO{O*VJrUXOc}Ox2ek1X4-AKVCpyNyokGMlG=!MMZrQj#rB-2nc{SgJEiN# z-mj~k?q}Qljw#|>nbhFdZu3)Z!}PinjNMtkdDku)l{n6yGo{Yol2|{^---uCJapl~ zoOmLNM%8zAbl&C^9~O8ZXNS|NK6WC+e}+>$k%IvQbRgc@ffO~WQ^cty#A^pq2^j+a zMcxh(=_ECK$8Ak`by8DDC+k41XgUyqVrT_lP7$ki2l>!WQwOSNO(IqlM6>vyY)6dd z9h`jJfz!-LTBUwNX8iDxhr`anxpec3Y!3Y$)@%y%z!LrwT7Cq!$Y-#q-^6nfEW00l z*hTgxY?H@e3+;uKz6d?Uhks4od@n8KA4%)c#&(RT3DG!7#^+%+9d8@ZviLN?1JR~F z{qQAhpGV+r=o*ab`x5^0Zz$x(&tOrB)cx>REE`Nk_#hu8-lSxVonY6A&wBM6Upk+( zkUz(H@7;L03*L^qXeRpvz1$^@q{m=Em9wA16R?VQ@`dQ>CRki$nlL765}&$%!Z{*V zgVNu^uXr8z7tZ5O0PY1C3V|NnAJ|3|vf9Mf!-MJ|>EZ0a}5th+EjC z44`i#(cY+3U_5YusLTvkZ7Wg5Vxlqc5c$3$szf;MDAD*+aMQOFO{^xWI)v4g)BIqIC3IkaZV;x*zNz zdI)(RQUKKb&|^f8ApVh$i5}Ghg}{6O@|fEz?lWdkilKfgwF9P@O%2G|6g1TGQzgMnP271#&723#jPkqV3l z)&j?X^F&Xh?WbdioNLHUC*MfXHnO)sOve@^;`|G2{;L$ zu3yMNF3<|>BRcglj*9947P6dd_eT647h+AU=wf>I8StbG12dl z_WN9*71#&72B7=}~zzU0Zv{F5 z@eKdF(FGI(66AsGW~Ds<%6Sob5h@tZ$TPq>Vx!L!D@PwH zkgo!D_)xwQ{i#G-m8hq3JTL=5eU)p0oy5ih<5PiLViS-)v6Wa=JF)8PIMTa8Y)TEb zLWNlEVPaG9UKb1`5UXEHtl>Pd88O5fR{|JcGumxIzgp0*nO0)6T8PbFOl%J7oLdZ_ z{&{r(`Zy0|=Aq2I1H=}h&V{IR(YM5wAb%U`S&p(Dn{d7FV`8_VFSkb!TZyz)h+B0S zzA0%gS3%fvQd92?P>P3X&}LICY z+>ZLTqx|+8#JXJo+V0*5yarq+wgYiHS^>1TGZ?4=78Be37_qxi*WD-&HembE{(+;! z4x^p>(BAzhcQgSwKN0A(KCMC@k~0P_6|_5JJ;vBz?WJ&AUXp}n7fK)-UPat%df+i)ucQ6fQQxnT z{ze3ValC&zE4^Jl=&2Oe~P+4y+-Uaq<@CE^HyLVfVj`A zf!B!r-US>Wb^&P@wgKM~>)8Y#&&4|660tuZ?;lPP`vQIV;$vc$788T)WM9?*$onPw z@g?&8F@o6tlo7jv@&0u!Z~#F5ugyRUfVi&__cii=eGa%x?3+$v*JA*rUGE|GH`Moc zJ+XgaeBT29=M7@ty+rJLSnnxfeW>h<;k9jkr<#G!yZjFk6_z7G^8s*1lABpl`mW_PZy` zaCZV#;tYnk19p+bzcBXc{ic-)PR%dQN|Zge&_GJCj(6?k;4a^JDn`qI;XmCkla*S}l$ z=9_A}FG{aV_JMZml@e*c2}fc_*l8vk7&GYhD{UHP(o|_qYoy{f%~=h12>yE zUpaeGjC~07`>mQ)IO|GgIZCBAff+2O&>&;DF)|@hZ-|I6u*rsF#_$MZc(@rbMkOcd zw9(pF>~Wfy7!8}OIi`({&_+j_0j*gH3iFYcsji>Zcs3iglB6}7<>2HvcN{mxi9ia9 zi?apGI=vKhJd`CSNuiXGESZj5BBNp@i^)=D;lnRju3EU;;>F8u%LR)@JRG-37A4!` zs|wv6%Dtf?I1(y{T0$+Fi0Bw8T&p!0B~47%`Z_0hqdoXm@1EYZl~b~_uLcy~LCw>q8X*S4O)$E<#bF;C3Di^t41H8RN-X_M^X0`@!) zU<>z10v^QM!lnBsGP&=|x}{V5?x|T?^XA2gH<+b`O%OW%?%McN!TZVF<_ z=^36}jW$V};4QU;>2--o?TLxHqNUN=b{#FvI+JxSiz_JU&ly;VGD&^5dr5Ti^)hC!7ZMX}Pn3(L4612Ht z9eN6`4sHpSf^`~Qu%y4aR#+wTl^m=X9>TCBjbWZJtd^i?LCMZ#R|4zQxD5HkKSW+p z*Jp5@&YWS5V7tJFzQqH4zW^_q|43XL!?rP zMI`a)@K8z49<%NBb;--EZ@wS@P_VmlUU|#-t#gLg?0UX6c6t1ibqxiSYdd%W@Pc}4OA4~3pQ;wYF_V%M-Mk$ICkYZ|vt z&tx078$ASs|wK zPWAUF20TQFHyz%dLCA!7o61<|vm04+V_PFv z#C*Z zvSkRdUJ#7Su7nkV9}CUbuULxQg~PIir;NaRjm+6-vont)Cy7T!Xmz2IJtd3h<>nWZ zO3^8yT&InUi1Or=a-8tyrm_EBuE!sdAN<+(ALKQS$uJmV8Wt{d7c^I->kV;r3zua( zDoS(1BSsb5N0;VWBE8c5|5Y~4`e1A2idsiw_D_yZPf4Ayvc@qzYva*r35%mgubj2e zH)}?0*~;d&aV=B(YW|lQu@^{W>%T02rP+@A0#)$aFeQISHd~*?)@xa}!nz9B<~-It z6>1sVJb|sM*j~ZcjArXb>>k0_3}?%VSYAq-&I=C@9E}Bc#l_{=D zO`&C^XG%q~KILc~<4Rq7-JUv0Nnt61k%LX)zPgmUl(>?hAUegul!B^~BPGX6&X!y# zxmuzvDVaUa7pEAbd~u$N!jT$HY4VgIB_*Yelg{6ng-z&6P7c;r zEG|SZSayJa8?WTJ!&G66ZLzr#31H!2+0@*q?vvnDaI7FPQu!9&p^`3%d2;d#bfL_i zk~Tzq3xWUzWHAwVR2m4C1t*tE1q@lUq#>bFB-l7SA_{SNU~D^RNA8I)9Wy06eNL9u zlNgdzls2||#N;>bSa`5CyKu?T`N8J=x%V`ccTCP4Qr6VA#%porj!aLlC@qNXonOCC zagI|wmN9J;(o>WfzYfw@b6M{1$jplR}|0PI<{f! zg4%RXr+3QEIYnvZEhCDXCY0t6i_M!iZ$UxUyd~}Vy*u;Wc`H`ko|{lSwpq!qS5l=1 zGp7$t%vB~2x8)CsvMSA>2}W;Wqkcuz4_r#3v|p)W%}@_;+~dYW8?NOj5iveSU1Il- zdlxe;w*7$(f7-x4FY0OOtCOZ_-oZVWSxW3G{dPTH!tda_xU|@`*0j?k>Cz!7LWRgk z4+-;WwbIu{GMh=E7N~GCoNC4@BR3h7ji-%gjj~Z2kKTE`4C@S@E;K{s!Aioxtu7vu zDJ;SgZBN5m#qk1ZTHmFo@56EWVs?;F3v5tw1-`P)IQ)1QEusrD`ohr&V<=+=SaY zRk*1lx-BtW28L#%_EmIM$RI&>Lgol+lTs-xmk;qoSt28hT=#3-TS~jx)xNs#coSRt zGOIgqpzpp{pY8j3-`T!zmSH|u@~5R5tV<|8uY}LR)v?VawaM$`-LfP@7sO0ndnR{; zn9*y`q-BV(M~RA#^M&3U0&nOuvm<1cOQc#etO4F=ar`fHMp#jGFaK%}3CPZC+@KH{YJGuzlW|#-G z0Adisbp|w`Fi)KM3Wl8{#4lE>kvXC@(YhdPi;e<~AWo+ICnyHn+g>-`|MIf$K?S+|UfVNYmJLpT0} zom_mHZ(R0@qmeUi-*8o&BE+qwmcg;ZHwf;yt}WCJ$5&H zjJNdFuw#4KvA&wUAU3q?==+Y1gNHtthA2@%x_BKo1n<`#5md>In#Y0;F(bJ>y_Zn; zAFmXN5jr42?4_*0Uf^L)V|>ccxHU!NLh6RT>dBinZj^Q8rjeha-UaL&U(4GefgMT| zbbFX89JUeBi$~a5#@$fxNeDF5i~Va@c6fnJw;-8aPiE(i9z{O*m{=+LB~g?T#P^e8 z5U!a+pgytlfz~|G|Ery^IzRhNjH7>N|7HFs(6~fHo~y*=I&;9x2sPoBSQT8qvS=HE zRIpc}*dQBqxF6TaPv75L7NP0+&I+Bq9(%>zAc3X z#ZQl46faGWT@=gZAXZ~+HFB9VP227?mX?+`tj%Oewz#26Cv}9xW^};l@nWY&d+HvI zE(^s;{S0oiT7HZ4E!9Y38!m+qP7~4<@)=t*KR;>rf|8)H^i7Yv*fizNc_X;isE@C1 zXQ5)U9MqH!y=<%XNA-S-*@+5u8i-@0PtXm+|{RDVeY1 z#}TLJoyHiAV8M8EluvU+W*Kr3@<>*^N;+B6Y6tBFCsx?w^a`c3F&f*Ln<{=0lrEV$ z4>CH<#aPc`r`fkvV-^;I`3wvL5QL||7Op99Ks~)}1rX?Mevo4(c z^81*BV%@L9&Z@ca#(oH7KRmRVW}>|nBEV}j`1KX}vXVV486l~jfaQ7We`6(r}o+S@Do`{*e@`1Va`-7&taF*m1ieRb`gHH%wYS{Icb1kGoIre|V)O6V;mc6DKQ zAzz-gA&WPpE=*mWDmB;_+E?49xKsV-l+Iw2PZJdx<%p6Ji({7ROl@Y{(tK}mRWZ-b zSMs^JI6uGGEIGS$dvv@_w@$ZPC+T!vSDRBYMEIPE#hq0ptfVA-SVvm6OL0}Zx?Fo) z8u8GAhaT58m)7M9U6UzggSzlPOIf?zzXzu z1NM5f*e1Y(o*(RSbHK_Irg$UvHqTbGbF1?;=sK$YC^mCpgcmT_-8zZcq=}^`OM5%w zCC{%MygQ}mZj8TEs_2D~rYz9hN{rn_zf{sz4c$JJufi!VU+w62 z@a6Fv;(0^Z!Z6-oUTEfWa71uwuq2Bu-4f+P*z+uyOLoE0(W6vEC-@{Q;wAiAyUpc_ z30-PuY!4XNo)y~`qZlH6F)mknhkU`n$Y3^bgMpGd!mDFjVtH(Ac&yY(148JEpfHud z#AfMni#>Fve#+mcx>}|aL zSKZ@s>Q;_%rcYART@70sMz@Z#6}bfMbYT8Ez_UYeV!lPG+u~qrJUcwR!@1GP*GBA& z;KSWx-CP3?iNhvo%*+_X>`Rk#+&MfaM`LN*6VAfJN5rSMY4SSaFC;NaGAHq*q|B&} z5DIS>qAo<2*@2tb*q+o`!32m|VeTtAVot<_2nUy#24OY`XaM@tE7Ex^HjKUo~e`gm3-R3ko)SUt*@8PfjjdFfMgcUB%A#b~H5S&E7e+?TrHs z?D>+#ld?({-Br`E)iGs@s>6H$UV9q!W~MQUAyvb4OZEED5J5oJm;_&U2&)fS5V9&n z3R!A4TS66Ms4tX4J55@>4iej=?n}Z)bpfNtBOuo2R))UAGBzN{L)EZpS#@ z35|%a$=Z>{Z!6qf$gz!lrG#P|afnSQJO*DD5j6=9wYoVvZj50@2h&KAl0%a6+U(|~ zmC2Py?SA}(rqW&+mud8Bmafa(oyjvZtGr9&6#GyZkp_z|&X}A$yaR{#WgU^R*@Nl_ zMCz5IfZ`fZL&Ayz_F|)@NVt0f%PcOu2&l9l&`yMZCkaMhK%A=fVP3#M3|Gafy2!#N z)InDf;h?nTXIc)o6=zOaF_z`j6x&8`eq;0U$Cl=gp1*Fy^wv>pPnJzv>W$vGu4MlB zp>f5t!zZtql#x=tz*oMqAvbCC63so?b$3iCU)@w1B%5-_wTzhg$g1+9xjQD$>~6{r zk<)%UZ`#h8dARUwWY61-<>X{ zr+ck!$;mlO^-8eCrzfeyRFGWIT+mh^7ZilpI}%J*X0;~7W_EfTK$6Y#Kss8lI4 zsQd-k2~zb3y>C#u=mN@M8a6I)SSb%5FtP`ywjf{HQ2O}dnfqEjW4E1N(R^_62w7*; zIl3NtdDa6jq~0I1VtVz~r8Na9Bc~VnR@V^|v#@bYXb?Zvv}DxMyQ-GGanH2U zRsQAiWtHPoUh4b%V-xml&a9m=cK12<{_t62(hKJOw8h(^*itGMRh9@jGzkjUf8$n@ z@ZZ~&aA@JA-H+WU*e7ca%XFAQxxNz6$1zZ$!^0&He4p`ZM&I4|k`|pkd*hz7_*);8 z20PIw#zj#4bDrD58?CxnHBs0N2oXjkDjhc94xgd7ev-C}*HYP1suh!9WzWQ0hf zRo4%ONcyWI(_c-$X^foZhtUC5PHd!m<+<_`_@&AwCFGLKUYFTPncX8ZsIea_(PE!T z$JhU03lrgX9QD9V5osR0ZedKD&8vAkKLfS@0y25E>$JPEZD%76&H@-YCaB46@)7y0 ztdS{J?6mJTzT3#e=GqATkAzbOPB}0dKH+&R(!e4&N?+V~RBGTO-enKo+qbvxF31v2 z8(5t@N4l)eeYG-Nr|=fOhfAXIb6l$8EE_w%yc@T>&f@n-_`%*&$r?9o*fz}t4UR6J zs%GaHIG715tMSV38-*Q$$%E>NeuU@QA{np&O(#lUNOk-6_0c|Y&WIaQx6IL>IeU|{ z7dg9^2j=02If83Iog*##61MWqInuCfZMC*tdqyj3B{4@@O}5-Fcfo@9!h5hA<1ukB zZ-ZHWmTS0}BQZlBkC-F$@1N(WAkToGFky}wFh^h9dl2rKrT6wV1hT|b4>*ss2>&nuFBWve#X^+HD+xSf1 zr6I2l(d^G}*)U<|_BmtiF`f~=tclBNmBjd>is>cUlZvc6SEtv&{q@~K^{5MU@~gft zb-Q8rHqvsgRnoREWh>IQr181sE6TT&OLJ$idDCv6#uwDE`rPfgd`2M~8_yJB5MJ=J1R;MRT~LmaWn4fLFl^7T1ceDDjmtJZJc*;gV)}&v-sV%6LB{ zH8tc=47(J<_JuHCOh`zKPnt}>m4{s##-AMa;;^@eNt=c-=P)*GSZ60z>KS*APlU>W z6ev@jYdZOpoiBFslJ`?5vll0`$-<2@SsoKHCUuNt7&B&+YtcvHA)0VU>gtsEh>|7^1)5-juOzC9Voy^+F%(tYrce0jFR{bxj z#XYx_Jle^Yi%*ka=fFo;c7f0G<;}~5iWQ)iFb_^$+;Aa@!xv~7IN}n1#Va^N6S@Zq z-*OUz2s2W0@Quydyj4Dx%>;b9`33ete}BNQ z8x60Cr1}*GcWWqd^35$KTI5a@9!5(khgl^@Su{7dp};9YDbwv9z2f0ng;m3%In&0J zH{Ld;=8j3}V>Ug%ytt(@-=ZCIxHH`1OeAOwx0TM_T#*V76Y+j{lyE+<9y?qF zolsU(;;M$0>9sN1bZwn>zIG)Xpy6D?ajX;~&5$(_G61QDU(tn76Ar?4I;|ljHY5WY zrW_I?uhB85ZiMbdVH!srlAn}cgyd5^`7q)jbKElH$ufTR$(tjfg=;mEBa0_u6O;l zb5ivuEVXZ)-elA<9-Zon>H8X+!QsAE&HC@w$;OO{Bb~DB8d;Uzmkx>S#etX^bAZ3% zK||?RigQIUTbRyjEvzMB)*9NNIz?*1g=uL~ z+Uf3iZ+um}bUL2xi9Z_8;{|)h$Gf73T9cElY_hc}iXx)$H@JW(#bEM98HP5WiDp&N z>!SIEXciq!*5)Wnax+Pbmx!ZgFpqky6>tjtV720WQawEuo)lrNz?QQV2`{3`9$*mL zz&Vzxz{++TBE<2Q9rHDyyxLeb>zn`RZQf%)8?&U+ku|w(<6UEWpJFrExMzR9ePiFV zzhmta?`|(Ua!=o0&3f0AmE-n4o)}zE-C8td!>rLm!=<4cx|$omKM|v}*Dq=h=oicS zFKd2_{m4lVDCt}6Y)2@A@v%z6EhPA&=A>^<=W`=i-q7-)wL_&MT_p~GXTUvmMk#}t z5*6i~;l)K9V&Zh53(G>8wg$KAu#WK=6Pm-SqMM_6baa@U?NprA&MxN}=Q*d==}c?Z zgb&OYXeMCN3E`ruo|qt<@(TTPP(FxIXrN$q_1s4uu;=g|%5G}%1)7P&R^RvL`hB18 z_2swjdG@1~>wnc<6MoWEI&<^1y_fEvoICH{xefd8n4QKZRc@GBG~>wc)~xM0eQ5EB z5iM&+6}YFBXDs?-bm(+tcq-oJRK=VMkeawqvN?LKE5e5BqB64B-9*& zZ$U~S@y(%#(nR(AR4eyv$wb@;yzY(DSbSm)HN|30Sfkt_RG{(O0X(4EBi z7GQiq^b@7JUE8JIqm`^!K&?@0P_MUyX!Hgdmry1fn#6TMh9A=~25+LtNBZVu4XeTt z#Bt44jeHzh9M@uNR6PGfC?^N|%2=9eW=Vf7XY|@QK z>!hTl8-Hk)HYRD-AMPu@w=WWosz9Gdqt6Cf{0!$>y&%UC!9(Dc3c^Jc3H3vL(5j>TNE2?*HPGLKO*y=R4R$@CCyMwV+A=^W^ z%3ifY<_zJriL4=EVFGW6X2LnL*(MFmV3RYNC^G`=1SgU+IWv11{uzibBhZ_ zj28r^3U0x37%2Y3*PWG~X~s4BfAw{XJzZ?)DofsUJK=yOz?znTYZJZ}!J9W()63wM z&@3(7c>gb#Kls-pRkwe}3SZfHddrlkS4@S|H`E=x^5B&GwkJBL|8(W_Bv0RTE3f@+ zc1>yOw8NindWntrVqN}>m80_AQ@oA^FZS=B`QUA%k}?Zp`;KD51N89b01f-585z?X z;9i;>)I^2|G8oJNevN=@1Cmz4rr0F5M2*G_z%VrVWTQdC?xqK&h|Mhsn_GY(#RjMD zZ(ebT_OHbILk#vgap44i8AWVu;?56~de`-`b*Gu-bSLlce4+0zd{=LWW_|B5Ui00$ z0G+^AdQ*4guvXTjmk;6_qX;i27Ih3bU{x2tAd|G&t)Ya4i(Tqid5wC3eS= zH{%|QZf`BF^JawH*Y|l6yZB1@n6~>PUN~X9ZLO<%S(%i1_(odm!<`Uq0or%~;|-?Y zJWocr*Vf@!J64~euf*B6oEVJ5O%{UFGp-5B3xZcVnBmyZ2(uX4Qd4kHL~wAB4#H0> z8O9rVgb{z%fElG=b8xWHtR96Mjp9Dae;_Q*bV0Iu4-Iq`EDzEZu0@FZXzGQCA82S$ zL1--WxC=35Meu9u#q~1{bSZJjv{ipb50zTA!yBD8rc-GRer^Mk`ufseUI=M)dfQHZ z@Nc~qp2;6v-+KY{Heo%_>FvJpx4>LTmoOI^oY$s<^l@nsS9JcBxFFgEaor-re@y=Y zJw}LZanc20efk^z21NAt@j?KqbFO^??F^Nwm822r4MD6kh)oTe8^r6R`8f5%As5%hFex2p724p5 zDd`a@De0i|gp`Dd;UPk+J(Tif3eQieNST@6V`xN1#?TD& z(4j-)GuQ_imooU8j2#)gETblaXJoi!SFWqfRpXL7UHe=oT~d&1x{F_Ou@_xzpNoxi zEpl<$72)FHv21eegt+*KxcGQ802e51`iyj0%YwDx3E}Bdyp$3gG8As^5GhUwq0e0G zZ5Mme#SXdH4yE74*0@-!i`BSTnTw^m*q>eOH5WUnwzJ8_a$QVD3FJq+fp;W7tUh;& zo`@U~E_Pk57Ts%A>vFnSkc%C>q3O>OXDHBb9s6}f76 zBGmS8)^pOu{;4WtAQxM3&Bc%j6?Oi&e4wP?#rRRRXp4(gyO^T3Z^nZIg;Dk#{!WCN z-sWP>gXu^rLfUZ`%-bJq+d;#&84s%2`(w*jJ=7e`Gng*I!5RkA{!J88)QhEVFtgz1 zFi%sX`^S6|;8ZlMs&T@Nr3z@3n<3>&U{K>TU?2?y14fca7`Ol>F2o7{8!oOom)XK` ziy^-Ncj9oUW|wejF>Q$YB4j~$5{r(C#w|Jg^&A_MubebS8H0jjGcMEnEvDwEbQ_xr67$~7a1OA59S$+`mRbxJqul)>|Bql#c^Z2bTX+qGY@}7#XUFu zMvG?s4Yz5uJw4l%ZXXjZy@|~vAY0!6pNG@;ic@EqXW=j4FA{x<{VXOq-FF}U{tPE^ zRsaK)vf-3DX?)H2fl-3nv`2|Vj=jbQjeL&rHsfZaRAHP76-J!t8jusx*BE3JKTi%~ z6akSO6vifpiFb!3Ji=lLx0u7j!>cTeEM^NoZaHn??UpWat{sVanXD5zQ*dKhkKG^8 z3r5ph!k95kGDzY0OIbSopyKCo=crdrf4p?6hzo31;u^5JWIwDBY`DOp;JeYrZY}^* z=U#07pY>kR>a-^PvzX}*cod$^#%GW9U6o(R%FbvUa$^#tCoG{B%~FL0`D& z(5y}G?w=iN&<$UFuV#Pky&rT9TabP4KYrdc?Y>VpX0;64|M%m~_m189R+n?ykVOwH z${V-q%{zuRq~S4N=tcp%s}i&oiWym~j9wkt9m%J~GR^co;YY){TO1e0n?T&L!4T?A zA)yg4s(vu6f|^Z=A;E|9`dFA)A;E)-9nkXsy^;@?voOX7-J-C&?JQii!EtlYCe?I4 zzoDY)?)NwK)rpNZpKUzza^~cYioS<5>+@UhYFK)F)yUq@u;Gq4d}!p1V*Jj5=Jeg6 z`8(EhD3#HdO30RUwmE~X$Y8k6hAV7eDF?wD-9uRYkOf1ySjH^|DUY+^*0EMTbaeM< zo-u+s@;B%6v@B+fkBc7~FAW5SkcK3RWMPp>kx6z$?k>=Ss7X3vnyFS_yVQa3!6id!YwVa&H%NzVI_!( zf*v_wA3!Ju{DMvD#W{jI@FxAJw?u9sBV5?T8T7z~P26k{cdfx3|Bdq#`5(|@f8-?qAhI za4-xtq+{+Om(29aRt>a6p%y*2I>T96XiX?D)7R+vDrvjK7fH-9BQ>>X zy4l^(9@iDe47hCDd5(7HY#Nf zIGS&!%K#z)a+hdcc`$G34L!xxTkNP<|v&}k`(WqGtT2H@+8HKn6|cI!QD+xSJl#yp6u~? z33l%^PyU#kjF_0=^_yn@=J#T*Pr-FD12m|kMM@cI&05~0Wv8{QOM6ty-P&f1y%|S! zyh^?RI}3l-9-f%VtVvvWlW;Rll7zzsH)=ppoc$p30{3Tb+J@NLRIidCpvGmO8}+t~ zO(!*6fBAY}LXx}%4vp{QMx`g<6Q*30Cj)H@v z$mT&S6JiRs68?Ks?Q7X`80xSuQ__RXI$g@Y+Lr@PHL^JL2~|w`_^xP2zXX|HZb6_zjc1|MY`8V@};_-?V#fuZ?`{x2Uh&MP5iwv^Cz+s~ctK5QH#MhKqH?QK&II)zu*<_M% z_6iR;HxT|}23+>Ixu(ul;0|=*t^WESR2)o?_zA|XDy4@%@_OHIfAzX97`ME_qr&kM zuCjAb46T{<_KYEp412v>>JfB|vFNifmTWoxnJs2!6GmR5!-w#kQWBZq+hp5kJ86?v z*jSB?F>A8JR|VG`XI5ODI?FDIr?3d$3Wh)YH=V6vQW3+SiDdC^v1|4`gG?e+f^g^R z9(n|S`$Amz{_2_NB%cVCe%Q5EE3Q~CurjI6$||kQDvks(b+!!Kcms?25ii}8Uh#`lCYD7BtTfwKnSEw$<_u!wx&solhCBJ?-kn8 zGSem)5P-cyLTjG6W;6lFJMcu=FFWtckXu1`ObIt8mY@T z&nPCU=2wXiR?$sWw7QBatD39CoGN-C_iMROFLQFca_8l)&ei1_tE!B-lB3o>w|1ab ztgQ_j!*o~p>F~+$+u`@ay097yC$@zt8lDnf5SFU=+hF*v@T=iZ!Ula9kU0mr9janW zIBG-1MsKD!BsruiL}BLINU~?5xKG3Mewf}4)2m@R8K$Sh^dP$iq1d(R2rHY6Vftje zNa}c)7KEugE%WO8V}zaw)6HQTW}TbT~`{VVWBze6M}^Cjb6P zeh62`xj0cf%ZOT`3Lk{&S7CZBOy3O?F7Pm~C@!-$OiQ?QYnXyzD&STAG)!3cel@Fi=!55jbgU*lAm zj`KS0*BWokH9}*qadEuxK`uPD9%yq^;ai|@i240_NVnr zJ_n~2P`oombL_uyf_w!VBg{XJm1du>Qt~*FvB$@&zbs6Wd3*VrHk&S80&yt18=%4h zgN{(02?&qm^}zS+eZ2NM{x8g)(2tmBkm;}dET+S9?TJNrN$>!s)K}=Tm**M;@Zb*# zx-1-D_^iAATo>$7ur`XhdD)@NMeZTKRO#RimBCtMi>|HCD9B0k?G(^?{v zXf#L}dPPr&T$VC)g3g3bBGBa-pyckufo`1uq^tn6I=-JKWjG}%L$Sl?dr6Hz;Z<~s z4n9y>mW=iu<94HX3XV&)q&%_SxWg#gj2>gi$R>0h!uUokEWGxTnk%WWj!9&a4YJOd zkpW9K_5%uY({gJgoADjo;MmN)v`}0(u*b>0FU`#wgf{i`>~~rj!j``cLa87+rBVq=T<{6;>AUq<>tJ@xNNs5_*CBF5qVp1Yq~o|Bw4On4 zn3jQL138p2~P5<6?$n&rlIsBV}Zm7))37oRVn=oY-tn zF6#wsxJ8)zIRmVi+9;vl^G3qv5y&1W%G&aX-rtN)KfYr#;19Fr1Y&&m&8)P1r@4VP zY+&0d-Y}A5!}m5NcbuWHSo@Zk82+}HkfOs&#beNKSzNrJD?~pt3eT!Vr5W`ZlTcf4 znLm-?#X>fau{T2uNUc&gx;^W9IKa`x795-+EszGWv6gXbr$KmUPcR&@7o9_9{}FJF zFbG4&bS)6VV6f>u`Ve4Wr}R3#UcsPYOAD;W{d(9&``Mm=MF7BwkDU`6=8=%r9hfB^ zrg+2+K`#yjHnQI;W?Dkn3%vF%pFUC`7Y!q5_KyXh{)e>Xpgi|0`#*gW_x5&jp7RNdX)uh=~N6QQD#oNOU%asvJF~>JphH^ zob18k4h9ZD#t=jF7s4Ki#c*jMD$J9>R3NlIh4DmK-C0eSNN~|%?_#K+jdXwB`^i)9 z=OyPEw(R`mz)sfoh@<@z@)WSRn+COjMU;tiMY;_nTcTixp-8gZBoDe98|x=%Gz6y^ z6ClFHFpVKbDvQ)_-uJu~gu|cbJlyj#e$Sn1XfnTpEl7>~8;J zD3Jbwc(l}=usF%|t#Hc|ZUYcO#3~ol(|PYv$NPD7fAT*Cq^%oTq z78yoB*lpOr*Bz`pOP9R9lucI~0Ze4Zh>iRivo*yi;wh6B~E8pz~%Bz>7EpOkzIHWnTPnnsqx}#kA7!xV z%Ste;1l(N1ED3yaV#b;>hT$z|rkKkhE4f{~9KSUh)T1|~F;|UEFbnENZGf`J9rrGo zBa`0Oy=eW^8C!ZQLo=`6w!8g3MDbs{v~Es3Vt+ns;&l&Sw_xAWIwb>PAr*d`$C~`? z$CYzubvGAhS9es;yJK;KQExC#pJww}8)mn9!n3Z6&fGe?p~y^9X@wDUk!pjQE;Q(>oNYS@W38a2UdhP zg?EJ|zp_xd7TO|U>7uUd^aQG%Ks`K`SrS;_Pe-Vuy`!f?Dy_4-ii%v+=jzUJJ9FG_ zJAQN2qLM@oi*?Q^atk@GY%Db$wb5KK|JI``wmsyPbXJQ| z{Dx$**bRR;w5E^9^8EMoNc$FD`QHDkNin` zAALC_>`=XTRnYzt+9J`Q@h+oSp3|5k>I$;~UbW_1Mg0`+vTne_qSJX2f|mraqo4O) z^2%OsU9i)xnrsPsS#SP2FL@D6(yLph8^Wrf6M7YNW7bzkF+ds^;W7ejH=uDfU6>id_63`W=FIV%RQZ&+~W(gxjgQek9j-Q*kQ zwPhPkt8Xce&8e^JzHxT2ZQTP4E0+e>J-%gHz)>@MOIKbHfm4+W?;WhYwtPEyX@tom zAIMLmZ&e^7?VGB)HE(a8cx@TYv8}Rgu}RVDDb=E1AwN(vQoiJeDJE2s%WNXuB{}dk z=HMsChQuf#;B-%w=y;fYX^uNlQqtbq1*4ZWq4N}F=jI3{A-g2h_gd_gy@Km1-HWwM z0E-^X8*P<2_L7pe&zm=ZM8LM2w;Ahj=@=Lm5#-822jk`obUSSds9?>CPNv1ib6y3y zcWifi*sBxaGNQf{e?f?{o}N`qy3+31WBb~KRWm2#7#++v@h$iQ5AE1EN2d%JN9>Ci4`9 zElrJRFh%ipzEMTBRn$}UM%B40NvNW#s)mTvsWK`GNVpJSCiF%k`kLMXyS~@KdKtI= zGjtw?>P9VYhyjxr?j$x@fn7q7$b~6t^}ILrd+cko+ZND$FnR7fPL3 z)`mvvYwWgG0a#aspQ@0R)>}_o#U5snpItkfcFZRI6jjG2985Q@UA0A2#M+|>r%FO? z$d=VvCFOPZmfHEg#{okTn1kAS^RkV-Lc!%h2yhshiOuwQ;r)lLuUcF^wkB*$&Z?}k zTdlMH!I;F*B+UlRLuQz>cFbzr*p#ousAzoU36TcG*DM^qIRaJMKaj>aiTO9I>2ZpyaYV!JRM6b5p*X`rO@x1D%;7MWjT?1_|Nz5CJ-}hxp#`bUBo?)8ZuWne}Vc+@!c~5lfZB~;g z?F_-H|HXZ!3(JQ$iF+z$7WK@JEZH@4_?Ye|$=i!3HdPvUUoXbO`WLhdi%;73tsTa* zu*HjG0Xer#`8&si^8 zC97HyOjx1d;sw>U8W=m2!692=mR5S@f>gyd*fhX)3@8?0K1XdtxhicKQC9t}nVg?v2|HV3jz66E&|EQb1-q$FnvA+Hi2B)! z+pe7fh$Z$qtiR61{h6ugq+o=`4HzCC%y~7}?M)mq9yJabrFkOF)6*A~W6JZ2gth4G z3+15%dsiQTkWR@~wkVQLS;gM;rg9U*Gz^&bnt(y2%gC}mEDHwX3=K`jtouSY!WIAFx4yqNz^;;Ro8JyVwYysa7H^7)aq4fohHZz)R(Cc6u zFbkD6RN*+>nfSBhM_)^@ zQSib~Xz;hg{|Vjw<6Dz^bwBxR?e}PMGBkWY+V*mvU$1u0mB6)4%XG9ucUC9%=!nID z9RY;+lx_qjJ=~c9eNgJY40l{6&<^3OAoimu){F2ajow^4It1wk;_t>kj_Zx_s<`+` zoZgMogYj46;?_7Vh*M*nobl4QsE?DO5Tk}U!MYjn5`G8v%Wl=)+UvKM65KOT3)E1mv|#Xcw52y7h6wkMo-Bjnl?Bjf&N?_GC4e_k9 z-{b(VZzC6i1O(3TRydr#9-4r9<1~pkz$IQUNIcFDqx!Schm<&7`)OQVM(5*nmX|Wb z#dh$!T*<|pads4!*GkyWzYN5oB(X--&N|AQA7osU;#7HaURDQXpQ~5x&YhbYsW`l#|ZL|V~S(apOa?*Ho(__M&)}Om_V5U;qnS` z=V`bse{V0g4;Y8=kj&Hmkf(OE*<-22_IDN-mCiyC=#By=H0wI~0L{B!w| zfN0hHhGh-w8^nq(3)A{7p5APGb#I2IfZ=98e$= zA8g(Ioy`rc8y;H|TG@E}x3)GnU-$5e>DNv6=1seMbJGfc-{wSd?(}=M#8$F>&g;-c z>Y$4@5Tzd3&K)SlHfLNu9b^PxFj)yqNln*AQSA6NvFXp0 zp|}87>BV!fnT(?*hyQlk!^U6Rwt=67Gqt2TD|Wqn*(efo#Gg7s`Rc&*uyVtRV+ONg zlD;k(O^8tQOqkdmRPqaQ4bsn?HV zJ=vaYkEv>k;6LgYm-)~8FZm_Ee}Xb)9k%OY$i(8ZB$H}$CQR93U2tZp*MG<_`u(ML z`%Imp=$Ppqfzq1IGi7{9p)56sWkBOO;fOwM0cPyA44V7jgV%VIu>lP5nzVn?ZO^Y; za`&ou)|YId$^Gr>+UbGv$txywY+2CIb@QVO`@S$R(fpX}membQI;+YjFONmHEJdc( zIooGfc_%DtZLVw&xUy>!k;-VGEGs+Q*{}9(N)#7PSUx$gr_)~AQdQODFV4!W?OLU+ zolC%Fw?kJAJJnHjw$2L$-|cp|EGCa5GT}!tZ8wm|Kn7QSmT9KNAaOjeEx%V9HHI<=CF6iWG&&-RHo##EyGv1}2~e-1 zE2hG0oa@lK^0A&`+nF~A*D+_*5YAFda zXZ{Nzw3VUg3d)cQhRnb$SYjp4{!?u;I?VNJyl8v zRkJlWt1$6+7Cl%AASv|PyfOxdJF7Y~vl9bI@q1X1X1mY0-*jJa%Wl>c-zq3eG`eZ8 z<*?;Ri&Sq}Y1wR%qVT(Wae$cjcprV8#8zPN#Hw-%RJ2w+Tp<;Du?Lz^NR@>QYt!d0 zc6S>H0~mCveHMeI$5rTbxeC#+6~z_PDg`IFovhSnciFo&*z`QZQ-+reQgd;lcz*F0 zioaH@Yj!1E^IczX$u2{2vB4$TD((78SLNKwfl9ftaw|B>4_C^SmHwywbl6W({}lfM z|IL2c<)=^l^p^i!zc`4$@A6BH{!YxXY=$;v=O(`Br`>+i{=}m;`lEi)fNjJjbp$|d zIIt@#+=a!6%qTZB8YClP`>hFs%W5yIkZgEf|Mfdt?tDgq^)aj>sKYi?{ zlYT;dZ~^W6+5AwWpNxKb{UblU>!&k*deKi$rq3Vr(_DVC(@%OXfFd5`MQrxdNn7kFJ!!oz-cQhoX)*-wKX_~|u2eb-Og;n{v_<%e<3 zSzhL5KTYD75YlD6nJ#X>pSJNk^ZEgm@)IPY=l%4CUklJ0RfE$?Y+898)i^J!tL#w! zRgPblxI#@Hi-JpQ#|-+)<1Vq@29N*%e{jTbGeZ{umWF@q5^PV$p;0NM+j!*9_`kTS z%g)yJ*m%`G1Q@n$G&D=Ywn^DqU>1Y9r9aBmKP!W=izqYG?z1L;e0K5kl|YApXVOxj z(46mAOA>TRw}oa5iQjR~s0>Ad)#ZJjVJtMm#pb37QA??>Xkw1|hvB@&m8JMR$qkYx zwjPaq6Z2gRbAv)|wX+;XNYQ9whBxE?08bkSNW%~YRBgHjRa5LnyY4$9z~^B6RC92e z(T2c(1k7UhDfMF3&RWh}bQUn$omMAcqb*3il9(f@7H2{-0+mG| z5dlig#!+h7US#dGkYGUwpM}m_=&XfKTWH8aM=dEEHtQ9mShgIn!|`W_N5`Kej^e3? z5Ksq@TQmNR1GN2jcs5pMvBZIF9I_?;;AhF*Kc+t=|Mt({r=>ql29Ux2pOTZrYSEfp zL{AKVH2ibkws%86S&g>!2yd#5i-`OZl_OmeGBYt>I2x`fw`jUYaC$r`G;z0vCT`8` z&Rvu%<>q=EY#yrUcdT=4cS!vXayT+P_Fm62kH}tjcqEUf*jua?ivYE8dV?%L4s98p zj1W*_Z1&3V6y>s}&1PumTu-i;YcGQ0DA=>jy*iGN(pH}(0p3pN-mJs3jt#ofjsG3y zTZ=d>X0iOK!0X0#V!%v|RXsv~X&P9+YfkaxB99~B=)bqK;O&>z>#b%ZnY%3l=& za}cw5V)m|Du`6l@t0;aLSc!c#Vl4njQi2`XQILQ!83Dz5u~%m!L$8qiSA||u=qZKn zSLj9-Kulcj}*F~(3@lA@8j~DKPN9_B8F0-OA4J=)SuB=g-$Cpq|i|Xt_<3t&_;!> zVRQ*JQK4E`bQIFg82mFkg?_Hk4-|S{p<@c+8h7yM>SgH@!U~n}I*Gha|H12YhTp^E z`~sT%HQ=6BXp%yje5OKUlm` z*nB*GX-H4*={1DCU*&|f!8NsHjFrADpE{^nWyckgK7x0k4N{oO09Kt4W+}EZ@N>A; zAce5x9JY0}Y%TPqft7~p0?9r6RDsl7@Yy@!Qw4WP<@@jY?B9Xxmj1qSsd&oKI-#ba^+x? z24)_x7rCl0Su8HQRDTI-$tffjx(Gjcb$xS*udvdhvqy7W1(o){IMmFxgu~HX_?H5q z`7coof|(h;H5ADGkj36Gx&ro#AEuXm2p~C#!_tsO&XdQcA0RE@vtxw&%mvQ*IkOnV zsRA~?CKxzd$)KtIQ24VMFEVp~XX{<~o^EJeG{Hjv?lv2jAmQ=ci8bcV!u#*JGq1gF za-CP1Y^qUv1CwX!Cjg)*iCxocf%z<-xVX{m@p=^UR2R>hB5xN9o9DODwFeLH%&(u> zocv07HdZsyv2sR%H~D>AadD9n>)OfN`IpHS+J<&!T<%8c8O&NRbOHp}&dlAMI1>8g zFP}`d7{2(o8!)de2mbQ_e=cvUfFnemQ=Eg&o1J$#^_ZvMV>wnhe%A%X7*0(@>Kt!A z)hsS+Uf;X}&xbv+1(0cOF3?@7w_K`L>#4rJ+Lc3nIhRU&KoKEqlYG^_QzbCXPzl(o zjusGH;dsb+LJplJYOk+u_LWo?RyzJ-XHN^(C_E_vPi*|t;*aX`6N7HD1j;dkex$Lq zx$McM((2$Ku&JP5Ld#47m)sY37y$JG3V(mqMd6RX6iC>7P3 z>@Y-A9*aW9mgMXUY_rd_qXIN~F9yxgg3|?O3v>l6tHn8(ZgRh&!r*)QM0qS>f*N(2 zMIEtswaV&D2s;YYs#u~x_YDtxvPZ1aI_q}49k;##pZAE>U}e-AR)ML;AD#*1|0WQ? zD?BS`XXdenITp>zT-KU%q#;F5Tm-v!V^g+OlCGV6@06YM%R6Gt?(puJRXx+Y%d+8i z%$dBc-V>WJNvx{Q51Mnl*$2yOEY5t3!EVh6`@ih+WM!a>ih`Q_j+iUAqVF1nrRv0! zMEB9-1aLF*m)$2kE{HcFVb=q810992#UMU#P&xv*GqXZVsxu!iFxw*@0zlNS8f>5f z5rKVe*HW6H?hLG_QBtwHh)6yR+9oPQRx|oHWHl4N`I%X_6I73)@W{wJ@+93(Ap^*+ ze;1xBl-h^fks)hn$oTq@5FRQE4N+vM==Gt3Fb$Q5hO*xpGQ2)yun+luJyi1gP>Fr0 z;;o^^{F80J{6BA+h6-z}L-y0U=iItatV6cbx*@y$IeVdDNH{%2r-y9zA>-*G>uD~m z;MkA@A_e?F(dnVG)6YBXh2DMMeLlSvBS3f_h=rP(8dA*y8LjxEpwL}ZhHpmy4XOF` zoX_CM98#h~C5|C;RCo?qlro1b(V;TOxAclRyTn(T6NPU$i&@SwAR3#1i9}=I!8X)4 zWj0~+6fw9E#$tf;=)*@>UEgNQoA*yg6JObs{JHnml81YyPTp`S5^>+^epz+O&ner< zEyXMw?wz*d*IONhKdMXq#P-xTayDN3%n>< zs<~Te4~g5v{i5h|lg?S@6lJ^9F4~!ze;aHVPA_?Li%dum$_U9lCB7n`*_eT(0)4&> z85TrSSnz{iIcHsNZVjR&z}@c4s4OvV@mPv7ZRX?VH_R8!a>(3oUS~dNK4LzFFGkE6 zW)-*xv$?WhQ>Kt>$0js4)1a%|EZeH7x>`;%xwFXx&Mx+b-6sYGRtccaU}wVQgJpqz z%MDFISdcO@f}b}urLbt2d4c-C2~aP@hM`1 zVX|Qkco>;?qnLeZ@EOFGlBp#NOQaHZs=FjuA~rZDJLfnhCzIl*I(<&D!9LkO$1dTS zBLtG2YWLa27ICV$5G=p^RB!@`Qq53i>Cj1xZ53RqLT7YtbRa5q;^oEYThVu;A4m0X z;YEBf`gHVUREn|`e)=exs)h<8Lq<7DA4TciD7_V>7o+rKlnzH}Z#9=+C3{gD9Pd(&JIOZ~O_9 zq7;r&Y5D{d{ce=r9525&N?UokrBQVxRkBtv1ka=kMosQOJn8xJVz$=)&H^C1qA7c+v8FMM@ob;^L{-_!H7gpoPwBe7arv5MWr6DQl>@69Z|_R% zs2>~{D64uXIBlRMIxxMudip@LWnfxRe70+Q96uEtp{+F6)eNB}}jmxFcGMFDlkdL130uabyJ!+^41d03Ikg+@W#=GgBL zD}i6CbT&K1hir7fM&foGU2mgq+ajCjw87%SGEIT(4tAc5rADjM2+9-$h4*DUv!^kZ zQbI=3T(rP*!>l;l!IbW>I5M)bT~emrm`?D>0!*<4usBkf+%$IKbbd!3bk~d^ zR7~+$6!0E#TrK=Am6|iPFlgRYMl}r0BHeiypCtPaA0i^w*ndQoQbc^8I&zaci)edZ za%b!Ft;t<^belK1Gndrl@4o*ree}BcquU6_)o((i+5r#^UGuu`oQaa zPvuv$B*Qa;EN2*XR$0|DZX=lM^-w^_&$Um-5(OOrOB`6sQGW@)zkKq^C&eF1rq4e9 zbgQI*jg4UqemgQ-o&cQznH*_?`t`j9v}MwsN#eDhTRi(b(h6~txC?pNs&1{iyGpua z2F;nVYQ~ls(!u$(c>Z`qQ*GUr-p_#gA83}jwB6kS?E9SOLxrp%_ z6%y;_(p9h!M`=INQzOtvAes7|nVHBeBLn7L@?qTGpY%Y>QrEiLsz z?o1@@ScbG6JF;YN)}gFRSyEQk4s-_WBiZJWJ3cuRIqwEcFAe|{f@jSGFChZ<4yG?+ ztZo2}2UuabAYNEH6udBhQi^+$zcc43AF}_gk916;z;qsvl&e(7Jf;k)1m7z5-hVDn ztZI*TPrChu^^5k8bDvgpUensKbwNYblz~`mYS%4KEa};^k~vXtT+*^4QB~QwtU0=6 zS!}qZZpJlr(bYXQwS70VHTO>{7ayIlbaKdAy0-W8?$y>{S*Ejj#+o=2v6NpwV~m^C zQQlTl+5~~jaQAhw(Bw$DP0S4?>T2;RQ!0vdpVTnX&S1%caek)KWaPcwe#9P%^n>Pl?n}F`41|8_Fk_?%mV`Hq+$5<4W?)UXecRhM?<%*L>_bjciU%LCzZ?8N|h97NMyyDs4@4Dl{Gb>g+ zbK#C%zkhbc@O`Vkd`)fb@-JS~y}q|8bE6saP-yZBwWv70GG5o+Tw&ehYUx`i{&3~V zM|ZKJSg|X={pjwc^&8J5KRJB4tY@yd=9%9o|5e@i2$*=*wU)MaR}|-m%X4aGZA~O@ znp;~CY%cT#X114OYQ?x%AnuoHbPot^2q8YB=3TG5O?N;i&6&Pxx){i1hIq;8#oji= zvOrWIq{dy?-lKSwiu09~uG)9Dd&tw~X?{mg?21>BU=ry9GbjJf z;|jS%SNny+LPv021)Zy)3N~;mDoQQ{f7?3s_lk7LK?fb+Gg;;k9W1Q{3@ywu$Sh$S z*c!*o0{juOeaeT-3d7W)4_WvnVfaWv$;3SWu%vD-njHr6KAUbhIsIf2VZeP7C{(@OQskOMZ)@R&pshhF3Yx{n9 zWyA)3PM$f(_{#pk?2bxvL6y5^PP`^Vd_-?>6jiwN`5C@wrOVSaEjVL&vy0?dQBFgh z#amzQsOxR!iQSWGRl-I?LP+Un|_gqA*Q3Ag@LC_YA=y>{r*U z*59bVQxCSzo1`yD(rVj{wmWUo8uLx&FPJ68UI1OzZuyh#gI3cACh@zBUmVZQ16~Ci zKHCf)Py`v|Kj}Z{N3Mq~o1*q>~PI()Q2E+dBWze9BY3vniL&nD!4hgu|Fg*#>0X zjLJ5od^!zdw6c7NK=7rq+y1|pS0x`yaT>-b)f=*;de}%E7^eUG=``8*<43ns!+LK1G;ltw0ZT6v>2h(he5$sAdY)yf%{y*m*{AY<&BN+327k@?7hxO?q>*mv z#Lic)PLdd$G1%I=W=2iTj5V#TgENBH7u0r?mvz(@7S?u@m3Pz@$TzpHnH~&IU(*^} z!?dq8vGR_(!ooTxRp&+kCSN1)DwoPX1oyp77*x9~H8TBH{!dxFTmA~-t3+$DLO)a9 zQN&%!moVS6q_+LY0CW!a6w5cv_cOaF(%L=%8|pqWqFOnuO+w8{(EU(a_har^Y6Wv>3@Laitl1C=SM7^ z9X`{A<=2+qQZDHWUBI%k;hBR4tg7f8CF{J+>2ZoqP!V9^68VBsw>tfbOmaZp@{egYE^+MinD}N81JBy#i z@&t{If$suKK*qqVcRV_j)9tab!7OrF%$vhD)S7vFlu`g|6tlw_iZb1ao>`TV#a&gC zI=roQr5U$bA~V*;+pn4EYhJiM>YLOWve9X$7+GD@-BzDl)Z8|uCX&c3Z78wT&z(?Q z(lR4Bam}30VyE99%-O*DB?-Wri~oi;Y7u{VQpkqK1q(zm>xwo^WO*$)@953vEuMGk z>)9ecV$V*n3@D=rSIv5(nf(b`QnT?w=5ODQ(Q7gKZj7Fe(StGC8l$=F;`AcBB29@=G)7f1 zDu|IWMnCyDM(@Vxtr)$`C2o$=Yl3TI1i@}DsEH{tdPtM|Rl4M7V)Sr~MoV76>yXE*2+0q4iLZ{i ztNpwVRWZ_JhPe#x;>8#}$!{UbZ_*e;Bo4pBx5r-z=ie41S{I}K81=-cotJ$)Mn__F zFa}EpGV;dA6C+!UMq>0vjMm4f7kA3PX}J`mQ!zRkONHR1H`iRXVP>UKaM2i}QhN8z z-p8As#_!X|%8i#y%cUhSgd%{bjL96UC60yMDE0`SHzIzOv21Y+FjIKhw8(>n`H&iu zc^6L7(QJofalv5An-=wS#wsU9CoRvqEi*O~W~e0-eg9q&^Si?HwoaOTecYqpDdz^- zs>&wTxxKY51ry{vvoBU#+uqrc*V0#?>uH)Ac%fP}6xXzP)fJO`)sy=w>>gyK&#Gz* z_-YCjZQLD~pA)~pxl?LX&lYJ997+3B#+t&IdqhyEAiA#|NTG`?!<$B-f(aj6nk=9` z4Rl-@ep{E^CqL&Tk30ECUgpRM@HLZx5edlOQ}ecD?8y)(8CY@^iDwg?9GDYW72uqI z>LUOnww865Z7Y*nE!|**ml`uWGq+~$&6Gn~J($Z5X34x#^T0tWHAs^sX$?^s(zA)h zm6$X$?74Q)DA_S3+kK8qP5|O_APC>(^Z6W(T;CesO+M)rxL%nBt zmx6RhkTwQsMUeXV3E?2&gv=m`LHcu$eix*l2k8evdOk>yzn9Bz3eq(}niZsYkRm}U z`JDU@gY-g>9t+ZaLE6R3A1hxPq^uyxL3&WTz?=L6+6iC63F=6YHV0`Xzr-Yd3GEaq zNFN31LXdCDM!x48F1FYjFiU?omLRJ09ADrh`_%Z%R`NmN4SOB_JaE99Jyd z3P?(qBE~Ln@Nq(>3P7pLzO`4Ucll%G(G@+-)~zSLs*{TfloG||ZZ4nNCU!iqxNTw1 z(&Rkd?}q!_VOK5kd4(6>F;l$ev$u1C)-s=7+?ioszPNv>zbsj(&CQ3Bf0G;FH^>sE zsx`Xxda&{_#;;ybf63VEi01yxBcRSdmnmm5m#%0QR|eS=JEl-#1T;=n6jhjo$akZ=~-9=kJ|Ad(V|Un97Wo7ga@BU4jb${ zNI!lzl2?H;PtahK*1NqKwO~1vGO#**7+S&-v+;XoW*Elzh6*jwrE|0csl=WhTZjE* z07F~b%$X%qHq9(-%+0Q?T-4p9T8pf!)-KZ}T7!wY0$Ez5uMT>uM4)kT4->GDza7uR zB>qvIaT7=#fps)6XUutxBo63{yo7{cek5i=2*32Scv5^-l*+|Mu@g$i(`M3}%gv4E zPV-^2?y&hu^T%dsrJ3FWWdPsR0;6IyFE9_9B}0aI!E{a+m8Zy}UUtchi4yoq{jc?A z`q)hG@{?XR(~D+$+)RhfwAW0V%`{-9*=Cw#rbfiAn#sj3$YgL7hvts?^3{*c^m6)` zb|wyYnyG$_(7R@OGcERH`Z8O&SYuiY9&eZ{If4Ni*TLA2ic`Go#HP zG}9t8b(^WmOnE$a&oC19d|;+unduD`7dvI9XUz04m)~Zlb!O@{1LVP~RcaGz5O09{5MW`Z^=4Ye<5%yc4MC}dFRs7z_PM6JM4;rn=L5bNcQU(2s83TAj;&4ej1?a|^B44WlK z|Bat6KQ~#9%4r|^ub5ub@?*uumUy+}Om-t@ip5dHq=vbRl}+5h6(g7&>>U~LLaqeZYa3y&%@gW`Yt-yp za`xqjx8&~26!s1py!4-*zuO`om)BPqj)TvRNr-Kc&EM)_|SM6DCywP~4F>Uo+E!-&FDWol;vc(B))nfXS{eyb* z8M8RsyxaVdS!%`5mt_#u%QF4R@Ik$_QW7sAnb{CP&TLA^gE6`tpH0F=SvX6*8GWyBp{+_QxY+ zFdwLZ;dOI%@<4m?OQRnJVPpi4LD1LgcG}B@n|*@4Li9>V%jGbL4@pahe<~PXkVFR( z+BY;X*$`wmXtICSWGzfq*eLCXtl@dl#zfIwAo~Sg?g5m$Y1Ze8%n z97{Gd@Qa8-NAd~T25ek~5D@;ZKD{T4wiNCu6jzz|n8m5cz$`LU&MI^_d54ka7*`p$ z7^NL1T4~yB68lV4W+FcfJ}e~J#M(8x+Ww%e@__(nDhsIY(nNp-#xj@tTAO4G*aFqQ z2bGdVCE|Ah+f=QYV0{bbKkz9mV5;4jSf`_YmO>GRY_2Csx_n^=(4qdX25f3cZ^^Qf z9VLfA8OKvQzqk(6s8~i=uV$7Q2Fc?Yp4t3>Ws@6YQi+~f%r}Q{vTV=H#?8H@tmAWE z!e#s|Of`&VD(xl!gbjpK4AA#qDlcVK7Ml&^e7mEKjSxyvWUn#H4!0=Aj=Sf;Rw*1Bu-r6hWQUB*h<)5I{ zkk5NjVim$xgu*BCa!_d>ZRJgG`)y_8`C zj)Ox#3ws5dkT0B9_v`GK@@!U*Ra|E!uN9FdCad0(C-N-64lSjjtaBi#q0wx~u)xWg z!4vrb^T~kU`1J@QY(|trETE$B2>10$EURx`exAAC*u#=Bc#O!EyB+jX7mf09<2g`M z8G*=ifI7|6&tTQ*93?qOA^BmLrG$n#kb5Ij_zfOp!oI#tUy*`na5CXk^c240e==@i zmVpA~7RBUacnt>D!!P-)I#3Kt{WFK(NxuJ@bV`1rXxKepe5z=8LBC}73NH_=`^FDnpmBR8FB3YlE2 zw_!fPtfU*{{j!qJPmfYHdc=J$ZQv#~X@Ep-hU*I1~_ zxZ)a+mnMzM>&0Ws*~mf))pGhac}~cNtH|f;**&oG_2OgS(^^P}6Yev~m!;93tZapk z6HWGsGlIZtkJ3eYIzvCM_M!m(E!~6Y@0mbQ{$6d~mBI4t-fX(dB;M@3%PZbtpgD$B zhAjr^podJJe2*U>8QJN~(JfNlL+;b=vu^pcn-JJ~`h}R9;P3TK{9Wy<4kdcr{ch3i zE)#5aa3Bs4fw|I>>9imK55HPgUYE5$%j2LP#AY3LTy#JuItS;iBgfaD zDdmjK)|=}8De7#_*Mh9Q=?>3hJ1{3>DrPQ52xIrbCSX1nr@3I^i7#eh=mlE$2aukV zOBRctv3$N0u7BXApWZP1tJ@wbdT8quI$V^zb{W;C6Z zlh_SN;uo45wKLM=1`F!5!0t`}c2^O0o&-4rr_jds37#$BXcdN?HL@23;_*WuiqsBq zSS=^s=73!$O-#_lUO>+No|=OXZ^{>B@q~Ovep8mbh!7_EbRds*=hN1FI)E`{qyu%dt&XB~gLUE^ zJO^WA^+2__Aa^kLu3Tv@kHwSx%~V!Ka*?yBv`8vqPNpUB>7_`l6-$97V38~=t%^)e zDn-WUx?s3k_J;(I)ge2J?Zw5?LR-iKJNCiQkZ*>UB2IVc38~SYu zQb9#n3ZR_mhgyCEHrS9rRRfSQ_Hb-%n{mhik-FNHq2{u+23sQSUwT|Z)lcWa1MJgR zRs-54p1F(zLnAr{wO~p@4wYxsS5Q%!#Zz}MQa_p#?~YfeKKjoj*GE!W%Z_aSm+q%d zfA(1_KVFSAaQ&ZZK6|L)-bJZ2dGfasFRV_b$WvoqO?H3xhE&e7i4z|CUMf-E7y6~X zrO8=)mVP!=6Vx*1@p+%;XyRkGIW=Y%Xk&y*FverPa$VuLF?*%_G5Tzb{P#-At}4&Q ztXBSGv?nF+L;3xcC0CVaYnLYf1nttsAPC>t?0nI*z(%Hs?IW>X*pUz5h&pX8&%-V)w9rEHwPum0TnBTeY0;r<3b7p6 zZn*%dim`kNi&QxUv}CQxFiA=QN$kI{!~gq%vB{Lzmsgql zcM+PYxq0%$IQ7NH(4A?_i+=k=YSzhRy@l(b&Xg3oCiKdvN}6dBE@HkL#8A83I%%R1 zrlN2dr>2j;OnJXF$l56TvQ+X)h;+a1zjsBXW^(W3 z8h_iAHh=w^2UhuK&zO|g9Na#&b#8M(Ra;kERd7F{iujt=b%5SFY$H@P3_Pndg{~}#J zbpy<#=}B`?{6`{z;lK9)&?>s72i_bpj_75Rv6TUuTiBopNMI*Ah`@y_{k zwKK+yQGO5lP3d!>$6(i{owIkkUS7c>>T6iQ>*td#fC>@4Tbmm&JjY~OC>ulgEd2&VuX$)7BIz^qpKbbS)q=UxTS{`P+bb6wSk-0{lcES3{77*5f+^Wol9S)PqxqVI z|9{BKFgMXXYm~U$gPaVR-o_bCW$o2@?aMYdoEghxj{YA<+%j?rpzn0D`5hk2Q9W$; ziPnhS!$u3_I2Sx0R|zf`Cx5CR*P|cTHZ;4uLC7{k8iCGgP)}J zFWe^G^Ev%1KJs635!RJjVXE+J)xIsUKOt^z+1(;;)ZYPL5!(Qk1K|=V*;cZ@L~8VP z`sVtiQXlzqs4N@lX~|@$fpH;2#>V-Q+KOD1p`y-=3|m)~r!&+khC16jdpe~~wYW6V z$yb?9>g=2r?ZhaxC5q~LORGw2OoBnq03!Fa$_%`rkR+7aOMAgyRVu}L^T1b*sTT_j zqo`s&DP%!qL?CwfYqY`1w|>~}Fs(Sn#If<)P6HqyWouzhKR zv$08Ae{yQ13SHBvH5d*bL^o>z0^)|+C5=Osy`^Nxdb+kZ>Xi&ey?e=?Z`?3=glwZH&`^}TnElnxb+cO>dj0KNS_kG#bm>ecy|-hTt~S_R>TGZiTo;dS zJMh@@0yW-Iu=~3=Hnp#RaADCT?_2L#@+%4&2baw)*>F>GpnT@e#oo5uJMMN*-ZIY- zX`N6|+C8BH-4J~OGqBvn=lz*%ykK{et@UTBM6Y9jtuM81)u;M@ zYJ6FQ|51MY`1*c*(^chJALZp^d@-O1v+uF-wX&uDs`6~z(&YPuzmBQTwG+CpD$mBF zR{ud^81kBa$HwFJ&5ha_>G6m)s82X2H^^J?$p9U1o$8X5X9PsM9?~Dx59y^Hcsr|? zIRAe`eo>+y7BXllve+*7AaFurpD)Vr%XxhX+4xO0<4}f}0rExRn>}=dka-(`-QNNM zH;fksj6=8?y4nWT30VjbHAJ!*&X2!e{Mzt$rQZ#I=WEjUUV15c?2BJaZhz?|7B5jf z^6&Bqc`^32)fnf0P|br)G*Cskxxlf3hx|R&hz|uSyqe6{$K0IYka)G_5`J% znp>C%st#=a02~R%JY7YCWCu;01WGyXNGJrInB;bqgo5xT}$cZDUISLWD?|*uJ&h5v?%>2}v zG*RoLe2mppRr{>t#>|!So9Tuz@+T_;SCwaDhL?{qlarFK!WdpwQ+ZW+HU_l%43a|| z10t)CNE@sD+8OCF02?&tEaLaDnBT)@!g|J6B`>b=N3Sn~ufUq!Jf@c>0SR9d1v!78n%`kM zy+AWA`%XM3T;qHvTJAF4EXYsdYsvJ{xf?2hxPQ(SI!el~@yB%e;@9Y(QKwPg%+wS4 z#bbRZsq)_)Eq?|KVcOcj%KzGeW#e^}k%yCWwA`6um0)6WB}qkL#`xedr={c~<(C};O49usHr`cgGa4Bw`z6V)ky02>7u z_bmU_MLuStQ>&T8x=L)uwi>X=;ld~H=; zq+_+=RMBJF<#N3rr>~{z!OA^TQ-76m|2C%H(}cHD@~mCH9B#g%-pseMhx-k%X+gU` zP7Rv8SO{~_y+x&#@it=Q+tU45_Y?SbySV%j>HCP0%~VYyA|6{iJO3|xZyp~nm2_Ydl1nvnZISE+$o_cFWl5KL%_xpX{U-w=i^QgLNs(ZSt zs-JpSua}-_6W=d@|8Tv5Q!gDrpOmW$YOpt@ENMK;NZz%fZcm-O0Wb=$p5&_Jsiab#OeW=XNtR5uWVRp}kF7OOTROGMUe3;xe_lRNu9TNYD=VZz zYiA@{9!*9MM2|+FjOwGD;X2%*)~F*Xbs{?P=)uUypH(&Coe9$`kOzn{NwYw&QUsZX zu&EniRwoozIFiW;mo9N!rRLb9I}Nun%G|`uUY@rJ`UWA~WZjfTO$eV-;lAA-+L#JU z2+9!F#JIC`Dk`p$&J@OeG!*4K6`=W7}DkG1TP)R^Y|AAym=h59_+e_Z8jndpBCw=*2< z|M7<8Mf*t6KcBne`a9TjskzJhKW0s7^9JX-xPCr&MgJY}6{EH9w(8V|6Ge>wrv4e~ za#|xrzfY{tH1ncpysekP8 zu`WE!p!KHy6Syb$RhOAvbVVs!0Fft*`kzQ+pk4ZNs(=3em@#LXT7E%$(LaBtqW{Nb z{{G-&rVRamziPxc_RpWG=zofYO*1{yY3Tp1**)LbKYymwKh|S=>Y4KXzgt%Rjs5dy zD*EqWKJ<_G)ML|Tq`Id2zNEiF{Wa;V#dRNIpQ0T-(?|L$>qW14K9o02AHh0F{mDle z-xTsOJ|EY6Pxq{gu9rX0*Xe%8Q};Wq3*AG{q3lCH4i=qfMe;4@uhFr4Kf%wvUkxaw@4d(6`?%*C-d}O# z^$pClv6*?4?M~J*LK(8=2svcU95TyLIG#&3^V~(zfJtOhbGS5Edf?xMnbb;i^CYj& zpW_b)137*qM~LJ^{Oy5=D-ek|0uin$PuAhp6^evJYPG!J)LeU*eI6bN%i(au)hU@A zUY(>m)Cf}+#}va{VMcMnDk zKmQZmv;2i~r%2F-IRz<-vXS)7!fxTwx5bLVjUrCl!$T_`+G2J>QuKYyKa#PruK$z_ zKX_is`TF#UPd|nC*?H;2AN~MD`S-sU_{k>%fA>34|MAD7{M+9O{LOC!{`%LV{@gi% zXU~e`zxtIZ|MHgt|Lxxd{^A#+{$Kx9lt21N;D;Xy{NMwQ$rC^SxhTK?zQFh16Zo^A ziTa=ZRFvO+SKvGE2t0E})W7|dr!RbiYUMQvcQ*K68OU(iu%*1Mfubzfj{_x!0&%w)W7(mz!zQ+$N%MDMEUvW z1)e-9@VVzi{j<-C@-xo}?CTSF;snRjC!T&V2XqJHmQQNHOWfj8bLaL*o5zk9d9UAx5bojXN& z#}0wpw+p=C22tPJE6Urp3B3M#fm^q7>^*Vab)vjwi@?pB1#a3T>Njo_X$ARBQ{WqC9Jsz?m}z&X^(Ur%xBz?FdP>3+1Ua^A#pqy6c`AI<9@$DpHHCIE70v0n3W~a$-~m(*9zbRA07CEpLht~p2M-_w51`}V0aOML zAOsH}1P`Ek@Bk`<2M~e>5P}B)i3d;_Jb(~9fDk-@>cIo33?4uT9zX~lK=t4Ogx~>m z96W%^-~oi-0fgWIR1Y3NW$*w(@Bl*a03h)IDuV|Qf(H!K1E>rhKnNZ{2p(Wc@c=4=2M~e>5P}C#J$L|>!2<}v z0|>zbs2)6k5Ilg6g9lIcIo33?4uT9zX~l03;qjW$*w(@Bl*a0ICNM zpfY#>A$R~GcmUOd2M~e>&~fkpDuV|Qf(HrhKnNZ{ z2p&N7-~m(y4zbs2)6k%HRQn-~oi-0aOnjKnNZ{$H4=r3?4uT9zX~lK=t4OR0aqt z4}kWNcmS2b0|>zb2*CrW9z1}`-~oi-0fgWIR1Y3N2p&Mk!2_ra9zX~lKnNZ{_22!K0|>zb=s0))mB9lD!2<}v z1E?N6fXd(jgx~>$-~mA50aOMLAOsH}1P`Ek@Bk`<2M~e>5P}C#J$L{icmN#-51=x5 z03mn)A$S0gcmS2b0|>zb2*Cq@!~=lD1L!z-03mn))q@8Rf(OuX@Bl*a06GpHKnNZ{ z2p&KP9zX~lKnNZ{2p&KP9zX~lKnNbdaTpI6_V)Wffd`DybNQi=1w0P54wrs$7Vv^; zm`4a?0pI|NUHxQ`15EjnIDlJl0C$Q5xKkW}5FCII9DwS<0jLZPKnM;%2o6B?-~fc+ z0CXH2fXd(igx~;#-~dz)4nSpa077s8LT~^eaR4fV0}z4(5P}0xJvacB!2t-t0SLhX zs2&`E5FCJxg9A_*9DooUfDjyj>cIi13=TjD4nPPF03;4TWpDsOZ~#JZ0ICNEpfWfB zAvgdbH~`gy0}z4(&~b18DuV+Mf&&nO15iCU0F}W32*Cje!2y880jLZPKnM;%2o6B? z-~dzx2OtCoAOr`XdT;GnZdx0{u%5)Rj%8cM7qhT1Mp)ZTa}EAVePnnL7t-L zIz!Q42Q)syhJ&+;Z|PEgkwOOteGqsBJBwIY3L+Fv@gVk9E#Z z`+lYy@ztwb4?Gom+CP(DKCanefQ{g0W#V-S{1(XDm(W z4eM|(;92UyS3Jqa^ab6DV^C8o4+M}q>3;QRk_o;%FL4jNFOqSnwv2wZCzE+7C|8!t zm4$z}okLXT^e>xk>P>y%3kyUw`I zB40+*>TGp+%&x2qUD9}GudH`x*)uxL0YipJfXm%iepTr|?|%yx#N4xXfXqB#!qcVQq_nEa|>>}NdT;KYZi`o8br%An0)=!_Wd*rKRO7+Y2rcwRCg zcuqd;TFxcCNb7RIbky{uNeLR6*@#4O4_Th3WF_yM$WQR}dhaA}LnGCaWsz0rgUu{x zW~v!s$PkZ3lFi8B2Mw*kG{r2t%qYR_N@}-STviLSC}yi8Oo?O^tJUl{YyR9kU{;PG zI_N30ve>*+B+!=4W}87)^m-c5I@P69;oYvP2#*8jN*QVWbgJ28LJCPJc&&pjNC-_% zc|L_R;E;xK4{05@;wd5tBG=p2b03a9ad;<@SGqC&${vIPN+oKC$qdW`$yha>L&@2z z6g*ETncwaI^6}OaKkZxcCR_fi^M8g9%wMnT-;I2n=YPP)_b1NZjr$KhyYeyFnIWPD zw=Tgg!rg0$p zKg5jb=lkR%VyJJ{!8*?0igl6o>y4?~Al4GB zi?~+opQumPA-yiz73&b3KDEwb%wocquR35>A$GgE2nkPTe|8|f3TKMZt%O4Is zviH=rtWpWx)$cki`)52bLw^1IBX{CH(|2P3fX?57yk)Lg$bqgUG?{1nl#NEq7l<>< zmHneLJ*|@LkPpCnQI?P`U2oALLl@7(*e+8i=L>Ql;8FPUjdz>OW@EF+?%?IDE&d!v`@;%|*hx&Hz z4ez~P&Tf97S#g}7-Fl?u{ARIU!QSLGngqW?o|4cgOsIJwW8E&AsDVm>=ucFYNFKl4V#w^tFVO}3IT6L@cN%(-=S0dB zGoli%a6GNKvjI-mNW_srNgwf^92zxyc-SW_uZT$l`wqM<>yYlyVyV0IZJD-Dl!Vm> z8DZ1Ql5z>c>so{lhFMK2MokrhHAONIstHLUmFcVQFS#YRr*_ozQSq!cy>Z9>?(g2R z(Ei<1?-?>2Hsz#ZwpdkN>D4#fHG|z}(NFM?SaifStNF_E#)>6}SF+97<9%0M+qit+ zn)Bw*o#b{mjUOLuTs)zm|Gv7Zk^Y0b-aP(Cr zcHm!#&+AZ8w%<{sCW3ZJ31!8ic6VpO!BhVVCWG)Scuq*ObCDx%`spBC7vj)jvf}~O z7UDwfwg3D9A(~>mTjg#ULu`h-CSLG-cmfvwfJAI_Wf`Y4Ekgw7_CfQU^?i>TG>ZYj zv`q*~H)z4Lo70>W>yf`0GSGQQnU>o=uH2xOG>mi&8u8$BSK=TJ?xerZb)7f zdqVWj?FU8w7utj#Ye-(?%Ov{$UxWQWCc`c-<;%olRs6Af^o6l1@SXWN_R$(C&M^&E z6so@8@3Kaqn54ATU^*=jpM#lvp^}leWr3C?TvT>3BlI zhl;G8@nVf5ln|{Vd5&1@8&(nUI@meG&^Zz3TNmzT1lpFv!)kJe+qyZbb1ITUwybQ;UtWIOukYz_hW7uIY4`aiw6_M7<=N*)%$E0NpI_f2f1dr-{gKA{ zGW+|Uk)s;i*S~Sk5}(mHYSn$JI{nZGI~H7a@Gnoymyw+|6^7_G{ zUvy$Zc^B*GI@5Kw3yyHCt82m7mLNA(50Y2({P``jMsyYz#2oq-xIlZ-F3{W$8h4+2 zT4H&phr!d62-oKXcWc9q#t*r5ah^Mq= z;L2)l6Z&;W?wI)vH7gc&1~g0F*m<=n*Du9|-4a{d71*dZbcq|SX1eo z*3v}_mdt8c_0Sb5->^m9-HQi3oBJkTIX)u%!iu}rwuFUOm~ps!Gwsd5b#5fC1LJAV z`PoaUwZezB;^#iN%U!_vbHQU9X&%qu?WJ|nv!XrA1=o2!H_x1NL3_@1ZX~WF`ai_> zqCL2dJg~bj6cxRL^S{yGAb(DF-qbHR{~*VBudldx&>o!I0^dknNA!1$jU9HqhXXm^ zaJ?&t>+pW{Y!XboIn&`+duOb8=xVV>jR1d18cMOQ)r#*XXRtPEJqkSP^HEJ|?1Jy6 z!q4XCbT4L#_OMMCy^ligzaQ0PF1nz-=-;32AO3RBuominH2S}7?5J<-pZnKQ|5!0! zPW3+;{jVB3>f-(pGb~B=*W&s&GA_#@UU^4fOX}+JJypm*ey(dz_Y0r){pohkpq=P7 zeLv`4Q-83dADRAsxZW2Vo4?_De?#|LT<;|5bh~)1u6I6oWdW|>1LPmj zBWO4ulM>`Ay=n>eR2!d{9C~RMdf~@U;@Quq>p?q=)nkMHhDKaZyV;@BUvR&>@qE5b zdo%Qa_D9&U)PAo(VlDak85z_Zp&1dUocE3WnH^DCE|+bpQEyGynvf=0aoE^78#`rV z2W&@eGC$a3Q*1Vs=aiK@tY;B_*s6zaO;XJUNVZs)xj9N+W#pW#Ao*hvvs?(b-(K$buUi>crSb9@L}9QCiw>bo!?jZMxIJ84>;iOXy?h- zjb5);4tO01>>D*jVLP1&cZ7Sw$HS+>`mo29;myd)&B+eQA-*eZ&+xhsU_K+mm*w?l z`IK!L3^B(tLcw5;cWNL*3*@<`=E@#VcEGOh)ZA%WSJjZ9FxO5*ov0+U_3kxDww~bc z$oxXDBO@dKQX+?UWXfP2#GJOYxEol(5qT=LS`Wsfu13yco<9lCPK``d_mX5wChLG@~<$mGh;|jlad#XL(3$Cqb zy2vM1#56jL_S8T8ef}%8=I|IuYoZs&G!p%D{~*!-WM&@L{}n|S`{;`PITsYy|2X^o zuOA+%5c+8|lC8;P&L;jt@ zFPY-8h;wuzl@xJ7nU%r;gk4sd@*E!a8Sy-LG4{9gXDBw)$=vuQv6=Y2dXUy< zah_@92SnFK{>CpBR$T0FEFFSh(z~?RrSq_<6we8ApYJ2k*)ZSDneg$Ps2eeOpQf_Y zxE{2V%FDB|XkDT4^AzG?meHDpC>U6S9+tb&4l?CRJl>N7=lkj^G6ubWu*NNgY`BKj zP{dip-k_a5CfXO{_MaU1OJAbMUYK6x(Efd_Tklf;SnGNJkT8qqWyT4tcGK|qQ-l7v z{GBpi^baY%XrF@msOs z$mSNMbP@an-!>!sclr+TXOQZj>o}-?$mrfh|9Do9n60Ub$65M21zqqG#3ppZr-(l3p8j-7i?3!>=3bFXW~2(~y^CN}HaSbZ|NEL^y;<77J&w zFH9_KYBKRCrbiIR^bCTS8cj~_V>C=B0^wK~LfQ^#8(!E)q%%lvWR#rH=ydAQ=TI;! zER|e8GE6;!^%Fl3_Qge-ain%n#VAm6{%FY?cV$<&$?J7gWyH$DPU9!KzL85O#r67| z6xCfky5OrN_*zKo5$vzmMb+;~0k#G2Wo)EOp`6w^&YPUFjf)l#$N{7JI-h zL;LoJhE|7qL%MRn>5zi0oD^bbLkux7 zDIl!VZli#(YiyY=n=R933uiKiBjS)VGaV7z0h?TIOWNeHjoGaBYfYI(Zv+{BotiS$ z?qw#gP3yg3_LCuYG{g>s*y<2l7-Go~D-W>{T5Vgtl2OG}O z(P4QWhQ%Tu3`HJCHUi|$A^##g6LO2?;w7$=!Fw-QnCYhQs?kz zhe$ApL6M3sVMlB>Bz56$J&FeI_ULpOjp1-vj@RV>neYAn{yrw(6SzBMEo`}JYNRU) zHz;#vVY2k59sl^gI&Q~3{1KbYe133$`_GLbjQbe$VFg&j@0V`GTJn%=Wu;QAkH`Df zon$XPJ30x!cpmxJJrwyDPL4bbHs@z$*yHasJc53jQ~kJ7^Z!BV8L2pF)?LT9`iQxO z-c}~H>$bshUXdRh48MiG5PF#VeTq23w1(^!eJCGe48li0fK}%t`$u0Odx-7f+!U{i z){sZV8Y061imxHc3Y_zc6<1}CyI@VAxRccXJ)(b^%OkS-6i{*HB+KX=tqSP1l-$^FCUu3884VJ z9Y2Zo#DxdjgX5z8RP2k03(JT8KFEc!mP|(eqEEQ)Q9ha+*kfWFjW-(Q8R`;MR!l}k z(eIRwNwNis$v6U->9E>WTNYdHv?vg-He1$Nc3O^EbWd1V%rYH+D}Mq_^%#6#lre5?3jfe zu&|vLwwl^cr}pLiG`ziJVQ+tV$p=L1#nd}CX4TZ%@lSfclX@48o70z){9ou?XJKDl z(l?r~voJJA+u@h-z?YXa-APTSQ`7(P>}VFF^U3Ed>>Ud`ZDCJZ*iriStLdxbo0QWx zzWSo-Ub;D*7KRoM3p+RLlc-uqw*jAwQ1=JYcQTpouJJUTXK3XaTzCeToePrBAV~hu z&rp-0&+``peuf&+U&CAC?{r25(RNsK3F})G*F5u-zI1#ol+N8?>!TA@;OacY`;cB) z{$!XQIjuD26M#uK>Yh?^X*}zblkb+;UUo0yT_O;ydQg>BRcj0# z3+WL@==q*YkDjAciz@Nx9W-M^HL0zODHy;Qz4hEn8g+pWT@Zfmhxs!&EoZ&5ojW9lF-irHq zl!$KLgg(WG4Iec~G)%*WMU}cL5BE!!O>aJS;&?SJwGb^TttYqajA)(d%d z&Y{*W6+#Av#viLpG?t%CUkE<1T4mw`NxGZ_bw8l8Gb%g6uQ7Qm!!f(c?#02+Rd$xD z#7D|i7N&!zRCb)c8IFsC_<(4?N@Y!S)i|=^bNc-7)?w-!O^?v!<7ztT&P3A3&#LT6 zs>0_RPN_UU4F91aHEh_VhRpwP7}ejDr|W|8HoSMy9Z%B)^%2jR1z|mJj6X46G4$e) zt3a3{_nrS&znrgoZyR5ewqs2SsGqK2_OARQD~F-IV@}sP@r^Ur*N_q6V9&i@^q_^a=2BG+3VQA)k6b{3#9H zmuO-fqZT*OMb)YdsYOI<^o6hYA1eDzy7AL0Lu1T~ofpocN)>ULS!x1bM?qYHY4u3lWt;$c%1Ezzt2m+>*R zLw7Re=e%%Krbl)PhcB1>Z#2FjZBlB63^p6;YD*PG@g7 z`)*F!Pe*U&$zOU@_z&=0nH|W{a|Bs>K39!AOV1jlNcQuSbD(@=@&747vi#lrv9l{H zXOGQKLC(pSy18K=2rI6Pp z2V|Zvvq;a=(ShJ5+`l&IMJV|2h^i#%dpPh_$S?3 zd`NFg9}_*l;A6*q>I_9OXH9=qID{A>h`hrBC zD}4;jMy9{8-N#1ApZnNZA3Nh?r+hpc?%IpeJ`SG^7jO{X_R>kikBXn6x}ld5?<`$E z{L{l)a1Qes{$YS~**$u}-h2(_S9d%!H=m~f=S%%i(!{^do?A4!HM}d(Jfg5LKT@4- z&z{gfW5LAgnaNVCt#s7Hcy7XCS&sZG$O(=2PNn*NDIl*+x@!F6{qhLwM60a0cYowz z9#4p3pI8ejT3-ph7m}Y%r4MljMB0!6tE1HfRk&>2ZE~yoleRm1Gxr-!QjydkZIV9a z*n1)NDqX_v5L-uW zCQt)Z{hX>09OcmvI~Za+)16EYu^6?{hmi1&_maGyH-063#@needb>QtW`-C#tiJHP zABWga)9sIi*q!P2OR0Tx`Z`@9hEx1C#C}89iT7(Rd#L_!Q7 z(BU>h#w0^p1Ub{`l+1|dEge4U#Cm_h0KQ=ST{=#Ojl*H%bZDfe`?~0psl$BeR(^&} z+h;JN{tV+5=}0;I#wv|V;anw}t2abNiaBlrjDalI6;B$U4A+fw&9&t>OdDA;X?$aL zZFA;)TVBI7-BWp`d5sOFJyR<2M^k-C&lIp2S@K{UC)JNZiY!VvS#gKyVUv7^@-R{@ zoAzFzXD9Wq=|4n3BoaJ4qp{uELG4jZ*`Tqd8k+&jMmRSlZ3Xex547KFf76s#=@7_` zFSk{%xivX;{85eFt+DmmZcSdMF{4(f)oX}}xi<+b!h590q1scBI@8)J8Ijl7VpI#& zdUd1v2URtx295nqW53tf2O9ga#-5{|_VCWwGL6mB*aVH$X{=CVpA2h*1~WC*LRAGC z^JvVVvDg2mFARQ;YlFKYK_Y(u*1e2uyJMe_!a)9H6p?J_#O zL1Tx+hd1+Ia zSo?$guJel#r)#0Fg!Uc?=ry(+ANW08_p2IvR%6FBcBjU6YHXdx7HMp{#>Q!^N@G$) zV-AhU8v9aXA8YI#jh)umlNvj!vD-DaU1MuCb~(@M#l~u^T4TBC_Fri1oW_2tv6nRV zbovCn!`e4f`k*hfa{RvG+9g3SHAf8e^#}=)>m}C;x+ci(%7YC}BFE8~^<$ z#O$H}4>nA9#c{BHu=e6}t`*`D3WwOe#t$AwKYRxQE`}d2+WicZE}d4qijs?al&{aI z{db;M&T~lT7*A&!XVRy-m*i1;1LjrenQiiZSyo=4EDJI-Kh+EFMYIcVB9W^6Q~mX4 z4E+PTmvUGvum2o!?tHC#Qr@Q@gU_2_kKB_qQ70B2GMSm4>qSJ$qnI*$9}z8v-!R@t zl3v?kH;2s~<{tAZGdFoRm`ywM7Q?N22xBczOJ+S}-9P^f%3XRPlE^#0hUZHcSE1yk z0*w9CaPtd>ulvvD9L!~7a}Va~p3GsZ^ZM@$e+NG)S@a*&?Lrm}oyzoq$B+%obwc() z?%e-heO|As=J7;(lu+^iV}4%9S79otzg*o88-zk0HIL4?h|b8cQ#&~@v(Ij4EOkb- zZ$tYZQ~S8opK8BwSo_InKX`?rJ%W$%{!66aUf3QU0=$2NVOakIe?#7h9LSAfyO$%b z#wNahEx!N2vcBqSr$~Iozd!naRgw(LsQ*#yV5>x$0sE9 zGu}U5!lHk-Xg!Lz9@{r!L`}`${Y7hdY{=i&k8$QRE{E5%8DQ9fq+OAjgUj;SPIu5i z@fV>3fb4!KZy;=b^jx2SKDf<}gUWee<**Z$nC>HWJI3FfWFeMj4K_J&nV@KS@R!i3 z_*D@h&klbHY%g?5%CH?llgVZmx)XgNoGNtsOKk>(WjnSxNXUms&L3r;ulo|&YSitA zzk;g(P7lK)==pT-pr9ga+a1Zp`^;>PdAWI$S@D^QOfq9#POEdi#1oM~1j(;TZ*}SQ zR%^uRXDWHpk8y{B3@D34fzZ&}3gG zbL*M8W<`x`E@Nupl0sR{fz&Js!2<>RY<2*)lqe- z>P(fci9qS7>cJmMRaHa&+$ML2yT`5A-3hns=2^7dJd2i_TV=YH9rLg#nsxub?noudx;K?2YvqG0YeaIaeEeC9=QI_AmHY7NxzAndxp6-# zJ)f0G1o;r=(eE&i9MYeXlWx+p+4?K=8}&*(Yh@dmqB^)Qn$D{$)F~SsH#+1E_C3fU zh_|pon{Ts2ooaV*!|w%1`ofsuQv-X}@Tx&>Lz*}4w=oaf;zI_u-mn`2ScA_{WKawS z+fD|-Xgiz7lr8KHCO0z{$ANw91*T&>7(aSq%(>XM&L-byd&2etETxi2zmc#p+2%0V z70qU2m~~Aepe6Vx&+$q&P5``7#jN0RxiuHg3_L5ox55+4iz-Nkkzara=RC(RtJ3}M z<(ExXtQeW@%!L2^^KUDiuQKIaVotOuub?CJ!qQFx8OT&W}RmEk(rHpmdy0FE@TX{VJ1>-mB8d!r-L&k2&}7BZ|;E*y~DE2 za@_K{MR(kCmU}Hoh913B=|PW!Lv*cJ!??_mD@x+RRR>>=Mk0c6V(Qnid|gBKS3g$v zgumJz)-BBb`nhcNhQnVyclfZp>)w0$cOe^W*!tx>HtosSJX_`b*!;gW+dS4>>Dh{l$!yy7S2B zy-^;?rIYEbQrsaG3D{GqMdJ$AW29F@8@cz|@9$}w`M@vtJQ#g0r-Bz|-T$jSN6Vfo zdGESK_pYq3p1Wb(_~-HG3jDc#?1T@;Z2sO=H@tc4q-UQUvx)z;f6{M%du!E_gNtTw zn_5t{_^w5Bwsj!}G2WFQ;n^IdXTfb<8GOFO&Rn))ikM`CO9E=fFSEn@BW|b>%IH$zr?3J?b8{_zpU;qI zDP{`;j)=wUwOW+;;7o;I93QMOHG!KG-ntmR+*g{OOi7`|VK9_j`eejZ=KG~oyIe;k z@=fh1GW*aHkg`+ZK7{+AMjMd7F8^Ss5Q;6T@tqVZT8hVVG!;_rnerW4LucmX>m$&tK%1 zch{oTQ?bNstQc6hOLmKPKhnlm$^D4}`K6T;uA5)G_D8UeJ2+|o+c#W)?uFaSkr-S#e6IM0W2crc zyMJZvg#91hK70SV`T6G#qe3ZIec;LEk1)e?m-BTH{6=vO&$awU%CZn41!VT*g}J`a zp4Lff|Nq(+QJOIDC*5hi0sEKh#qNkL##n(}klYGW(`#DWi$(PKbgcGV*o+^h_S;kK zouWN!ge~Ha8pmAZ8%aKP)P85G{oQB}TSr*3!q!piD=oJb(uN9Wz<0o2NM23t#eQmf zwXk%=Zi>C{$P{}i9z_9duYf(3oAzF}Q{OY#3iK^@UwO3qx;kDFA@9rd?hEy@_|EYA zt3V!#ZzVVEoxt{1vn`|9D$h30evh)szs78fd+No$M6+v>BZmh7YslKV5hvp(6EG;6IsUt2>9Y`bi& z#Unezd2U~p#a`K|f|NEP+j@}01P>P94#)_|h?~YJjZ;2wQ)ATwS3HOtp@G5J zjFu^*kx?DaI4Mr95G6-Ko0P*1@$qRBuIj8rU~x|O54;UO z%Z1R@Mx;itk)s>3*}f=S;<(l!&vmVE$#b*VT;B@cW}h+#sU$Z^%96;n5qV~WwM5v? zFzXC246hC=W5P_;XX*3wSQcZl>_i^9$OsM3)!3RG!A2yZ+LeGKl6isba4ZlDv#xM^ zM8fMt!mo5DayBAIlG(9VU!*941aFZ@IFMJ|>Cvh?V^U3x;`g|7Iu&WqARq7Rh6bJl zQbf~8`KvV*N?7}wE`3Kx4SOgEx_DPWv;z+-?vKC~+=EY%o+B+nta~Uas%#Jm>$p=@ z6r3IVa>$pJY&cz}vWBNtrQek@`QN7BxUjlv+LaBp^T!vo?0I?X(nss`Ce1S9gyPux zSk439=8jqM$-VQd^IKPr54NtJT}~m^f|wf)Q4&zH3Etkw8Y#wCktWEod&d)m%Dp#e{fERS=I4@Ay^yvvrpto`hFAm>d(g zF`tcLn`G5nCu??>rrAB%CF!MhugB%}dMq%Ni(pHm*LyX`LJdyuOw;^JGOx|tm8oR9 zTwd<79<+NC@EYVcuHJy2+3^{>Co9vcc@=n=G+}ASiyF%_mh*bNp@oxrJ*S@WZVKyw zs16Ve?BNmtJ~c9N2Qf1s7l1$@3YRp6m8{hn4LVaWC)duddFlMqzS^pC*Qw&U#cv^~ zL$_yabE7kB^RCbIgwS=jjrBypk{GPK?(qc=P=@S+b>UUzxm4+o{Rh zwEddQm7enA%ypSNGZpOoJy3uIcL(LQf$ae~z;}jo@T!YNtE0`)b_o~@3W=`=px8cHkBI@Z$7Ya3^Yz_B)(4)uQ&d_Ww$F_P%@9)KOO->c07% zyBCMqv<-L49YBQhB|+TUJ&$Haqq@lzA8K1>WoJc&RM44S9($5A_-u#NiF^?z z?gUAm?g^wS=2Mf0f)PHD*li-y9|=i#5?I_!+(}4EK)s>hA&*DC$TrGNj5C2onVe5k zM&g_)R2gz9yRpn}Tk+t8+3|T-Y?(BD=e+X7jP)(7pt(A`;fg!vHm;o07Ti;`sy#ks zM6_!5#_@sbD|)7liu%jPt?O)F9v@rgjdxrsovO9kTg453lgkms+Mca4Jn>l&PBC ztG?;#Y2_0(9RBXpoy=NZTVd&c-><&8t?j`lo<8yPjQGfsNQNU?IiV^(xur4KYKs=d zvjPQ?fXz`l{_=6%KX~D}?>^Y7=gGa{enjIa<-=dXBMg=HWCF(kh6|PD#2>iE$GpNX^P3R?W(FG3{Ii1b2V@6T>Qk_#<&u z@@cj#Lua1DXO~i5`AwXU@}#7=ZA0uo?UT>tHneXjErg6gDjoLja8M2nh&Qo{#Sw9f zWnScePcya(H+z^1zP$O}VNUn=sRkAMbVYtt!LEJC@BXK_FJ;5s?Xx^(#kponUO~QQ zEvcz>5Bl7H^^=s0Q#YR0yeN3l!Q`l=h3gA<7b?s1nZFQ1OYEcUtx7>=!MK8H1&az` zahfH|R$tIp;zPPWza|$46l9s~EM17SlTeME2qs!NCN0PhQ(9V86b!dgzL?B#X{#d; zoLXE|B)K~+?ig5ZTKbb(*D87D7EHd+s$l9UUY>9+!cxXW6au}Rb_1`ib4m(N;|j-c zJ_{L_N#=Q zuI;>XOzPbTzRDiPyU{LfN{%yHycW4P%&p4IwdB#A7L8n;p(zLf5 zEiM@7;#aZsa^VbCdc>GetQL^URJbZW>oAnKBLqbG4s1(}M#r#WvqvSFIH(537G`Fh z%MCJ!$PFSK6^v|Lui9*S^^v|ub?UzS){tvVVW1(PO!@j-u1dc4LN@$=<3s)b93KjD zx`#25c3=+qq%Zn5L%8qcX)`Z}`liaoWP{7&g0=){uNl&>GTG-TLLwv&Q(!XHY-Z(V zCQD|AIbv4K<}64=d>)V2&DgLgX%I$xJ>JPa$pwjtCxS^ij+?t=`8cBjBR6NY^0#D` zBa81lq%4+|6$pEpJTNqXBQSI6>{(u)&z+T}?}X1KU$eLX1j`n3qBCrv$@fU@ z0(O;_G4lvoBCZR!z9W_a>>*P;NT_?d8D0V0DgrMAdNXE56&96CGOXT6c`UQo=(Xh) z6*_9Cj4XD(-FIDo){m6;7foC=+HaJ%tDtGIsXKbU+9&s)#{$d--tXU_8-qDj%371A zT>&=3zsoP2r9xQM*Jf?clAE)pXD!ZBayiq46vE!RoveJ$B@aR(k)OvBg-wMWh1&}E z7pi%_8sB)|wZ2_G^$JJ>JvAQL3(QyZ^VIx;Le^E-uI9T`*pI6D`AM=MwyR+Xx>vzi z?~JNHhwyu_uuu3LF7DFHy0Y5c9+w+J0k_BF<~xdWZWe}0rpc|i9crQCu@vOzdp&k{ zRv>}}F70^6Z}{LXhuoqY?+Q%svOLr3S~= zJ!(otMywC?1@9D`2y!Q|6RI(~3p zfUJm|o#Yo(A^k933vhX64_Pzlj$0hcdOH)74UyG&@_X#z~W=PWF{GXj5HfMbM!}CreRI%^DI&#YK71hxl1H zW-pw?yFpn&(j3?ckP-;IKmEf5-}n4?#7(ZN5u~;W?@wJE`OTEqYOh{0du(3KoNF3= zV|H!5sw?qN$=syVq&$@(g0%&%;yDX~uU%bV2>y)#%?KdEv=dznYS!x&DC zopt57|AyfuTCZrG|H#hynX0LH?5x@emyO9c8)Z;E;(CzxP#hk_`_l6`Ja|s?pqJf( zM^-JpkX*URdy`iV^Hs4eLR?O`fvWaGY{!f)metykzb$_tU&+sR1re01T6pWb;H}Sp zU0-A%8zht9tDjLDj|W=9!9-9F${Cugx;iU=YK1)z#KJkuV;{N@1SOlu+DKi<0i>yKZaIO#{f`P)|6XLlkTgJ#&Lw-|LhQu3Oah-DLk?(7%I zoV{_jG{|nQU@Iy%SIAeCY%G!Yc-ggHHo-g7yVR?AIL%&!8$aH|MmZ)q$--S$TPN%n7y+phX$x6ST`uEvjJ-yQ>?{l)n&UMb6P9-I8fLi5TayD-DoFJQ* zSecM(vd3r3wdM(CIZMrhQ%`lEI$k}mTGv*<3Ub;?N~BHNO^BK*Sy%Hg9Ce-+q45h= zlvH>WL%yTaP_C`kdNoDU43J1-8FhS?z#Y!ucbK-uZ^7#bfq}3cPDv7Q=Wq}C9n7q5 z)D7DQ>qugL$k2uO`Q0hCys7zbLCZq zuPhy1JAd;{2b#;0Wx4-kUsyhQHJ8~mV=c(US`fnSWlOSaFFex9cb3cXAj=C}8`u?4 z;%0VT@y*4uzj#G4)S4{I*AmT-mcujJwlK;PQ5KFiMG;CU(~aDVRf!gri)_^_N5NFh zj`2sbm!X|8*T&;!k*bgG42Bck6LnA+%V#SJ1%_;FO>A-j31A@MN@xY2& zgE6@6sSmD|3;nm5qQ&tH7D|l2`Taf1Z=IFzaZKyFx3@dXXuRIlxcca2vFU{eUfEE+ zgKvLzqaVI(_n)6ywrl#qcehU6Q(M@5@am$r=)U_gmf9hsUxs@Tm3ot7vkT#u;PpC@ z1=i+-=}vnj3YlmWztOYm02e%gpsK4P?28ENh%hO_BF>1E)fsf#q^TBrYT7~bD^@xv zRbEX^y;xxsve4M2Q;(K8j2cK#XhSBJt{h}wAF}r*H5m3QY|603N^bV|zq9Lt1x~Nq zsXRBdI-Q?(!BW=Xlpn{d0N7}G;Q|P9Uyp*wAOqUaujMi`pbt#0U*tz5k zROq4Ekxf3IugIquW8RqTYDs9vHMvRa&`xP*w6mJ3Y554QjKD5AF;`Ab48JjZ4jaf} zIY~Q`Z{^66zBAy?a5!R8>SmESfVf>qB1$ALuQ2Aw$;taCw+bS>)a>GExA-*U)XWX> zua5W&4Io`w%%91QZn7;}(YCs)A=mD)yr{WCqq=(^S=zb#vRb3@8b~+qXschcx~^yY z!DC&s?kTUlZT7OCJbT}oWXbFs=SAC#l9>dw@ity82rR#!JN(t>@9 ztu?DOCMBYsn!|0k1p-B#lHZ?R{CSv4NaJ}-IcRUw3zUc-kcvt<0yo-jA93l8PU&K8 zDcw4M_44@&p-bw|p_c&!@CYME5>XGt@%&ME2Hy(wO2$)B-W_-4sMzs%-YhPJ0g*1TQg-F0{?5)q^`5P=jQqG6;*e?-8)k0 zU(W8Eq<)&4#@O*{3upn-m3>jOLI(G%++cv?2um1)n4*wN7mzjBjKJ3H>2m z$SeB-%pddw11K{ujw7gI7b+$PJ+OfAAap2>Pmz6yr-qU27SAp}5zl`H4veDr7FLE#zIlXjK|q6Jai zLo)jmMR#C_gN)`}ZQ`I9&Eo%zziRkT9*mb@<9ETflE-R}%Etb}AIX<3a|E+vK1+@< z*A~ssca$f~3*0lSAPt=hkV|A3pqijy@rd+u?m&J3LG1r?b7w53W5td#}vQw)` zFoh)iH(^yV`n z$6<#Ch|il`%$r>N=5lr8QXW)P=k|y2#M<3HY(9PX^||>9B)OT}U}9aScAXx!p?du4 zJ9TW8Zkuku?zrxh?hInjBcZY`&mGd4bPiuM7e<(Pa8gccl8GgrrWNg~giwG)0;yRQ zE7Zf9^cz-s*Z`%ru`%#4xBDBKq{Ifa48{s`F+0DZ(V(RSW+nabb&wXrR2fqhySdap zH8np|(k_~wRq`L^@0i!%y#Cp(-kG<&wI4Hb)kDjxjK=lPY&Mu~y7jYfZ(!lJP4nd4 z=dauRovUkD|4McjOU&-=inJEWi_hN@O$OHV$hsd97Xz30hpvOP!`E!Gu&o)lWXLNs z*p}vf&GL$7wz6tV)xIjF*gV24=Xx;@4Aq|MWz`$1_f)IqN`IwnF7=nn3!SU6adl3x z*VT<~X^Tymknpy2*csMAyS;66%bCR4gq+~wzXa+sjbl5*#qK(1XKg0uVSJtAt6YNi zw?apONtNK?c}d$tlLSi_(OKLiiueUJ1mDeb%8K~(Q0@#-TTCNTVCbOCfE1skrSM&W z{gAR0dex%Qg<~#ntIWulv!HUu<|#3|Z|-&X&l}q_Hs7f8kDGO6^OVigOZ;^+Hr%_= za`R(Rli3w-PAt16yW&yy!%&T{qHAPM^`cwn&X}<7NMxUV=GH@99g};1aD8Lm_(h`| z7mbfKEI+uQxZ7^Tx!!clhhH&i|0QC2d13EkB*IbAZxcX1y+F z%7U63P!H<;sXgiap)~#vOlXR^1hv1+78ezywEpx7vMHmzBeyF^#c-X@dhr=<=TME0 zd{}*Pj96h?!0jUF9BewfO0;J<1v{NrD9*qw5z_su{~)`Cl$E)>Hq{<>6zr4M_JlWW zEu2!@@JCSw%ZJClDPWSdf=~gs#c{NDR3E99_gAy(tm>>- zxtT9I3&Js1I2^-oObR=~a(Orz?hN;a4}^~*&>ffQ=T6{M>U6RNDFVXbI#vdKX@Yen z7L-@G$}1`y<%vpGSy^5oRrXZH!hy1)9y2)SIX#O&!K<%Q66MZ7k28rC%k4~so~9iK z-s#vAui=|%dbQ&<4;2)u(s{(OnIlq(RROdcQnOmT=fmND65N+=}8=9ujn#`9L)?}}V zp-`@Keg=Pl-7rPY$S9c~E=`m+l`5x7*}~G*rM*zylvV|E=W>m7%5NF_18(>j?&%1! zgux#P`NU@vb(hjma=O$X5 z3y{J(&KEPG+!1?mzRpYCV)rz-tHe{{`W!<_yF-f`bg}Ydw|{h?rDoBsPycNFw$pp2 zW%k)e_3W5&%@1y0?3Xu$&hKXowu_4w99%y?Z-4p5`(Aa;Kk~tr>&~6J@5+%Q7hX5E zE-`C-(S&VJU3srF=3MZ=HOY#u<#lt~wqMpD<}$@0CEMXznwuBfnRB}=3)xvgn~1y) zkGwM6F6&zt+hq;#yeY!JgD3ou2ksd7!sx=h&4)mwBH4AL#P)eWzq#kdnq?RlN|D zBE-QTlG!+3emxzpjtx2=1)mq+j+Rr-t?5v;p z{oaC}d?#F;y^2HYF{S1=w;2W>#9IqHW^U@lhXALCn4*xD4V&Q1>v)U5^2MmYAHrgi z#MeaC@jsf!ISLejsR_)@Uv&_dDLEv|vB}Us=R`7i{NWtkw*IP(^*ys^_FOs6SEglK za?RQGBN|(0RzIp#T*Jm++qb1@(t)>bIREgkpy^u^pK|rM{>OCx4{dJ(A4heikJhQ` zeOE8)rCPmc>z3M5%UUhTmdds)wJaOS!V6v`HO4#Mu?^UOZSaD(h_IL~V23Rs3npO9 zmc$-HfRJHu2mvPKKal(x0+SFkjE9*3YQOK^>Xu|%WHRsfp44mgt*TqM?m73Y-}!y! zmLFvP^W}YWO0?R$G?KUF%18h5ZORP&p!V_um#uhUee<2Flv-FkWt0pNNbpYHUI z0C6*=iQfShI>M-sN#^H`K4-5;&}MYW^!rga#sTij!b+IBgcmh{y+(?{X)rwMCAmSD zOg9Xjz2PL~oxDo4Ty^p)^5lj&^CN1{j`G{$p`oj^JBNNC&c1kDIo7PM6yq8dURBIy zhwKoofr5Y-$u4XtT**N6@>6N;de|@6!c<(Q;(E+R)Vo$od2Z@;_j`QIVdG#<8&8oT zD(LlUI)?~voamr#c<5M|iZ2Koug4h;4j9&3AGe-jwlJ1%#p2#a;)XG7jFWM}%-P7? z4p4M1UZ6$>E|0JbVZCC@5Rd#-SC;7TD#X;x5qH11TwO+<3=N%WE9_sjvF?SR=rt>U zx=N#4b8XF8Q&Bz=zxSiG`cgij9q5mrup(EY%*ova^EhbasGt4$RkzGrEf*%lT z8Q{2*rsIJE|1es$8f&S2UUvp-&v9PoX(~+ZX2f_8VU2lNUyI|M5EyEK0 zh{w+tv$a5I!S}yl>>NTsxI;(THd~}s`CIcv(?WNWT7kSoVRzC!h`Lwj z+**{W(gaX{3|_Rneuv;jT!{nEK?gZl^&3ZE;FN~CHK;HK#ZSl^aPTCXT=|<(+!*Gm zQIxI@4j0LCYcs6mSbs+deT2PIk92=%C_0DQauh!F@b5+#De`eLsS9{!cFqmwI_Bgb zs6> z*^pYtzr#lHjuOQNj2ldMlE#gM9hS2L*Sm`?f-bfQy4WJP7>l5bErPD~tfGhAjyfZp zPvkk_7joj^L?FkxJTXsB``7z-_>l!^M0ufJU6p1U)5F3aakZ=&Vc=l1XFZmjb%YACfNFWZ!XWkg?va^&g zn~bS5$v6<#I3#I|#(cJdA*p;k(N|K#gk3yYOfe=gKEvmv*>Y^%@t!Zw_B^@vmYTl# zmsLI@IWx0=o$0z}+@-U6%8w4cLU(0Wh!bk|KlQ;O%3x+s&fK!+|2(mer7C6b?{ID< zY*wg{rGF0hGeOrV4wyblY;`tqsco}uH&WjiBX<;*qzWz77ICp#EEs2)hdk z!o|^WKvPg|;t>)ilTMNnT6bBwt2|FgxXP)wyg%V|C!9_Po{5CEpwLmMI4V+wg#``{ zB4WZ6vLmvfpaRRxr%EFW3`imuOR>5k6AJ-`>urKZ+7#6lBb&8MNV}8FgQ!8lsq#cZ zQ&?1_)#S2saf8@RVoqzmkT4S&`&T!siwfdhu>oNi2!LXh%y3V0^qIAh;GTQ2vfxK= zBg|>p+}M#fwo>CSu>tjWHsApQhN=To8j=E~8iahZDu7IAV!^UJK@hSO{udWs78yGI zC-FW{N7b)hxHUO-$|a50oV=!VQe)$^xs7D_r_9}#u8z{uUuS;p8V`5p4X4E0?DnB< zN$*@YK=))`zhYaPPrP#I4pU%U`{F4yK6Cly%e`L>iBIV*M$OQbVEZn}!KYCFXd-g9 z?%Q2YTl45zai1uz&|IUrStHFUqB%{Qo5c3&xz)?6rMfE8COiqT#$CBkW>x=0w_AIQ zd{h=0M6M{y6BeF!P#7%F(NePLmeQl8Vri*v(d*jxm{TKIl+r2=Xf6ueAKDssy4yp? zJnwl#kH=irT{2K2@|O|?lwVR387R}32LuI+4py2G4Te?5K>8)SVHSho4`9dqUH zdYk5MopafN%WLj`_=;c$%)t$XlLscvz9HQnc%+Bqw-!IJWpZ8Ljv3Vh(<^5ke#>36 zaK|O>O_z7XN;}tdti1kqxn+4*MOS^e7*#SRJ^F*{`PX-KU%z02*_fQMV#>;gHzCOx zYc^zs=Rub);R_`^*SyRu&Zm_mPB$(vipDA^5k>}VHO)20C4xo{*mIsR-@DJ z6m^DtKt3Ug!|8ekVQglD5pius3v)5C?-7$31p>_`)QB>7n}~%riR(=}01;>Mhz9C4 zutrTInU_d&M<(Jl=NzY~b$Xm7PHDPffk8AG0)}#fP=iDlt+7>p=@l7=x=FOKsi{o-}!kpx|Hq zOXke?o-()%Cev3Fv^&3AQnNT#8P`1a#dhhtvH7JHi7#QJ$;nY%UV$X@=Q~a$i=b-D=R7eJD7^n3BU` zc9@l5go#$Clvc)>P;}8Oi(Zru^&vK_pGAg25MaS$Y&88=ufb$@decdiniJt<&vQ7< zCfti`zG61VILS1FPZ}Z9^3s?i3@2xFf&C=QjxOLhe9Di-OsHo}#_=o8IIcSd$^RlQ z@i6BS+pvfb(~p6bwiDpIPb=*Q@`=kq?M&rubklxTEbUqpOVdu%GCW^Ri8M``BT41H zRlcph{XUI8p3{ggPb_xI!gBClY#Ht2a zReK#7Aa(g2R$950iSb_ONn`1#RHS(%WBEFbbXMlfb6=-?vczC@o?VomJ&aR;-_Of= z&dy8ZJUex?Hn+caIIkGLZ-AWt2DfuS+sF6w@@{b-xE_G};1wQyG9VDb89!BQ(6F2i zyZ{Cw-;>X%IOHX_*Gr&$lyU8MIo(L{bCQRJ;&?n>aE(tD;|>?OTn4WLUXfFZ%i>CT zy?!ta;O8NX&gEgpKBZXlact}^Sq8K)BEf(JU9^U;km}I0=tNZb*<6Chq*v@gHJ z-`GEGzVw5yK9wL#Lp9%uJY!2X&$x~x<-X0)-BA&l#%g}?_-W(kjF)zM2}#DADt1+< zxySnpS)wtLj(Iw=nt8lZ9LYScyEyY$3DV=iQ^C_gX(R`^n`a?w94xV+A(_oU_VWzn zx=@{(fm~;<`(I`t4_i-J4)Pe*Q^*)JJCqVN{O9vpf7=7=>=2K`{l>XmJ~j7CbCQwyxlV zpdFC&^7Ad>kP{N{NCtdPyj2sd`7l5wi6C9rXpCH}=kTG~Fv3tV{Z#Rl+w8fGFBjCV zLIv_rRUjY#chTi*H#hwJWvzDAPgk*#s}~-S*SWnV-^E%t*qm@M6)>{~2X? z7tex|a@j}U$LGsi$_l+Hivq!Ja~4`hBN#56Bs~T~!t;6q*$qiUE9&8CFh5lEn+t^) z%n#BbK?0%OdO*z&m~#S4b#8tzng8NB0vf|uV7$I|lBf$#whaUL8GD*=xrUqPV3mM8 zbMiC#1oMNvWX|L!2(w7AvB<2@$1xUmYB`^F?jDnN>=%y9667!#G!`4nZRodH-4=$$s3!if z%Uhl&W{p9+4Kz<$E{S8=eYP_th)fGr3z5_fBaxW17M+2;NC$fnGa_}9MCX}?BxV{C z2BfS+n1cyohI$@Tt=fanHwUW}%B=`6290GI5^u`Xg~a_=QSq>4=%sgQZr1qwG7Om) zTYcGj3tN~jgK>a_F9d~8l?P!BV?v$CFPx7^b}+Q4UcOMscjO<dJ}>W(gY!}uv#+xjyx8*>%eo^lolLqN+Vc! z5T)*bO(+laoyGm&TrY|6>k%)}u z&U|@nw5^StC;xM_xeZ!LMb6#^BG1pdDY7=kXE3fdWboduEZM7}Rqn0GyEf7s!&1X$ zgQQy+b|#&JPN~~@(kZH+Pdd*y&pCBYXF%}TfrXT#Z!3t&Y8?dWGE1YyH*kt!=FofK zC4$W=*#|5mWwx2x%$+`C4~H7{hASWYs_9$QI6Q$K=a+kBb`Cu;_NAf^ZpgG<@Q)^h zx0S<(^5`Z9&2g|?b7@Y1ri%+iak_KAQ=I6eIt$gAiCGZ~3w@yA zm?jphkQQk){zdQlsM*)$Tj-N~%&g#JW`(2=?8_HM4s+o^cM%;cdJnOqMaJNOs|tY0 zJunlV_G|rqm#bBRqhUZ2C1z-V^>F|WU3kQpSwZ!W@;Srk4S6)!h@ZG|0hEW_9MwmP z^cqHPOax<-WhBkU;|5d@4>HXaSO!I^*>95NzTZuJG```^lUw%u`qr7NUe3J#^p#Hy zE|niEf9dWuKfZ0TbLbngc!em!jjH)#_carHcP^-!^R0LHJfC^#gYA`l2m6|KADpt| z`D^ZRm$>>5uWV%gF4Q>vGsZRy)SYLP>zeG-?P8N-xY$5>b>cJ}7Jhvw(-iGeM>3_xjvkcx~|XGGW8(8GvUo-|I{a zSX~i-e?T$Ik{uKTs0bK8=2je80EX38j=2Mm4O3J3;Bn&&kMm<8Fk&{M5zCf5J$wO}Sv;%&+Fwy?Dr>%b3{HFNTp z=8JmsaMVFCl7Kr9&oh;JJ`-^zAfA8c1{`I09CSx;$GPZVRjic&@V$FTr?{ zEww=ef4ftHm23mR$PpX^CRkK}sGKF(T-+*xjCh6#q!eCDIw#0NI%8}L#z)u-RMTTl zT7z{FAr!@B1T#t$Osr5%Es8D_%PTJLt_4&_LFn5;gD}l*rJ46Qe>Hm}gJb ziiW9^5ob2KI~$6n&Y_r^x5@4OjH|^xBfPzJ@957ff~; zjD@XpZO^@8H29YsdSb=p_pEM2HO|!!{QmCd<^7k93t#r|z@@vdoZVLM3w1BwDQypR z?_F7tDwuUuXJN_geM`p8EKDs|Lc4zP)6$-CyMBCiL(_Gq_D;C#lJ9?b=e(df&t}nm z8+IjILB+Jy_pY9hPL*+eH-@$ETFGiWxP*?Qm5*17-IXL%Qe|ahLwTsKj%gHKz%vlHDGaF~_T~(9gCLoi>LY;hW-l+r&XZq)0Gn`YH6xN%MySn;Zx9$T4+ zDE(iZ)97{5lGQ!^H})@{Jj2tvv7e1EAwB)i(u;^IY!L2MiZ+kiJx<*0*zFMOtJ%56z6NTz&$`TA=VW3#BwqmG_6Kpz!}8wW|%>g@y6_vP2pGz z>?bTchE3;T-3y_E*)T$IM-V*V9;Z&2+d9sExb!zaT5@Q9S?#jh)4kj0j@z)YbzoZ3 zep~6g&s=-oqN3(?4=!JNt>W>F?~z`}D{hQeZMi-%{fdV6rCm)yUs<)1yrjz&n;4(4 z<@%aSm)Fi*cWG--P9|$Yfn<>fLx;78#qW*Nz6O-`DZ&GaWs|Z?5zFJWEJ`qkzOO8U zDe0QZn=8eI6<1b>^0;l|4vdrhr7HnP=AG_c;Fa1OvmGLGK(-q98xaVl9beSRB;;22 zF$l}AyLF1Y$DMXdf}7m#ww88ee_-6}XE|FLd`Y=~RzxVzC4pd$ZWu=GVIdRDp_7eS z#ySJPOJ6pS38+$h-gzCemVU)`wH`!2uf$h_+G zoM}F(X2G_Ok{;AozoP0ti6WuJH$QfHdfSY+)tuk7cxL;$OPk?n3{4tvw_f$_#i6N% z*FUwX!4s=5EG=4n&lQz<=501t=8ep+zWMy!Z|`f}-M(=!)zn;_?y4xCe_(OZ)bQjL zoiXrQ)Gyb>IIl%C3T%IfAkh92`4nYzRJrw@!tWJ6S18?M{GRbSqjZn%d%EXz(h<~u zKk1Wh2^?siEgqMcJl2~1AT1+ZID$_JU^5xO@qGXSTqoOh5kBB~IcD+O{ zaKqkeWuXBxOa?d#3RfS=MKHN z$>h|!V{unJPbhQoMBXx|yA0kuJO|6DtV}CA95Nv;`;=mXHBv3ED9Lhyd-ED_lVKkM z2QyfG1sana2ykCsSXT&NUQC!Z?8~#q%gQv7XjH4orsy-HB$mgqeR=qaK|#QY$5zn9 z25z(*kxxeac&Zz(k@@oA3&it0e;!|J`PLB#Tn-h%{d-KJ;RK5%Xq0~sz#OW74^fTW zzlTI@?%xwH4@}6*?0ALVu_Qxx9$OksCMrUUjxP#TPwZ{^*{RHD6Xy8;^xSi%aADLs zaGd_tV#w&ld6v$p)OBV?U$ujNn<;fv_suwyd4B4=yrC2H1(=*B1H(C;`w#H79mrj4 z7WOE{Jzml_dKyJ6rynUD;9lch<2wA&&ccQcTOkTmx(X$U$;hYuQa1z1;=nGGG@TM- zUeb71kJ}wH9z%LfG}se!7<&W+FklU4xgU&)u~gU5z%3ip!~({~R4*LMpV4AOvca94 zz8bjWO^xu(W~g({=22G&+WzP(@^_6TO}!1txsyxFF4>R@PTg_Eq$Rz@?Gvl44|}FB zzv+@$H!d3AIDggn`Z-PE=JgM`x9%EOGAZHpRLyLt?x=9@v6s)7G_BnlnUHXIEN+oY zrwuetTiRBv)#%1eon1L&+dRa|g3fq-V${XevHHQe?^;%W7~i??NC*03u{Nxp@Y;&T z(e;F}hA`{~Bk0m2eEO_lMrr{((QKh$?^ebMZ^nlsx?@0GmRKDx@fO2T!{Y{tdC$c; z)}>a_!Bn}KHY)&btvcq3yToXQea~#x8OiFfq2#99=vZ&uVHCs0B%Fapujm47EeomY z@(6D5V+Wqd)yYqJ%sPYH8ksN# z5#$6+mJGoC)}!Y&41q;wk1vWgcg!D|!aoUhRW3TNX}Vy#er7ghiZEC22cMgd@$(DE z71vfLEy&-WFE;DD^jGR7_|XtZ@{!Vyj5v`68+8G*f$1x5LG1C;EevhJBKVLh;~!OZ zmYEhykyx_@u4@Q zr(etb>Ye@FA+!EQtts4d;BBgW^~;ykxul(gu?W%4NW36Gv7qADPd|hNg?*NJw$;JY48BR zg=;ndvm}Fyk^zzfhv<#LO($Go3EC6P0lBbh7#CG?=-xP>LiS zRQp3A!;s)MYg~48DG&j2L;xrpd7{avab_7kGco4XB>G@$gh6pkQzN(9G98J#nc2+) z3<3YkdZw~(jBPXNG;2TOajkGdV`B5|%zFp{4EB9!2AJ81#bLI7j%-E^q@DA|4-aas zHoGokx??{uLQL9&kG(<0**c?6Dwr0e0Zr4FFNtsFPgEx5f7Ky9?VmKM(K|E_>449b zFJCslpy%#7@eU?SsXU|=_uVWUQ5G9SpD2pp<$S{HBsYR&9C!kgEs1zMu#r|>HxS&9 zATCdfC_F-`9D(N>F%Lh&Gx0RB!8I98dc!P{13;T}S`T8a%;z%##tAU%FR*5YvdW6Ym!PQ|eAl?A>I1Ij|B_JCH<_e2~Xx7})sL4Q>9 zrOHSvvNmF|)Eoswj=grnD4eH9^j5Q;zNa%9H6tKT z+r{0ZA)YQR?3!Ao*N$s!b`PUF4PAv>4xia;$oCx2<-1FBVD$5!a! z?GK_oa)_}`Ey4jLVOnOMZY?k|CJC1cE2u{)+CPO>EvD6zubV8c>e$*L&R(=@k=VA7 zx|$a@i&O2F+QkO&QWlg~4{mV+;@7l#f>7|jat~znL>VRWDdC}nn-VT6fhSW0W`K5{ zXC6Q)-N>_*Lu$jJ2Cw&Y1Em|*Hymv^ z)o{8&Yi|fQhz$+e;*N96=>28Xz3la6?=6#rWwdPBs#)2j?uL5V=M`p8@0pNv0AV0( zbquuhj8mZ>lhrJ06t|JR@dQRS)(4h3EKxZR%}Ri_G&HOn!=nZAN4zw`oVcwd`;n=p zstu3JZXk1(-KL6fOzApbU&!YupIGhqWEg=cK<+r3BA6{ea$zFPDD8nIsSev7Smv;r z1x8nO0s+!s4$Rtk$D)n56lf85izWZ~S~1!-bAEkl!wmfB@7;LYqK$(ET8+W1>wR*A zHrjT{0)6B1c~d>EaAQwZ!-7DTH@a1-sP62 zx#zgWiQyUHMPX?dM0&Q4e;XW~86!7~yG6|&J8ia8k-f$~)h?A#9f_vmK(WZN%M4|{ zGA88ox@UOuAULYni5Nl>aqp_a~XuA)7lyw2yPD!29E?^59+${hJ2U=0111g zuF^7hX=xcAW#Q5!V$qaPVXBnj)|3XVQYlzQVZO^b3;9PL*=DttozEIMwmQTCj%Sn2 zuP=|6IZ8{*$CzZyC6#q725t<98#Om-#MQ3rTq2@*Pd^D9X!!K`J95zQZcicl_p}nj zT3)ED)=kkZ)@{_?sM8f{tF=?K8?`rTwS~dz;FRFT;Eh2ol)%%>PSCijCER)<$ z#WvcKw+G?SjFlUa{JKfp1p$J6v*DYYib>p5yQ@~LWt;9`n`(+{L^B|H4-`x0aq>7Z zFqw>#DKMUlSru&T9-Z-Q%jN8fWk7uIbx7OI-BT56_n)uHGNpB5xgbv-MN_+x_y{OQ|aH75O{g%no?;$!l0{w);81FXIx{HW*YhoYYfs9#+HW|*Pgs- z@~+9!w8?WOi%Y6ERqv{nmK1I(+*K&8Y^35w@;B0!iF+oB#S?2MwoR1sCsJ`mZADuJ zi?h-ej{}lpZ9!YXrh;7s+TtKAbOqK5t7P`b9?{=KfpRjIBO#590a7MY@8lU%rn;w0 znK}i3%2b*|GpOK zki_gn+G%oxdLv!l00Q0ucm(X;us7+Iylm2WCr^=^o0oVu7GFMu;a3lmyZ{4 zIswK;xd_^jJ!Oi{vRZvc(<}gcx_mtb?I=G7*ZjHHk}3XR_aoIo3VD3k7)u7UB-{|L zD#Bk5Igq&*1mE4jm&SdWBO0W#~M$jdgsPeQ*!Z;V%}j)rJrC- z9q^u?P{K=nn|)$-<{{*J^yHfZfCx66T^LSGH2*sPcH|tfiRNb$jTaT*M$@P{oM$p| z31-ynWP@l3&w^5JwCCktF!3OufX{HckMNl`Mo`}HxbcaZ9Vs?Ye`AoOpZv}7`I}>d z1X+yD6VTJkgol((9LLY(fOuEtC4kLqC681hNu{0PSTZKYT`Z)~2p1Y!%~ElxwfEC5aB2_cN@+BL@Ojfad2-!s9&o`KoU_B}Ml_fM&sImbW6*KLp>e=PEBZQ;hHissP@JBWK@#$2g+k<<#qh)( zA`N?yu<$dpf*Nm;Ly;WeRs9)9SF&ePbYca( zIn0j6eSz9fQIm5jYI0g&1AO+_u6Jr)}^wh}P-?;N|!Oe$i?vu)y13pu9-LSIw zRy?eBI2 zm5$0trBqqzD-gUsfWGGd-x)D5YY;RCuCp^`TutjlV3)eqI5@Ps<3;Z6tE8x*hSiP5KlFrAwnJcKbQDet zc&*j$=4R*qVLRg5C;#mx|4jwQ3#h`M`E`NLZZnA^=EPequ_;$%<{7m&i@Mp1#8-xu z_n}ucCvLj!wwrE`PHBkiM{J9pmQ|N9NDJ_&DD?O3YAtKwK4scmi40nwR@`jd4HwdW zC8=XpWJxC|X81(}HFCOZbYwm5gvM~kj+16uZ>H1cPkHVqazYZ7sbgkJn|EY4I0FSK zv*A*6o;XWu*W@tMK}W5hfN7(2@Wzd-zVE1jFf{-_*%#pDc2UECS(97IRih`#>CfEu z^zqYANkjjv&Ft4S-Skzbrs?WjDPf2zuqjLL!M9m0+^zWPywkj*$riAcqfS@C5l%&P zoepkn!j!^_g@B6))}z|;s&)qx0v#2z%FBh4o`5|PjwCq?K7)`A#qLT)A^}vTFtft1 zDujXC%OMPyp;W5{YBUxjm2;x!OLwJj3e>rG?B@JI zi?Mo3(*=Gpmw(5cptIV41eVCZzrt$ccE2JBzkL?V{i!D>Oh^I7;nb524eEP+z4~5P zr@q(Ls_!*5>U(vy`aXWV`d(F~z9&(CnH{IJl)o!w(P+x36cpga=fjKJjTe^-FHR?3 z%x1ipOeu(AHWOHKm|Gk1T~TIwLFMK?60m4iT@Y< z@oPrKSn)yaHg?T#;F^V%sIIW!S;4CI5gyu8$^`V$%FbbBeY7fGFJ915E9bU%&3#IbDonk^XPN!KVsZxGFoIlPTgs&Yo$Pj3Mu(p19YXdO*$Y+ z`$H6BbsV%7;-HQ$I64=L8^s&5de58^Dq-At7er4JuYn94PmY zBHX5KRy4{KrAXNB>&eTHjXPgv%{ofi?1$Q!RhYOf9%4P@|9URRj*-j61QWh`_HAfv zRN1i_KEXtPW^_4ErX9G7sLJY z!E^N&CAiA8)wJIv8N5EPc!lvQ<4s0MYxEe!IaxFJD%>rK`B5>zVTWP>=d|4i!akm3vv&JN z8_u#i7zdN#mWvUQWmp^pd9b_0?@>JNgY7MT@k*DCg;6zI)&I!`odsv8`Yo3jqb?4{ z%wwgX-#XPn&fbfKndw|gq^IUZY%T)Jd;-HMq#KU zK#PUET~Szq5P?865Dv;w1Ut!*Xe7`jN8EBG;*cX8R-ggmM?s_jHUk(AN`kWrL0xCU z=UB>B#N8tp9X<_P7$QI#&{`rw0Q6@WEi(?w9)F+Uk&`?|?ow95V0Ik*0sk!LbcFZ$ zYvl{Nx-d0w<<#5nuo|v@YRli2Ddvv|3HOS9Un58`nUcq|Xg>hf%v)Th+(aGVVJ!Y7 zAMr1*pqx&I7r>lE=}&U&-QVU~5p16TxxrD;aMHj~oZ` z%G}H*&2-S11dZ-GrDR5QQFL`wO1fHI-LCDfL6_E$@FhecPNvd8sc6Xa<%!VBPcbxM zEI98gK^QTs5Wk=&qgz+3gVEisE!NJ~F2UjrV>pVRqCqj$QX48wlZSx8%@Pr_I)s{t z7>e_(lde#nI~2;p1BwW>-4c2~B)%V_fzZ0pvCzqo2A@Nr3XLA6io4xI+K9Ln!$Hns;q0m?jN9^9?c$(B0Z9wG^XcKs{H$L^CC2E=aa)HA>NjGhEkdvE^s`ZZ_mzJvN1hy_p>?Es0$rM{F(sakm>Sz7vn;s zvjqM|$#ZQ>Xz16jOgJPeuBQK8*>myaJ&W&IEWUj~vcTW2e9rrwjdi%N-z6jZHsB1@ z=+m(^ZRy%j<`GxsgAi?V(Q%6{bmc9NFG+uE-6S$v3YH!G(aKfdUNhmj!pVyo>#n@K zN4A-IIHwC?1R`Wv3w@;4Q3mUl^-C-gz|`3K6SBEz%0n;xZS zk(9=A#itYTBra|@WJW?Fma!!w+C)Wu8Uc$!MR61ulr#f^9>`BlaRBi@JShYoPiChW zf!Hx{fG5M(&1^pV4IJNTj5f>y4nYB$0xVEPBt6!1eC^N=QL{TwyOZt(63&-F?GE%5 zJWRRD*%|3Z7l+t~@WkFOm$I`7Yg$KNEDX4|t9Y$_W!a`aLCXPBvK z{~s3Dp*_ce#R(AYUst1^A!m;mz6NH)P_JPny_DMrClRTvBB7pT)IPsKEcCDVI89W? ziEzi+I2rjkeRSMp)fh&`>72S2v2o&S5v(X5XRm{;MdPBy=dDE~tfbT8b>ez(qXOY^ zA^AAHh6g>aNj@&T^|<)j9L>F%wD%eBkfs+i=fvM`8L=IwMG5oa==>YlXX|DZeysN|Q9~gD~C43axa>sY_GYd9SR3Zki6HjDFZr8a4g?Iqh= zXrS4Y{h3)Lj5fcm%q9U^;!fR>xu(OWM^Fck$=fC6DfX_M3l^q20o7n4hlzphnaFg% z_EEE>;q!V6r<(R0W&PDgAKU1SQPc47*H9LAAEB%l`J{bh)TCXOBSmz-nA4GN6n&}f zAL4Ux$@%1f=_=ajGaLOOH<>W|FuiZ(vvs457W4K~Y*cMS+#Wf3`@28mJG{v|{=|7Z zDCgLGRcGJuvxzZ~_dcdac8hrnnh-HeS%0&v-u3N73AU}E?9)gO2Vl3>qSmFXH6(E{Y#U$-85)A0z6a`Ta@gkFhcY}E%%cN z1v-X4V5T%SUg!cN`I1p*6-J-6woavZeo_*KK=XeF%{zo*#VOhh2H{Rik`+)A?>9aQ zT!hw7*ySO_o7GQ-@T@Q5!Gzh`q`+eObLnN0#_!+e$TJv%+MPdll;zdTuZ+z}L^{g9 z$*xNjZkL`xwJ@y7SXEaj?K;F_s!(%YtNV!WPP28JRcyw~0qeb1%}FctY6Zf!Rb7xz z3WNuso)JG+`!C(BoJ+Wy-C{!DtQVydGU;Wn+#t`B_sVz5-;wnl z_{csyE&rFStCOizZ6Q7)Q-^#tZ>2F{TfYBJ*O8R>F7J1}&v-R@uh%Pj75EFT_L9#V z_olp;c{N^s`}L;1Ch-{)fm7z22+%gqm|ii7%S~6CM1#p^ikng9;byD$~<4eMhFlGVPV= zYMGYHG=aDDvfk4#KavRteOYa%oRjGenfA!EMW$sk&6BBJrh1uTGO6u9m+3>9-jeAh znNG;`h)j2mI`SNurt$WrGPz~a%JiQy{i{sBhKMNB6EZz0(`_={B-19DmdJFeOl>k@ zC}C3osvAL+y!%s`{vgwvGQF5P!>uyy;(MfJ>XAv2samFDeg=(9=+Or^VXi;N`6&+bQ*6whf?aVv?~j|G;4DA>Te%V4!<*r->^iGt zipV57_{cPVD$_}RfvQZwIKnU&9mPZW$MZ#tzafKUcO0Yr@Ud|o{QVDSxIlV2uQllT z0|e1Almga_{k9Rj4B`1;rJS#3H!`M*(~e@R+7VtS{ubm7@rIN43z@q|bG~7IJNakd z%yM3cJ@BnOoB0A->CYH{k~S!D*`08UvOWR&z8aJ)wzGL11CdL(b05l|%VLAv0kBdg z!c;~|QYEZbR=zBL&Pe2K`H!-Mo4K45yMzo_Sp*3gIK6YEygWb_G7`TFA%>j5Rapqx zRd<=?S zCCgv@mrQ?@>1|HQCpcYVmhP5mvrJ1-k(bjk=&V$xd|v+RKRI3gCU@LNa>u=zpBwF` zajGenDGw7+rq4Lxydl#IW6|+!-X6yX9b;BYxDxE}f3t)SS?Gc;T_F>80U^}NsP{)6 z&MH3VtmRFaelF9KoK*<7$h3n~+Tt9gVF&cd&96c26Lxq}rpIM^AV>L|a+EfOlX4+% zFOEsdx8_c80V(4Iny)A2^*PppE2!s#>Ku!d!J3z3Io5Db$T3XIVvd*xn1(Q#+Rh^$ zMl2WnT|jH2X;T%T{x^H5C%fPb=j$vZ{n;oay){GCA7m*x!Ks+NX1kOVjUk3 z$fmC+h&DhHVKu)WL>ABq-FqRx6WvQHZDLx`3D_< z>9sHDA>1TeKliF~2tb7q`q{?=s^Buql~};YtGp4f1mBmoLCcJfdhvJvan;M2dn2@< z;_aohJR*JcKh1XDYqIHzTxUzVy-X14Wb2D92@DLaolYxcH>6_v~ z>BoYLexNjdFOMF!(!-m}cJ#UpL;JmNXwkvct8>Y>j)^z862Mi1$E z0DuLyXu)WD7QtR7=H^lUf(`Z^MlDQyew00EyJkNHp6 z^Plp0>$UHC=$9UPo}U5xG$V1&Lj@i(d+19Ko#AJ|*$#MUn}^nTsLw-P9-8E#3J=ly z9y;Zr$33*eLjxXCJOua~!9y*ddgwI&4b(c!1F{s)X>^#1hsiO^Jw|=!ZAN{EnGn{5 zz-tM%IKRf+J)EDQ+4w5Xl+-xVN6c3J@xvZ)2impVW>x#yvWi!W#-)mYn zqoQI4wl0~TNKEJPdXn%Q?9a~%@4{}W2i>NWYTaRg1DFmQ#KU12F$VzZ#t`1#WB6_Z zfO|W_;oi;w@Xa9LR^DsC^i5VX!2w#zhEI|_sAhasl!Z`bpW1&+ypZ_n>BOnTS5GB= zmUx%{0m5FWId@k7OYI?OB^5%4uu#}6>=O=2XBFEc`)Rokvt#qp-Alzy%=72Mx2+rZ zZxkE(_J-XJV*Bv+w*76QnQv#2Q9ica&)Tyz^m`9}|Dbs7!F>lsX>|l1jsfOqG~z=G z`!+uJ&B4CGm!N{f9C92!^x+|~^AH_6q}@CJc+E3pSFGK~A5l&yFDcT83Mp(t8zD0& ziu3y8YuWExFF$_m-hF!?-TUm`U+mTHRZ7cJ^?PYA3lZ46ckt0cIy^|%4$`tg>Kvrz zK`I?2;~;%LNdG)YFAma)LAra8_6*X>L7F>Aje}G?Ncn^G*FpM_{q&1LQT=h@Ahi!t z*&w+GNjFIP#BuHO1i&fb06>Ay84olb5UmFeM33v7`ObQ0yK|Xyi}SGa5$6e~(dRs< z+wa@ad!l(u>cpbTr&l)ni%(de4xAA5=0W;+klr1n*9Pg)i@K8Fef^U6^_RTCy@Pas z_Z8hiU(iwX{!e7G_rLLYVw(v^d(QQIITFeD1j{bjDn^Mmvd?N*A~*<&J&+fxTU!~s0YFH9-vr0mPqO^zYr2L-3JCBUhR!k$ zz?>RqKVh&UeSH%c6cYPm9pZ3O3{vWXryBpAD;v(-(=94=981C-NxkK9EdNuhlE14J zf_-afsQR)gF{8mdciGCan)xWqXz6GY$N*X)YFtJz;q zKdgD+JK95m=7DLU8Kui^>II#bG;~gL)-34gX-+9akN%wv zSO1;uh8}<^g|GD|q^-p2h)6=6QpEkm^(ZGL8YRC}CP~%e6j2nF9Rh=a5QI|*dT0&2 z1}@(hcnyFH&WBV{L&6t`V@h`IPDiO20H}#x4A2~YuDQY?@lNRn zST*{TY0Gt2>-OrT71A}*%|JBJ%X$cWnm_CQ1dVzj-3tj(DJV)2v&R7XKxFPSk%TjL zDJiX!wo50a_aqpgl6}nRmW6l(1Oo3=32+^1P`g!61DXG_&|UW#Q54uH9-=j^bX7ca zF!N2o{NZ{W=ua>2&myI8nXpCJBSY$YvMho+VUK)A`?>grZZ__Z?Kr~ae+*$4u0puuC|Va4 z-x$(~UuZw?M^k}@eho$XPyAliDE?iLx|w_}IR+(KFHsU`nd>A;fCXxgmgkEVbwERt3TGDf0dA-fsQ043 zsH_O}lFKBqxP*F3-dCYR2<|)jSoo_T{xb%eh0+`rs8N_Ni1}PouT=mYeV}&nnb8MB>F>S24H6BY>YnOM}IL!$7A$Jj1I?W zPmH$2XjzQr#;7eu)iElLkvm437=0e2Kg5Vm$LLgy9*@yOF}fv2J7TmcMoVJU!*{6V zt%Vr9dX^vlP2TkAsHW*rO(VHutBrB!7kOWB=v}$v&W=$n?}a8tpT_9@ShfepV}!%r zo@yf!$?Eeg2N)}B^wC{#gAdq)Ln>ALjT#R)g^0FE1exXmS_w* z?bRqfEsCB*tx`U#Pp9pIcD%(~m6sg9q(5^zopV>l3pA2kSY!!SM)Kw_%C8D$7;~p{ zzd|0)TOj52DSzCX+{+R#R3(~fb=EF?!eZSe6`Ix*rrH}8$^_<1^+zXuC zCb~kY-zx4GMV4U-yyaC!5!Na2pF&5qV~Rs6j8BR8#uvvm9>f`10v6;JkQP-n#C)a~ zJOICFeB8KDDbUMxu2^}ZGFh5*+A%JrNfh1PU~&OC&xMD}B&=(yYEqiSh^Y#mo+eY1 zu3(*mg$+*BDds>*XG+#qnm5}LwsQMsXUtXV^o5-?2&e;bz_|{<&qticoLUs-4W#Tg zI%lH++dA8z?TGC?n-+dLo73j3ExO7V3ihtmSAZL$WB0n67bp7 zL^;eRHlrAF9TUv*YS;@$p2X-P<~r95aBV#NsyXg*n1C9Ua7=`MW@5sT+NQpq6W!j97~1K3I3S#208KY+F%0UK4A^3fjGw0N;Urh#eTCllBLK*S*C=h zCjXM9*G?U3FIc?At9{P@oQ$X6Ic?27%iFK*S8NaKi|gCs8@Kylb@XRe$aEK8C1YaJ z%nEvET(?pMH}jmUJEQkh9=+DT^We@K7u~k3A=ET?LbY5~?78at{n+N5uzX&d$Ca2? zjdk*pbALAM!2C8L!*M`ZOD`(M>qJ^0t`fzGJeCyL7A5vN`!xGz`)<3oB)&PmJ1$9m zOwWVz_L1^ednfQAq_T3oe%W%JZaMWX->_n(d&SC?4m?*dhy03V zJ$mGB*J`GY-(22ZIZ(NdWlohBISXvXn`7nXSiEd|8Kui;J7cHm7%)c5%3^wHhOTF& zZuttId&cIe1yhyIp3ZdVsZLF2=T!gj1mklk8@8Hcg=71W2#{Bkomq%6X9+UiMh6Q{ z8VPcS1xii*VZUKFz9UClfE!DKb(+j2aGwgX@~qp zz@-3BR$HS7To)>;<1;XQk#wV?q_C-2qpi-zL_~fbM??lZqZ->@$x4F(Vzq@A1dB2B zMUPz8P9=%3ChGoYBJK{8|0LBv=txw`47S zeCU4nQS+>>>6f`HyXvdwPA;ii@{I*u8!oMP-0iyTG86&}`D%J5Bzl!tUH^`un(o`) z+;!usrzr0?o1VR~UHXs7izWt#-uzbLrpy-a_`F_&?gpJfD=wa!KOsQ(-1ONWH>dsO zO|hi6Q1)3Z#SJs-rY@Nh^;XTOt8JcO_9vo&;-JrNF0ARSnbJ2=esS%wYbOuAtKC_@ zggJBxHQ)ZC`A6`=X5lmCJ6j9(7l>Pf`-9@PrUOmlPB+bQFLiHrOP>0YdeKu^Qdw6i zc{C*&(G(??TV8@lD!aWgFR+ehJQq(`=V**HRy9hEjgz#%8ZIfZY4g@r#fljgeQ_Qp znk{Ci*j%Np8i=inory{8Z7_Bu_FhbDuS!<6RvoK)y-J(J+qo(!Ru!wtuW4))uBy@J zUlkVoX}{?Ahy7Wmmcz`&yg_|2m5;G0he^hbNSF{jAB8EAKp|F{lZvCGa;82+d1cF2 zGpJ|qp%9xdxm2wfCI}1CCC0Rz#SJRYBB`-<_42-AIz2hli?~*lnW5JwXI4{0Nl#g| zyZkP>XYIjxec#$JS^U1TUNRe?ZrnO~!zC1ZZQjjSv{=6%xJoNge%CG6ZrAEHvFSxS z=N?>KxAO9;DQmhD%Fdtd+F3L`ra^=N(HE5H-Y~6b;T7OfH=R4H`=$1vFbC-uc~>9+ex&5%!!-XK|5CpsSt1ZEks0Hch`wliD`@3@OSv6r)e2sWDByG^KaA zyea&6I8Dpbd(&b(O~$l84b-gT8cp!H{8aq-e`7&Y%IZ@#rJxiqdES(*>%{nH=A3Bp zCv_D*=u{_+&qPn;>w{zt2J`iQOw-$G`fi%;O4H40x+YC4({x#y-bvFh()2XnzJhN_ z@m|DvM+|9t^%=g~&1t$eO{>zhAWfaTMHz4LU;NkQX~J&4H0jfXEjZJ~drePMbDFSM zfL#sU^ASJ!D`|Q*O^>FPV{}iN4y0)-@7a8QmI=H)4r@r$Uvqs`k9-%u47E#_@y1P~ z4*pJ>Ud~qRH8bHvLuvXXP5+Xnf8yQvUYhPs(~34jepXByA#P_)}P4m;Fj#45`!8BPg&R>q|AxTYF*Eif4<=X^ufn1xQUNFnx@ zI~W8gc)T~lj>V*{WO9OPzZ!ta3V3sfEus-_quQxP1S4=Jz9hL=@q@q53iU|ms^HGN zrrGP-XRL0I>R^9bv$L&j*|cIhAUcccOKN*tD^WeActRDk8Xc(X?w4z<>-*c|)t6n{QP@#>{ez2RD)4>q_66RqYDd*XI?YAWMSn^N9P4sd#7HrcvWigB7fU8 z1M9ldNIn%ACb9S)YK@j7Hv9zz)^*xlMtjE7BHe1;L10>r?;g)_#eiCZ8rAVYe1BXD zRN-?tYKWEuOAE&rjyIUA%q8Jq71QZA21@}1Rth9hNvS7$J=NC-OG|@*1q+&e&ak0W z5(=xvmq>|d)Q4iSg5Bp&z-`d_TI*|A9&me-5JFzVliev44`-?dyM2AHRVQClb+2qP zoMW$n`2(SXKvrRb1{ZQ!1p$UtpM__uN4Qc}&}c>dDz=C~N8<%7P+*)Rvsd@@wQag* zZiP-?6>geaI?dlWZBBi9jX@hqrR=30g&Xaasio7x#_HGO?o>5QU@y~&Kd{OUWNSQ4m0g#@f0 zvyn@%klS<$0Q}ua*C_5e@=|%TECrdjV}WgzZL3YR+w@ZM{_Q3&&IU z)&Ho6BqqDO!Rr(A$m2#uV+FQac$rh;vRS%uY_#-tZYhJH;uSTOV z`2DWL2~Frko(BIyYpO%(Xpm6)B@MjH`p(JPmaHNQt^5;Swo0W}j#*7?7JS06`f?g6 zlQwyk4RwwrxTeH@1$-E>3hQHBO^x-dEEz z9-UriH#~Ke@?NFC;IqT@)B~9_x88~#cCUq0AL-vSH$Lj4_<*ZEJ+r(mJbqfj<({yl zyS%-if96*;n&&F3%KBno%|iX6$%(46r8Uxn{||9*0v}b8?T=U0y}hM7`$`tl*+?LJ zItw9zbbvsB$POY1*+~K+fh>Z15L^)<;)eUEqmJV?tPY~!I5Tft5Rp+_*xY8+8AsoY z&o>W9^8cQ5yE_f)yx;r1&+qdO^tpBGR-HO^>eQ*abaD(f&?mHq zm7n==j;zR;gDW7P zL~bHhC1Pxv$Z}@Q&g#q3ZCSb6LoT_+C2L)BmP;nOWUxyf<`~>w5H9=1C7*NQD+-S) z!x~*wyt!;C9czoJCF~-2$|~MuYB^f^ zsPqVnIU+JNB@_ofV?mm}P!xr`uq9hcXi8C1r1?G=Cz#cr+8>n`dypj*+k5xDd`g*= zIUYjYzY{m^kSh%=C{^B=wyDiK#@P>RqMUG4j&nm`d{h^kD*EGw(AuY%f<{BNA z93Q6S1Lyur-80at?&*XRjubd?)U*%t{!%&1egEYpvR9LhQQc7+qO``y?#K<1T4P#w z+J-c(F||8&L#npPB`Oqh!7_EVX(|zFeBWD*x(ytt@{= zzOr$g4LhOGwk=wjZH7(RW8-2oW0%CPj5XPvan4NV66Z>%DeuyZfJ?*r!elVcxSAEF zi412(UIs3wGrrBxGH4dsXb-8*2+IhINg3@LtxTgMN2~Wn|7rC1qqWh=OLB`&6{(`> zp^HORSLk)2YI&%f4^EnvoQnA-W{3}kN~1%eA^_mm8xvC&p6-VEKzcxMUb;S}7O(0F zIJXF&tKgW1#?3@KU~!H{ckC&>bPZY(2Nd|A>9V%5Yd0PV0v2QLYI~6!2&d%gv1$?Ffm9 zOps=ryv}?7W0d4!l)mWDyZfP~1+gK=!H_kuGe9q>^_;gcL1%rJ6iV+|#-14Ap*a{Q z!L3lW^19#OP?c4){Ib4^HLu(@=iKk2+%p&D%;;H)!*WvRb!>SfysTzvk?Z2=xEf;AMYIa#Xl;Ow8a{P*3=lG#+Qg z&gAbRm*cxgx^gOfWP(rK16s$xAz|O8YbqI=dK6)A98N!h({ILily2Kkq@U}bzR`2=_mN7zY6Kc@wct}Cm88};h|@-m!9GDaJ772Hkj|!CSnaX#%&Y94y`Z55*i|M z!SX|~Zx)o2J@-gSrrha87$?XtuO(;vO9(Uc`xz~-WPAp5%PN?+chRVsS$E1!~ zJ-M=>%5+I? zBlSzKotYAqJ8k*Kd1Eee7li0iyKa4a*|_59)Gn^igZ7%bi-Za8kZAIC4y7 z*2MOCW3{JpT2`*e>w9bKMHMS=nNhxF>kX|Hv3Zlmj2M^v+avby_|R?9>5J}sb#U95 z=_O9r{EnWUyrLzOMm7HI5M)&0OR&IN;(DC35eDCoO6-=ede7 zH;%++Yw7rYAd_O`iSOj6gXYlWI81uN5FHZPPx_shwG@>%}u`bI$Dr z$(RS79V|8N2hA2S)}3luTp{EvK^AE^Wx%E_&ebdQhxF%QE^5+Mtx1D98!XgtS{F#- zedpI;+d&zcgn3(1s7XJ!=YamoV5#}>|5|LR{wI!&mEv{auLFL&c*-5R(srdyy@&mD8{%fk?%vie_s_FrEDT zob|Bv8#)-=9$sxFE-8)tjx>A`qI{1IX)ER4FwGE-L;^4ax4T1@Z2Ow*01sD}OY-Y|7)$Dr|C@U5Te{s+y1FSlq5M=T$4 z*$D0C0$DVvYLW^FmN*I0gcb5gnI3B`)Wbw#ZbdFEE@W;w?O=W_On6gIm&lUnG2c|m zN^5#ytZu3ZJP@cV0%yS_fp=cQxn_yM(TAhIiPmhDs^k_?ZXXv}hHJA?S6>XRd5Iw zJ9hyiov`5Dy_A+sytb!N(Z;93H0;_r{51w27QAz5`nuDZHMeVO{gYD?YvwP`sp_7W zlQp+z>ar&%Ij7HG=BnzNlRda|%G$B6wHIYgam6LLrVcK3t(}=UB|mON-qhSF>(tDd zn^#P@{?3Z!)#)WoTNll_t+o8dJ0>ijmQm8Y<;iQV>+8#yvaGoH(yH_`M$omiYV%P7x-u~e2EdHy@zGoEh z=JZcMU2V-8KXY$C;}UOXZ$288NcfIJ{P1RK3ia4-(;9iz!kfkzU>aD_2d`;Cq3fjW$XaRhvL979_a$Bv4Hh9eNdw-)-xT|6%p};@$=(* z;`MkMdaQ%`e0XSn!Bv+&B~K;I8!YYCFb2&Olf9704j^ z;weMC`5Alg8^OvJ>;}4E8U%yqSINRE8Bry_t&*QrNhtZ>R>@se^06v;N0r=IC3~u5 zQ9+8upq?NFFMD#a$KC?9_%5nIpRJMN(Q zp(dKdcluO)HbUeVlID)I+H59a%=$6e7bZ8(J;LY-cNEMGFh_$DqZuL1LFw0|eR?jz z_-Xs^xMN`K9e2nkMvgPj@y?N|9~Nd+k7mmoD$r>6D@O-J#R~832O7 z%^0^S)`jp5QU=iM)M5#SQwqL%(grcsXDqYKwKz~sGrQGn)u-_4B4+D-Fyj>?XrcEI zmbYSfvsd~gz~YMu4snVUc6gDDw8=;l$gTv*k86w1#bIK*_K@6k@8FYzPj3HfKMXGv z&(?l7TdCe&<@Ur2<)&neG58?*ikC^ehhI?<-#)Z$t=*Jy$$hEfNmM5hZD zgqUd4*(rX6meBMb?@UbNV$jI!{p)tQNG`f}u=miw%?6DRfW{JiJ=R8TBE=onX1m;` zXu^v_?M;GO3=gvk^3)(L zIibHaiCy|syfe_;31q_p`%RhqW`14?O`Sri#h=}!Cfq(aAL5-naIf}``6&@C687j$ zEB1&LFG#UVhC66hpL+-c-`B{RDKXZS6dPM5rX)j!q+=syd_`5ex6Pm1S24qtHZsr} z8<7-}YBLYqYgzc)E3d4`%P0)cW1`a2Q`(3wX&Sg!?M7Mg!ue~V51rnH;v9;Ki;uyU zTAtztxHuP`6O6g2ce)ylMIbmB;Mgyfylrtt%Y=~yFd2$Y3`>eNMFuB^q**QIr~dim zp{vIhha{xOL?p)qg{bI=w9$zxsO+KdRhntH+5=`p?b1wO`wLS2N}j{J5>}+ix=BNe z1(*(~G-@BLQ4MTEdsHKR%!^o(L06MzEMyLfGh~t-^8MF56-|-v_xD?>zn?*6;DDHI zT!u*`?|{|CKIFoNXgkfOL2MqOekG_h&P(Ot^$)MMYO|Ix(AKZ^^q<971EOG@3 z0*6tqNt$rh_NcYIDE3``r7M>a!heyF~obMjAL?L_TyYgAVP?1$2A|P7ocg&;WHe z(V^dd2CIBNegKC>r0>+>gyv<4)xwqWk-|KIv7~o)Piz4>;0x0O&UpA@;QEzCCrlt{ zuT@yhgW4I(Fl#D^H!vme5(V~vEQ7A^XAp-N7q^o|WbDvvjLagG z0&;B;X?rz`#U^yC&R7Qwa?g^KQm+734_;Ph=p1J%(Lp}w?;pGeokYnGAgk|yUcxn| z(#B$bfyHYmr2hkL9RZBQq9qiy^kaA+A%zj0xW8<=FP(SK~a{reE;vx)rO%51g| zccTE?Ahq)jowi~@Z!+lg$_`5p+KB#nyA_L&wxa)7|G8w4L-caH+k|U}w3`orNE?R8 z0Pf#d$Jh~|%s{=%c22hrg2OnNPJ&`@M_~cLN5oLm?!g2g2;o%R-R1$iS1oWwP?(272Gski8T!KD*RO155?Ie-PxZi;;vWY^pF2;lN*|j%71b|WIVJSx|8{N-OI}yVIwBy7tLq~Z;kz|qN z2ecdf(y;-So%v-&qc}hS)rB}m{0bPr;SMCHzcLN;%hNX8_9!N)2%{7#q`lLBZnx^` zA52g^+KMxyO;fl|q}SnK1o?-|l6>h_t6iB%J;D|MVt{gXmOVC^j5;I_gskfrKl2L`Os#;EkB4Pzx5Fl-gSyA=fU~;!j8PJI z6!b@LPcf7h7nTHmk$jy?)n@b$TKWfWm7k(YrYUD>*1+;bI)-(4@gB%mpjib_r-TyU z>97w{Q9l$hLx=r9$b7?~XDDPjBTeShH7JK>{bP6Nfu2(I9S+4>0G!m zT(WI938SHpXikC}<0J(sc7VArNV7Y@0d&LpKR60#J@LbG7Y;?2^hS0p_~%7)Ekcy34Lt`o`s8F@0B{ z)f#G}UWtKucL>%!LQFIW;JgX&7i$7LgU{MXv+XwP19p%2qLQpYsea(HQB7mGaj^(X zn4Um@iLHhVtDZBJ7~oD)olVCK{lGej^#k#DoZDux+XIEw>hKU?hK#|eeYb7EH+!Vf z&c*X}qf{S*5inlijd`SJZl;|?U*Y)~>sOC18n?q9Xe0g_QxRi6uQPuw+8Hbu`1tGc z*RdY{QjuI53Gp%XP0yT_XO<+te&bTGsBzgo`|o;@Z^!;oq+=;R=Xq+8^<$e}o5+>GC1Gl1rB*lXXrURC0R9NqX5JMl646~l#dWVaeJ@(+> z$Us{NUNWIA!bw}$BcLrJ!k{f2G@saIGo1~g`tJxNVWw%dcWRB6CaqC0JQ4Qj4v<9u zyb~wSWSiBrN0)eXH7OK-dM?_jM~2yGULTB(tnY|W*b@TVLZi_k?Gb?yA_T1w2x|6V zlfiSOziE~Lg1ll6ay&iUH(?;j=l)Ht#e;@ z$EQWErl|}hws1Vj;)V9_KUV@`APD6N)_B%o#zw* z8>Lm-_{5e?H@+aXT^k>5XRM};TukBlmoA$B(7ahQC|rI2?3(IH6;mlZ z*ZB)Sio-eh;^_4D{prknua*(lS`r+B(?K+NuZQT5tRg52r@(M`C{h z2j%l%KKGm9@Y#P1<0C4D|4hjZ^jBXu{MR@ftn-BH>91iK{WWmdNMiY#Gs_gd9X!=A zc&cvj1pe0#o~*b0tq$soJQ4oi=?n2on1t^Xd&P^`Tk2KuZ{iIYh#wWluq)Lm@u~Q; z_zSFi{vrMoD>Fl|A+boi43^(Aw-%(sFL`|00!q&>z!pT3WQ9pI;z~Y575s23P0i`qE?AD*m3Q0akW@4 zuBV;XM8CLGY!?rRpNXG~Ux;ULmdS4MYw?nJ1$$z@CJu=s;$889I3Ye3pX2n6uW?f8 ze_)rF0XWp+TbB%!q4+*ELMF&0nI=ceY?+TQR;6;htdv!-{<=uc!9)csD@9PNKlr&K zT}40sONqqm<_~}Jf(+3}lkWGkT=3k4XC;1dCWMplvlOL7rr{cnYl`3R!uxyDQ}{jf@=ahwJ=%A0ib6lq->s{;R(bT3-PO zu3Ry=5KrFq=Js8Q2o!`&1KVVBJ^fFqlSvKy-w<#Qe8o2|VQ5;00m{6>`~0BMErvVf zLGP1Sy@9&A!BgHY=j(67QNCp6Lmpf**%QEMh(9WVhu4F2XP|wB5Hd0Y5k0A61e{sm zPhyVTt1s4R4<`|hw}>mXQ|FTO`R9`G!ht1Ep&!;?!!wJB+-t!!AK%sSRrSybG7F*) zw3Hf6rF1*Y@zLFbqkgC|d)Emh4_w>0q7Zh9E+nl>@h zU51ZGPM9jxnfh>!EFb@8+&pYLXgY1OnJmPz3N;HBx+digiLF-VU^p2n4Qo;`&ss`{ zMBzg`3@@;!Wr|I@Y`SvK9W0Tr&6RHq1`R$a8)Th2td0!itD=E727aR^5KokNLOi19 z=%evol`eAJ@src$r>Pl9OOn)#xFvCFa`gOYH8LPVj0jF46U5`x;A98J<_KOpFxXnr zhw2iKsW^F{k`d(-Q?7Vy*|MKsId$rlKVP=&u`8x5EN!^HrsjsmapM|qsHwTWp;R^b zKc?h08yibY8#j7W5sg^65I5^7+8Mn0h3>K9ba+I;>V!x^qZiPZ@sV)Kv6%5)o_F7X z)qk;C(Xp~fRdM0m_)_Kr7UcD( z^$n1XXvlbw(lK+hkr9GV#__@)Dle$wXnn&uy=vXVOP2g>T~*b(pDkJP@VY8}!`Z7< zC58N+U`1c}myrpH4$@;}P%O2N0}#9JXJ-jbdx?9Zvl3%H#ABP7fj*LeMm z8P^kIjq62zCHv9jpM;YBfJP;{<@Ksj>lI-l8GHyAarQ)WGhv0yYswV7gXLFO=eyL^MGMpLByC~Q z^6Kf+r?$`&*OaQ_{QP3Hg-QMjed==bw@{HJvT?rBWbA9aG{Twc>~U%l@u{$n)8b+> zW3G(RXbbi$BecoM85M;&8Te-nON4a9TJzH~OEM>nYaJ&q87HTXlOwQgQF&!>W^HC? zre>|AQz9}eGb{C|VAw*B)Tw@C9fbE)7?*_{BIi8bW+vni-Y$L5@Bsfkmc`Of;)n|) z<%Mhjr;PnzU^Zg%3rjNOh^A-h=F4~O92nTSYw_Y8gM&L3FJ8R;?hik_d;9i#KKS6C z?E{N*oX+fQr!z+$&2l<(bDhqtfv;+|_18>j`1P{Qd8&GF$IhKQ2K$aJ*|B5EvAz$s z-+S-&58fvzFLvg5GxK%uz|A+O?5kY@F_%Zc-|fub1juBesB@Q$35&^19T|gv!kQk3 z#l5`3)JvY9`4_SP`Z-Xfk+ zJRzb0 z8`9vMH&4#s@g=qUQgB|Gl2eAd%|Ut@){dr#O}`eou%rvb$$W)5vM^Pyg{eN-)?I{{ z$Pj?C2w4VaV-DuZ!W3DEJ(#U>;uf6ws#XWeia_av5S3<5=u3P%@pPhgZKBMB(@COC zEDXky;4GJBb+G{&c`dTqG6G&XA}?;kNyimAQj0E$hCMpH?ZBBS+L8{4|CnmQ?dDlSPciH4eshVeSM2-#!PphEOX?dFNj(&D0oec3N$XKBZdS+g=;$lsG#5*;%x zd16IJ-u}!z=qAUGIcD$5c&T)Cabi_(UPD7(Z`G!{#>V_!z3cucG_(Y(+3~ea#D6>zu@Yun9x@Xq#0S}@dbHJH{IM&kXv7W%gv2>1*A*mhp=fI zrLTvbQ?6)mPlyT|fnFV)DZj}a%2b)yWh_V}%hSo=Zdf2Bg zJxqu`Sb&KHH}+dhz@asVGLJ7?T5t$fol9NZ9oKZBv##l)4c*A|OOi1bR1ukTMq{f4 zi`n8?oyNDy@MN$Ld$W3srwT`Mu*2nw2>G41z9m$$U@Dd zlZ&9EwV}}BwD^o5%%pI3rYShkS>eoaho|Cmo6VwIVce%|CNL|}{`LHY7hiVS#q;Mc zxMJ$Odlyd_Ki<9QlJW@?$}#f`3&JMWS^zfZqYPG?#q0>Og(c6*PIM>RLrf-|%b7lA z?wG&LS#|ZoMf2zP_FlX&(>Y=Mq9xH<~= z=m9~=Zdk=7N0ZGV=3BW-p^-7Up<@Th2bHYgj5bC!i6MzHsiXid?&v3;ilbZb1i%@T z5gT7ywK*lNV&b9)8nx6>kKSxO_l5cDg_#qxB4qZ)d5b4jjEtUHTGcUXtbVw-HY>BO z;+lz*rqotUJh1KJg^jkfwEPiOOV-Y)tDRV}WO~_{1-a-F^C04&AE!Z@bogh?5IE?( z^rDJr8C^JjQef^#Jq+9H#f8O%;nflreTEwpRw4_i79nrt=Z$9ih{U48QS7--0|5yz z8WxV*)ad@wOs?MS(``&~$b327%L#qTxjokT?Qx^sv4v{f;><M$PN#o;7zAwx51X`^d;E}J*I+!a1Pd(PE4*)@sj zBf*R1Lto!{=fXpY>yPO=ylf1~HmYij zp3rCBY8pBZUm_cV#>B?A)QqhP%1oO)WqHHQ_}tLs+ZGipNSQJ^VrKb_c4uN%^w{RS zf^ExdmPS>s7~3*+N_G6~;ObbEh#GyZH@1V_MMcCdv*g|8M z{7AB91z|qvGK%)m$HJs6*=wq*dUDg(&8u3G5ttG+s-Zk+*_?vD*=2CinfddjOE(u~ z+|{}Cq3RLioTWF;j#%4R_VCJ!V4*Cpyb}ffe>(qJ`{T#`xax`0g7Ca5OC_>@LYzhU~7j^qqBR2C0c%2v)wf=FDEx4 zK0cw;ot9SJUOKM4Vs=)JzP{;|sz(>hyrHb}s{ znp4u!i*svA%fi(AO9pP3&@gIxQFcvfdARy^Ido&l(7Pz>MwB%QEI}2hulhijYIHE$Lq5{;0kh}nQz-+iihTj?-lNeJGGb=`q3B1*5 znQf`FthVUzm!=I}z9yCRwc&!Bsq_9l)XPgS^P_H`GDcdk+wwIlQm1!LPstw2M$a? z9Qfq?uvJ=b)B0^9mk*l5F)FZHUTT6olixd*T;U{(?HP+F+ZH)ree>ep+`PJ+Cx9AS zHeI>YJ0{wrGFrUuj$9Ee7h*Slwa$E#Sxqr7G%q)6a8dcf-HWX<(n1%6Dx5F&7u#8z zveD)Zuy(Y_FggzfXPlo__6UVDGlF$lt9Qb;RnrgH55xVyJ*J@A?oLXrw(B;#1}{p; z#a8vOQn`4og;BZoxz!Y^1w(nNFgJW+!osB8Vm>a_DdP(;SeXew1AJeFzrmU{w8bsX zqQ(2}dHW|}+a!87rP&mA8?~h1Ja6o9<+5n--_??^OOmQc*^n~0GE#o_v-Yc_?y`+c zOtAfj3>>RjtQPh8jvbvUd*IzQDmpMNJ;grwu}lgYH40rsikT{1eFGki68G=E%yxrK zVTW$0wK*RwR-? zx1N}o5&dy)R0Vc>1^&u~B*bm$4~b8(#P7V&D2oq$TI z!tj0q57@`nMG}_zHC{@1HyG3#d(|dcNRiUkKgMf0Hk+iK2TAHMTTJ;LCyoquO^RMv zK6XiEYR0U~X1VL9X4`Kx#iW;}&zX18go29_t}?A(^UtXC$m+HCK+G27r`|vB?he$ z8iOW@B2TOUQ<*JcM1o4FbbXQ(^hsqB{>7L+smPp_c_330nIV{+W=<{pTH&S&BzH3v1t*VvGjgwbdUBA?!Pu(N1bDQ(h&77>})1j)N) zOE9J{-X@_9KT9y7r)xOx1B$i@LW_-57VldTt)j?}%Id1p;tHl_$?}?vIakb`(l#YI zef&kEO3FuH)R~!8>U2z+m{^n^73g$DE?hXZX7;j>lv2~?gu)p)W2#F>nYGZxQ^z$< z8=X9Ue*U<5rAZbQy<~jh{Bh2~yAv{tVv5Q_Qi{@|GLs{h*IiUPWAVb69NN)~+IOt} zyf)VK70wiz<{r5*oQ}=y#@U}%*<+OV;kAtB~F`(SJq=*FTSUpwJVT9!S2C{iqNij8{u;{ z(|njgw_Id8qdqWy3(n-Z6T7tQG^KBHOYQbPA@YPZGCX@$y08n9aNIG93U#OnoCr=Vh8y6AX4FQUFE$QXln+n9bq^lot~az zN?m2f0xygK=8GH8JE0vHNO?SYP z+N9jp+1Az8KI>Mi$!fKC+GU8{4N|pJcG+i_iFSF=E^A@9j%q0@ExB%iqn1Sn+2k7; zjY^n)vgHoJBay}^a?@QyzPD5ZssIcWc-d5@q@zi(JFrEqd-Fwo=^^!fk8V2iP2{Fc zkr<2r6ZB6t?|@dr27>N%vt=W8htM|4JRuLGdJ<|*=y|$Z*ACJU3rkG!4(l+?J1&_= z2#gZc(8l3LYHi9hDFa7So=Y*Ec>#aa*3Y1vrz~bz(oA%ZzCKKvMn;ZI8>s~Z#RiQD z(qeB8j!VSRg>gv%H{0V(Tiqes`Xaa5A)N(Zqk+DL;^P6xtFO_qd7~vLZ5n!b114-z zsGb{BQ&o`|H##vUpebln>AbS6n#mJJ(!FhYP+I9c)0vF?jPT^#gzVax1qenY=f-E% z&cyC~G%r~L8m}_{JNk`Xtk|{NBsFYm@wR(1oKtPL*v)a~O!Ex$67x#)S-b+fLnErq zJ2k0p*B;T7uSZzXCG0=hCtTcEv0sz7DOmV*ChaE`UV)Q2OfH6Nw3Fx~I|j5&{-k)Y_V6f0R)^S}2unBMm3(S? z$MiJWqqWT2>F$lJTymM|+tF2v^UI36@|RR$>6l6s5qRIy7Qs2dhP{Y1kvg;suzU#T zV#$RRmX~t4mctDU+c>6+;X1%n43E12ONLqjONX`qj^p@pj=6-xiy2-z^f!^Jf{}hS za(x6CJorz*P=={UpUd&Nj4hY3aCi|zs_8gj+XWcH z;ZP2za(FVsYKBBxIrFWY>sXHTt%#vImWu@BDo5!Asq}J`PEV^jOr@8jUKBr%;e3XR zIQ=Dn(-dq_@dcfDzJMuN0L;6@0*=3g>w5{;bTOw{jPMq5DaT(*@p2)xlw64Sjk^F> zF1ZiGVv~8EpT2c8yL1Ryd1}4 zT`I3c`b*_i46oss^$a(1%nb~0WOy^fTlwi$hT9n4Emi;n9k#WU%U#Ol)-wIIOn)uY zUn`0JTBg62>91w_YbDWN%k5p%a# zCL6fK1}?FIOKjj08@R*+S-`5Hm0qOX?6z*nt8N*_)tu%U4)+Z`iTL$H`vI@xn2r4OhM{i}b2Eo; z;WW4M)2$3|=lE?LzJs6MIkXVzsRh@_d-&;getIv$U0jzg>YE5tUAm}m0-|rqF0M-# z*QJZ=(#5stLaX77G?wiymhCRIAjOcRb%ACIlZ1CklJG7`65a)x36g|&Ns{m`NfO=# z+ExIPgm%?SjV+k$F*3;wOGfsSjV+k$F*3;wOGfsSjV-v zLT(v)O|EBdte2aIjw0O8@Q$J1$?N#(29$WO+<lDfy1|O_|~Bcq}e>Q32-at?dS0AoNF7$+{N&2u?A_jbNF6Qt!JF zB@)~K%s0wS45{DU2x=*Ydf$zxDM9LeHv$_$>U}o>&!>P_qj%f{Y!qJ4a0A0l3~yz) zh2d6)w{!Y!LmwdhT^w@{$86{Dy&S#|;Z2Nx6Jy@Qm{DVnC(N7B76b|NCeTT68$-f# zE4RX};1Y%V8B!~3W}KTD=Vr!<7QoZZjBPVx+sxQDGq%l4;bzqLQ+XS=>22Jm{fwud z@$@sEe#XwCWun%5zQOTtgZ^jaDbD*BgrDWIp5?NhMOph1LuEZH z_cElio<;2lQd!S(SF!a)#v2f43BWGKQcVV z@O_3KF#HhQcupSY@F|9WX7~l?{fgmV0G~%)z65*?oOxaz71@BN5dIa@v%>?4Cp(xY zJCL5jzek!KTx9s+!UX?Q`t$zh`Q1^Fp3zksvwqOnK8(+KYaRr>&` zefKd{`#{55gsBDh0Rute?LMYqAKLDy+>aT;D}bR46HxAcZngbrul;gAx7U7Pu0wbS z_=$Q`F5q7a_yVOzeK!MAefL9VDCQLozro?R8B&kf&pl$l{0qV_u}r?ioP3FS{u0yt z661V{`STJm??+0K{Fi|9DB!E0;U&o7T0r8)OU$vCm>Vxc0;kKDA%O&`E-!OkUgol1 zMp;MY%W?;Dz04)P%q6}oU*H&O!Iwcl>cegEvV4Q%-{knWIQ|I3KQere;W38qGyIU@ zM+}cMJi+i3L+U9n%g-79nc)`KgrAAl~Rl<#o(D8oN8e2?KV$jkxxK8HVG_#vnNh{MMjp5T~| zIs6I3&lrBr@GFMYrUyaiX29psItM`^g-JpVq8;l1NoO8psXoX(;vh@oK}h3K`3kql zD`BL|lL3#Q&;AA$3)%8_h#`0cJoz2>;@<=3e))UW=YIe_ zkIO%ByZ-^wyBXn~c=`wC-XB2QQH1wFR{ww!3BHU`;17^AO8F{u-5($cl>QBR3Y=#7 z8nEpH+<`v(8t^;@xDTbjhI(ZKzQRwbm%S$6=9qW*>H7>x8^6v}y$(E^5vJbyI`isv zmigCNC%g_y)*_yI?(3lBG5MxE3Y>3p&wW!;>%0k`><9c5Dc@v`aR?qE@d zL(I2Btfdbj<$lEfi6OPuThOsb0cp&73p$p#KXWzVbXzzS?Uh6PB;vl6hpb*<{o{7 z^B&>6N4Q2uIPVdr=LqLL!nuwxB}bSWM?lG!@(AktI3UsUjywsTyu-5b4r_^b>y2bf1lI5&++dwp7*)!K4hMM$Z0<0(m&*~K4dNZAuv29KlG#|E*)n&k8}Ft zoc=gddz^C}=N3QCxsG$LQF7Xs_t_Al)34%-6fYc*S0ne9!G=80C?w#gd ze46o}M(?J0!giW#@iEi$F-y_MOv%Sw`o~=E$1H&#ds5Qq`3a8>pK!Z>&f(7y&X!*w z&2;%C*XTr3$A2x6#5e8ur!ar{@z!><_U8PsC4JcC+nhOZ=r2R-3-P&kOO@~8?@ zK|{N+UZV|d0bDrrkqU;!*rg(1Z)^s9VkiOd<-xxJzB+ge@b^Orv_vT4v3i4bLjJ20 zzwv}6wk4S43B#TnD;q`{O(e;uJz-r$!S33KH;EvX?+Kelq-ytsE#eAoohNJ+5qg3r zY!gA)gU!fm$1dI5Jz@!U|rv z^E_cqOq67|P3d)!CExOdO(I@}d%|WhM&V-$r?H6d)ODV)Rb*)Y@Puukt-uq;E($tU zia5PPT&mOFScE@NoHR}HgoDIY=56p!>BJ|G%S9KwQCdW+=n+nlAsVp`>J$a|gp-Gr zMyHqyZhhLbQFMGiuhq8t{MKR&4|5>^HRzh7u5sp}DKRr>(QYIj_D05z~;W9xQ1C zgT%C+`t~+lE0MGjNIJNFT|khH-@ispT=qj%+1=RO(bU}K%ywQ-rqf%>{|MhtAz8?E z?e^6)AI(f+kOOmCv9P(To2r_hlUI_1`yVUO%bM)-=^rkS_>}#xY5w0y1xaHw}iF#tmy4(>uzdmBoXaK**_FTQUYC20|bX!D+o#_0z$k@}fdR%Vq+O=zQ>OFGb2r6`xqNkVQ*(D)O9%QvPHWF9q^B-mbZt_2BtX3^NQ9V_8VSTtb@eIC zwjL1R^k@tcM#4i%madf7TitFCAD1JxiFsC!>U1Hr0x=|FIUL)G+EB>=4K}~T{qfpd2 zKonVsDU9|e_@B|9dN$Z**s(uMAZ(6s!VtVe!r-+O0SlEV*saBg5wPowgUxw@)bUl@ zDUw98NWr(wG}vsVW4sr0gC1NU7GlqdMetZ%jGZSgh2QEj z*e=(h13xM@!ef={_=UJZ+=l%p9)Q2X_C4`O z_`SBm3;k;Lra~Y8{Mu4dTFirN~{xCpr>C6zuK$B3*u+6-MI#4 z9qYwk#oxp}836BfhYW=2Ot2UbgE9mjY+*8748aB=0{*j6Qo^%627a`$@NbWYH!ay> zJ7tnghFjf#$rPCi@7huDde49%^ceWQXTj4p2OjWw@V70%rfkRH3ttG&+hSQF$HM=1 z9Q@+T;e|`5dAnt$_(XgvC&EvDGQ4u9z+1i=zPZ!kGhYJ_-I+L#Vzzi*{7TM&(cwHf zUoOB`$cwSb>?LwB{O`|+&&22W3VA8M3ogU=y*m1GCmUsxY{vJt7JOA}lPly(*^X~s z9XQuwwRjO(L@Gd?L zALCQ_dT|}T9&D7?%Nwxbf0Mjf-a@PHSS!CxJSn%xt+HRovB4$|CIlQ6NJygS93rP${|Hv6~|&Ioz9U{IK55TRDiN8hYG~I zOt1=3p(;#;s|XdTqExhsQ6p5Wic|3_L5+lGqf;fRWR;>)Rhk+FOUn#3T8&YeDobUn z9GHmaseDxcKfs-^R4o*H#9r}RRU~%7@B4S+I=toFh`kwpuZmTPcv&1!V^ygdr^>{C zs&X}6O@M)^TUCl%)kHN(O;%NEikhmb)igC-%}_P!A~jRoqh^VF;Zyz%Jj!p!&dhVf z{qT(3qUMS_#GPuMny(h9h3aBBgtLxMTwNYKKZcsO3 zSNWT9^4=zOEB3IxO>GgsR$EoSx?OEkcc?qnUDz%49<^QFtL{_xs|VDB>LFOr{!Bfh z9#xO2pR32!6Y5Fz3-y%xrFvRDqn=gIspqi~+z#yLxXaSp(UzB2ndiAq=Bulu%DA6c z;JLay*CM)N%ac;ieW|5#RefVuXNRTUxSA^)x-gG1*Ym|v+1b+B(Y(@9Z(Qw@8r!-W zdsi)QZ(e6_91aIeYU=E%Z^TI06VT`jnkP5b1GTxyxJ*Vl^*xph%~b}*X1>^`4ijQ;84d?b^&tpo@dX`IF^^hRZxHKf@w>5C z59hVF4u|#XhWakO6~H{Lr>(uI+1$n#%QR0-+B`LxX4It3VBIti&27e2O`ERTR@kQx zD(-kxT2J6{|%{^{ENZp<|XgWCC`*P!-# zJAiqnk-x*pKhrOZqr>mUJk!YEVKBI(esyPePgm#a)@H4$qeW}(XtB)lRKC+w`B_Hg zJNXhgtF^bIrM|0oReODJPhhA2oq4W7Lzh9rT)$!+U4A#_xdshg#$}$7tD7(OdBd!; zcMpdh^ZgJwdi-w8^9?jTe9`AqllD-PE-;$3*J#oOo?7*KYPGGf1Lt*{qHObJWbzgTTsAVAiVU79&w;8>&C%6wzxu_h#it6X(|(klfTll`%CnxA)XrJh=2L z@Ia%7c<(@!2$^XmSE=2oQb(^^*JkN7>Z5hFcAD{awyHkg#22jxb@G&}twwzs@dFvA zPGYcw(0R!TzJNT32dB|Go`-xBz?nJ7GEtb9XRBY{)|Q`_SK#u7it>G-0$*tOX`wH! z$QLU1g-U#(vA$5LH&pB^tvKIP{$$@XZ&~?;KD_xpZ23NH`95p~K5PX(iVM70@(O%( z7WnWL_~#OfrUm0V4WsLQeG1gbcSYH`qePxXGl`+;=Myaoi zQePRRzA{REWt95LDD{<5>MNtP#E{J*AGVTVY~HfaV>}$l&+|Ub&-0drO(J|xy=CR+ zdCSVr^Ol9vFMR2|^~ujOW_N`}-oBBa?<>n!hkRel=MUFmnBvm0=8Idp>M;{sYg{ho z;lsR^FSd)ls>8O{8!}&FB)pt2#DRjme4dx$YSa))B5!OUFQ#C$!LnXMd*GU03>h@# z?7~8ec~x5n&2PG!8#_CiY|ZN$q3V#zz~1#LeA3n6&jYH`y?|ZKfskR!2`O=2=F#+rGT*GH=+? z4dOaH4{Y^aU7c&&o0s=kIMlm3fUmsDZai%2T-#xU8n6gygnCyubvQg3nmQV~o4G;- zdBp*pT|KQdOQ>&mv~_?Hs5vcG+MCz(wp~`=-rUjHY-;W7?QRYPkJ>w1+8XQIF~|l` zVlcYBXSFZb&|{r91!9Fi3UCMkG=dS6=kf;dseseuL46y6vED$bH&E;ilz0P0omk1d0TrsB)@Z=xf;wVaha?0#}*YDm!kXt<8c?nz|zyzR^QUQ+PHRk z?wdM{`}XB#nlZLFbAAjdZ5@|2^a6Dcg^Wi5ovWKWJdxe2+90;|jm?m=%X~LlcW;Mz zIbOxun{@)F?$w}JZ*1>vFts+$Q_Eo*z9@%RS@(X)AXL0c)lIbZE^LSv! zm3!Niyu2cG>L$!v?SAu1$MVkJuI}E|&0TGsT@LDO-kU(~ZN5A1cD}nteC}$;f-BF) zLwF|dhcwhL+(k35W-F_roZdz&Yb^u<9En&grKu+Yb;Mvrl%G-xBNTwO#+gE&Jf&v7h((3kMJitK zR*IbV`koG~S5htktndf=mmzvcSOc&o*>N7eQH*Z_`)3@eibCdZt z^TX!X%@3P4n*WG+{DTW^geBJEv@EwgZ~5NZYP|w}UiVu6VEx4USNJPu*{-p@5HK!a zf54yY!X9Lgw~vD7+HLk-_P6ZcItm<%9LpSA9gjHPcYNjeE-)Z4D{yXLYv3b+`vcz# z{3kxjJA%T3QiF}T*%Xg{A|d}20k|QZ691B4;%8YA@3UUZn1F8a6T$&GI`V>{T5EY>%8@)_7mNSH(pd(qrJ31d3g0!`0)=OtMCuO7a916pv9)3Wu|!L@jPCW zKM3)gd_l+)g#19r3xs??$ODA@Kgj!IJ*=R}^Mm|8$m@fAKFH&P{5{CqgM2;6(}Vmx z$jgI#Jjla?{5#;?f$v2IB_w-93CrGjl>At$leGMww!DV+c;43z3Vi(tJcTwuecnYq z-UX(2LGQbu_g&EXE>gS;`rbvVcSYz=((*r%J_#H9*M!bePQDi8X#p#GSo)BM1^HKy z#X9*`kY@$?RghN&`Bacc1^H8uHwF1pkS7KCQIHn}`B0Dt1^G{q_XPP)kmm&aCSbF3 zzKs2j{Uu1g$XDXWWGnho*;BUZM3jU5mF0x%G%XQLTu+}0t9qmjW0^}b+-T~wrK%N2Q7r+*- zu+$@u0P+VQ%Sy5;Bu@ack0YCTSSSKZ9rP7>0FX^GS^tw&HCe)w6*5_oll>>zJ(F!X zS%H$RKUqDKl{i_rlBFkE$HAf$^bBJ%?6b)__=lKaN6VP@u^m6zs1iLNBM$b7_zDZ( z2(B|MkD=AS^wu5N>p$r_(*@nMONmL*Gkvco4Ud|0(Z%dkh@> z(y(yFm1G1qh$6_-n$(i;0suC$p{AOUH9W}#S;CVYJjnx7j%T1;IMITTPW(KycrZc2 zP&Xeogh@8Yx|*d4_iP7@d$MmQ>vpnjKZYOKwUbpl*|d{IJK3|7#V^SWS+bKIJ6W-l z4LdB@nSwe{PzMS~hK>%`1*MQwl79fTIN8>dtvXq%ll3jx9h2SiQT)h0ovhQzHk~Zf z$u6BNkICwpY)#4jn5@ys7M(27$qt>Y(20B0a+}d|Yf;)}56|nsHCUdb#9`?;>XWj4 zkdS@M4JreDN@(EfWVSMgjXB?Cvu!F#sG{D-x`5j0ag;>;cQd$4G6|0i{K%r5?8(WR zoNUR-lAP?wVMUIdW|m@-tRu+r7+a5%?KoMEQ;#C6ak7vli*f4RHcvU5J+ipjC)K1Q z44HfkwIOS5l1kzdwGr8cldM}=R}gQ>&YpCJr^mD9GVv^g+n;n5*?=1@anys4WPmK7 z4f|)Z`6k?C?@iX#Wa|xUWY9>~)J8iRZAg?*+j%t~rIN_|B}N->T5o$Bl4fYg;S#8S zJx--SPmm3_w|~NVTLdx=gJMI&dh0h_Ce@WF{r}23dnAH^C;*EYIfOg7fipOOF{9I| zwHl>Hq0w>)lNm=)(<_ut!1ukEh>6N>lDFR9ZjzbrZJZlwS}~5bG_J#r=ZxvA5v_`K zz|8ZUN0dt6Ny2+3eP`xt`7sFRw7j(nHc;bZT;_&982$I z#w*R*MBj*&NUo4vVNqs_{&wXm!$#{N)L4pIIdmZp{1@*=+S9}(BN_JKjd~sYwnoGZ zUK-Ce{*{$hgWn_O+A*Hqx9CK?&@Nlh(X!FpH({fBmxhf_GMe5%E?O#DCR!poc<2HO zhCLD>fBa4i`wRr*Glt}GfB9)|1~Zb3$UOLvMQ|ZYd60$%p`w=RSQwDxJS047x&y95 zs$&n&ae(8H-RKY-RA5PJoI8|;ghuiiT{uyqf04avO)&Kwi literal 0 HcmV?d00001 diff --git a/public/pdfjs/web/standard_fonts/LiberationSans-Regular.ttf b/public/pdfjs/web/standard_fonts/LiberationSans-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..366d1489cdde3523e488ed1ee85583618fb0c120 GIT binary patch literal 139512 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 a!I)3b-v8Gh+`}J-#|&)Z@#Ld7T>RftGoMcY literal 0 HcmV?d00001 diff --git a/public/pdfjs/web/viewer.css b/public/pdfjs/web/viewer.css new file mode 100644 index 0000000..4639f99 --- /dev/null +++ b/public/pdfjs/web/viewer.css @@ -0,0 +1,5193 @@ +/* Copyright 2014 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.messageBar{ + --closing-button-icon:url(images/messageBar_closingButton.svg); + --message-bar-close-button-color:var(--text-primary-color); + --message-bar-close-button-color-hover:var(--text-primary-color); + --message-bar-close-button-border-radius:4px; + --message-bar-close-button-border:none; + --message-bar-close-button-hover-bg-color:rgb(21 20 26 / 0.14); + --message-bar-close-button-active-bg-color:rgb(21 20 26 / 0.21); + --message-bar-close-button-focus-bg-color:rgb(21 20 26 / 0.07); +} + +@media (prefers-color-scheme: dark){ + +:where(html:not(.is-light)) .messageBar{ + --message-bar-close-button-hover-bg-color:rgb(251 251 254 / 0.14); + --message-bar-close-button-active-bg-color:rgb(251 251 254 / 0.21); + --message-bar-close-button-focus-bg-color:rgb(251 251 254 / 0.07); +} + } + +:where(html.is-dark) .messageBar{ + --message-bar-close-button-hover-bg-color:rgb(251 251 254 / 0.14); + --message-bar-close-button-active-bg-color:rgb(251 251 254 / 0.21); + --message-bar-close-button-focus-bg-color:rgb(251 251 254 / 0.07); +} + +@media screen and (forced-colors: active){ + +.messageBar{ + --message-bar-close-button-color:ButtonText; + --message-bar-close-button-border:1px solid ButtonText; + --message-bar-close-button-hover-bg-color:ButtonText; + --message-bar-close-button-active-bg-color:ButtonText; + --message-bar-close-button-focus-bg-color:ButtonText; + --message-bar-close-button-color-hover:HighlightText; +} + } + +.messageBar{ + + display:flex; + position:relative; + padding:8px 8px 8px 16px; + flex-direction:column; + justify-content:center; + align-items:center; + gap:8px; + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; + + border-radius:4px; + + border:1px solid var(--message-bar-border-color); + background:var(--message-bar-bg-color); + color:var(--message-bar-fg-color); +} + +.messageBar > div{ + display:flex; + align-items:flex-start; + gap:8px; + align-self:stretch; + } + +:is(.messageBar > div)::before{ + content:""; + display:inline-block; + width:16px; + height:16px; + -webkit-mask-image:var(--message-bar-icon); + mask-image:var(--message-bar-icon); + -webkit-mask-size:cover; + mask-size:cover; + background-color:var(--message-bar-icon-color); + flex-shrink:0; + } + +.messageBar button{ + cursor:pointer; + } + +:is(.messageBar button):focus-visible{ + outline:var(--focus-ring-outline); + outline-offset:2px; + } + +.messageBar .closeButton{ + width:32px; + height:32px; + background:none; + border-radius:var(--message-bar-close-button-border-radius); + border:var(--message-bar-close-button-border); + + display:flex; + align-items:center; + justify-content:center; + } + +:is(.messageBar .closeButton)::before{ + content:""; + display:inline-block; + width:16px; + height:16px; + -webkit-mask-image:var(--closing-button-icon); + mask-image:var(--closing-button-icon); + -webkit-mask-size:cover; + mask-size:cover; + background-color:var(--message-bar-close-button-color); + } + +:is(.messageBar .closeButton):is(:hover,:active,:focus)::before{ + background-color:var(--message-bar-close-button-color-hover); + } + +:is(.messageBar .closeButton):hover{ + background-color:var(--message-bar-close-button-hover-bg-color); + } + +:is(.messageBar .closeButton):active{ + background-color:var(--message-bar-close-button-active-bg-color); + } + +:is(.messageBar .closeButton):focus{ + background-color:var(--message-bar-close-button-focus-bg-color); + } + +:is(.messageBar .closeButton) > span{ + display:inline-block; + width:0; + height:0; + overflow:hidden; + } + +#editorUndoBar{ + --text-primary-color:#15141a; + + --message-bar-icon:url(images/secondaryToolbarButton-documentProperties.svg); + --message-bar-icon-color:#0060df; + --message-bar-bg-color:#deeafc; + --message-bar-fg-color:var(--text-primary-color); + --message-bar-border-color:rgb(0 0 0 / 0.08); + + --undo-button-bg-color:rgb(21 20 26 / 0.07); + --undo-button-bg-color-hover:rgb(21 20 26 / 0.14); + --undo-button-bg-color-active:rgb(21 20 26 / 0.21); + + --undo-button-fg-color:var(--message-bar-fg-color); + --undo-button-fg-color-hover:var(--undo-button-fg-color); + --undo-button-fg-color-active:var(--undo-button-fg-color); + + --focus-ring-color:#0060df; + --focus-ring-outline:2px solid var(--focus-ring-color); +} + +@media (prefers-color-scheme: dark){ + +:where(html:not(.is-light)) #editorUndoBar{ + --text-primary-color:#fbfbfe; + + --message-bar-icon-color:#73a7f3; + --message-bar-bg-color:#003070; + --message-bar-border-color:rgb(255 255 255 / 0.08); + + --undo-button-bg-color:rgb(255 255 255 / 0.08); + --undo-button-bg-color-hover:rgb(255 255 255 / 0.14); + --undo-button-bg-color-active:rgb(255 255 255 / 0.21); +} + } + +:where(html.is-dark) #editorUndoBar{ + --text-primary-color:#fbfbfe; + + --message-bar-icon-color:#73a7f3; + --message-bar-bg-color:#003070; + --message-bar-border-color:rgb(255 255 255 / 0.08); + + --undo-button-bg-color:rgb(255 255 255 / 0.08); + --undo-button-bg-color-hover:rgb(255 255 255 / 0.14); + --undo-button-bg-color-active:rgb(255 255 255 / 0.21); +} + +@media screen and (forced-colors: active){ + +#editorUndoBar{ + --text-primary-color:CanvasText; + + --message-bar-icon-color:CanvasText; + --message-bar-bg-color:Canvas; + --message-bar-border-color:CanvasText; + + --undo-button-bg-color:ButtonText; + --undo-button-bg-color-hover:SelectedItem; + --undo-button-bg-color-active:SelectedItem; + + --undo-button-fg-color:ButtonFace; + --undo-button-fg-color-hover:SelectedItemText; + --undo-button-fg-color-active:SelectedItemText; + + --focus-ring-color:CanvasText; +} + } + +#editorUndoBar{ + + position:fixed; + top:50px; + left:50%; + transform:translateX(-50%); + z-index:10; + + padding-block:8px; + padding-inline:16px 8px; + + font:menu; + font-size:15px; + + cursor:default; +} + +#editorUndoBar button{ + cursor:pointer; + } + +#editorUndoBar #editorUndoBarUndoButton{ + border-radius:4px; + font-weight:590; + line-height:19.5px; + color:var(--undo-button-fg-color); + border:none; + padding:4px 16px; + margin-inline-start:8px; + height:32px; + + background-color:var(--undo-button-bg-color); + } + +:is(#editorUndoBar #editorUndoBarUndoButton):hover{ + background-color:var(--undo-button-bg-color-hover); + color:var(--undo-button-fg-color-hover); + } + +:is(#editorUndoBar #editorUndoBarUndoButton):active{ + background-color:var(--undo-button-bg-color-active); + color:var(--undo-button-fg-color-active); + } + +#editorUndoBar > div{ + align-items:center; + } + +.dialog{ + --dialog-bg-color:white; + --dialog-border-color:white; + --dialog-shadow:0 2px 14px 0 rgb(58 57 68 / 0.2); + --text-primary-color:#15141a; + --text-secondary-color:#5b5b66; + --hover-filter:brightness(0.9); + --focus-ring-color:#0060df; + --focus-ring-outline:2px solid var(--focus-ring-color); + --link-fg-color:#0060df; + --link-hover-fg-color:#0250bb; + --separator-color:#f0f0f4; + + --textarea-border-color:#8f8f9d; + --textarea-bg-color:white; + --textarea-fg-color:var(--text-secondary-color); + + --radio-bg-color:#f0f0f4; + --radio-checked-bg-color:#fbfbfe; + --radio-border-color:#8f8f9d; + --radio-checked-border-color:#0060df; + + --button-secondary-bg-color:#f0f0f4; + --button-secondary-fg-color:var(--text-primary-color); + --button-secondary-border-color:var(--button-secondary-bg-color); + --button-secondary-hover-bg-color:var(--button-secondary-bg-color); + --button-secondary-hover-fg-color:var(--button-secondary-fg-color); + --button-secondary-hover-border-color:var(--button-secondary-hover-bg-color); + + --button-primary-bg-color:#0060df; + --button-primary-fg-color:#fbfbfe; + --button-primary-border-color:var(--button-primary-bg-color); + --button-primary-hover-bg-color:var(--button-primary-bg-color); + --button-primary-hover-fg-color:var(--button-primary-fg-color); + --button-primary-hover-border-color:var(--button-primary-hover-bg-color); +} + +@media (prefers-color-scheme: dark){ + +:where(html:not(.is-light)) .dialog{ + --dialog-bg-color:#1c1b22; + --dialog-border-color:#1c1b22; + --dialog-shadow:0 2px 14px 0 #15141a; + --text-primary-color:#fbfbfe; + --text-secondary-color:#cfcfd8; + --focus-ring-color:#0df; + --hover-filter:brightness(1.4); + --link-fg-color:#0df; + --link-hover-fg-color:#80ebff; + --separator-color:#52525e; + + --textarea-bg-color:#42414d; + + --radio-bg-color:#2b2a33; + --radio-checked-bg-color:#15141a; + --radio-checked-border-color:#0df; + + --button-secondary-bg-color:#2b2a33; + --button-primary-bg-color:#0df; + --button-primary-fg-color:#15141a; +} + } + +:where(html.is-dark) .dialog{ + --dialog-bg-color:#1c1b22; + --dialog-border-color:#1c1b22; + --dialog-shadow:0 2px 14px 0 #15141a; + --text-primary-color:#fbfbfe; + --text-secondary-color:#cfcfd8; + --focus-ring-color:#0df; + --hover-filter:brightness(1.4); + --link-fg-color:#0df; + --link-hover-fg-color:#80ebff; + --separator-color:#52525e; + + --textarea-bg-color:#42414d; + + --radio-bg-color:#2b2a33; + --radio-checked-bg-color:#15141a; + --radio-checked-border-color:#0df; + + --button-secondary-bg-color:#2b2a33; + --button-primary-bg-color:#0df; + --button-primary-fg-color:#15141a; +} + +@media screen and (forced-colors: active){ + +.dialog{ + --dialog-bg-color:Canvas; + --dialog-border-color:CanvasText; + --dialog-shadow:none; + --text-primary-color:CanvasText; + --text-secondary-color:CanvasText; + --hover-filter:none; + --focus-ring-color:ButtonBorder; + --link-fg-color:LinkText; + --link-hover-fg-color:LinkText; + --separator-color:CanvasText; + + --textarea-border-color:ButtonBorder; + --textarea-bg-color:Field; + --textarea-fg-color:ButtonText; + + --radio-bg-color:ButtonFace; + --radio-checked-bg-color:ButtonFace; + --radio-border-color:ButtonText; + --radio-checked-border-color:ButtonText; + + --button-secondary-bg-color:ButtonFace; + --button-secondary-fg-color:ButtonText; + --button-secondary-border-color:ButtonText; + --button-secondary-hover-bg-color:AccentColor; + --button-secondary-hover-fg-color:AccentColorText; + + --button-primary-bg-color:ButtonText; + --button-primary-fg-color:ButtonFace; + --button-primary-hover-bg-color:AccentColor; + --button-primary-hover-fg-color:AccentColorText; +} + } + +.dialog{ + + font:message-box; + font-size:13px; + font-weight:400; + line-height:150%; + border-radius:4px; + padding:12px 16px; + border:1px solid var(--dialog-border-color); + background:var(--dialog-bg-color); + color:var(--text-primary-color); + box-shadow:var(--dialog-shadow); +} + +:is(.dialog .mainContainer) *:focus-visible{ + outline:var(--focus-ring-outline); + outline-offset:2px; + } + +:is(.dialog .mainContainer) .title{ + display:flex; + width:auto; + flex-direction:column; + justify-content:flex-end; + align-items:flex-start; + gap:12px; + } + +:is(:is(.dialog .mainContainer) .title) > span{ + font-size:13px; + font-style:normal; + font-weight:590; + line-height:150%; + } + +:is(.dialog .mainContainer) .dialogSeparator{ + width:100%; + height:0; + margin-block:4px; + border-top:1px solid var(--separator-color); + border-bottom:none; + } + +:is(.dialog .mainContainer) .dialogButtonsGroup{ + display:flex; + gap:12px; + align-self:flex-end; + } + +:is(.dialog .mainContainer) .radio{ + display:flex; + flex-direction:column; + align-items:flex-start; + gap:4px; + } + +:is(:is(.dialog .mainContainer) .radio) > .radioButton{ + display:flex; + gap:8px; + align-self:stretch; + align-items:center; + } + +:is(:is(:is(.dialog .mainContainer) .radio) > .radioButton) input{ + -webkit-appearance:none; + -moz-appearance:none; + appearance:none; + box-sizing:border-box; + width:16px; + height:16px; + border-radius:50%; + background-color:var(--radio-bg-color); + border:1px solid var(--radio-border-color); + } + +:is(:is(:is(:is(.dialog .mainContainer) .radio) > .radioButton) input):hover{ + filter:var(--hover-filter); + } + +:is(:is(:is(:is(.dialog .mainContainer) .radio) > .radioButton) input):checked{ + background-color:var(--radio-checked-bg-color); + border:4px solid var(--radio-checked-border-color); + } + +:is(:is(.dialog .mainContainer) .radio) > .radioLabel{ + display:flex; + padding-inline-start:24px; + align-items:flex-start; + gap:10px; + align-self:stretch; + } + +:is(:is(:is(.dialog .mainContainer) .radio) > .radioLabel) > span{ + flex:1 0 0; + font-size:11px; + color:var(--text-secondary-color); + } + +:is(.dialog .mainContainer) button:not(:is(.toggle-button,.closeButton)){ + border-radius:4px; + border:1px solid; + font:menu; + font-weight:600; + padding:4px 16px; + width:auto; + height:32px; + } + +:is(:is(.dialog .mainContainer) button:not(:is(.toggle-button,.closeButton))):hover{ + cursor:pointer; + filter:var(--hover-filter); + } + +.secondaryButton:is(:is(.dialog .mainContainer) button:not(:is(.toggle-button,.closeButton))){ + color:var(--button-secondary-fg-color); + background-color:var(--button-secondary-bg-color); + border-color:var(--button-secondary-border-color); + } + +.secondaryButton:is(:is(.dialog .mainContainer) button:not(:is(.toggle-button,.closeButton))):hover{ + color:var(--button-secondary-hover-fg-color); + background-color:var(--button-secondary-hover-bg-color); + border-color:var(--button-secondary-hover-border-color); + } + +.primaryButton:is(:is(.dialog .mainContainer) button:not(:is(.toggle-button,.closeButton))){ + color:var(--button-primary-fg-color); + background-color:var(--button-primary-bg-color); + border-color:var(--button-primary-border-color); + opacity:1; + } + +.primaryButton:is(:is(.dialog .mainContainer) button:not(:is(.toggle-button,.closeButton))):hover{ + color:var(--button-primary-hover-fg-color); + background-color:var(--button-primary-hover-bg-color); + border-color:var(--button-primary-hover-border-color); + } + +:is(.dialog .mainContainer) a{ + color:var(--link-fg-color); + } + +:is(:is(.dialog .mainContainer) a):hover{ + color:var(--link-hover-fg-color); + } + +:is(.dialog .mainContainer) textarea{ + font:inherit; + padding:8px; + resize:none; + margin:0; + box-sizing:border-box; + border-radius:4px; + border:1px solid var(--textarea-border-color); + background:var(--textarea-bg-color); + color:var(--textarea-fg-color); + } + +:is(:is(.dialog .mainContainer) textarea):focus{ + outline-offset:0; + border-color:transparent; + } + +:is(:is(.dialog .mainContainer) textarea):disabled{ + pointer-events:none; + opacity:0.4; + } + +:is(.dialog .mainContainer) .messageBar{ + --message-bar-bg-color:#ffebcd; + --message-bar-fg-color:#15141a; + --message-bar-border-color:rgb(0 0 0 / 0.08); + --message-bar-icon:url(images/messageBar_warning.svg); + --message-bar-icon-color:#cd411e; + } + +@media (prefers-color-scheme: dark){ + +:where(html:not(.is-light)) :is(.dialog .mainContainer) .messageBar{ + --message-bar-bg-color:#5a3100; + --message-bar-fg-color:#fbfbfe; + --message-bar-border-color:rgb(255 255 255 / 0.08); + --message-bar-icon-color:#e49c49; + } + } + +:where(html.is-dark) :is(.dialog .mainContainer) .messageBar{ + --message-bar-bg-color:#5a3100; + --message-bar-fg-color:#fbfbfe; + --message-bar-border-color:rgb(255 255 255 / 0.08); + --message-bar-icon-color:#e49c49; + } + +@media screen and (forced-colors: active){ + +:is(.dialog .mainContainer) .messageBar{ + --message-bar-bg-color:HighlightText; + --message-bar-fg-color:CanvasText; + --message-bar-border-color:CanvasText; + --message-bar-icon-color:CanvasText; + } + } + +:is(.dialog .mainContainer) .messageBar{ + + align-self:stretch; + } + +:is(:is(:is(.dialog .mainContainer) .messageBar) > div)::before,:is(:is(:is(.dialog .mainContainer) .messageBar) > div) > div{ + margin-block:4px; + } + +:is(:is(:is(.dialog .mainContainer) .messageBar) > div) > div{ + display:flex; + flex-direction:column; + align-items:flex-start; + gap:8px; + flex:1 0 0; + } + +:is(:is(:is(:is(.dialog .mainContainer) .messageBar) > div) > div) .title{ + font-size:13px; + font-weight:590; + } + +:is(:is(:is(:is(.dialog .mainContainer) .messageBar) > div) > div) .description{ + font-size:13px; + } + +:is(.dialog .mainContainer) .toggler{ + display:flex; + align-items:center; + gap:8px; + align-self:stretch; + } + +:is(:is(.dialog .mainContainer) .toggler) > .togglerLabel{ + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; + } + +.textLayer{ + position:absolute; + text-align:initial; + inset:0; + overflow:clip; + opacity:1; + line-height:1; + -webkit-text-size-adjust:none; + -moz-text-size-adjust:none; + text-size-adjust:none; + forced-color-adjust:none; + transform-origin:0 0; + caret-color:CanvasText; + z-index:0; +} + +.textLayer.highlighting{ + touch-action:none; + } + +.textLayer :is(span,br){ + color:transparent; + position:absolute; + white-space:pre; + cursor:text; + transform-origin:0% 0%; + } + +.textLayer > :not(.markedContent),.textLayer .markedContent span:not(.markedContent){ + z-index:1; + } + +.textLayer span.markedContent{ + top:0; + height:0; + } + +.textLayer span[role="img"]{ + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; + cursor:default; + } + +.textLayer .highlight{ + --highlight-bg-color:rgb(180 0 170 / 0.25); + --highlight-selected-bg-color:rgb(0 100 0 / 0.25); + --highlight-backdrop-filter:none; + --highlight-selected-backdrop-filter:none; + } + +@media screen and (forced-colors: active){ + +.textLayer .highlight{ + --highlight-bg-color:transparent; + --highlight-selected-bg-color:transparent; + --highlight-backdrop-filter:var(--hcm-highlight-filter); + --highlight-selected-backdrop-filter:var( + --hcm-highlight-selected-filter + ); + } + } + +.textLayer .highlight{ + + margin:-1px; + padding:1px; + background-color:var(--highlight-bg-color); + -webkit-backdrop-filter:var(--highlight-backdrop-filter); + backdrop-filter:var(--highlight-backdrop-filter); + border-radius:4px; + } + +.appended:is(.textLayer .highlight){ + position:initial; + } + +.begin:is(.textLayer .highlight){ + border-radius:4px 0 0 4px; + } + +.end:is(.textLayer .highlight){ + border-radius:0 4px 4px 0; + } + +.middle:is(.textLayer .highlight){ + border-radius:0; + } + +.selected:is(.textLayer .highlight){ + background-color:var(--highlight-selected-bg-color); + -webkit-backdrop-filter:var(--highlight-selected-backdrop-filter); + backdrop-filter:var(--highlight-selected-backdrop-filter); + } + +.textLayer ::-moz-selection{ + background:rgba(0 0 255 / 0.25); + background:color-mix(in srgb, AccentColor, transparent 75%); + } + +.textLayer ::selection{ + background:rgba(0 0 255 / 0.25); + background:color-mix(in srgb, AccentColor, transparent 75%); + } + +.textLayer br::-moz-selection{ + background:transparent; + } + +.textLayer br::selection{ + background:transparent; + } + +.textLayer .endOfContent{ + display:block; + position:absolute; + inset:100% 0 0; + z-index:0; + cursor:default; + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; + } + +.textLayer.selecting .endOfContent{ + top:0; + } + +.annotationLayer{ + --annotation-unfocused-field-background:url("data:image/svg+xml;charset=UTF-8,"); + --input-focus-border-color:Highlight; + --input-focus-outline:1px solid Canvas; + --input-unfocused-border-color:transparent; + --input-disabled-border-color:transparent; + --input-hover-border-color:black; + --link-outline:none; +} + +@media screen and (forced-colors: active){ + +.annotationLayer{ + --input-focus-border-color:CanvasText; + --input-unfocused-border-color:ActiveText; + --input-disabled-border-color:GrayText; + --input-hover-border-color:Highlight; + --link-outline:1.5px solid LinkText; +} + + .annotationLayer .textWidgetAnnotation :is(input,textarea):required,.annotationLayer .choiceWidgetAnnotation select:required,.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input:required{ + outline:1.5px solid selectedItem; + } + + .annotationLayer .linkAnnotation{ + outline:var(--link-outline); + } + + :is(.annotationLayer .linkAnnotation):hover{ + -webkit-backdrop-filter:var(--hcm-highlight-filter); + backdrop-filter:var(--hcm-highlight-filter); + } + + :is(.annotationLayer .linkAnnotation) > a:hover{ + opacity:0 !important; + background:none !important; + box-shadow:none; + } + + .annotationLayer .popupAnnotation .popup{ + outline:calc(1.5px * var(--scale-factor)) solid CanvasText !important; + background-color:ButtonFace !important; + color:ButtonText !important; + } + + .annotationLayer .highlightArea:hover::after{ + position:absolute; + top:0; + left:0; + width:100%; + height:100%; + -webkit-backdrop-filter:var(--hcm-highlight-filter); + backdrop-filter:var(--hcm-highlight-filter); + content:""; + pointer-events:none; + } + + .annotationLayer .popupAnnotation.focused .popup{ + outline:calc(3px * var(--scale-factor)) solid Highlight !important; + } + } + +.annotationLayer{ + + position:absolute; + top:0; + left:0; + pointer-events:none; + transform-origin:0 0; +} + +.annotationLayer[data-main-rotation="90"] .norotate{ + transform:rotate(270deg) translateX(-100%); + } + +.annotationLayer[data-main-rotation="180"] .norotate{ + transform:rotate(180deg) translate(-100%, -100%); + } + +.annotationLayer[data-main-rotation="270"] .norotate{ + transform:rotate(90deg) translateY(-100%); + } + +.annotationLayer.disabled section,.annotationLayer.disabled .popup{ + pointer-events:none; + } + +.annotationLayer .annotationContent{ + position:absolute; + width:100%; + height:100%; + pointer-events:none; + } + +.freetext:is(.annotationLayer .annotationContent){ + background:transparent; + border:none; + inset:0; + overflow:visible; + white-space:nowrap; + font:10px sans-serif; + line-height:1.35; + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; + } + +.annotationLayer section{ + position:absolute; + text-align:initial; + pointer-events:auto; + box-sizing:border-box; + transform-origin:0 0; + } + +:is(.annotationLayer section):has(div.annotationContent) canvas.annotationContent{ + display:none; + } + +.textLayer.selecting ~ .annotationLayer section{ + pointer-events:none; + } + +.annotationLayer :is(.linkAnnotation,.buttonWidgetAnnotation.pushButton) > a{ + position:absolute; + font-size:1em; + top:0; + left:0; + width:100%; + height:100%; + } + +.annotationLayer :is(.linkAnnotation,.buttonWidgetAnnotation.pushButton):not(.hasBorder) > a:hover{ + opacity:0.2; + background-color:rgb(255 255 0); + box-shadow:0 2px 10px rgb(255 255 0); + } + +.annotationLayer .linkAnnotation.hasBorder:hover{ + background-color:rgb(255 255 0 / 0.2); + } + +.annotationLayer .hasBorder{ + background-size:100% 100%; + } + +.annotationLayer .textAnnotation img{ + position:absolute; + cursor:pointer; + width:100%; + height:100%; + top:0; + left:0; + } + +.annotationLayer .textWidgetAnnotation :is(input,textarea),.annotationLayer .choiceWidgetAnnotation select,.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input{ + background-image:var(--annotation-unfocused-field-background); + border:2px solid var(--input-unfocused-border-color); + box-sizing:border-box; + font:calc(9px * var(--scale-factor)) sans-serif; + height:100%; + margin:0; + vertical-align:top; + width:100%; + } + +.annotationLayer .textWidgetAnnotation :is(input,textarea):required,.annotationLayer .choiceWidgetAnnotation select:required,.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input:required{ + outline:1.5px solid red; + } + +.annotationLayer .choiceWidgetAnnotation select option{ + padding:0; + } + +.annotationLayer .buttonWidgetAnnotation.radioButton input{ + border-radius:50%; + } + +.annotationLayer .textWidgetAnnotation textarea{ + resize:none; + } + +.annotationLayer .textWidgetAnnotation [disabled]:is(input,textarea),.annotationLayer .choiceWidgetAnnotation select[disabled],.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input[disabled]{ + background:none; + border:2px solid var(--input-disabled-border-color); + cursor:not-allowed; + } + +.annotationLayer .textWidgetAnnotation :is(input,textarea):hover,.annotationLayer .choiceWidgetAnnotation select:hover,.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input:hover{ + border:2px solid var(--input-hover-border-color); + } + +.annotationLayer .textWidgetAnnotation :is(input,textarea):hover,.annotationLayer .choiceWidgetAnnotation select:hover,.annotationLayer .buttonWidgetAnnotation.checkBox input:hover{ + border-radius:2px; + } + +.annotationLayer .textWidgetAnnotation :is(input,textarea):focus,.annotationLayer .choiceWidgetAnnotation select:focus{ + background:none; + border:2px solid var(--input-focus-border-color); + border-radius:2px; + outline:var(--input-focus-outline); + } + +.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) :focus{ + background-image:none; + background-color:transparent; + } + +.annotationLayer .buttonWidgetAnnotation.checkBox :focus{ + border:2px solid var(--input-focus-border-color); + border-radius:2px; + outline:var(--input-focus-outline); + } + +.annotationLayer .buttonWidgetAnnotation.radioButton :focus{ + border:2px solid var(--input-focus-border-color); + outline:var(--input-focus-outline); + } + +.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before,.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after,.annotationLayer .buttonWidgetAnnotation.radioButton input:checked::before{ + background-color:CanvasText; + content:""; + display:block; + position:absolute; + } + +.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before,.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after{ + height:80%; + left:45%; + width:1px; + } + +.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before{ + transform:rotate(45deg); + } + +.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after{ + transform:rotate(-45deg); + } + +.annotationLayer .buttonWidgetAnnotation.radioButton input:checked::before{ + border-radius:50%; + height:50%; + left:25%; + top:25%; + width:50%; + } + +.annotationLayer .textWidgetAnnotation input.comb{ + font-family:monospace; + padding-left:2px; + padding-right:0; + } + +.annotationLayer .textWidgetAnnotation input.comb:focus{ + width:103%; + } + +.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input{ + -webkit-appearance:none; + -moz-appearance:none; + appearance:none; + } + +.annotationLayer .fileAttachmentAnnotation .popupTriggerArea{ + height:100%; + width:100%; + } + +.annotationLayer .popupAnnotation{ + position:absolute; + font-size:calc(9px * var(--scale-factor)); + pointer-events:none; + width:-moz-max-content; + width:max-content; + max-width:45%; + height:auto; + } + +.annotationLayer .popup{ + background-color:rgb(255 255 153); + box-shadow:0 calc(2px * var(--scale-factor)) calc(5px * var(--scale-factor)) rgb(136 136 136); + border-radius:calc(2px * var(--scale-factor)); + outline:1.5px solid rgb(255 255 74); + padding:calc(6px * var(--scale-factor)); + cursor:pointer; + font:message-box; + white-space:normal; + word-wrap:break-word; + pointer-events:auto; + } + +.annotationLayer .popupAnnotation.focused .popup{ + outline-width:3px; + } + +.annotationLayer .popup *{ + font-size:calc(9px * var(--scale-factor)); + } + +.annotationLayer .popup > .header{ + display:inline-block; + } + +.annotationLayer .popup > .header h1{ + display:inline; + } + +.annotationLayer .popup > .header .popupDate{ + display:inline-block; + margin-left:calc(5px * var(--scale-factor)); + width:-moz-fit-content; + width:fit-content; + } + +.annotationLayer .popupContent{ + border-top:1px solid rgb(51 51 51); + margin-top:calc(2px * var(--scale-factor)); + padding-top:calc(2px * var(--scale-factor)); + } + +.annotationLayer .richText > *{ + white-space:pre-wrap; + font-size:calc(9px * var(--scale-factor)); + } + +.annotationLayer .popupTriggerArea{ + cursor:pointer; + } + +.annotationLayer section svg{ + position:absolute; + width:100%; + height:100%; + top:0; + left:0; + } + +.annotationLayer .annotationTextContent{ + position:absolute; + width:100%; + height:100%; + opacity:0; + color:transparent; + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; + pointer-events:none; + } + +:is(.annotationLayer .annotationTextContent) span{ + width:100%; + display:inline-block; + } + +.annotationLayer svg.quadrilateralsContainer{ + contain:strict; + width:0; + height:0; + position:absolute; + top:0; + left:0; + z-index:-1; + } + +:root{ + --xfa-unfocused-field-background:url("data:image/svg+xml;charset=UTF-8,"); + --xfa-focus-outline:auto; +} + +@media screen and (forced-colors: active){ + :root{ + --xfa-focus-outline:2px solid CanvasText; + } + .xfaLayer *:required{ + outline:1.5px solid selectedItem; + } +} + +.xfaLayer{ + background-color:transparent; +} + +.xfaLayer .highlight{ + margin:-1px; + padding:1px; + background-color:rgb(239 203 237); + border-radius:4px; +} + +.xfaLayer .highlight.appended{ + position:initial; +} + +.xfaLayer .highlight.begin{ + border-radius:4px 0 0 4px; +} + +.xfaLayer .highlight.end{ + border-radius:0 4px 4px 0; +} + +.xfaLayer .highlight.middle{ + border-radius:0; +} + +.xfaLayer .highlight.selected{ + background-color:rgb(203 223 203); +} + +.xfaPage{ + overflow:hidden; + position:relative; +} + +.xfaContentarea{ + position:absolute; +} + +.xfaPrintOnly{ + display:none; +} + +.xfaLayer{ + position:absolute; + text-align:initial; + top:0; + left:0; + transform-origin:0 0; + line-height:1.2; +} + +.xfaLayer *{ + color:inherit; + font:inherit; + font-style:inherit; + font-weight:inherit; + font-kerning:inherit; + letter-spacing:-0.01px; + text-align:inherit; + text-decoration:inherit; + box-sizing:border-box; + background-color:transparent; + padding:0; + margin:0; + pointer-events:auto; + line-height:inherit; +} + +.xfaLayer *:required{ + outline:1.5px solid red; +} + +.xfaLayer div, +.xfaLayer svg, +.xfaLayer svg *{ + pointer-events:none; +} + +.xfaLayer a{ + color:blue; +} + +.xfaRich li{ + margin-left:3em; +} + +.xfaFont{ + color:black; + font-weight:normal; + font-kerning:none; + font-size:10px; + font-style:normal; + letter-spacing:0; + text-decoration:none; + vertical-align:0; +} + +.xfaCaption{ + overflow:hidden; + flex:0 0 auto; +} + +.xfaCaptionForCheckButton{ + overflow:hidden; + flex:1 1 auto; +} + +.xfaLabel{ + height:100%; + width:100%; +} + +.xfaLeft{ + display:flex; + flex-direction:row; + align-items:center; +} + +.xfaRight{ + display:flex; + flex-direction:row-reverse; + align-items:center; +} + +:is(.xfaLeft, .xfaRight) > :is(.xfaCaption, .xfaCaptionForCheckButton){ + max-height:100%; +} + +.xfaTop{ + display:flex; + flex-direction:column; + align-items:flex-start; +} + +.xfaBottom{ + display:flex; + flex-direction:column-reverse; + align-items:flex-start; +} + +:is(.xfaTop, .xfaBottom) > :is(.xfaCaption, .xfaCaptionForCheckButton){ + width:100%; +} + +.xfaBorder{ + background-color:transparent; + position:absolute; + pointer-events:none; +} + +.xfaWrapped{ + width:100%; + height:100%; +} + +:is(.xfaTextfield, .xfaSelect):focus{ + background-image:none; + background-color:transparent; + outline:var(--xfa-focus-outline); + outline-offset:-1px; +} + +:is(.xfaCheckbox, .xfaRadio):focus{ + outline:var(--xfa-focus-outline); +} + +.xfaTextfield, +.xfaSelect{ + height:100%; + width:100%; + flex:1 1 auto; + border:none; + resize:none; + background-image:var(--xfa-unfocused-field-background); +} + +.xfaSelect{ + padding-inline:2px; +} + +:is(.xfaTop, .xfaBottom) > :is(.xfaTextfield, .xfaSelect){ + flex:0 1 auto; +} + +.xfaButton{ + cursor:pointer; + width:100%; + height:100%; + border:none; + text-align:center; +} + +.xfaLink{ + width:100%; + height:100%; + position:absolute; + top:0; + left:0; +} + +.xfaCheckbox, +.xfaRadio{ + width:100%; + height:100%; + flex:0 0 auto; + border:none; +} + +.xfaRich{ + white-space:pre-wrap; + width:100%; + height:100%; +} + +.xfaImage{ + -o-object-position:left top; + object-position:left top; + -o-object-fit:contain; + object-fit:contain; + width:100%; + height:100%; +} + +.xfaLrTb, +.xfaRlTb, +.xfaTb{ + display:flex; + flex-direction:column; + align-items:stretch; +} + +.xfaLr{ + display:flex; + flex-direction:row; + align-items:stretch; +} + +.xfaRl{ + display:flex; + flex-direction:row-reverse; + align-items:stretch; +} + +.xfaTb > div{ + justify-content:left; +} + +.xfaPosition{ + position:relative; +} + +.xfaArea{ + position:relative; +} + +.xfaValignMiddle{ + display:flex; + align-items:center; +} + +.xfaTable{ + display:flex; + flex-direction:column; + align-items:stretch; +} + +.xfaTable .xfaRow{ + display:flex; + flex-direction:row; + align-items:stretch; +} + +.xfaTable .xfaRlRow{ + display:flex; + flex-direction:row-reverse; + align-items:stretch; + flex:1; +} + +.xfaTable .xfaRlRow > div{ + flex:1; +} + +:is(.xfaNonInteractive, .xfaDisabled, .xfaReadOnly) :is(input, textarea){ + background:initial; +} + +@media print{ + .xfaTextfield, + .xfaSelect{ + background:transparent; + } + + .xfaSelect{ + -webkit-appearance:none; + -moz-appearance:none; + appearance:none; + text-indent:1px; + text-overflow:""; + } +} + +.canvasWrapper svg{ + transform:none; + } + +.moving:is(.canvasWrapper svg){ + z-index:100000; + } + +[data-main-rotation="90"]:is(.highlight:is(.canvasWrapper svg),.highlightOutline:is(.canvasWrapper svg)) mask,[data-main-rotation="90"]:is(.highlight:is(.canvasWrapper svg),.highlightOutline:is(.canvasWrapper svg)) use:not(.clip,.mask){ + transform:matrix(0, 1, -1, 0, 1, 0); + } + +[data-main-rotation="180"]:is(.highlight:is(.canvasWrapper svg),.highlightOutline:is(.canvasWrapper svg)) mask,[data-main-rotation="180"]:is(.highlight:is(.canvasWrapper svg),.highlightOutline:is(.canvasWrapper svg)) use:not(.clip,.mask){ + transform:matrix(-1, 0, 0, -1, 1, 1); + } + +[data-main-rotation="270"]:is(.highlight:is(.canvasWrapper svg),.highlightOutline:is(.canvasWrapper svg)) mask,[data-main-rotation="270"]:is(.highlight:is(.canvasWrapper svg),.highlightOutline:is(.canvasWrapper svg)) use:not(.clip,.mask){ + transform:matrix(0, -1, 1, 0, 0, 1); + } + +.draw:is(.canvasWrapper svg){ + position:absolute; + mix-blend-mode:normal; + } + +.draw[data-draw-rotation="90"]:is(.canvasWrapper svg){ + transform:rotate(90deg); + } + +.draw[data-draw-rotation="180"]:is(.canvasWrapper svg){ + transform:rotate(180deg); + } + +.draw[data-draw-rotation="270"]:is(.canvasWrapper svg){ + transform:rotate(270deg); + } + +.highlight:is(.canvasWrapper svg){ + --blend-mode:multiply; + } + +@media screen and (forced-colors: active){ + +.highlight:is(.canvasWrapper svg){ + --blend-mode:difference; + } + } + +.highlight:is(.canvasWrapper svg){ + + position:absolute; + mix-blend-mode:var(--blend-mode); + } + +.highlight:is(.canvasWrapper svg):not(.free){ + fill-rule:evenodd; + } + +.highlightOutline:is(.canvasWrapper svg){ + position:absolute; + mix-blend-mode:normal; + fill-rule:evenodd; + fill:none; + } + +.highlightOutline.hovered:is(.canvasWrapper svg):not(.free):not(.selected){ + stroke:var(--hover-outline-color); + stroke-width:var(--outline-width); + } + +.highlightOutline.selected:is(.canvasWrapper svg):not(.free) .mainOutline{ + stroke:var(--outline-around-color); + stroke-width:calc( + var(--outline-width) + 2 * var(--outline-around-width) + ); + } + +.highlightOutline.selected:is(.canvasWrapper svg):not(.free) .secondaryOutline{ + stroke:var(--outline-color); + stroke-width:var(--outline-width); + } + +.highlightOutline.free.hovered:is(.canvasWrapper svg):not(.selected){ + stroke:var(--hover-outline-color); + stroke-width:calc(2 * var(--outline-width)); + } + +.highlightOutline.free.selected:is(.canvasWrapper svg) .mainOutline{ + stroke:var(--outline-around-color); + stroke-width:calc( + 2 * (var(--outline-width) + var(--outline-around-width)) + ); + } + +.highlightOutline.free.selected:is(.canvasWrapper svg) .secondaryOutline{ + stroke:var(--outline-color); + stroke-width:calc(2 * var(--outline-width)); + } + +.toggle-button{ + --button-background-color:#f0f0f4; + --button-background-color-hover:#e0e0e6; + --button-background-color-active:#cfcfd8; + --color-accent-primary:#0060df; + --color-accent-primary-hover:#0250bb; + --color-accent-primary-active:#054096; + --border-interactive-color:#8f8f9d; + --border-radius-circle:9999px; + --border-width:1px; + --size-item-small:16px; + --size-item-large:32px; + --color-canvas:white; +} + +@media (prefers-color-scheme: dark){ + +:where(html:not(.is-light)) .toggle-button{ + --button-background-color:color-mix(in srgb, currentColor 7%, transparent); + --button-background-color-hover:color-mix( + in srgb, + currentColor 14%, + transparent + ); + --button-background-color-active:color-mix( + in srgb, + currentColor 21%, + transparent + ); + --color-accent-primary:#0df; + --color-accent-primary-hover:#80ebff; + --color-accent-primary-active:#aaf2ff; + --border-interactive-color:#bfbfc9; + --color-canvas:#1c1b22; +} + } + +:where(html.is-dark) .toggle-button{ + --button-background-color:color-mix(in srgb, currentColor 7%, transparent); + --button-background-color-hover:color-mix( + in srgb, + currentColor 14%, + transparent + ); + --button-background-color-active:color-mix( + in srgb, + currentColor 21%, + transparent + ); + --color-accent-primary:#0df; + --color-accent-primary-hover:#80ebff; + --color-accent-primary-active:#aaf2ff; + --border-interactive-color:#bfbfc9; + --color-canvas:#1c1b22; +} + +@media (forced-colors: active){ + +.toggle-button{ + --color-accent-primary:ButtonText; + --color-accent-primary-hover:SelectedItem; + --color-accent-primary-active:SelectedItem; + --border-interactive-color:ButtonText; + --button-background-color:ButtonFace; + --border-interactive-color-hover:SelectedItem; + --border-interactive-color-active:SelectedItem; + --border-interactive-color-disabled:GrayText; + --color-canvas:ButtonText; +} + } + +.toggle-button{ + + --toggle-background-color:var(--button-background-color); + --toggle-background-color-hover:var(--button-background-color-hover); + --toggle-background-color-active:var(--button-background-color-active); + --toggle-background-color-pressed:var(--color-accent-primary); + --toggle-background-color-pressed-hover:var(--color-accent-primary-hover); + --toggle-background-color-pressed-active:var(--color-accent-primary-active); + --toggle-border-color:var(--border-interactive-color); + --toggle-border-color-hover:var(--toggle-border-color); + --toggle-border-color-active:var(--toggle-border-color); + --toggle-border-radius:var(--border-radius-circle); + --toggle-border-width:var(--border-width); + --toggle-height:var(--size-item-small); + --toggle-width:var(--size-item-large); + --toggle-dot-background-color:var(--toggle-border-color); + --toggle-dot-background-color-hover:var(--toggle-dot-background-color); + --toggle-dot-background-color-active:var(--toggle-dot-background-color); + --toggle-dot-background-color-on-pressed:var(--color-canvas); + --toggle-dot-margin:1px; + --toggle-dot-height:calc( + var(--toggle-height) - 2 * var(--toggle-dot-margin) - 2 * + var(--toggle-border-width) + ); + --toggle-dot-width:var(--toggle-dot-height); + --toggle-dot-transform-x:calc( + var(--toggle-width) - 4 * var(--toggle-dot-margin) - var(--toggle-dot-width) + ); + + -webkit-appearance:none; + + -moz-appearance:none; + + appearance:none; + padding:0; + margin:0; + border:var(--toggle-border-width) solid var(--toggle-border-color); + height:var(--toggle-height); + width:var(--toggle-width); + border-radius:var(--toggle-border-radius); + background:var(--toggle-background-color); + box-sizing:border-box; + flex-shrink:0; +} + +.toggle-button:focus-visible{ + outline:var(--focus-outline); + outline-offset:var(--focus-outline-offset); + } + +.toggle-button:enabled:hover{ + background:var(--toggle-background-color-hover); + border-color:var(--toggle-border-color); + } + +.toggle-button:enabled:active{ + background:var(--toggle-background-color-active); + border-color:var(--toggle-border-color); + } + +.toggle-button[aria-pressed="true"]{ + background:var(--toggle-background-color-pressed); + border-color:transparent; + } + +.toggle-button[aria-pressed="true"]:enabled:hover{ + background:var(--toggle-background-color-pressed-hover); + border-color:transparent; + } + +.toggle-button[aria-pressed="true"]:enabled:active{ + background:var(--toggle-background-color-pressed-active); + border-color:transparent; + } + +.toggle-button::before{ + display:block; + content:""; + background-color:var(--toggle-dot-background-color); + height:var(--toggle-dot-height); + width:var(--toggle-dot-width); + margin:var(--toggle-dot-margin); + border-radius:var(--toggle-border-radius); + translate:0; + } + +.toggle-button[aria-pressed="true"]::before{ + translate:var(--toggle-dot-transform-x); + background-color:var(--toggle-dot-background-color-on-pressed); + } + +.toggle-button[aria-pressed="true"]:enabled:hover::before,.toggle-button[aria-pressed="true"]:enabled:active::before{ + background-color:var(--toggle-dot-background-color-on-pressed); + } + +[dir="rtl"] .toggle-button[aria-pressed="true"]::before{ + translate:calc(-1 * var(--toggle-dot-transform-x)); + } + +@media (prefers-reduced-motion: no-preference){ + .toggle-button::before{ + transition:translate 100ms; + } + } + +@media (prefers-contrast){ + .toggle-button:enabled:hover{ + border-color:var(--toggle-border-color-hover); + } + + .toggle-button:enabled:active{ + border-color:var(--toggle-border-color-active); + } + + .toggle-button[aria-pressed="true"]:enabled{ + border-color:var(--toggle-border-color); + position:relative; + } + + .toggle-button[aria-pressed="true"]:enabled:hover,.toggle-button[aria-pressed="true"]:enabled:hover:active{ + border-color:var(--toggle-border-color-hover); + } + + .toggle-button[aria-pressed="true"]:enabled:active{ + background-color:var(--toggle-dot-background-color-active); + border-color:var(--toggle-dot-background-color-hover); + } + + .toggle-button:hover::before,.toggle-button:active::before{ + background-color:var(--toggle-dot-background-color-hover); + } + } + +@media (forced-colors){ + +.toggle-button{ + --toggle-dot-background-color:var(--color-accent-primary); + --toggle-dot-background-color-hover:var(--color-accent-primary-hover); + --toggle-dot-background-color-active:var(--color-accent-primary-active); + --toggle-dot-background-color-on-pressed:var(--button-background-color); + --toggle-background-color-disabled:var(--button-background-color-disabled); + --toggle-border-color-hover:var(--border-interactive-color-hover); + --toggle-border-color-active:var(--border-interactive-color-active); + --toggle-border-color-disabled:var(--border-interactive-color-disabled); +} + + .toggle-button[aria-pressed="true"]:enabled::after{ + border:1px solid var(--button-background-color); + content:""; + position:absolute; + height:var(--toggle-height); + width:var(--toggle-width); + display:block; + border-radius:var(--toggle-border-radius); + inset:-2px; + } + + .toggle-button[aria-pressed="true"]:enabled:active::after{ + border-color:var(--toggle-border-color-active); + } + } + +:root{ + --outline-width:2px; + --outline-color:#0060df; + --outline-around-width:1px; + --outline-around-color:#f0f0f4; + --hover-outline-around-color:var(--outline-around-color); + --focus-outline:solid var(--outline-width) var(--outline-color); + --unfocus-outline:solid var(--outline-width) transparent; + --focus-outline-around:solid var(--outline-around-width) var(--outline-around-color); + --hover-outline-color:#8f8f9d; + --hover-outline:solid var(--outline-width) var(--hover-outline-color); + --hover-outline-around:solid var(--outline-around-width) var(--hover-outline-around-color); + --freetext-line-height:1.35; + --freetext-padding:2px; + --resizer-bg-color:var(--outline-color); + --resizer-size:6px; + --resizer-shift:calc( + 0px - (var(--outline-width) + var(--resizer-size)) / 2 - + var(--outline-around-width) + ); + --editorFreeText-editing-cursor:text; + --editorInk-editing-cursor:url(images/cursor-editorInk.svg) 0 16, pointer; + --editorHighlight-editing-cursor:url(images/cursor-editorTextHighlight.svg) 24 24, text; + --editorFreeHighlight-editing-cursor:url(images/cursor-editorFreeHighlight.svg) 1 18, pointer; + + --new-alt-text-warning-image:url(images/altText_warning.svg); +} +.visuallyHidden{ + position:absolute; + top:0; + left:0; + border:0; + margin:0; + padding:0; + width:0; + height:0; + overflow:hidden; + white-space:nowrap; + font-size:0; +} + +.textLayer.highlighting{ + cursor:var(--editorFreeHighlight-editing-cursor); + } + +.textLayer.highlighting:not(.free) span{ + cursor:var(--editorHighlight-editing-cursor); + } + +[role="img"]:is(.textLayer.highlighting:not(.free) span){ + cursor:var(--editorFreeHighlight-editing-cursor); + } + +.textLayer.highlighting.free span{ + cursor:var(--editorFreeHighlight-editing-cursor); + } + +:is(#viewerContainer.pdfPresentationMode:fullscreen,.annotationEditorLayer.disabled) .noAltTextBadge{ + display:none !important; + } + +@media (min-resolution: 1.1dppx){ + :root{ + --editorFreeText-editing-cursor:url(images/cursor-editorFreeText.svg) 0 16, text; + } +} + +@media screen and (forced-colors: active){ + :root{ + --outline-color:CanvasText; + --outline-around-color:ButtonFace; + --resizer-bg-color:ButtonText; + --hover-outline-color:Highlight; + --hover-outline-around-color:SelectedItemText; + } +} + +[data-editor-rotation="90"]{ + transform:rotate(90deg); +} + +[data-editor-rotation="180"]{ + transform:rotate(180deg); +} + +[data-editor-rotation="270"]{ + transform:rotate(270deg); +} + +.annotationEditorLayer{ + background:transparent; + position:absolute; + inset:0; + font-size:calc(100px * var(--scale-factor)); + transform-origin:0 0; + cursor:auto; +} + +.annotationEditorLayer .selectedEditor{ + z-index:100000 !important; + } + +.annotationEditorLayer.drawing *{ + pointer-events:none !important; + } + +.annotationEditorLayer.waiting{ + content:""; + cursor:wait; + position:absolute; + inset:0; + width:100%; + height:100%; +} + +.annotationEditorLayer.disabled{ + pointer-events:none; +} + +.annotationEditorLayer.freetextEditing{ + cursor:var(--editorFreeText-editing-cursor); +} + +.annotationEditorLayer.inkEditing{ + cursor:var(--editorInk-editing-cursor); +} + +.annotationEditorLayer .draw{ + box-sizing:border-box; +} + +.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor){ + position:absolute; + background:transparent; + z-index:1; + transform-origin:0 0; + cursor:auto; + max-width:100%; + max-height:100%; + border:var(--unfocus-outline); +} + +.draggable.selectedEditor:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)){ + cursor:move; + } + +.selectedEditor:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)){ + border:var(--focus-outline); + outline:var(--focus-outline-around); + } + +.selectedEditor:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor))::before{ + content:""; + position:absolute; + inset:0; + border:var(--focus-outline-around); + pointer-events:none; + } + +:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)):hover:not(.selectedEditor){ + border:var(--hover-outline); + outline:var(--hover-outline-around); + } + +:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)):hover:not(.selectedEditor)::before{ + content:""; + position:absolute; + inset:0; + border:var(--focus-outline-around); + } + +:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar{ + --editor-toolbar-delete-image:url(images/editor-toolbar-delete.svg); + --editor-toolbar-bg-color:#f0f0f4; + --editor-toolbar-highlight-image:url(images/toolbarButton-editorHighlight.svg); + --editor-toolbar-fg-color:#2e2e56; + --editor-toolbar-border-color:#8f8f9d; + --editor-toolbar-hover-border-color:var(--editor-toolbar-border-color); + --editor-toolbar-hover-bg-color:#e0e0e6; + --editor-toolbar-hover-fg-color:var(--editor-toolbar-fg-color); + --editor-toolbar-hover-outline:none; + --editor-toolbar-focus-outline-color:#0060df; + --editor-toolbar-shadow:0 2px 6px 0 rgb(58 57 68 / 0.2); + --editor-toolbar-vert-offset:6px; + --editor-toolbar-height:28px; + --editor-toolbar-padding:2px; + --alt-text-done-color:#2ac3a2; + --alt-text-warning-color:#0090ed; + --alt-text-hover-done-color:var(--alt-text-done-color); + --alt-text-hover-warning-color:var(--alt-text-warning-color); + } + +@media (prefers-color-scheme: dark){ + +:where(html:not(.is-light)) :is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar{ + --editor-toolbar-bg-color:#2b2a33; + --editor-toolbar-fg-color:#fbfbfe; + --editor-toolbar-hover-bg-color:#52525e; + --editor-toolbar-focus-outline-color:#0df; + --alt-text-done-color:#54ffbd; + --alt-text-warning-color:#80ebff; + } + } + +:where(html.is-dark) :is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar{ + --editor-toolbar-bg-color:#2b2a33; + --editor-toolbar-fg-color:#fbfbfe; + --editor-toolbar-hover-bg-color:#52525e; + --editor-toolbar-focus-outline-color:#0df; + --alt-text-done-color:#54ffbd; + --alt-text-warning-color:#80ebff; + } + +@media screen and (forced-colors: active){ + +:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar{ + --editor-toolbar-bg-color:ButtonFace; + --editor-toolbar-fg-color:ButtonText; + --editor-toolbar-border-color:ButtonText; + --editor-toolbar-hover-border-color:AccentColor; + --editor-toolbar-hover-bg-color:ButtonFace; + --editor-toolbar-hover-fg-color:AccentColor; + --editor-toolbar-hover-outline:2px solid var(--editor-toolbar-hover-border-color); + --editor-toolbar-focus-outline-color:ButtonBorder; + --editor-toolbar-shadow:none; + --alt-text-done-color:var(--editor-toolbar-fg-color); + --alt-text-warning-color:var(--editor-toolbar-fg-color); + --alt-text-hover-done-color:var(--editor-toolbar-hover-fg-color); + --alt-text-hover-warning-color:var(--editor-toolbar-hover-fg-color); + } + } + +:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar{ + + display:flex; + width:-moz-fit-content; + width:fit-content; + height:var(--editor-toolbar-height); + flex-direction:column; + justify-content:center; + align-items:center; + cursor:default; + pointer-events:auto; + box-sizing:content-box; + padding:var(--editor-toolbar-padding); + + position:absolute; + inset-inline-end:0; + inset-block-start:calc(100% + var(--editor-toolbar-vert-offset)); + + border-radius:6px; + background-color:var(--editor-toolbar-bg-color); + border:1px solid var(--editor-toolbar-border-color); + box-shadow:var(--editor-toolbar-shadow); + } + +.hidden:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar){ + display:none; + } + +:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar):has(:focus-visible){ + border-color:transparent; + } + +[dir="ltr"] :is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar){ + transform-origin:100% 0; + } + +[dir="rtl"] :is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar){ + transform-origin:0 0; + } + +:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons{ + display:flex; + justify-content:center; + align-items:center; + gap:0; + height:100%; + } + +:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) button{ + padding:0; + } + +:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .divider{ + width:0; + height:calc( + 2 * var(--editor-toolbar-padding) + var(--editor-toolbar-height) + ); + border-left:1px solid var(--editor-toolbar-border-color); + border-right:none; + display:inline-block; + margin-inline:2px; + } + +:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .highlightButton{ + width:var(--editor-toolbar-height); + } + +:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .highlightButton)::before{ + content:""; + -webkit-mask-image:var(--editor-toolbar-highlight-image); + mask-image:var(--editor-toolbar-highlight-image); + -webkit-mask-repeat:no-repeat; + mask-repeat:no-repeat; + -webkit-mask-position:center; + mask-position:center; + display:inline-block; + background-color:var(--editor-toolbar-fg-color); + width:100%; + height:100%; + } + +:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .highlightButton):hover::before{ + background-color:var(--editor-toolbar-hover-fg-color); + } + +:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .delete{ + width:var(--editor-toolbar-height); + } + +:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .delete)::before{ + content:""; + -webkit-mask-image:var(--editor-toolbar-delete-image); + mask-image:var(--editor-toolbar-delete-image); + -webkit-mask-repeat:no-repeat; + mask-repeat:no-repeat; + -webkit-mask-position:center; + mask-position:center; + display:inline-block; + background-color:var(--editor-toolbar-fg-color); + width:100%; + height:100%; + } + +:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .delete):hover::before{ + background-color:var(--editor-toolbar-hover-fg-color); + } + +:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) > *{ + height:var(--editor-toolbar-height); + } + +:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) > :not(.divider){ + border:none; + background-color:transparent; + cursor:pointer; + } + +:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) > :not(.divider)):hover{ + border-radius:2px; + background-color:var(--editor-toolbar-hover-bg-color); + color:var(--editor-toolbar-hover-fg-color); + outline:var(--editor-toolbar-hover-outline); + outline-offset:1px; + } + +:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) > :not(.divider)):hover:active{ + outline:none; + } + +:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) > :not(.divider)):focus-visible{ + border-radius:2px; + outline:2px solid var(--editor-toolbar-focus-outline-color); + } + +:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText{ + --alt-text-add-image:url(images/altText_add.svg); + --alt-text-done-image:url(images/altText_done.svg); + + display:flex; + align-items:center; + justify-content:center; + width:-moz-max-content; + width:max-content; + padding-inline:8px; + pointer-events:all; + font:menu; + font-weight:590; + font-size:12px; + color:var(--editor-toolbar-fg-color); + } + +:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText):disabled{ + pointer-events:none; + } + +:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText)::before{ + content:""; + -webkit-mask-image:var(--alt-text-add-image); + mask-image:var(--alt-text-add-image); + -webkit-mask-repeat:no-repeat; + mask-repeat:no-repeat; + -webkit-mask-position:center; + mask-position:center; + display:inline-block; + width:12px; + height:13px; + background-color:var(--editor-toolbar-fg-color); + margin-inline-end:4px; + } + +:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText):hover::before{ + background-color:var(--editor-toolbar-hover-fg-color); + } + +.done:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText)::before{ + -webkit-mask-image:var(--alt-text-done-image); + mask-image:var(--alt-text-done-image); + } + +.new:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText)::before{ + width:16px; + height:16px; + -webkit-mask-image:var(--new-alt-text-warning-image); + mask-image:var(--new-alt-text-warning-image); + background-color:var(--alt-text-warning-color); + -webkit-mask-size:cover; + mask-size:cover; + } + +.new:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText):hover::before{ + background-color:var(--alt-text-hover-warning-color); + } + +.new.done:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText)::before{ + -webkit-mask-image:var(--alt-text-done-image); + mask-image:var(--alt-text-done-image); + background-color:var(--alt-text-done-color); + } + +.new.done:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText):hover::before{ + background-color:var(--alt-text-hover-done-color); + } + +:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText) .tooltip{ + display:none; + word-wrap:anywhere; + } + +.show:is(:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText) .tooltip){ + --alt-text-tooltip-bg:#f0f0f4; + --alt-text-tooltip-fg:#15141a; + --alt-text-tooltip-border:#8f8f9d; + --alt-text-tooltip-shadow:0px 2px 6px 0px rgb(58 57 68 / 0.2); + } + +@media (prefers-color-scheme: dark){ + +:where(html:not(.is-light)) .show:is(:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText) .tooltip){ + --alt-text-tooltip-bg:#1c1b22; + --alt-text-tooltip-fg:#fbfbfe; + --alt-text-tooltip-shadow:0px 2px 6px 0px #15141a; + } + } + +:where(html.is-dark) .show:is(:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText) .tooltip){ + --alt-text-tooltip-bg:#1c1b22; + --alt-text-tooltip-fg:#fbfbfe; + --alt-text-tooltip-shadow:0px 2px 6px 0px #15141a; + } + +@media screen and (forced-colors: active){ + +.show:is(:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText) .tooltip){ + --alt-text-tooltip-bg:Canvas; + --alt-text-tooltip-fg:CanvasText; + --alt-text-tooltip-border:CanvasText; + --alt-text-tooltip-shadow:none; + } + } + +.show:is(:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText) .tooltip){ + + display:inline-flex; + flex-direction:column; + align-items:center; + justify-content:center; + position:absolute; + top:calc(100% + 2px); + inset-inline-start:0; + padding-block:2px 3px; + padding-inline:3px; + max-width:300px; + width:-moz-max-content; + width:max-content; + height:auto; + font-size:12px; + + border:0.5px solid var(--alt-text-tooltip-border); + background:var(--alt-text-tooltip-bg); + box-shadow:var(--alt-text-tooltip-shadow); + color:var(--alt-text-tooltip-fg); + + pointer-events:none; + } + +.annotationEditorLayer .freeTextEditor{ + padding:calc(var(--freetext-padding) * var(--scale-factor)); + width:auto; + height:auto; + touch-action:none; +} + +.annotationEditorLayer .freeTextEditor .internal{ + background:transparent; + border:none; + inset:0; + overflow:visible; + white-space:nowrap; + font:10px sans-serif; + line-height:var(--freetext-line-height); + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; +} + +.annotationEditorLayer .freeTextEditor .overlay{ + position:absolute; + display:none; + background:transparent; + inset:0; + width:100%; + height:100%; +} + +.annotationEditorLayer freeTextEditor .overlay.enabled{ + display:block; +} + +.annotationEditorLayer .freeTextEditor .internal:empty::before{ + content:attr(default-content); + color:gray; +} + +.annotationEditorLayer .freeTextEditor .internal:focus{ + outline:none; + -webkit-user-select:auto; + -moz-user-select:auto; + user-select:auto; +} + +.annotationEditorLayer .inkEditor{ + width:100%; + height:100%; +} + +.annotationEditorLayer .inkEditor.editing{ + cursor:inherit; +} + +.annotationEditorLayer .inkEditor .inkEditorCanvas{ + position:absolute; + inset:0; + width:100%; + height:100%; + touch-action:none; +} + +.annotationEditorLayer .stampEditor{ + width:auto; + height:auto; +} + +:is(.annotationEditorLayer .stampEditor) canvas{ + position:absolute; + width:100%; + height:100%; + margin:0; + top:0; + left:0; + } + +:is(.annotationEditorLayer .stampEditor) .noAltTextBadge{ + --no-alt-text-badge-border-color:#f0f0f4; + --no-alt-text-badge-bg-color:#cfcfd8; + --no-alt-text-badge-fg-color:#5b5b66; + } + +@media (prefers-color-scheme: dark){ + +:where(html:not(.is-light)) :is(.annotationEditorLayer .stampEditor) .noAltTextBadge{ + --no-alt-text-badge-border-color:#52525e; + --no-alt-text-badge-bg-color:#fbfbfe; + --no-alt-text-badge-fg-color:#15141a; + } + } + +:where(html.is-dark) :is(.annotationEditorLayer .stampEditor) .noAltTextBadge{ + --no-alt-text-badge-border-color:#52525e; + --no-alt-text-badge-bg-color:#fbfbfe; + --no-alt-text-badge-fg-color:#15141a; + } + +@media screen and (forced-colors: active){ + +:is(.annotationEditorLayer .stampEditor) .noAltTextBadge{ + --no-alt-text-badge-border-color:ButtonText; + --no-alt-text-badge-bg-color:ButtonFace; + --no-alt-text-badge-fg-color:ButtonText; + } + } + +:is(.annotationEditorLayer .stampEditor) .noAltTextBadge{ + + position:absolute; + inset-inline-end:5px; + inset-block-end:5px; + display:inline-flex; + width:32px; + height:32px; + padding:3px; + justify-content:center; + align-items:center; + pointer-events:none; + z-index:1; + + border-radius:2px; + border:1px solid var(--no-alt-text-badge-border-color); + background:var(--no-alt-text-badge-bg-color); + } + +:is(:is(.annotationEditorLayer .stampEditor) .noAltTextBadge)::before{ + content:""; + display:inline-block; + width:16px; + height:16px; + -webkit-mask-image:var(--new-alt-text-warning-image); + mask-image:var(--new-alt-text-warning-image); + -webkit-mask-size:cover; + mask-size:cover; + background-color:var(--no-alt-text-badge-fg-color); + } + +:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers{ + position:absolute; + inset:0; + } + +.hidden:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers){ + display:none; + } + +:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer{ + width:var(--resizer-size); + height:var(--resizer-size); + background:content-box var(--resizer-bg-color); + border:var(--focus-outline-around); + border-radius:2px; + position:absolute; + } + +.topLeft:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ + top:var(--resizer-shift); + left:var(--resizer-shift); + } + +.topMiddle:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ + top:var(--resizer-shift); + left:calc(50% + var(--resizer-shift)); + } + +.topRight:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ + top:var(--resizer-shift); + right:var(--resizer-shift); + } + +.middleRight:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ + top:calc(50% + var(--resizer-shift)); + right:var(--resizer-shift); + } + +.bottomRight:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ + bottom:var(--resizer-shift); + right:var(--resizer-shift); + } + +.bottomMiddle:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ + bottom:var(--resizer-shift); + left:calc(50% + var(--resizer-shift)); + } + +.bottomLeft:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ + bottom:var(--resizer-shift); + left:var(--resizer-shift); + } + +.middleLeft:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ + top:calc(50% + var(--resizer-shift)); + left:var(--resizer-shift); + } + +.topLeft:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer),.bottomRight:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer){ + cursor:nwse-resize; + } + +.topMiddle:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer),.bottomMiddle:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer){ + cursor:ns-resize; + } + +.topRight:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer),.bottomLeft:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer){ + cursor:nesw-resize; + } + +.middleRight:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer),.middleLeft:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer){ + cursor:ew-resize; + } + +.topLeft:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer),.bottomRight:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer){ + cursor:nesw-resize; + } + +.topMiddle:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer),.bottomMiddle:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer){ + cursor:ew-resize; + } + +.topRight:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer),.bottomLeft:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer){ + cursor:nwse-resize; + } + +.middleRight:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer),.middleLeft:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer){ + cursor:ns-resize; + } + +:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="90"],[data-main-rotation="90"] [data-editor-rotation="0"],[data-main-rotation="180"] [data-editor-rotation="270"],[data-main-rotation="270"] [data-editor-rotation="180"])) .editToolbar{ + rotate:270deg; + } + +[dir="ltr"] :is(:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="90"],[data-main-rotation="90"] [data-editor-rotation="0"],[data-main-rotation="180"] [data-editor-rotation="270"],[data-main-rotation="270"] [data-editor-rotation="180"])) .editToolbar){ + inset-inline-end:calc(0px - var(--editor-toolbar-vert-offset)); + inset-block-start:0; + } + +[dir="rtl"] :is(:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="90"],[data-main-rotation="90"] [data-editor-rotation="0"],[data-main-rotation="180"] [data-editor-rotation="270"],[data-main-rotation="270"] [data-editor-rotation="180"])) .editToolbar){ + inset-inline-end:calc(100% + var(--editor-toolbar-vert-offset)); + inset-block-start:0; + } + +:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="180"],[data-main-rotation="90"] [data-editor-rotation="90"],[data-main-rotation="180"] [data-editor-rotation="0"],[data-main-rotation="270"] [data-editor-rotation="270"])) .editToolbar{ + rotate:180deg; + inset-inline-end:100%; + inset-block-start:calc(0pc - var(--editor-toolbar-vert-offset)); + } + +:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="270"],[data-main-rotation="90"] [data-editor-rotation="180"],[data-main-rotation="180"] [data-editor-rotation="90"],[data-main-rotation="270"] [data-editor-rotation="0"])) .editToolbar{ + rotate:90deg; + } + +[dir="ltr"] :is(:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="270"],[data-main-rotation="90"] [data-editor-rotation="180"],[data-main-rotation="180"] [data-editor-rotation="90"],[data-main-rotation="270"] [data-editor-rotation="0"])) .editToolbar){ + inset-inline-end:calc(100% + var(--editor-toolbar-vert-offset)); + inset-block-start:100%; + } + +[dir="rtl"] :is(:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="270"],[data-main-rotation="90"] [data-editor-rotation="180"],[data-main-rotation="180"] [data-editor-rotation="90"],[data-main-rotation="270"] [data-editor-rotation="0"])) .editToolbar){ + inset-inline-start:calc(0px - var(--editor-toolbar-vert-offset)); + inset-block-start:0; + } + +.dialog.altText::backdrop{ + -webkit-mask:url(#alttext-manager-mask); + mask:url(#alttext-manager-mask); + } + +.dialog.altText.positioned{ + margin:0; + } + +.dialog.altText #altTextContainer{ + width:300px; + height:-moz-fit-content; + height:fit-content; + display:inline-flex; + flex-direction:column; + align-items:flex-start; + gap:16px; + } + +:is(.dialog.altText #altTextContainer) #overallDescription{ + display:flex; + flex-direction:column; + align-items:flex-start; + gap:4px; + align-self:stretch; + } + +:is(:is(.dialog.altText #altTextContainer) #overallDescription) span{ + align-self:stretch; + } + +:is(:is(.dialog.altText #altTextContainer) #overallDescription) .title{ + font-size:13px; + font-style:normal; + font-weight:590; + } + +:is(.dialog.altText #altTextContainer) #addDescription{ + display:flex; + flex-direction:column; + align-items:stretch; + gap:8px; + } + +:is(:is(.dialog.altText #altTextContainer) #addDescription) .descriptionArea{ + flex:1; + padding-inline:24px 10px; + } + +:is(:is(:is(.dialog.altText #altTextContainer) #addDescription) .descriptionArea) textarea{ + width:100%; + min-height:75px; + } + +:is(.dialog.altText #altTextContainer) #buttons{ + display:flex; + justify-content:flex-end; + align-items:flex-start; + gap:8px; + align-self:stretch; + } + +.dialog.newAltText{ + --new-alt-text-ai-disclaimer-icon:url(images/altText_disclaimer.svg); + --new-alt-text-spinner-icon:url(images/altText_spinner.svg); + --preview-image-bg-color:#f0f0f4; + --preview-image-border:none; +} + +@media (prefers-color-scheme: dark){ + +:where(html:not(.is-light)) .dialog.newAltText{ + --preview-image-bg-color:#2b2a33; +} + } + +:where(html.is-dark) .dialog.newAltText{ + --preview-image-bg-color:#2b2a33; +} + +@media screen and (forced-colors: active){ + +.dialog.newAltText{ + --preview-image-bg-color:ButtonFace; + --preview-image-border:1px solid ButtonText; +} + } + +.dialog.newAltText{ + + width:80%; + max-width:570px; + min-width:300px; + padding:0; +} + +.dialog.newAltText.noAi #newAltTextDisclaimer,.dialog.newAltText.noAi #newAltTextCreateAutomatically{ + display:none !important; + } + +.dialog.newAltText.aiInstalling #newAltTextCreateAutomatically{ + display:none !important; + } + +.dialog.newAltText.aiInstalling #newAltTextDownloadModel{ + display:flex !important; + } + +.dialog.newAltText.error #newAltTextNotNow{ + display:none !important; + } + +.dialog.newAltText.error #newAltTextCancel{ + display:inline-block !important; + } + +.dialog.newAltText:not(.error) #newAltTextError{ + display:none !important; + } + +.dialog.newAltText #newAltTextContainer{ + display:flex; + width:auto; + padding:16px; + flex-direction:column; + justify-content:flex-end; + align-items:flex-start; + gap:12px; + flex:0 1 auto; + line-height:normal; + } + +:is(.dialog.newAltText #newAltTextContainer) #mainContent{ + display:flex; + justify-content:flex-end; + align-items:flex-start; + gap:12px; + align-self:stretch; + flex:1 1 auto; + } + +:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionAndSettings{ + display:flex; + flex-direction:column; + align-items:flex-start; + gap:16px; + flex:1 0 0; + align-self:stretch; + } + +:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction{ + display:flex; + flex-direction:column; + align-items:flex-start; + gap:8px; + align-self:stretch; + flex:1 1 auto; + } + +:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer{ + width:100%; + height:70px; + position:relative; + } + +:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) textarea{ + width:100%; + height:100%; + padding:8px; + } + +:is(:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) textarea)::-moz-placeholder{ + color:var(--text-secondary-color); + } + +:is(:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) textarea)::placeholder{ + color:var(--text-secondary-color); + } + +:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) .altTextSpinner{ + display:none; + position:absolute; + width:16px; + height:16px; + inset-inline-start:8px; + inset-block-start:8px; + -webkit-mask-size:cover; + mask-size:cover; + background-color:var(--text-secondary-color); + pointer-events:none; + } + +.loading:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) textarea::-moz-placeholder{ + color:transparent; + } + +.loading:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) textarea::placeholder{ + color:transparent; + } + +.loading:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) .altTextSpinner{ + display:inline-block; + -webkit-mask-image:var(--new-alt-text-spinner-icon); + mask-image:var(--new-alt-text-spinner-icon); + } + +:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescription{ + font-size:11px; + } + +:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDisclaimer{ + display:flex; + flex-direction:row; + align-items:flex-start; + gap:4px; + font-size:11px; + } + +:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDisclaimer)::before{ + content:""; + display:inline-block; + width:17px; + height:16px; + -webkit-mask-image:var(--new-alt-text-ai-disclaimer-icon); + mask-image:var(--new-alt-text-ai-disclaimer-icon); + -webkit-mask-size:cover; + mask-size:cover; + background-color:var(--text-secondary-color); + flex:1 0 auto; + } + +:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #newAltTextDownloadModel{ + display:flex; + align-items:center; + gap:4px; + align-self:stretch; + } + +:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #newAltTextDownloadModel)::before{ + content:""; + display:inline-block; + width:16px; + height:16px; + -webkit-mask-image:var(--new-alt-text-spinner-icon); + mask-image:var(--new-alt-text-spinner-icon); + -webkit-mask-size:cover; + mask-size:cover; + background-color:var(--text-secondary-color); + } + +:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #newAltTextImagePreview{ + width:180px; + aspect-ratio:1; + display:flex; + justify-content:center; + align-items:center; + flex:0 0 auto; + background-color:var(--preview-image-bg-color); + border:var(--preview-image-border); + } + +:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #newAltTextImagePreview) > canvas{ + max-width:100%; + max-height:100%; + } + +.colorPicker{ + --hover-outline-color:#0250bb; + --selected-outline-color:#0060df; + --swatch-border-color:#cfcfd8; +} + +@media (prefers-color-scheme: dark){ + +:where(html:not(.is-light)) .colorPicker{ + --hover-outline-color:#80ebff; + --selected-outline-color:#aaf2ff; + --swatch-border-color:#52525e; +} + } + +:where(html.is-dark) .colorPicker{ + --hover-outline-color:#80ebff; + --selected-outline-color:#aaf2ff; + --swatch-border-color:#52525e; +} + +@media screen and (forced-colors: active){ + +.colorPicker{ + --hover-outline-color:Highlight; + --selected-outline-color:var(--hover-outline-color); + --swatch-border-color:ButtonText; +} + } + +.colorPicker .swatch{ + width:16px; + height:16px; + border:1px solid var(--swatch-border-color); + border-radius:100%; + outline-offset:2px; + box-sizing:border-box; + forced-color-adjust:none; + } + +.colorPicker button:is(:hover,.selected) > .swatch{ + border:none; + } + +.annotationEditorLayer[data-main-rotation="0"] .highlightEditor:not(.free) > .editToolbar{ + rotate:0deg; + } + +.annotationEditorLayer[data-main-rotation="90"] .highlightEditor:not(.free) > .editToolbar{ + rotate:270deg; + } + +.annotationEditorLayer[data-main-rotation="180"] .highlightEditor:not(.free) > .editToolbar{ + rotate:180deg; + } + +.annotationEditorLayer[data-main-rotation="270"] .highlightEditor:not(.free) > .editToolbar{ + rotate:90deg; + } + +.annotationEditorLayer .highlightEditor{ + position:absolute; + background:transparent; + z-index:1; + cursor:auto; + max-width:100%; + max-height:100%; + border:none; + outline:none; + pointer-events:none; + transform-origin:0 0; + } + +:is(.annotationEditorLayer .highlightEditor):not(.free){ + transform:none; + } + +:is(.annotationEditorLayer .highlightEditor) .internal{ + position:absolute; + top:0; + left:0; + width:100%; + height:100%; + pointer-events:auto; + } + +.disabled:is(.annotationEditorLayer .highlightEditor) .internal{ + pointer-events:none; + } + +.selectedEditor:is(.annotationEditorLayer .highlightEditor) .internal{ + cursor:pointer; + } + +:is(.annotationEditorLayer .highlightEditor) .editToolbar{ + --editor-toolbar-colorpicker-arrow-image:url(images/toolbarButton-menuArrow.svg); + + transform-origin:center !important; + } + +:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker{ + position:relative; + width:auto; + display:flex; + justify-content:center; + align-items:center; + gap:4px; + padding:4px; + } + +:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker)::after{ + content:""; + -webkit-mask-image:var(--editor-toolbar-colorpicker-arrow-image); + mask-image:var(--editor-toolbar-colorpicker-arrow-image); + -webkit-mask-repeat:no-repeat; + mask-repeat:no-repeat; + -webkit-mask-position:center; + mask-position:center; + display:inline-block; + background-color:var(--editor-toolbar-fg-color); + width:12px; + height:12px; + } + +:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker):hover::after{ + background-color:var(--editor-toolbar-hover-fg-color); + } + +:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker):has(.dropdown:not(.hidden)){ + background-color:var(--editor-toolbar-hover-bg-color); + } + +:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker):has(.dropdown:not(.hidden))::after{ + scale:-1; + } + +:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker) .dropdown{ + position:absolute; + display:flex; + justify-content:center; + align-items:center; + flex-direction:column; + gap:11px; + padding-block:8px; + border-radius:6px; + background-color:var(--editor-toolbar-bg-color); + border:1px solid var(--editor-toolbar-border-color); + box-shadow:var(--editor-toolbar-shadow); + inset-block-start:calc(100% + 4px); + width:calc(100% + 2 * var(--editor-toolbar-padding)); + } + +:is(:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker) .dropdown) button{ + width:100%; + height:auto; + border:none; + cursor:pointer; + display:flex; + justify-content:center; + align-items:center; + background:none; + } + +:is(:is(:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker) .dropdown) button):is(:active,:focus-visible){ + outline:none; + } + +:is(:is(:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker) .dropdown) button) > .swatch{ + outline-offset:2px; + } + +[aria-selected="true"]:is(:is(:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker) .dropdown) button) > .swatch{ + outline:2px solid var(--selected-outline-color); + } + +:is(:is(:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker) .dropdown) button):is(:hover,:active,:focus-visible) > .swatch{ + outline:2px solid var(--hover-outline-color); + } + +.editorParamsToolbar:has(#highlightParamsToolbarContainer){ + padding:unset; +} + +#highlightParamsToolbarContainer{ + gap:16px; + padding-inline:10px; + padding-block-end:12px; +} + +#highlightParamsToolbarContainer .colorPicker{ + display:flex; + flex-direction:column; + gap:8px; + } + +:is(#highlightParamsToolbarContainer .colorPicker) .dropdown{ + display:flex; + justify-content:space-between; + align-items:center; + flex-direction:row; + height:auto; + } + +:is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button{ + width:auto; + height:auto; + border:none; + cursor:pointer; + display:flex; + justify-content:center; + align-items:center; + background:none; + flex:0 0 auto; + padding:0; + } + +:is(:is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button) .swatch{ + width:24px; + height:24px; + } + +:is(:is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button):is(:active,:focus-visible){ + outline:none; + } + +[aria-selected="true"]:is(:is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button) > .swatch{ + outline:2px solid var(--selected-outline-color); + } + +:is(:is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button):is(:hover,:active,:focus-visible) > .swatch{ + outline:2px solid var(--hover-outline-color); + } + +#highlightParamsToolbarContainer #editorHighlightThickness{ + display:flex; + flex-direction:column; + align-items:center; + gap:4px; + align-self:stretch; + } + +:is(#highlightParamsToolbarContainer #editorHighlightThickness) .editorParamsLabel{ + height:auto; + align-self:stretch; + } + +:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker{ + display:flex; + justify-content:space-between; + align-items:center; + align-self:stretch; + + --example-color:#bfbfc9; + } + +@media (prefers-color-scheme: dark){ + +:where(html:not(.is-light)) :is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker{ + --example-color:#80808e; + } + } + +:where(html.is-dark) :is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker{ + --example-color:#80808e; + } + +@media screen and (forced-colors: active){ + +:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker{ + --example-color:CanvasText; + } + } + +:is(:is(:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker) > .editorParamsSlider[disabled]){ + opacity:0.4; + } + +:is(:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker)::before,:is(:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker)::after{ + content:""; + width:8px; + aspect-ratio:1; + display:block; + border-radius:100%; + background-color:var(--example-color); + } + +:is(:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker)::after{ + width:24px; + } + +:is(:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker) .editorParamsSlider{ + width:unset; + height:14px; + } + +#highlightParamsToolbarContainer #editorHighlightVisibility{ + display:flex; + flex-direction:column; + align-items:flex-start; + gap:8px; + align-self:stretch; + } + +:is(#highlightParamsToolbarContainer #editorHighlightVisibility) .divider{ + --divider-color:#d7d7db; + } + +@media (prefers-color-scheme: dark){ + +:where(html:not(.is-light)) :is(#highlightParamsToolbarContainer #editorHighlightVisibility) .divider{ + --divider-color:#8f8f9d; + } + } + +:where(html.is-dark) :is(#highlightParamsToolbarContainer #editorHighlightVisibility) .divider{ + --divider-color:#8f8f9d; + } + +@media screen and (forced-colors: active){ + +:is(#highlightParamsToolbarContainer #editorHighlightVisibility) .divider{ + --divider-color:CanvasText; + } + } + +:is(#highlightParamsToolbarContainer #editorHighlightVisibility) .divider{ + + margin-block:4px; + width:100%; + height:1px; + background-color:var(--divider-color); + } + +:is(#highlightParamsToolbarContainer #editorHighlightVisibility) .toggler{ + display:flex; + justify-content:space-between; + align-items:center; + align-self:stretch; + } + +#altTextSettingsDialog{ + padding:16px; +} + +#altTextSettingsDialog #altTextSettingsContainer{ + display:flex; + width:573px; + flex-direction:column; + gap:16px; + } + +:is(#altTextSettingsDialog #altTextSettingsContainer) .mainContainer{ + gap:16px; + } + +:is(#altTextSettingsDialog #altTextSettingsContainer) .description{ + color:var(--text-secondary-color); + } + +:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings{ + display:flex; + flex-direction:column; + gap:12px; + } + +:is(:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings) button{ + width:-moz-fit-content; + width:fit-content; + } + +.download:is(:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings) #deleteModelButton{ + display:none; + } + +:is(:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings):not(.download) #downloadModelButton{ + display:none; + } + +:is(#altTextSettingsDialog #altTextSettingsContainer) #automaticAltText,:is(#altTextSettingsDialog #altTextSettingsContainer) #altTextEditor{ + display:flex; + flex-direction:column; + gap:8px; + } + +:is(#altTextSettingsDialog #altTextSettingsContainer) #createModelDescription,:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings,:is(#altTextSettingsDialog #altTextSettingsContainer) #showAltTextDialogDescription{ + padding-inline-start:40px; + } + +:is(#altTextSettingsDialog #altTextSettingsContainer) #automaticSettings{ + display:flex; + flex-direction:column; + gap:16px; + } + +:root{ + --viewer-container-height:0; + --pdfViewer-padding-bottom:0; + --page-margin:1px auto -8px; + --page-border:9px solid transparent; + --spreadHorizontalWrapped-margin-LR:-3.5px; + --loading-icon-delay:400ms; +} + +@media screen and (forced-colors: active){ + :root{ + --pdfViewer-padding-bottom:9px; + --page-margin:8px auto -1px; + --page-border:1px solid CanvasText; + --spreadHorizontalWrapped-margin-LR:3.5px; + } +} + +[data-main-rotation="90"]{ + transform:rotate(90deg) translateY(-100%); +} +[data-main-rotation="180"]{ + transform:rotate(180deg) translate(-100%, -100%); +} +[data-main-rotation="270"]{ + transform:rotate(270deg) translateX(-100%); +} + +#hiddenCopyElement, +.hiddenCanvasElement{ + position:absolute; + top:0; + left:0; + width:0; + height:0; + display:none; +} + +.pdfViewer{ + --scale-factor:1; + --page-bg-color:unset; + + padding-bottom:var(--pdfViewer-padding-bottom); + + --hcm-highlight-filter:none; + --hcm-highlight-selected-filter:none; +} + +@media screen and (forced-colors: active){ + +.pdfViewer{ + --hcm-highlight-filter:invert(100%); +} + } + +.pdfViewer.copyAll{ + cursor:wait; + } + +.pdfViewer .canvasWrapper{ + overflow:hidden; + width:100%; + height:100%; + } + +:is(.pdfViewer .canvasWrapper) canvas{ + position:absolute; + top:0; + left:0; + margin:0; + display:block; + width:100%; + height:100%; + contain:content; + } + +:is(:is(.pdfViewer .canvasWrapper) canvas) .structTree{ + contain:strict; + } + +.pdfViewer .page{ + --scale-round-x:1px; + --scale-round-y:1px; + + direction:ltr; + width:816px; + height:1056px; + margin:var(--page-margin); + position:relative; + overflow:visible; + border:var(--page-border); + background-clip:content-box; + background-color:var(--page-bg-color, rgb(255 255 255)); +} + +.pdfViewer .dummyPage{ + position:relative; + width:0; + height:var(--viewer-container-height); +} + +.pdfViewer.noUserSelect{ + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; +} + +.pdfViewer.removePageBorders .page{ + margin:0 auto 10px; + border:none; +} + +.pdfViewer:is(.scrollHorizontal, .scrollWrapped), +.spread{ + margin-inline:3.5px; + text-align:center; +} + +.pdfViewer.scrollHorizontal, +.spread{ + white-space:nowrap; +} + +.pdfViewer.removePageBorders, +.pdfViewer:is(.scrollHorizontal, .scrollWrapped) .spread{ + margin-inline:0; +} + +.spread :is(.page, .dummyPage), +.pdfViewer:is(.scrollHorizontal, .scrollWrapped) :is(.page, .spread){ + display:inline-block; + vertical-align:middle; +} + +.spread .page, +.pdfViewer:is(.scrollHorizontal, .scrollWrapped) .page{ + margin-inline:var(--spreadHorizontalWrapped-margin-LR); +} + +.pdfViewer.removePageBorders .spread .page, +.pdfViewer.removePageBorders:is(.scrollHorizontal, .scrollWrapped) .page{ + margin-inline:5px; +} + +.pdfViewer .page.loadingIcon::after{ + position:absolute; + top:0; + left:0; + content:""; + width:100%; + height:100%; + background:url("images/loading-icon.gif") center no-repeat; + display:none; + transition-property:display; + transition-delay:var(--loading-icon-delay); + z-index:5; + contain:strict; +} + +.pdfViewer .page.loading::after{ + display:block; +} + +.pdfViewer .page:not(.loading)::after{ + transition-property:none; + display:none; +} + +.pdfPresentationMode .pdfViewer{ + padding-bottom:0; +} + +.pdfPresentationMode .spread{ + margin:0; +} + +.pdfPresentationMode .pdfViewer .page{ + margin:0 auto; + border:2px solid transparent; +} + +:root{ + --dir-factor:1; + --inline-start:left; + --inline-end:right; + + --sidebar-width:200px; + --sidebar-transition-duration:200ms; + --sidebar-transition-timing-function:ease; + + --toolbar-height:32px; + --toolbar-horizontal-padding:1px; + --toolbar-vertical-padding:2px; + --icon-size:16px; + + --toolbar-icon-opacity:0.7; + --doorhanger-icon-opacity:0.9; + --doorhanger-height:8px; + + --main-color:rgb(12 12 13); + --body-bg-color:rgb(212 212 215); + --progressBar-color:rgb(10 132 255); + --progressBar-bg-color:rgb(221 221 222); + --progressBar-blend-color:rgb(116 177 239); + --scrollbar-color:auto; + --scrollbar-bg-color:auto; + --toolbar-icon-bg-color:rgb(0 0 0); + --toolbar-icon-hover-bg-color:rgb(0 0 0); + + --sidebar-narrow-bg-color:rgb(212 212 215 / 0.9); + --sidebar-toolbar-bg-color:rgb(245 246 247); + --toolbar-bg-color:rgb(249 249 250); + --toolbar-border-color:rgb(184 184 184); + --toolbar-box-shadow:0 1px 0 var(--toolbar-border-color); + --toolbar-border-bottom:none; + --toolbarSidebar-box-shadow:inset calc(-1px * var(--dir-factor)) 0 0 rgb(0 0 0 / 0.25), 0 1px 0 rgb(0 0 0 / 0.15), 0 0 1px rgb(0 0 0 / 0.1); + --toolbarSidebar-border-bottom:none; + --button-hover-color:rgb(221 222 223); + --toggled-btn-color:rgb(0 0 0); + --toggled-btn-bg-color:rgb(0 0 0 / 0.3); + --toggled-hover-active-btn-color:rgb(0 0 0 / 0.4); + --toggled-hover-btn-outline:none; + --dropdown-btn-bg-color:rgb(215 215 219); + --dropdown-btn-border:none; + --separator-color:rgb(0 0 0 / 0.3); + --field-color:rgb(6 6 6); + --field-bg-color:rgb(255 255 255); + --field-border-color:rgb(187 187 188); + --treeitem-color:rgb(0 0 0 / 0.8); + --treeitem-bg-color:rgb(0 0 0 / 0.15); + --treeitem-hover-color:rgb(0 0 0 / 0.9); + --treeitem-selected-color:rgb(0 0 0 / 0.9); + --treeitem-selected-bg-color:rgb(0 0 0 / 0.25); + --thumbnail-hover-color:rgb(0 0 0 / 0.1); + --thumbnail-selected-color:rgb(0 0 0 / 0.2); + --doorhanger-bg-color:rgb(255 255 255); + --doorhanger-border-color:rgb(12 12 13 / 0.2); + --doorhanger-hover-color:rgb(12 12 13); + --doorhanger-hover-bg-color:rgb(237 237 237); + --doorhanger-separator-color:rgb(222 222 222); + --dialog-button-border:none; + --dialog-button-bg-color:rgb(12 12 13 / 0.1); + --dialog-button-hover-bg-color:rgb(12 12 13 / 0.3); + + --loading-icon:url(images/loading.svg); + --treeitem-expanded-icon:url(images/treeitem-expanded.svg); + --treeitem-collapsed-icon:url(images/treeitem-collapsed.svg); + --toolbarButton-editorFreeText-icon:url(images/toolbarButton-editorFreeText.svg); + --toolbarButton-editorHighlight-icon:url(images/toolbarButton-editorHighlight.svg); + --toolbarButton-editorInk-icon:url(images/toolbarButton-editorInk.svg); + --toolbarButton-editorStamp-icon:url(images/toolbarButton-editorStamp.svg); + --toolbarButton-menuArrow-icon:url(images/toolbarButton-menuArrow.svg); + --toolbarButton-sidebarToggle-icon:url(images/toolbarButton-sidebarToggle.svg); + --toolbarButton-secondaryToolbarToggle-icon:url(images/toolbarButton-secondaryToolbarToggle.svg); + --toolbarButton-pageUp-icon:url(images/toolbarButton-pageUp.svg); + --toolbarButton-pageDown-icon:url(images/toolbarButton-pageDown.svg); + --toolbarButton-zoomOut-icon:url(images/toolbarButton-zoomOut.svg); + --toolbarButton-zoomIn-icon:url(images/toolbarButton-zoomIn.svg); + --toolbarButton-presentationMode-icon:url(images/toolbarButton-presentationMode.svg); + --toolbarButton-print-icon:url(images/toolbarButton-print.svg); + --toolbarButton-openFile-icon:url(images/toolbarButton-openFile.svg); + --toolbarButton-download-icon:url(images/toolbarButton-download.svg); + --toolbarButton-bookmark-icon:url(images/toolbarButton-bookmark.svg); + --toolbarButton-viewThumbnail-icon:url(images/toolbarButton-viewThumbnail.svg); + --toolbarButton-viewOutline-icon:url(images/toolbarButton-viewOutline.svg); + --toolbarButton-viewAttachments-icon:url(images/toolbarButton-viewAttachments.svg); + --toolbarButton-viewLayers-icon:url(images/toolbarButton-viewLayers.svg); + --toolbarButton-currentOutlineItem-icon:url(images/toolbarButton-currentOutlineItem.svg); + --toolbarButton-search-icon:url(images/toolbarButton-search.svg); + --findbarButton-previous-icon:url(images/findbarButton-previous.svg); + --findbarButton-next-icon:url(images/findbarButton-next.svg); + --secondaryToolbarButton-firstPage-icon:url(images/secondaryToolbarButton-firstPage.svg); + --secondaryToolbarButton-lastPage-icon:url(images/secondaryToolbarButton-lastPage.svg); + --secondaryToolbarButton-rotateCcw-icon:url(images/secondaryToolbarButton-rotateCcw.svg); + --secondaryToolbarButton-rotateCw-icon:url(images/secondaryToolbarButton-rotateCw.svg); + --secondaryToolbarButton-selectTool-icon:url(images/secondaryToolbarButton-selectTool.svg); + --secondaryToolbarButton-handTool-icon:url(images/secondaryToolbarButton-handTool.svg); + --secondaryToolbarButton-scrollPage-icon:url(images/secondaryToolbarButton-scrollPage.svg); + --secondaryToolbarButton-scrollVertical-icon:url(images/secondaryToolbarButton-scrollVertical.svg); + --secondaryToolbarButton-scrollHorizontal-icon:url(images/secondaryToolbarButton-scrollHorizontal.svg); + --secondaryToolbarButton-scrollWrapped-icon:url(images/secondaryToolbarButton-scrollWrapped.svg); + --secondaryToolbarButton-spreadNone-icon:url(images/secondaryToolbarButton-spreadNone.svg); + --secondaryToolbarButton-spreadOdd-icon:url(images/secondaryToolbarButton-spreadOdd.svg); + --secondaryToolbarButton-spreadEven-icon:url(images/secondaryToolbarButton-spreadEven.svg); + --secondaryToolbarButton-imageAltTextSettings-icon:var( + --toolbarButton-editorStamp-icon + ); + --secondaryToolbarButton-documentProperties-icon:url(images/secondaryToolbarButton-documentProperties.svg); + --editorParams-stampAddImage-icon:url(images/toolbarButton-zoomIn.svg); +} + +[dir="rtl"]:root{ + --dir-factor:-1; + --inline-start:right; + --inline-end:left; +} + +@media (prefers-color-scheme: dark){ + :root:where(:not(.is-light)){ + --main-color:rgb(249 249 250); + --body-bg-color:rgb(42 42 46); + --progressBar-color:rgb(0 96 223); + --progressBar-bg-color:rgb(40 40 43); + --progressBar-blend-color:rgb(20 68 133); + --scrollbar-color:rgb(121 121 123); + --scrollbar-bg-color:rgb(35 35 39); + --toolbar-icon-bg-color:rgb(255 255 255); + --toolbar-icon-hover-bg-color:rgb(255 255 255); + + --sidebar-narrow-bg-color:rgb(42 42 46 / 0.9); + --sidebar-toolbar-bg-color:rgb(50 50 52); + --toolbar-bg-color:rgb(56 56 61); + --toolbar-border-color:rgb(12 12 13); + --button-hover-color:rgb(102 102 103); + --toggled-btn-color:rgb(255 255 255); + --toggled-btn-bg-color:rgb(0 0 0 / 0.3); + --toggled-hover-active-btn-color:rgb(0 0 0 / 0.4); + --dropdown-btn-bg-color:rgb(74 74 79); + --separator-color:rgb(0 0 0 / 0.3); + --field-color:rgb(250 250 250); + --field-bg-color:rgb(64 64 68); + --field-border-color:rgb(115 115 115); + --treeitem-color:rgb(255 255 255 / 0.8); + --treeitem-bg-color:rgb(255 255 255 / 0.15); + --treeitem-hover-color:rgb(255 255 255 / 0.9); + --treeitem-selected-color:rgb(255 255 255 / 0.9); + --treeitem-selected-bg-color:rgb(255 255 255 / 0.25); + --thumbnail-hover-color:rgb(255 255 255 / 0.1); + --thumbnail-selected-color:rgb(255 255 255 / 0.2); + --doorhanger-bg-color:rgb(74 74 79); + --doorhanger-border-color:rgb(39 39 43); + --doorhanger-hover-color:rgb(249 249 250); + --doorhanger-hover-bg-color:rgb(93 94 98); + --doorhanger-separator-color:rgb(92 92 97); + --dialog-button-bg-color:rgb(92 92 97); + --dialog-button-hover-bg-color:rgb(115 115 115); + } +} + +:root:where(.is-dark){ + --main-color:rgb(249 249 250); + --body-bg-color:rgb(42 42 46); + --progressBar-color:rgb(0 96 223); + --progressBar-bg-color:rgb(40 40 43); + --progressBar-blend-color:rgb(20 68 133); + --scrollbar-color:rgb(121 121 123); + --scrollbar-bg-color:rgb(35 35 39); + --toolbar-icon-bg-color:rgb(255 255 255); + --toolbar-icon-hover-bg-color:rgb(255 255 255); + + --sidebar-narrow-bg-color:rgb(42 42 46 / 0.9); + --sidebar-toolbar-bg-color:rgb(50 50 52); + --toolbar-bg-color:rgb(56 56 61); + --toolbar-border-color:rgb(12 12 13); + --button-hover-color:rgb(102 102 103); + --toggled-btn-color:rgb(255 255 255); + --toggled-btn-bg-color:rgb(0 0 0 / 0.3); + --toggled-hover-active-btn-color:rgb(0 0 0 / 0.4); + --dropdown-btn-bg-color:rgb(74 74 79); + --separator-color:rgb(0 0 0 / 0.3); + --field-color:rgb(250 250 250); + --field-bg-color:rgb(64 64 68); + --field-border-color:rgb(115 115 115); + --treeitem-color:rgb(255 255 255 / 0.8); + --treeitem-bg-color:rgb(255 255 255 / 0.15); + --treeitem-hover-color:rgb(255 255 255 / 0.9); + --treeitem-selected-color:rgb(255 255 255 / 0.9); + --treeitem-selected-bg-color:rgb(255 255 255 / 0.25); + --thumbnail-hover-color:rgb(255 255 255 / 0.1); + --thumbnail-selected-color:rgb(255 255 255 / 0.2); + --doorhanger-bg-color:rgb(74 74 79); + --doorhanger-border-color:rgb(39 39 43); + --doorhanger-hover-color:rgb(249 249 250); + --doorhanger-hover-bg-color:rgb(93 94 98); + --doorhanger-separator-color:rgb(92 92 97); + --dialog-button-bg-color:rgb(92 92 97); + --dialog-button-hover-bg-color:rgb(115 115 115); + } + +@media screen and (forced-colors: active){ + :root{ + --button-hover-color:Highlight; + --doorhanger-hover-bg-color:Highlight; + --toolbar-icon-opacity:1; + --toolbar-icon-bg-color:ButtonText; + --toolbar-icon-hover-bg-color:ButtonFace; + --toggled-hover-active-btn-color:ButtonText; + --toggled-hover-btn-outline:2px solid ButtonBorder; + --toolbar-border-color:CanvasText; + --toolbar-border-bottom:1px solid var(--toolbar-border-color); + --toolbar-box-shadow:none; + --toggled-btn-color:HighlightText; + --toggled-btn-bg-color:LinkText; + --doorhanger-hover-color:ButtonFace; + --doorhanger-border-color-whcm:1px solid ButtonText; + --doorhanger-triangle-opacity-whcm:0; + --dialog-button-border:1px solid Highlight; + --dialog-button-hover-bg-color:Highlight; + --dialog-button-hover-color:ButtonFace; + --dropdown-btn-border:1px solid ButtonText; + --field-border-color:ButtonText; + --main-color:CanvasText; + --separator-color:GrayText; + --doorhanger-separator-color:GrayText; + --toolbarSidebar-box-shadow:none; + --toolbarSidebar-border-bottom:1px solid var(--toolbar-border-color); + } +} + +@media screen and (prefers-reduced-motion: reduce){ + :root{ + --sidebar-transition-duration:0; + } +} + +@keyframes progressIndeterminate{ + 0%{ + transform:translateX(calc(-142px * var(--dir-factor))); + } + + 100%{ + transform:translateX(0); + } +} + +html[data-toolbar-density="compact"]{ + --toolbar-height:30px; + } + +html[data-toolbar-density="touch"]{ + --toolbar-height:44px; + } + +html, +body{ + height:100%; + width:100%; +} + +body{ + margin:0; + background-color:var(--body-bg-color); + scrollbar-color:var(--scrollbar-color) var(--scrollbar-bg-color); +} + +body.wait::before{ + content:""; + position:fixed; + width:100%; + height:100%; + z-index:100000; + cursor:wait; + } + +.hidden, +[hidden]{ + display:none !important; +} + +#viewerContainer.pdfPresentationMode:fullscreen{ + top:0; + background-color:rgb(0 0 0); + width:100%; + height:100%; + overflow:hidden; + cursor:none; + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; +} + +.pdfPresentationMode:fullscreen section:not([data-internal-link]){ + pointer-events:none; +} + +.pdfPresentationMode:fullscreen .textLayer span{ + cursor:none; +} + +.pdfPresentationMode.pdfPresentationModeControls > *, +.pdfPresentationMode.pdfPresentationModeControls .textLayer span{ + cursor:default; +} + +#outerContainer{ + width:100%; + height:100%; + position:relative; + margin:0; +} + +#sidebarContainer{ + position:absolute; + inset-block:var(--toolbar-height) 0; + inset-inline-start:calc(-1 * var(--sidebar-width)); + width:var(--sidebar-width); + visibility:hidden; + z-index:1; + font:message-box; + border-top:1px solid transparent; + border-inline-end:var(--doorhanger-border-color-whcm); + transition-property:inset-inline-start; + transition-duration:var(--sidebar-transition-duration); + transition-timing-function:var(--sidebar-transition-timing-function); +} + +#outerContainer:is(.sidebarMoving, .sidebarOpen) #sidebarContainer{ + visibility:visible; +} + +#outerContainer.sidebarOpen #sidebarContainer{ + inset-inline-start:0; +} + +#mainContainer{ + position:absolute; + inset:0; + min-width:350px; + margin:0; + display:flex; + flex-direction:column; +} + +#sidebarContent{ + inset-block:var(--toolbar-height) 0; + inset-inline-start:0; + overflow:auto; + position:absolute; + width:100%; + box-shadow:inset calc(-1px * var(--dir-factor)) 0 0 rgb(0 0 0 / 0.25); +} + +#viewerContainer{ + overflow:auto; + position:absolute; + inset:var(--toolbar-height) 0 0; + outline:none; + z-index:0; +} + +#viewerContainer:not(.pdfPresentationMode){ + transition-duration:var(--sidebar-transition-duration); + transition-timing-function:var(--sidebar-transition-timing-function); +} + +#outerContainer.sidebarOpen #viewerContainer:not(.pdfPresentationMode){ + inset-inline-start:var(--sidebar-width); + transition-property:inset-inline-start; +} + +#sidebarContainer :is(input, button, select){ + font:message-box; +} + +.toolbar{ + z-index:2; +} + +#toolbarSidebar{ + width:100%; + height:var(--toolbar-height); + background-color:var(--sidebar-toolbar-bg-color); + box-shadow:var(--toolbarSidebar-box-shadow); + border-bottom:var(--toolbarSidebar-border-bottom); + padding:var(--toolbar-vertical-padding) var(--toolbar-horizontal-padding); + justify-content:space-between; +} + +#toolbarSidebar #toolbarSidebarLeft{ + width:auto; + height:100%; + } + +:is(#toolbarSidebar #toolbarSidebarLeft) #viewThumbnail::before{ + -webkit-mask-image:var(--toolbarButton-viewThumbnail-icon); + mask-image:var(--toolbarButton-viewThumbnail-icon); + } + +:is(#toolbarSidebar #toolbarSidebarLeft) #viewOutline::before{ + -webkit-mask-image:var(--toolbarButton-viewOutline-icon); + mask-image:var(--toolbarButton-viewOutline-icon); + transform:scaleX(var(--dir-factor)); + } + +:is(#toolbarSidebar #toolbarSidebarLeft) #viewAttachments::before{ + -webkit-mask-image:var(--toolbarButton-viewAttachments-icon); + mask-image:var(--toolbarButton-viewAttachments-icon); + } + +:is(#toolbarSidebar #toolbarSidebarLeft) #viewLayers::before{ + -webkit-mask-image:var(--toolbarButton-viewLayers-icon); + mask-image:var(--toolbarButton-viewLayers-icon); + } + +#toolbarSidebar #toolbarSidebarRight{ + width:auto; + height:100%; + padding-inline-end:2px; + } + +#sidebarResizer{ + position:absolute; + inset-block:0; + inset-inline-end:-6px; + width:6px; + z-index:200; + cursor:ew-resize; +} + +#outerContainer.sidebarOpen #loadingBar{ + inset-inline-start:var(--sidebar-width); +} + +#outerContainer.sidebarResizing + :is(#sidebarContainer, #viewerContainer, #loadingBar){ + transition-duration:0s; +} + +.doorHanger, +.doorHangerRight{ + border-radius:2px; + box-shadow:0 1px 5px var(--doorhanger-border-color), 0 0 0 1px var(--doorhanger-border-color); + border:var(--doorhanger-border-color-whcm); + background-color:var(--doorhanger-bg-color); + inset-block-start:calc(100% + var(--doorhanger-height) - 2px); +} + +:is(.doorHanger,.doorHangerRight)::after,:is(.doorHanger,.doorHangerRight)::before{ + bottom:100%; + border-style:solid; + border-color:transparent; + content:""; + height:0; + width:0; + position:absolute; + pointer-events:none; + opacity:var(--doorhanger-triangle-opacity-whcm); + } + +:is(.doorHanger,.doorHangerRight)::before{ + border-width:calc(var(--doorhanger-height) + 2px); + border-bottom-color:var(--doorhanger-border-color); + } + +:is(.doorHanger,.doorHangerRight)::after{ + border-width:var(--doorhanger-height); + } + +.doorHangerRight{ + inset-inline-end:calc(50% - var(--doorhanger-height) - 1px); +} + +.doorHangerRight::before{ + inset-inline-end:-1px; + } + +.doorHangerRight::after{ + border-bottom-color:var(--doorhanger-bg-color); + inset-inline-end:1px; + } + +.doorHanger{ + inset-inline-start:calc(50% - var(--doorhanger-height) - 1px); +} + +.doorHanger::before{ + inset-inline-start:-1px; + } + +.doorHanger::after{ + border-bottom-color:var(--toolbar-bg-color); + inset-inline-start:1px; + } + +.dialogButton{ + border:none; + background:none; + width:28px; + height:28px; + outline:none; +} + +.dialogButton:is(:hover, :focus-visible){ + background-color:var(--dialog-button-hover-bg-color); +} + +.dialogButton:is(:hover, :focus-visible) > span{ + color:var(--dialog-button-hover-color); +} + +.splitToolbarButtonSeparator{ + float:var(--inline-start); + width:0; + height:62%; + border-left:1px solid var(--separator-color); + border-right:none; +} + +.dialogButton{ + min-width:16px; + margin:2px 1px; + padding:2px 6px 0; + border:none; + border-radius:2px; + color:var(--main-color); + font-size:12px; + line-height:14px; + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; + cursor:default; + box-sizing:border-box; +} + +.treeItemToggler::before{ + position:absolute; + display:inline-block; + width:16px; + height:16px; + + content:""; + background-color:var(--toolbar-icon-bg-color); + -webkit-mask-size:cover; + mask-size:cover; +} + +#sidebarToggleButton::before{ + -webkit-mask-image:var(--toolbarButton-sidebarToggle-icon); + mask-image:var(--toolbarButton-sidebarToggle-icon); + transform:scaleX(var(--dir-factor)); +} + +#secondaryToolbarToggleButton::before{ + -webkit-mask-image:var(--toolbarButton-secondaryToolbarToggle-icon); + mask-image:var(--toolbarButton-secondaryToolbarToggle-icon); + transform:scaleX(var(--dir-factor)); +} + +#previous::before{ + -webkit-mask-image:var(--toolbarButton-pageUp-icon); + mask-image:var(--toolbarButton-pageUp-icon); +} + +#next::before{ + -webkit-mask-image:var(--toolbarButton-pageDown-icon); + mask-image:var(--toolbarButton-pageDown-icon); +} + +#zoomOutButton::before{ + -webkit-mask-image:var(--toolbarButton-zoomOut-icon); + mask-image:var(--toolbarButton-zoomOut-icon); +} + +#zoomInButton::before{ + -webkit-mask-image:var(--toolbarButton-zoomIn-icon); + mask-image:var(--toolbarButton-zoomIn-icon); +} + +#presentationMode::before{ + -webkit-mask-image:var(--toolbarButton-presentationMode-icon); + mask-image:var(--toolbarButton-presentationMode-icon); +} + +#editorFreeTextButton::before{ + -webkit-mask-image:var(--toolbarButton-editorFreeText-icon); + mask-image:var(--toolbarButton-editorFreeText-icon); +} + +#editorHighlightButton::before{ + -webkit-mask-image:var(--toolbarButton-editorHighlight-icon); + mask-image:var(--toolbarButton-editorHighlight-icon); +} + +#editorInkButton::before{ + -webkit-mask-image:var(--toolbarButton-editorInk-icon); + mask-image:var(--toolbarButton-editorInk-icon); +} + +#editorStampButton::before{ + -webkit-mask-image:var(--toolbarButton-editorStamp-icon); + mask-image:var(--toolbarButton-editorStamp-icon); +} + +#printButton::before{ + -webkit-mask-image:var(--toolbarButton-print-icon); + mask-image:var(--toolbarButton-print-icon); +} + +#secondaryOpenFile::before{ + -webkit-mask-image:var(--toolbarButton-openFile-icon); + mask-image:var(--toolbarButton-openFile-icon); +} + +#downloadButton::before{ + -webkit-mask-image:var(--toolbarButton-download-icon); + mask-image:var(--toolbarButton-download-icon); +} + +#viewBookmark::before{ + -webkit-mask-image:var(--toolbarButton-bookmark-icon); + mask-image:var(--toolbarButton-bookmark-icon); +} + +#currentOutlineItem::before{ + -webkit-mask-image:var(--toolbarButton-currentOutlineItem-icon); + mask-image:var(--toolbarButton-currentOutlineItem-icon); + transform:scaleX(var(--dir-factor)); +} + +#viewFindButton::before{ + -webkit-mask-image:var(--toolbarButton-search-icon); + mask-image:var(--toolbarButton-search-icon); +} + +.pdfSidebarNotification::after{ + position:absolute; + display:inline-block; + top:2px; + inset-inline-end:2px; + content:""; + background-color:rgb(112 219 85); + height:9px; + width:9px; + border-radius:50%; +} + +.verticalToolbarSeparator{ + display:block; + margin-inline:2px; + width:0; + height:80%; + border-left:1px solid var(--separator-color); + border-right:none; + box-sizing:border-box; +} + +.horizontalToolbarSeparator{ + display:block; + margin:6px 0; + border-top:1px solid var(--doorhanger-separator-color); + border-bottom:none; + height:0; + width:100%; +} + +.toggleButton{ + display:inline; +} + +.toggleButton:has( > input:checked){ + color:var(--toggled-btn-color); + background-color:var(--toggled-btn-bg-color); + } + +.toggleButton:is(:hover,:has( > input:focus-visible)){ + color:var(--toggled-btn-color); + background-color:var(--button-hover-color); + } + +.toggleButton > input{ + position:absolute; + top:50%; + left:50%; + opacity:0; + width:0; + height:0; + } + +.toolbarField{ + padding:4px 7px; + margin:3px 0; + border-radius:2px; + background-color:var(--field-bg-color); + background-clip:padding-box; + border:1px solid var(--field-border-color); + box-shadow:none; + color:var(--field-color); + font-size:12px; + line-height:16px; + outline:none; +} + +.toolbarField:focus{ + border-color:#0a84ff; + } + +#pageNumber{ + -moz-appearance:textfield; + text-align:end; + width:40px; + background-size:0 0; + transition-property:none; +} + +#pageNumber::-webkit-inner-spin-button{ + -webkit-appearance:none; + } + +.loadingInput:has( > .loading:is(#pageNumber))::after{ + display:inline; + visibility:visible; + + transition-property:visibility; + transition-delay:var(--loading-icon-delay); + } + +.loadingInput{ + position:relative; +} + +.loadingInput::after{ + position:absolute; + visibility:hidden; + display:none; + width:var(--icon-size); + height:var(--icon-size); + + content:""; + background-color:var(--toolbar-icon-bg-color); + -webkit-mask-size:cover; + mask-size:cover; + -webkit-mask-image:var(--loading-icon); + mask-image:var(--loading-icon); + } + +.loadingInput.start::after{ + inset-inline-start:4px; + } + +.loadingInput.end::after{ + inset-inline-end:4px; + } + +#thumbnailView, +#outlineView, +#attachmentsView, +#layersView{ + position:absolute; + width:calc(100% - 8px); + inset-block:0; + padding:4px 4px 0; + overflow:auto; + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; +} + +#thumbnailView{ + width:calc(100% - 60px); + padding:10px 30px 0; +} + +#thumbnailView > a:is(:active, :focus){ + outline:0; +} + +.thumbnail{ + --thumbnail-width:0; + --thumbnail-height:0; + + float:var(--inline-start); + width:var(--thumbnail-width); + height:var(--thumbnail-height); + margin:0 10px 5px; + padding:1px; + border:7px solid transparent; + border-radius:2px; +} + +#thumbnailView > a:last-of-type > .thumbnail{ + margin-bottom:10px; +} + +a:focus > .thumbnail, +.thumbnail:hover{ + border-color:var(--thumbnail-hover-color); +} + +.thumbnail.selected{ + border-color:var(--thumbnail-selected-color) !important; +} + +.thumbnailImage{ + width:var(--thumbnail-width); + height:var(--thumbnail-height); + opacity:0.9; +} + +a:focus > .thumbnail > .thumbnailImage, +.thumbnail:hover > .thumbnailImage{ + opacity:0.95; +} + +.thumbnail.selected > .thumbnailImage{ + opacity:1 !important; +} + +.thumbnail:not([data-loaded]) > .thumbnailImage{ + width:calc(var(--thumbnail-width) - 2px); + height:calc(var(--thumbnail-height) - 2px); + border:1px dashed rgb(132 132 132); +} + +.treeWithDeepNesting > .treeItem, +.treeItem > .treeItems{ + margin-inline-start:20px; +} + +.treeItem > a{ + text-decoration:none; + display:inline-block; + min-width:calc(100% - 4px); + height:auto; + margin-bottom:1px; + padding:2px 0 5px; + padding-inline-start:4px; + border-radius:2px; + color:var(--treeitem-color); + font-size:13px; + line-height:15px; + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; + white-space:normal; + cursor:pointer; +} + +#layersView .treeItem > a *{ + cursor:pointer; +} + +#layersView .treeItem > a > label{ + padding-inline-start:4px; +} + +#layersView .treeItem > a > label > input{ + float:var(--inline-start); + margin-top:1px; +} + +.treeItemToggler{ + position:relative; + float:var(--inline-start); + height:0; + width:0; + color:rgb(255 255 255 / 0.5); +} + +.treeItemToggler::before{ + inset-inline-end:4px; + -webkit-mask-image:var(--treeitem-expanded-icon); + mask-image:var(--treeitem-expanded-icon); +} + +.treeItemToggler.treeItemsHidden::before{ + -webkit-mask-image:var(--treeitem-collapsed-icon); + mask-image:var(--treeitem-collapsed-icon); + transform:scaleX(var(--dir-factor)); +} + +.treeItemToggler.treeItemsHidden ~ .treeItems{ + display:none; +} + +.treeItem.selected > a{ + background-color:var(--treeitem-selected-bg-color); + color:var(--treeitem-selected-color); +} + +.treeItemToggler:hover, +.treeItemToggler:hover + a, +.treeItemToggler:hover ~ .treeItems, +.treeItem > a:hover{ + background-color:var(--treeitem-bg-color); + background-clip:padding-box; + border-radius:2px; + color:var(--treeitem-hover-color); +} + +#outlineOptionsContainer{ + display:none; +} + +#sidebarContainer:has(#outlineView:not(.hidden)) #outlineOptionsContainer{ + display:inline flex; + } + +.dialogButton{ + width:auto; + margin:3px 4px 2px !important; + padding:2px 11px; + color:var(--main-color); + background-color:var(--dialog-button-bg-color); + border:var(--dialog-button-border) !important; +} + +dialog{ + margin:auto; + padding:15px; + border-spacing:4px; + color:var(--main-color); + font:message-box; + font-size:12px; + line-height:14px; + background-color:var(--doorhanger-bg-color); + border:1px solid rgb(0 0 0 / 0.5); + border-radius:4px; + box-shadow:0 1px 4px rgb(0 0 0 / 0.3); +} + +dialog::backdrop{ + background-color:rgb(0 0 0 / 0.2); +} + +dialog > .row{ + display:table-row; +} + +dialog > .row > *{ + display:table-cell; +} + +dialog .toolbarField{ + margin:5px 0; +} + +dialog .separator{ + display:block; + margin:4px 0; + height:0; + width:100%; + border-top:1px solid var(--separator-color); + border-bottom:none; +} + +dialog .buttonRow{ + text-align:center; + vertical-align:middle; +} + +dialog :link{ + color:rgb(255 255 255); +} + +#passwordDialog{ + text-align:center; +} + +#passwordDialog .toolbarField{ + width:200px; +} + +#documentPropertiesDialog{ + text-align:left; +} + +#documentPropertiesDialog .row > *{ + min-width:100px; + text-align:start; +} + +#documentPropertiesDialog .row > span{ + width:125px; + word-wrap:break-word; +} + +#documentPropertiesDialog .row > p{ + max-width:225px; + word-wrap:break-word; +} + +#documentPropertiesDialog .buttonRow{ + margin-top:10px; +} + +.grab-to-pan-grab{ + cursor:grab !important; +} + +.grab-to-pan-grab + *:not(input):not(textarea):not(button):not(select):not(:link){ + cursor:inherit !important; +} + +.grab-to-pan-grab:active, +.grab-to-pan-grabbing{ + cursor:grabbing !important; +} + +.grab-to-pan-grabbing{ + position:fixed; + background:rgb(0 0 0 / 0); + display:block; + inset:0; + overflow:hidden; + z-index:50000; +} + +.toolbarButton{ + height:100%; + aspect-ratio:1; + display:flex; + align-items:center; + justify-content:center; + background:none; + border:none; + color:var(--main-color); + outline:none; + border-radius:2px; + box-sizing:border-box; + font:message-box; + flex:none; + position:relative; + padding:0; +} + +.toolbarButton > span{ + display:inline-block; + width:0; + height:0; + overflow:hidden; + } + +.toolbarButton::before{ + opacity:var(--toolbar-icon-opacity); + display:inline-block; + width:var(--icon-size); + height:var(--icon-size); + content:""; + background-color:var(--toolbar-icon-bg-color); + -webkit-mask-size:cover; + mask-size:cover; + -webkit-mask-position:center; + mask-position:center; + } + +.toolbarButton.toggled{ + background-color:var(--toggled-btn-bg-color); + color:var(--toggled-btn-color); + } + +.toolbarButton.toggled::before{ + background-color:var(--toggled-btn-color); + } + +.toolbarButton.toggled:hover{ + outline:var(--toggled-hover-btn-outline) !important; + } + +.toolbarButton.toggled:hover:active{ + background-color:var(--toggled-hover-active-btn-color); + } + +.toolbarButton:is(:hover,:focus-visible){ + background-color:var(--button-hover-color); + } + +.toolbarButton:is(:hover,:focus-visible)::before{ + background-color:var(--toolbar-icon-hover-bg-color); + } + +.toolbarButton:is([disabled="disabled"],[disabled]){ + opacity:0.5; + pointer-events:none; + } + +.toolbarButton.labeled{ + width:100%; + min-height:var(--menuitem-height); + justify-content:flex-start; + gap:8px; + padding-inline-start:12px; + aspect-ratio:unset; + text-align:start; + white-space:normal; + cursor:default; + } + +.toolbarButton.labeled:is(a){ + text-decoration:none; + } + +.toolbarButton.labeled[href="#"]:is(a){ + opacity:0.5; + pointer-events:none; + } + +.toolbarButton.labeled::before{ + opacity:var(--doorhanger-icon-opacity); + } + +.toolbarButton.labeled:is(:hover,:focus-visible){ + background-color:var(--doorhanger-hover-bg-color); + color:var(--doorhanger-hover-color); + } + +.toolbarButton.labeled > span{ + display:inline-block; + width:-moz-max-content; + width:max-content; + height:auto; + } + +.toolbarButtonWithContainer{ + height:100%; + aspect-ratio:1; + display:inline-block; + position:relative; + flex:none; +} + +.toolbarButtonWithContainer > .toolbarButton{ + width:100%; + height:100%; + } + +.toolbarButtonWithContainer .menu{ + padding-block:5px; + } + +.toolbarButtonWithContainer .menuContainer{ + width:100%; + height:auto; + max-height:calc( + var(--viewer-container-height) - var(--toolbar-height) - + var(--doorhanger-height) + ); + display:flex; + flex-direction:column; + box-sizing:border-box; + overflow-y:auto; + } + +.toolbarButtonWithContainer .editorParamsToolbar{ + height:auto; + width:220px; + position:absolute; + z-index:30000; + cursor:default; + } + +:is(.toolbarButtonWithContainer .editorParamsToolbar) #editorStampAddImage::before{ + -webkit-mask-image:var(--editorParams-stampAddImage-icon); + mask-image:var(--editorParams-stampAddImage-icon); + } + +:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsLabel{ + flex:none; + font:menu; + font-size:13px; + font-style:normal; + font-weight:400; + line-height:150%; + color:var(--main-color); + width:-moz-fit-content; + width:fit-content; + inset-inline-start:0; + } + +:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer{ + width:100%; + height:auto; + display:flex; + flex-direction:column; + box-sizing:border-box; + padding-inline:10px; + padding-block:10px; + } + +:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) > .editorParamsSetter{ + min-height:26px; + display:flex; + align-items:center; + justify-content:space-between; + } + +:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsColor{ + width:32px; + height:32px; + flex:none; + padding:0; + } + +:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsSlider{ + background-color:transparent; + width:90px; + flex:0 1 0; + font:message-box; + } + +:is(:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsSlider)::-moz-range-progress{ + background-color:black; + } + +:is(:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsSlider)::-webkit-slider-runnable-track,:is(:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsSlider)::-moz-range-track{ + background-color:black; + } + +:is(:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsSlider)::-webkit-slider-thumb,:is(:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsSlider)::-moz-range-thumb{ + background-color:white; + } + +#secondaryToolbar{ + height:auto; + width:220px; + position:absolute; + z-index:30000; + cursor:default; + min-height:26px; + max-height:calc(var(--viewer-container-height) - 40px); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #secondaryOpenFile::before{ + -webkit-mask-image:var(--toolbarButton-openFile-icon); + mask-image:var(--toolbarButton-openFile-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #secondaryPrint::before{ + -webkit-mask-image:var(--toolbarButton-print-icon); + mask-image:var(--toolbarButton-print-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #secondaryDownload::before{ + -webkit-mask-image:var(--toolbarButton-download-icon); + mask-image:var(--toolbarButton-download-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #presentationMode::before{ + -webkit-mask-image:var(--toolbarButton-presentationMode-icon); + mask-image:var(--toolbarButton-presentationMode-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #viewBookmark::before{ + -webkit-mask-image:var(--toolbarButton-bookmark-icon); + mask-image:var(--toolbarButton-bookmark-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #firstPage::before{ + -webkit-mask-image:var(--secondaryToolbarButton-firstPage-icon); + mask-image:var(--secondaryToolbarButton-firstPage-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #lastPage::before{ + -webkit-mask-image:var(--secondaryToolbarButton-lastPage-icon); + mask-image:var(--secondaryToolbarButton-lastPage-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #pageRotateCcw::before{ + -webkit-mask-image:var(--secondaryToolbarButton-rotateCcw-icon); + mask-image:var(--secondaryToolbarButton-rotateCcw-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #pageRotateCw::before{ + -webkit-mask-image:var(--secondaryToolbarButton-rotateCw-icon); + mask-image:var(--secondaryToolbarButton-rotateCw-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #cursorSelectTool::before{ + -webkit-mask-image:var(--secondaryToolbarButton-selectTool-icon); + mask-image:var(--secondaryToolbarButton-selectTool-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #cursorHandTool::before{ + -webkit-mask-image:var(--secondaryToolbarButton-handTool-icon); + mask-image:var(--secondaryToolbarButton-handTool-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #scrollPage::before{ + -webkit-mask-image:var(--secondaryToolbarButton-scrollPage-icon); + mask-image:var(--secondaryToolbarButton-scrollPage-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #scrollVertical::before{ + -webkit-mask-image:var(--secondaryToolbarButton-scrollVertical-icon); + mask-image:var(--secondaryToolbarButton-scrollVertical-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #scrollHorizontal::before{ + -webkit-mask-image:var(--secondaryToolbarButton-scrollHorizontal-icon); + mask-image:var(--secondaryToolbarButton-scrollHorizontal-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #scrollWrapped::before{ + -webkit-mask-image:var(--secondaryToolbarButton-scrollWrapped-icon); + mask-image:var(--secondaryToolbarButton-scrollWrapped-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #spreadNone::before{ + -webkit-mask-image:var(--secondaryToolbarButton-spreadNone-icon); + mask-image:var(--secondaryToolbarButton-spreadNone-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #spreadOdd::before{ + -webkit-mask-image:var(--secondaryToolbarButton-spreadOdd-icon); + mask-image:var(--secondaryToolbarButton-spreadOdd-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #spreadEven::before{ + -webkit-mask-image:var(--secondaryToolbarButton-spreadEven-icon); + mask-image:var(--secondaryToolbarButton-spreadEven-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #imageAltTextSettings::before{ + -webkit-mask-image:var(--secondaryToolbarButton-imageAltTextSettings-icon); + mask-image:var(--secondaryToolbarButton-imageAltTextSettings-icon); + } + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #documentProperties::before{ + -webkit-mask-image:var(--secondaryToolbarButton-documentProperties-icon); + mask-image:var(--secondaryToolbarButton-documentProperties-icon); + } + +#findbar{ + --input-horizontal-padding:4px; + --findbar-padding:2px; + + width:-moz-max-content; + + width:max-content; + max-width:90vw; + min-height:var(--toolbar-height); + height:auto; + position:absolute; + z-index:30000; + cursor:default; + padding:0; + min-width:300px; + background-color:var(--toolbar-bg-color); + box-sizing:border-box; + flex-wrap:wrap; + justify-content:flex-start; +} + +#findbar > *{ + height:var(--toolbar-height); + padding:var(--findbar-padding); + } + +#findbar #findInputContainer{ + margin-inline-start:2px; + } + +:is(#findbar #findInputContainer) #findPreviousButton::before{ + -webkit-mask-image:var(--findbarButton-previous-icon); + mask-image:var(--findbarButton-previous-icon); + } + +:is(#findbar #findInputContainer) #findNextButton::before{ + -webkit-mask-image:var(--findbarButton-next-icon); + mask-image:var(--findbarButton-next-icon); + } + +:is(#findbar #findInputContainer) #findInput{ + width:200px; + padding:5px var(--input-horizontal-padding); + } + +:is(:is(#findbar #findInputContainer) #findInput)::-moz-placeholder{ + font-style:normal; + } + +:is(:is(#findbar #findInputContainer) #findInput)::placeholder{ + font-style:normal; + } + +.loadingInput:has( > [data-status="pending"]:is(:is(#findbar #findInputContainer) #findInput))::after{ + display:inline; + visibility:visible; + inset-inline-end:calc(var(--input-horizontal-padding) + 1px); + } + +[data-status="notFound"]:is(:is(#findbar #findInputContainer) #findInput){ + background-color:rgb(255 102 102); + } + +#findbar #findbarMessageContainer{ + display:none; + gap:4px; + } + +:is(#findbar #findbarMessageContainer):has( > :is(#findResultsCount,#findMsg):not(:empty)){ + display:inline flex; + } + +:is(#findbar #findbarMessageContainer) #findResultsCount{ + background-color:rgb(217 217 217); + color:rgb(82 82 82); + padding-block:4px; + } + +:is(:is(#findbar #findbarMessageContainer) #findResultsCount):empty{ + display:none; + } + +[data-status="notFound"]:is(:is(#findbar #findbarMessageContainer) #findMsg){ + font-weight:bold; + } + +:is(:is(#findbar #findbarMessageContainer) #findMsg):empty{ + display:none; + } + +#findbar.wrapContainers{ + flex-direction:column; + align-items:flex-start; + height:-moz-max-content; + height:max-content; + } + +#findbar.wrapContainers .toolbarLabel{ + margin:0 4px; + } + +#findbar.wrapContainers #findbarMessageContainer{ + flex-wrap:wrap; + flex-flow:column nowrap; + align-items:flex-start; + height:-moz-max-content; + height:max-content; + } + +:is(#findbar.wrapContainers #findbarMessageContainer) #findResultsCount{ + height:calc(var(--toolbar-height) - 2 * var(--findbar-padding)); + } + +:is(#findbar.wrapContainers #findbarMessageContainer) #findMsg{ + min-height:var(--toolbar-height); + } + +@page{ + margin:0; +} + +#printContainer{ + display:none; +} + +@media print{ + body{ + background:rgb(0 0 0 / 0) none; + } + + body[data-pdfjsprinting] #outerContainer{ + display:none; + } + + body[data-pdfjsprinting] #printContainer{ + display:block; + } + + #printContainer{ + height:100%; + } + #printContainer > .printedPage{ + page-break-after:always; + page-break-inside:avoid; + height:100%; + width:100%; + + display:flex; + flex-direction:column; + justify-content:center; + align-items:center; + } + + #printContainer > .xfaPrintedPage .xfaPage{ + position:absolute; + } + + #printContainer > .xfaPrintedPage{ + page-break-after:always; + page-break-inside:avoid; + width:100%; + height:100%; + position:relative; + } + + #printContainer > .printedPage :is(canvas, img){ + max-width:100%; + max-height:100%; + + direction:ltr; + display:block; + } +} + +.visibleMediumView{ + display:none !important; +} + +.toolbarLabel{ + width:-moz-max-content; + width:max-content; + min-width:16px; + height:100%; + padding-inline:4px; + margin:2px; + border-radius:2px; + color:var(--main-color); + font-size:12px; + line-height:14px; + text-align:left; + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; + cursor:default; + box-sizing:border-box; + + display:inline flex; + flex-direction:column; + align-items:center; + justify-content:center; +} + +.toolbarLabel > label{ + width:100%; + } + +.toolbarHorizontalGroup{ + height:100%; + display:inline flex; + flex-direction:row; + align-items:center; + justify-content:space-between; + gap:1px; + box-sizing:border-box; +} + +.dropdownToolbarButton{ + display:inline flex; + flex-direction:row; + align-items:center; + justify-content:center; + position:relative; + + width:-moz-fit-content; + + width:fit-content; + min-width:140px; + padding:0; + background-color:var(--dropdown-btn-bg-color); + border:var(--dropdown-btn-border); + border-radius:2px; + color:var(--main-color); + font-size:12px; + line-height:14px; + -webkit-user-select:none; + -moz-user-select:none; + user-select:none; + cursor:default; + box-sizing:border-box; + outline:none; +} + +.dropdownToolbarButton:hover{ + background-color:var(--button-hover-color); + } + +.dropdownToolbarButton > select{ + -webkit-appearance:none; + -moz-appearance:none; + appearance:none; + width:inherit; + min-width:inherit; + height:28px; + font:message-box; + font-size:12px; + color:var(--main-color); + margin:0; + padding-block:1px 2px; + padding-inline:6px 38px; + border:none; + outline:none; + background-color:var(--dropdown-btn-bg-color); + } + +:is(.dropdownToolbarButton > select) > option{ + background:var(--doorhanger-bg-color); + color:var(--main-color); + } + +:is(.dropdownToolbarButton > select):is(:hover,:focus-visible){ + background-color:var(--button-hover-color); + color:var(--toggled-btn-color); + } + +.dropdownToolbarButton::after{ + position:absolute; + display:inline; + width:var(--icon-size); + height:var(--icon-size); + + content:""; + background-color:var(--toolbar-icon-bg-color); + -webkit-mask-size:cover; + mask-size:cover; + + inset-inline-end:4px; + pointer-events:none; + -webkit-mask-image:var(--toolbarButton-menuArrow-icon); + mask-image:var(--toolbarButton-menuArrow-icon); + } + +.dropdownToolbarButton:is(:hover,:focus-visible,:active)::after{ + background-color:var(--toolbar-icon-hover-bg-color); + } + +#toolbarContainer{ + --menuitem-height:calc(var(--toolbar-height) - 6px); + + width:100%; + height:var(--toolbar-height); + padding:var(--toolbar-vertical-padding) var(--toolbar-horizontal-padding); + position:relative; + box-sizing:border-box; + font:message-box; + background-color:var(--toolbar-bg-color); + box-shadow:var(--toolbar-box-shadow); + border-bottom:var(--toolbar-border-bottom); +} + +#toolbarContainer #toolbarViewer{ + width:100%; + height:100%; + justify-content:space-between; + } + +:is(#toolbarContainer #toolbarViewer) > *{ + flex:none; + } + +:is(#toolbarContainer #toolbarViewer) input{ + font:message-box; + } + +:is(#toolbarContainer #toolbarViewer) .toolbarButtonSpacer{ + width:30px; + display:block; + height:1px; + } + +:is(#toolbarContainer #toolbarViewer) #toolbarViewerLeft #numPages.toolbarLabel{ + padding-inline-start:3px; + flex:none; + } + +#toolbarContainer #loadingBar{ + --progressBar-percent:0%; + --progressBar-end-offset:0; + + position:absolute; + top:var(--toolbar-height); + inset-inline:0 var(--progressBar-end-offset); + height:4px; + background-color:var(--progressBar-bg-color); + border-bottom:1px solid var(--toolbar-border-color); + transition-property:inset-inline-start; + transition-duration:var(--sidebar-transition-duration); + transition-timing-function:var(--sidebar-transition-timing-function); + } + +:is(#toolbarContainer #loadingBar) .progress{ + position:absolute; + top:0; + inset-inline-start:0; + width:100%; + transform:scaleX(var(--progressBar-percent)); + transform-origin:calc(50% - 50% * var(--dir-factor)) 0; + height:100%; + background-color:var(--progressBar-color); + overflow:hidden; + transition:transform 200ms; + } + +.indeterminate:is(#toolbarContainer #loadingBar) .progress{ + transform:none; + background-color:var(--progressBar-bg-color); + transition:none; + } + +:is(.indeterminate:is(#toolbarContainer #loadingBar) .progress) .glimmer{ + position:absolute; + top:0; + inset-inline-start:0; + height:100%; + width:calc(100% + 150px); + background:repeating-linear-gradient( + 135deg, + var(--progressBar-blend-color) 0, + var(--progressBar-bg-color) 5px, + var(--progressBar-bg-color) 45px, + var(--progressBar-color) 55px, + var(--progressBar-color) 95px, + var(--progressBar-blend-color) 100px + ); + animation:progressIndeterminate 1s linear infinite; + } + +#secondaryToolbar #firstPage::before{ + -webkit-mask-image:var(--secondaryToolbarButton-firstPage-icon); + mask-image:var(--secondaryToolbarButton-firstPage-icon); + } + +#secondaryToolbar #lastPage::before{ + -webkit-mask-image:var(--secondaryToolbarButton-lastPage-icon); + mask-image:var(--secondaryToolbarButton-lastPage-icon); + } + +#secondaryToolbar #pageRotateCcw::before{ + -webkit-mask-image:var(--secondaryToolbarButton-rotateCcw-icon); + mask-image:var(--secondaryToolbarButton-rotateCcw-icon); + } + +#secondaryToolbar #pageRotateCw::before{ + -webkit-mask-image:var(--secondaryToolbarButton-rotateCw-icon); + mask-image:var(--secondaryToolbarButton-rotateCw-icon); + } + +#secondaryToolbar #cursorSelectTool::before{ + -webkit-mask-image:var(--secondaryToolbarButton-selectTool-icon); + mask-image:var(--secondaryToolbarButton-selectTool-icon); + } + +#secondaryToolbar #cursorHandTool::before{ + -webkit-mask-image:var(--secondaryToolbarButton-handTool-icon); + mask-image:var(--secondaryToolbarButton-handTool-icon); + } + +#secondaryToolbar #scrollPage::before{ + -webkit-mask-image:var(--secondaryToolbarButton-scrollPage-icon); + mask-image:var(--secondaryToolbarButton-scrollPage-icon); + } + +#secondaryToolbar #scrollVertical::before{ + -webkit-mask-image:var(--secondaryToolbarButton-scrollVertical-icon); + mask-image:var(--secondaryToolbarButton-scrollVertical-icon); + } + +#secondaryToolbar #scrollHorizontal::before{ + -webkit-mask-image:var(--secondaryToolbarButton-scrollHorizontal-icon); + mask-image:var(--secondaryToolbarButton-scrollHorizontal-icon); + } + +#secondaryToolbar #scrollWrapped::before{ + -webkit-mask-image:var(--secondaryToolbarButton-scrollWrapped-icon); + mask-image:var(--secondaryToolbarButton-scrollWrapped-icon); + } + +#secondaryToolbar #spreadNone::before{ + -webkit-mask-image:var(--secondaryToolbarButton-spreadNone-icon); + mask-image:var(--secondaryToolbarButton-spreadNone-icon); + } + +#secondaryToolbar #spreadOdd::before{ + -webkit-mask-image:var(--secondaryToolbarButton-spreadOdd-icon); + mask-image:var(--secondaryToolbarButton-spreadOdd-icon); + } + +#secondaryToolbar #spreadEven::before{ + -webkit-mask-image:var(--secondaryToolbarButton-spreadEven-icon); + mask-image:var(--secondaryToolbarButton-spreadEven-icon); + } + +#secondaryToolbar #documentProperties::before{ + -webkit-mask-image:var(--secondaryToolbarButton-documentProperties-icon); + mask-image:var(--secondaryToolbarButton-documentProperties-icon); + } + +@media all and (max-width: 840px){ + #sidebarContainer{ + background-color:var(--sidebar-narrow-bg-color); + } + #outerContainer.sidebarOpen #viewerContainer{ + inset-inline-start:0 !important; + } +} + +@media all and (max-width: 750px){ + #outerContainer .hiddenMediumView{ + display:none !important; + } + #outerContainer .visibleMediumView:not(.hidden, [hidden]){ + display:inline-block !important; + } +} + +@media all and (max-width: 690px){ + .hiddenSmallView, + .hiddenSmallView *{ + display:none !important; + } + + #toolbarContainer #toolbarViewer .toolbarButtonSpacer{ + width:0; + } +} + +@media all and (max-width: 560px){ + #scaleSelectContainer{ + display:none; + } +} diff --git a/public/pdfjs/web/viewer.html b/public/pdfjs/web/viewer.html new file mode 100644 index 0000000..30182ef --- /dev/null +++ b/public/pdfjs/web/viewer.html @@ -0,0 +1,637 @@ + + + + + + + + PDF.js viewer + + + + + + + + + + + +

    + +
    +
    +
    +
    + + + + +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    + +
    + +
    + +
    + +
    +
    + +
    +
    + + +
    +
    + +
    + File name: +

    -

    +
    +
    + File size: +

    -

    +
    +
    +
    + Title: +

    -

    +
    +
    + Author: +

    -

    +
    +
    + Subject: +

    -

    +
    +
    + Keywords: +

    -

    +
    +
    + Creation Date: +

    -

    +
    +
    + Modification Date: +

    -

    +
    +
    + Creator: +

    -

    +
    +
    +
    + PDF Producer: +

    -

    +
    +
    + PDF Version: +

    -

    +
    +
    + Page Count: +

    -

    +
    +
    + Page Size: +

    -

    +
    +
    +
    + Fast Web View: +

    -

    +
    +
    + +
    +
    + +
    +
    + Choose an option + + Alt text (alternative text) helps when people can’t see the image or when it doesn’t load. + +
    +
    +
    +
    + + +
    +
    + + Aim for 1-2 sentences that describe the subject, setting, or actions. + +
    +
    +
    + +
    +
    +
    +
    +
    + + +
    +
    + + This is used for ornamental images, like borders or watermarks. + +
    +
    +
    +
    + + +
    +
    +
    + +
    +
    + Edit alt text (image description) +
    +
    +
    +
    +
    + Couldn’t create alt text automatically + Please write your own alt text or try again later. +
    + +
    +
    +
    + + + +
    +
    + + + +
    +
    + Image alt text settings +
    +
    + Automatic alt text +
    +
    +
    + + +
    +
    + Suggests descriptions to help people who can’t see the image or when the image doesn’t load. Learn more +
    +
    +
    +
    + Alt text AI model (180MB) +
    + Runs locally on your device so your data stays private. Required for automatic alt text. +
    +
    + + +
    +
    +
    +
    +
    + Alt text editor +
    +
    + + +
    +
    + Helps you make sure all your images have alt text. +
    +
    +
    +
    + +
    +
    +
    + +
    + Preparing document for printing… +
    +
    + + 0% +
    +
    + +
    +
    +
    + + + +
    +
    + + diff --git a/public/pdfjs/web/viewer.mjs b/public/pdfjs/web/viewer.mjs new file mode 100644 index 0000000..c3d794a --- /dev/null +++ b/public/pdfjs/web/viewer.mjs @@ -0,0 +1,15365 @@ +/** + * @licstart The following is the entire license notice for the + * JavaScript code in this page + * + * Copyright 2024 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @licend The above is the entire license notice for the + * JavaScript code in this page + */ + +/******/ // The require scope +/******/ var __webpack_require__ = {}; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; + +// EXPORTS +__webpack_require__.d(__webpack_exports__, { + PDFViewerApplication: () => (/* reexport */ PDFViewerApplication), + PDFViewerApplicationConstants: () => (/* binding */ AppConstants), + PDFViewerApplicationOptions: () => (/* reexport */ AppOptions) +}); + +;// ./web/ui_utils.js +const DEFAULT_SCALE_VALUE = "auto"; +const DEFAULT_SCALE = 1.0; +const DEFAULT_SCALE_DELTA = 1.1; +const MIN_SCALE = 0.1; +const MAX_SCALE = 10.0; +const UNKNOWN_SCALE = 0; +const MAX_AUTO_SCALE = 1.25; +const SCROLLBAR_PADDING = 40; +const VERTICAL_PADDING = 5; +const RenderingStates = { + INITIAL: 0, + RUNNING: 1, + PAUSED: 2, + FINISHED: 3 +}; +const PresentationModeState = { + UNKNOWN: 0, + NORMAL: 1, + CHANGING: 2, + FULLSCREEN: 3 +}; +const SidebarView = { + UNKNOWN: -1, + NONE: 0, + THUMBS: 1, + OUTLINE: 2, + ATTACHMENTS: 3, + LAYERS: 4 +}; +const TextLayerMode = { + DISABLE: 0, + ENABLE: 1, + ENABLE_PERMISSIONS: 2 +}; +const ScrollMode = { + UNKNOWN: -1, + VERTICAL: 0, + HORIZONTAL: 1, + WRAPPED: 2, + PAGE: 3 +}; +const SpreadMode = { + UNKNOWN: -1, + NONE: 0, + ODD: 1, + EVEN: 2 +}; +const CursorTool = { + SELECT: 0, + HAND: 1, + ZOOM: 2 +}; +const AutoPrintRegExp = /\bprint\s*\(/; +function scrollIntoView(element, spot, scrollMatches = false) { + let parent = element.offsetParent; + if (!parent) { + console.error("offsetParent is not set -- cannot scroll"); + return; + } + let offsetY = element.offsetTop + element.clientTop; + let offsetX = element.offsetLeft + element.clientLeft; + while (parent.clientHeight === parent.scrollHeight && parent.clientWidth === parent.scrollWidth || scrollMatches && (parent.classList.contains("markedContent") || getComputedStyle(parent).overflow === "hidden")) { + offsetY += parent.offsetTop; + offsetX += parent.offsetLeft; + parent = parent.offsetParent; + if (!parent) { + return; + } + } + if (spot) { + if (spot.top !== undefined) { + offsetY += spot.top; + } + if (spot.left !== undefined) { + offsetX += spot.left; + parent.scrollLeft = offsetX; + } + } + parent.scrollTop = offsetY; +} +function watchScroll(viewAreaElement, callback, abortSignal = undefined) { + const debounceScroll = function (evt) { + if (rAF) { + return; + } + rAF = window.requestAnimationFrame(function viewAreaElementScrolled() { + rAF = null; + const currentX = viewAreaElement.scrollLeft; + const lastX = state.lastX; + if (currentX !== lastX) { + state.right = currentX > lastX; + } + state.lastX = currentX; + const currentY = viewAreaElement.scrollTop; + const lastY = state.lastY; + if (currentY !== lastY) { + state.down = currentY > lastY; + } + state.lastY = currentY; + callback(state); + }); + }; + const state = { + right: true, + down: true, + lastX: viewAreaElement.scrollLeft, + lastY: viewAreaElement.scrollTop, + _eventHandler: debounceScroll + }; + let rAF = null; + viewAreaElement.addEventListener("scroll", debounceScroll, { + useCapture: true, + signal: abortSignal + }); + abortSignal?.addEventListener("abort", () => window.cancelAnimationFrame(rAF), { + once: true + }); + return state; +} +function parseQueryString(query) { + const params = new Map(); + for (const [key, value] of new URLSearchParams(query)) { + params.set(key.toLowerCase(), value); + } + return params; +} +const InvisibleCharsRegExp = /[\x00-\x1F]/g; +function removeNullCharacters(str, replaceInvisible = false) { + if (!InvisibleCharsRegExp.test(str)) { + return str; + } + if (replaceInvisible) { + return str.replaceAll(InvisibleCharsRegExp, m => m === "\x00" ? "" : " "); + } + return str.replaceAll("\x00", ""); +} +function binarySearchFirstItem(items, condition, start = 0) { + let minIndex = start; + let maxIndex = items.length - 1; + if (maxIndex < 0 || !condition(items[maxIndex])) { + return items.length; + } + if (condition(items[minIndex])) { + return minIndex; + } + while (minIndex < maxIndex) { + const currentIndex = minIndex + maxIndex >> 1; + const currentItem = items[currentIndex]; + if (condition(currentItem)) { + maxIndex = currentIndex; + } else { + minIndex = currentIndex + 1; + } + } + return minIndex; +} +function approximateFraction(x) { + if (Math.floor(x) === x) { + return [x, 1]; + } + const xinv = 1 / x; + const limit = 8; + if (xinv > limit) { + return [1, limit]; + } else if (Math.floor(xinv) === xinv) { + return [1, xinv]; + } + const x_ = x > 1 ? xinv : x; + let a = 0, + b = 1, + c = 1, + d = 1; + while (true) { + const p = a + c, + q = b + d; + if (q > limit) { + break; + } + if (x_ <= p / q) { + c = p; + d = q; + } else { + a = p; + b = q; + } + } + let result; + if (x_ - a / b < c / d - x_) { + result = x_ === x ? [a, b] : [b, a]; + } else { + result = x_ === x ? [c, d] : [d, c]; + } + return result; +} +function floorToDivide(x, div) { + return x - x % div; +} +function getPageSizeInches({ + view, + userUnit, + rotate +}) { + const [x1, y1, x2, y2] = view; + const changeOrientation = rotate % 180 !== 0; + const width = (x2 - x1) / 72 * userUnit; + const height = (y2 - y1) / 72 * userUnit; + return { + width: changeOrientation ? height : width, + height: changeOrientation ? width : height + }; +} +function backtrackBeforeAllVisibleElements(index, views, top) { + if (index < 2) { + return index; + } + let elt = views[index].div; + let pageTop = elt.offsetTop + elt.clientTop; + if (pageTop >= top) { + elt = views[index - 1].div; + pageTop = elt.offsetTop + elt.clientTop; + } + for (let i = index - 2; i >= 0; --i) { + elt = views[i].div; + if (elt.offsetTop + elt.clientTop + elt.clientHeight <= pageTop) { + break; + } + index = i; + } + return index; +} +function getVisibleElements({ + scrollEl, + views, + sortByVisibility = false, + horizontal = false, + rtl = false +}) { + const top = scrollEl.scrollTop, + bottom = top + scrollEl.clientHeight; + const left = scrollEl.scrollLeft, + right = left + scrollEl.clientWidth; + function isElementBottomAfterViewTop(view) { + const element = view.div; + const elementBottom = element.offsetTop + element.clientTop + element.clientHeight; + return elementBottom > top; + } + function isElementNextAfterViewHorizontally(view) { + const element = view.div; + const elementLeft = element.offsetLeft + element.clientLeft; + const elementRight = elementLeft + element.clientWidth; + return rtl ? elementLeft < right : elementRight > left; + } + const visible = [], + ids = new Set(), + numViews = views.length; + let firstVisibleElementInd = binarySearchFirstItem(views, horizontal ? isElementNextAfterViewHorizontally : isElementBottomAfterViewTop); + if (firstVisibleElementInd > 0 && firstVisibleElementInd < numViews && !horizontal) { + firstVisibleElementInd = backtrackBeforeAllVisibleElements(firstVisibleElementInd, views, top); + } + let lastEdge = horizontal ? right : -1; + for (let i = firstVisibleElementInd; i < numViews; i++) { + const view = views[i], + element = view.div; + const currentWidth = element.offsetLeft + element.clientLeft; + const currentHeight = element.offsetTop + element.clientTop; + const viewWidth = element.clientWidth, + viewHeight = element.clientHeight; + const viewRight = currentWidth + viewWidth; + const viewBottom = currentHeight + viewHeight; + if (lastEdge === -1) { + if (viewBottom >= bottom) { + lastEdge = viewBottom; + } + } else if ((horizontal ? currentWidth : currentHeight) > lastEdge) { + break; + } + if (viewBottom <= top || currentHeight >= bottom || viewRight <= left || currentWidth >= right) { + continue; + } + const hiddenHeight = Math.max(0, top - currentHeight) + Math.max(0, viewBottom - bottom); + const hiddenWidth = Math.max(0, left - currentWidth) + Math.max(0, viewRight - right); + const fractionHeight = (viewHeight - hiddenHeight) / viewHeight, + fractionWidth = (viewWidth - hiddenWidth) / viewWidth; + const percent = fractionHeight * fractionWidth * 100 | 0; + visible.push({ + id: view.id, + x: currentWidth, + y: currentHeight, + view, + percent, + widthPercent: fractionWidth * 100 | 0 + }); + ids.add(view.id); + } + const first = visible[0], + last = visible.at(-1); + if (sortByVisibility) { + visible.sort(function (a, b) { + const pc = a.percent - b.percent; + if (Math.abs(pc) > 0.001) { + return -pc; + } + return a.id - b.id; + }); + } + return { + first, + last, + views: visible, + ids + }; +} +function normalizeWheelEventDirection(evt) { + let delta = Math.hypot(evt.deltaX, evt.deltaY); + const angle = Math.atan2(evt.deltaY, evt.deltaX); + if (-0.25 * Math.PI < angle && angle < 0.75 * Math.PI) { + delta = -delta; + } + return delta; +} +function normalizeWheelEventDelta(evt) { + const deltaMode = evt.deltaMode; + let delta = normalizeWheelEventDirection(evt); + const MOUSE_PIXELS_PER_LINE = 30; + const MOUSE_LINES_PER_PAGE = 30; + if (deltaMode === WheelEvent.DOM_DELTA_PIXEL) { + delta /= MOUSE_PIXELS_PER_LINE * MOUSE_LINES_PER_PAGE; + } else if (deltaMode === WheelEvent.DOM_DELTA_LINE) { + delta /= MOUSE_LINES_PER_PAGE; + } + return delta; +} +function isValidRotation(angle) { + return Number.isInteger(angle) && angle % 90 === 0; +} +function isValidScrollMode(mode) { + return Number.isInteger(mode) && Object.values(ScrollMode).includes(mode) && mode !== ScrollMode.UNKNOWN; +} +function isValidSpreadMode(mode) { + return Number.isInteger(mode) && Object.values(SpreadMode).includes(mode) && mode !== SpreadMode.UNKNOWN; +} +function isPortraitOrientation(size) { + return size.width <= size.height; +} +const animationStarted = new Promise(function (resolve) { + window.requestAnimationFrame(resolve); +}); +const docStyle = document.documentElement.style; +function clamp(v, min, max) { + return Math.min(Math.max(v, min), max); +} +class ProgressBar { + #classList = null; + #disableAutoFetchTimeout = null; + #percent = 0; + #style = null; + #visible = true; + constructor(bar) { + this.#classList = bar.classList; + this.#style = bar.style; + } + get percent() { + return this.#percent; + } + set percent(val) { + this.#percent = clamp(val, 0, 100); + if (isNaN(val)) { + this.#classList.add("indeterminate"); + return; + } + this.#classList.remove("indeterminate"); + this.#style.setProperty("--progressBar-percent", `${this.#percent}%`); + } + setWidth(viewer) { + if (!viewer) { + return; + } + const container = viewer.parentNode; + const scrollbarWidth = container.offsetWidth - viewer.offsetWidth; + if (scrollbarWidth > 0) { + this.#style.setProperty("--progressBar-end-offset", `${scrollbarWidth}px`); + } + } + setDisableAutoFetch(delay = 5000) { + if (this.#percent === 100 || isNaN(this.#percent)) { + return; + } + if (this.#disableAutoFetchTimeout) { + clearTimeout(this.#disableAutoFetchTimeout); + } + this.show(); + this.#disableAutoFetchTimeout = setTimeout(() => { + this.#disableAutoFetchTimeout = null; + this.hide(); + }, delay); + } + hide() { + if (!this.#visible) { + return; + } + this.#visible = false; + this.#classList.add("hidden"); + } + show() { + if (this.#visible) { + return; + } + this.#visible = true; + this.#classList.remove("hidden"); + } +} +function getActiveOrFocusedElement() { + let curRoot = document; + let curActiveOrFocused = curRoot.activeElement || curRoot.querySelector(":focus"); + while (curActiveOrFocused?.shadowRoot) { + curRoot = curActiveOrFocused.shadowRoot; + curActiveOrFocused = curRoot.activeElement || curRoot.querySelector(":focus"); + } + return curActiveOrFocused; +} +function apiPageLayoutToViewerModes(layout) { + let scrollMode = ScrollMode.VERTICAL, + spreadMode = SpreadMode.NONE; + switch (layout) { + case "SinglePage": + scrollMode = ScrollMode.PAGE; + break; + case "OneColumn": + break; + case "TwoPageLeft": + scrollMode = ScrollMode.PAGE; + case "TwoColumnLeft": + spreadMode = SpreadMode.ODD; + break; + case "TwoPageRight": + scrollMode = ScrollMode.PAGE; + case "TwoColumnRight": + spreadMode = SpreadMode.EVEN; + break; + } + return { + scrollMode, + spreadMode + }; +} +function apiPageModeToSidebarView(mode) { + switch (mode) { + case "UseNone": + return SidebarView.NONE; + case "UseThumbs": + return SidebarView.THUMBS; + case "UseOutlines": + return SidebarView.OUTLINE; + case "UseAttachments": + return SidebarView.ATTACHMENTS; + case "UseOC": + return SidebarView.LAYERS; + } + return SidebarView.NONE; +} +function toggleCheckedBtn(button, toggle, view = null) { + button.classList.toggle("toggled", toggle); + button.setAttribute("aria-checked", toggle); + view?.classList.toggle("hidden", !toggle); +} +function toggleExpandedBtn(button, toggle, view = null) { + button.classList.toggle("toggled", toggle); + button.setAttribute("aria-expanded", toggle); + view?.classList.toggle("hidden", !toggle); +} +const calcRound = function () { + const e = document.createElement("div"); + e.style.width = "round(down, calc(1.6666666666666665 * 792px), 1px)"; + return e.style.width === "calc(1320px)" ? Math.fround : x => x; +}(); + +;// ./web/app_options.js +{ + var compatParams = new Map(); + const userAgent = navigator.userAgent || ""; + const platform = navigator.platform || ""; + const maxTouchPoints = navigator.maxTouchPoints || 1; + const isAndroid = /Android/.test(userAgent); + const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent) || platform === "MacIntel" && maxTouchPoints > 1; + (function () { + if (isIOS || isAndroid) { + compatParams.set("maxCanvasPixels", 5242880); + } + })(); + (function () { + if (isAndroid) { + compatParams.set("useSystemFonts", false); + } + })(); +} +const OptionKind = { + BROWSER: 0x01, + VIEWER: 0x02, + API: 0x04, + WORKER: 0x08, + EVENT_DISPATCH: 0x10, + PREFERENCE: 0x80 +}; +const Type = { + BOOLEAN: 0x01, + NUMBER: 0x02, + OBJECT: 0x04, + STRING: 0x08, + UNDEFINED: 0x10 +}; +const defaultOptions = { + allowedGlobalEvents: { + value: null, + kind: OptionKind.BROWSER + }, + canvasMaxAreaInBytes: { + value: -1, + kind: OptionKind.BROWSER + OptionKind.API + }, + isInAutomation: { + value: false, + kind: OptionKind.BROWSER + }, + localeProperties: { + value: { + lang: navigator.language || "en-US" + }, + kind: OptionKind.BROWSER + }, + nimbusDataStr: { + value: "", + kind: OptionKind.BROWSER + }, + supportsCaretBrowsingMode: { + value: false, + kind: OptionKind.BROWSER + }, + supportsDocumentFonts: { + value: true, + kind: OptionKind.BROWSER + }, + supportsIntegratedFind: { + value: false, + kind: OptionKind.BROWSER + }, + supportsMouseWheelZoomCtrlKey: { + value: true, + kind: OptionKind.BROWSER + }, + supportsMouseWheelZoomMetaKey: { + value: true, + kind: OptionKind.BROWSER + }, + supportsPinchToZoom: { + value: true, + kind: OptionKind.BROWSER + }, + toolbarDensity: { + value: 0, + kind: OptionKind.BROWSER + OptionKind.EVENT_DISPATCH + }, + altTextLearnMoreUrl: { + value: "", + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + annotationEditorMode: { + value: 0, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + annotationMode: { + value: 2, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + cursorToolOnLoad: { + value: 0, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + debuggerSrc: { + value: "./debugger.mjs", + kind: OptionKind.VIEWER + }, + defaultZoomDelay: { + value: 400, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + defaultZoomValue: { + value: "", + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + disableHistory: { + value: false, + kind: OptionKind.VIEWER + }, + disablePageLabels: { + value: false, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + enableAltText: { + value: false, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + enableAltTextModelDownload: { + value: true, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + OptionKind.EVENT_DISPATCH + }, + enableGuessAltText: { + value: true, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + OptionKind.EVENT_DISPATCH + }, + enableHighlightFloatingButton: { + value: false, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + enableNewAltTextWhenAddingImage: { + value: true, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + enablePermissions: { + value: false, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + enablePrintAutoRotate: { + value: true, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + enableScripting: { + value: true, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + enableUpdatedAddImage: { + value: false, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + externalLinkRel: { + value: "noopener noreferrer nofollow", + kind: OptionKind.VIEWER + }, + externalLinkTarget: { + value: 0, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + highlightEditorColors: { + value: "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F", + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + historyUpdateUrl: { + value: false, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + ignoreDestinationZoom: { + value: false, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + imageResourcesPath: { + value: "./images/", + kind: OptionKind.VIEWER + }, + maxCanvasPixels: { + value: 2 ** 25, + kind: OptionKind.VIEWER + }, + forcePageColors: { + value: false, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + pageColorsBackground: { + value: "Canvas", + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + pageColorsForeground: { + value: "CanvasText", + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + pdfBugEnabled: { + value: false, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + printResolution: { + value: 150, + kind: OptionKind.VIEWER + }, + sidebarViewOnLoad: { + value: -1, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + scrollModeOnLoad: { + value: -1, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + spreadModeOnLoad: { + value: -1, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + textLayerMode: { + value: 1, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + viewOnLoad: { + value: 0, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }, + cMapPacked: { + value: true, + kind: OptionKind.API + }, + cMapUrl: { + value: "../web/cmaps/", + kind: OptionKind.API + }, + disableAutoFetch: { + value: false, + kind: OptionKind.API + OptionKind.PREFERENCE + }, + disableFontFace: { + value: false, + kind: OptionKind.API + OptionKind.PREFERENCE + }, + disableRange: { + value: false, + kind: OptionKind.API + OptionKind.PREFERENCE + }, + disableStream: { + value: false, + kind: OptionKind.API + OptionKind.PREFERENCE + }, + docBaseUrl: { + value: "", + kind: OptionKind.API + }, + enableHWA: { + value: true, + kind: OptionKind.API + OptionKind.VIEWER + OptionKind.PREFERENCE + }, + enableXfa: { + value: true, + kind: OptionKind.API + OptionKind.PREFERENCE + }, + fontExtraProperties: { + value: false, + kind: OptionKind.API + }, + isEvalSupported: { + value: true, + kind: OptionKind.API + }, + isOffscreenCanvasSupported: { + value: true, + kind: OptionKind.API + }, + maxImageSize: { + value: -1, + kind: OptionKind.API + }, + pdfBug: { + value: false, + kind: OptionKind.API + }, + standardFontDataUrl: { + value: "../web/standard_fonts/", + kind: OptionKind.API + }, + useSystemFonts: { + value: undefined, + kind: OptionKind.API, + type: Type.BOOLEAN + Type.UNDEFINED + }, + verbosity: { + value: 1, + kind: OptionKind.API + }, + workerPort: { + value: null, + kind: OptionKind.WORKER + }, + workerSrc: { + value: "../build/pdf.worker.mjs", + kind: OptionKind.WORKER + } +}; +{ + defaultOptions.defaultUrl = { + value: "compressed.tracemonkey-pldi-09.pdf", + kind: OptionKind.VIEWER + }; + defaultOptions.sandboxBundleSrc = { + value: "../build/pdf.sandbox.mjs", + kind: OptionKind.VIEWER + }; + defaultOptions.viewerCssTheme = { + value: 0, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE + }; + defaultOptions.enableFakeMLManager = { + value: true, + kind: OptionKind.VIEWER + }; +} +{ + defaultOptions.disablePreferences = { + value: false, + kind: OptionKind.VIEWER + }; +} +class AppOptions { + static eventBus; + static #opts = new Map(); + static { + for (const name in defaultOptions) { + this.#opts.set(name, defaultOptions[name].value); + } + for (const [name, value] of compatParams) { + this.#opts.set(name, value); + } + this._hasInvokedSet = false; + this._checkDisablePreferences = () => { + if (this.get("disablePreferences")) { + return true; + } + if (this._hasInvokedSet) { + console.warn("The Preferences may override manually set AppOptions; " + 'please use the "disablePreferences"-option to prevent that.'); + } + return false; + }; + } + static get(name) { + return this.#opts.get(name); + } + static getAll(kind = null, defaultOnly = false) { + const options = Object.create(null); + for (const name in defaultOptions) { + const defaultOpt = defaultOptions[name]; + if (kind && !(kind & defaultOpt.kind)) { + continue; + } + options[name] = !defaultOnly ? this.#opts.get(name) : defaultOpt.value; + } + return options; + } + static set(name, value) { + this.setAll({ + [name]: value + }); + } + static setAll(options, prefs = false) { + this._hasInvokedSet ||= true; + let events; + for (const name in options) { + const defaultOpt = defaultOptions[name], + userOpt = options[name]; + if (!defaultOpt || !(typeof userOpt === typeof defaultOpt.value || Type[(typeof userOpt).toUpperCase()] & defaultOpt.type)) { + continue; + } + const { + kind + } = defaultOpt; + if (prefs && !(kind & OptionKind.BROWSER || kind & OptionKind.PREFERENCE)) { + continue; + } + if (this.eventBus && kind & OptionKind.EVENT_DISPATCH) { + (events ||= new Map()).set(name, userOpt); + } + this.#opts.set(name, userOpt); + } + if (events) { + for (const [name, value] of events) { + this.eventBus.dispatch(name.toLowerCase(), { + source: this, + value + }); + } + } + } +} + +;// ./web/pdf_link_service.js + +const DEFAULT_LINK_REL = "noopener noreferrer nofollow"; +const LinkTarget = { + NONE: 0, + SELF: 1, + BLANK: 2, + PARENT: 3, + TOP: 4 +}; +class PDFLinkService { + externalLinkEnabled = true; + constructor({ + eventBus, + externalLinkTarget = null, + externalLinkRel = null, + ignoreDestinationZoom = false + } = {}) { + this.eventBus = eventBus; + this.externalLinkTarget = externalLinkTarget; + this.externalLinkRel = externalLinkRel; + this._ignoreDestinationZoom = ignoreDestinationZoom; + this.baseUrl = null; + this.pdfDocument = null; + this.pdfViewer = null; + this.pdfHistory = null; + } + setDocument(pdfDocument, baseUrl = null) { + this.baseUrl = baseUrl; + this.pdfDocument = pdfDocument; + } + setViewer(pdfViewer) { + this.pdfViewer = pdfViewer; + } + setHistory(pdfHistory) { + this.pdfHistory = pdfHistory; + } + get pagesCount() { + return this.pdfDocument ? this.pdfDocument.numPages : 0; + } + get page() { + return this.pdfDocument ? this.pdfViewer.currentPageNumber : 1; + } + set page(value) { + if (this.pdfDocument) { + this.pdfViewer.currentPageNumber = value; + } + } + get rotation() { + return this.pdfDocument ? this.pdfViewer.pagesRotation : 0; + } + set rotation(value) { + if (this.pdfDocument) { + this.pdfViewer.pagesRotation = value; + } + } + get isInPresentationMode() { + return this.pdfDocument ? this.pdfViewer.isInPresentationMode : false; + } + async goToDestination(dest) { + if (!this.pdfDocument) { + return; + } + let namedDest, explicitDest, pageNumber; + if (typeof dest === "string") { + namedDest = dest; + explicitDest = await this.pdfDocument.getDestination(dest); + } else { + namedDest = null; + explicitDest = await dest; + } + if (!Array.isArray(explicitDest)) { + console.error(`goToDestination: "${explicitDest}" is not a valid destination array, for dest="${dest}".`); + return; + } + const [destRef] = explicitDest; + if (destRef && typeof destRef === "object") { + pageNumber = this.pdfDocument.cachedPageNumber(destRef); + if (!pageNumber) { + try { + pageNumber = (await this.pdfDocument.getPageIndex(destRef)) + 1; + } catch { + console.error(`goToDestination: "${destRef}" is not a valid page reference, for dest="${dest}".`); + return; + } + } + } else if (Number.isInteger(destRef)) { + pageNumber = destRef + 1; + } + if (!pageNumber || pageNumber < 1 || pageNumber > this.pagesCount) { + console.error(`goToDestination: "${pageNumber}" is not a valid page number, for dest="${dest}".`); + return; + } + if (this.pdfHistory) { + this.pdfHistory.pushCurrentPosition(); + this.pdfHistory.push({ + namedDest, + explicitDest, + pageNumber + }); + } + this.pdfViewer.scrollPageIntoView({ + pageNumber, + destArray: explicitDest, + ignoreDestinationZoom: this._ignoreDestinationZoom + }); + } + goToPage(val) { + if (!this.pdfDocument) { + return; + } + const pageNumber = typeof val === "string" && this.pdfViewer.pageLabelToPageNumber(val) || val | 0; + if (!(Number.isInteger(pageNumber) && pageNumber > 0 && pageNumber <= this.pagesCount)) { + console.error(`PDFLinkService.goToPage: "${val}" is not a valid page.`); + return; + } + if (this.pdfHistory) { + this.pdfHistory.pushCurrentPosition(); + this.pdfHistory.pushPage(pageNumber); + } + this.pdfViewer.scrollPageIntoView({ + pageNumber + }); + } + addLinkAttributes(link, url, newWindow = false) { + if (!url || typeof url !== "string") { + throw new Error('A valid "url" parameter must provided.'); + } + const target = newWindow ? LinkTarget.BLANK : this.externalLinkTarget, + rel = this.externalLinkRel; + if (this.externalLinkEnabled) { + link.href = link.title = url; + } else { + link.href = ""; + link.title = `Disabled: ${url}`; + link.onclick = () => false; + } + let targetStr = ""; + switch (target) { + case LinkTarget.NONE: + break; + case LinkTarget.SELF: + targetStr = "_self"; + break; + case LinkTarget.BLANK: + targetStr = "_blank"; + break; + case LinkTarget.PARENT: + targetStr = "_parent"; + break; + case LinkTarget.TOP: + targetStr = "_top"; + break; + } + link.target = targetStr; + link.rel = typeof rel === "string" ? rel : DEFAULT_LINK_REL; + } + getDestinationHash(dest) { + if (typeof dest === "string") { + if (dest.length > 0) { + return this.getAnchorUrl("#" + escape(dest)); + } + } else if (Array.isArray(dest)) { + const str = JSON.stringify(dest); + if (str.length > 0) { + return this.getAnchorUrl("#" + escape(str)); + } + } + return this.getAnchorUrl(""); + } + getAnchorUrl(anchor) { + return this.baseUrl ? this.baseUrl + anchor : anchor; + } + setHash(hash) { + if (!this.pdfDocument) { + return; + } + let pageNumber, dest; + if (hash.includes("=")) { + const params = parseQueryString(hash); + if (params.has("search")) { + const query = params.get("search").replaceAll('"', ""), + phrase = params.get("phrase") === "true"; + this.eventBus.dispatch("findfromurlhash", { + source: this, + query: phrase ? query : query.match(/\S+/g) + }); + } + if (params.has("page")) { + pageNumber = params.get("page") | 0 || 1; + } + if (params.has("zoom")) { + const zoomArgs = params.get("zoom").split(","); + const zoomArg = zoomArgs[0]; + const zoomArgNumber = parseFloat(zoomArg); + if (!zoomArg.includes("Fit")) { + dest = [null, { + name: "XYZ" + }, zoomArgs.length > 1 ? zoomArgs[1] | 0 : null, zoomArgs.length > 2 ? zoomArgs[2] | 0 : null, zoomArgNumber ? zoomArgNumber / 100 : zoomArg]; + } else if (zoomArg === "Fit" || zoomArg === "FitB") { + dest = [null, { + name: zoomArg + }]; + } else if (zoomArg === "FitH" || zoomArg === "FitBH" || zoomArg === "FitV" || zoomArg === "FitBV") { + dest = [null, { + name: zoomArg + }, zoomArgs.length > 1 ? zoomArgs[1] | 0 : null]; + } else if (zoomArg === "FitR") { + if (zoomArgs.length !== 5) { + console.error('PDFLinkService.setHash: Not enough parameters for "FitR".'); + } else { + dest = [null, { + name: zoomArg + }, zoomArgs[1] | 0, zoomArgs[2] | 0, zoomArgs[3] | 0, zoomArgs[4] | 0]; + } + } else { + console.error(`PDFLinkService.setHash: "${zoomArg}" is not a valid zoom value.`); + } + } + if (dest) { + this.pdfViewer.scrollPageIntoView({ + pageNumber: pageNumber || this.page, + destArray: dest, + allowNegativeOffset: true + }); + } else if (pageNumber) { + this.page = pageNumber; + } + if (params.has("pagemode")) { + this.eventBus.dispatch("pagemode", { + source: this, + mode: params.get("pagemode") + }); + } + if (params.has("nameddest")) { + this.goToDestination(params.get("nameddest")); + } + return; + } + dest = unescape(hash); + try { + dest = JSON.parse(dest); + if (!Array.isArray(dest)) { + dest = dest.toString(); + } + } catch {} + if (typeof dest === "string" || PDFLinkService.#isValidExplicitDest(dest)) { + this.goToDestination(dest); + return; + } + console.error(`PDFLinkService.setHash: "${unescape(hash)}" is not a valid destination.`); + } + executeNamedAction(action) { + if (!this.pdfDocument) { + return; + } + switch (action) { + case "GoBack": + this.pdfHistory?.back(); + break; + case "GoForward": + this.pdfHistory?.forward(); + break; + case "NextPage": + this.pdfViewer.nextPage(); + break; + case "PrevPage": + this.pdfViewer.previousPage(); + break; + case "LastPage": + this.page = this.pagesCount; + break; + case "FirstPage": + this.page = 1; + break; + default: + break; + } + this.eventBus.dispatch("namedaction", { + source: this, + action + }); + } + async executeSetOCGState(action) { + if (!this.pdfDocument) { + return; + } + const pdfDocument = this.pdfDocument, + optionalContentConfig = await this.pdfViewer.optionalContentConfigPromise; + if (pdfDocument !== this.pdfDocument) { + return; + } + optionalContentConfig.setOCGState(action); + this.pdfViewer.optionalContentConfigPromise = Promise.resolve(optionalContentConfig); + } + static #isValidExplicitDest(dest) { + if (!Array.isArray(dest) || dest.length < 2) { + return false; + } + const [page, zoom, ...args] = dest; + if (!(typeof page === "object" && Number.isInteger(page?.num) && Number.isInteger(page?.gen)) && !Number.isInteger(page)) { + return false; + } + if (!(typeof zoom === "object" && typeof zoom?.name === "string")) { + return false; + } + const argsLen = args.length; + let allowNull = true; + switch (zoom.name) { + case "XYZ": + if (argsLen < 2 || argsLen > 3) { + return false; + } + break; + case "Fit": + case "FitB": + return argsLen === 0; + case "FitH": + case "FitBH": + case "FitV": + case "FitBV": + if (argsLen > 1) { + return false; + } + break; + case "FitR": + if (argsLen !== 4) { + return false; + } + allowNull = false; + break; + default: + return false; + } + for (const arg of args) { + if (!(typeof arg === "number" || allowNull && arg === null)) { + return false; + } + } + return true; + } +} +class SimpleLinkService extends PDFLinkService { + setDocument(pdfDocument, baseUrl = null) {} +} + +;// ./web/pdfjs.js +const { + AbortException, + AnnotationEditorLayer, + AnnotationEditorParamsType, + AnnotationEditorType, + AnnotationEditorUIManager, + AnnotationLayer, + AnnotationMode, + build, + ColorPicker, + createValidAbsoluteUrl, + DOMSVGFactory, + DrawLayer, + FeatureTest, + fetchData, + getDocument, + getFilenameFromUrl, + getPdfFilenameFromUrl: pdfjs_getPdfFilenameFromUrl, + getXfaPageViewport, + GlobalWorkerOptions, + ImageKind, + InvalidPDFException, + isDataScheme, + isPdfFile, + MissingPDFException, + noContextMenu, + normalizeUnicode, + OPS, + OutputScale, + PasswordResponses, + PDFDataRangeTransport, + PDFDateString, + PDFWorker, + PermissionFlag, + PixelsPerInch, + RenderingCancelledException, + setLayerDimensions, + shadow, + stopEvent, + TextLayer, + TouchManager, + UnexpectedResponseException, + Util, + VerbosityLevel, + version, + XfaLayer +} = globalThis.pdfjsLib; + +;// ./web/event_utils.js +const WaitOnType = { + EVENT: "event", + TIMEOUT: "timeout" +}; +async function waitOnEventOrTimeout({ + target, + name, + delay = 0 +}) { + if (typeof target !== "object" || !(name && typeof name === "string") || !(Number.isInteger(delay) && delay >= 0)) { + throw new Error("waitOnEventOrTimeout - invalid parameters."); + } + const { + promise, + resolve + } = Promise.withResolvers(); + const ac = new AbortController(); + function handler(type) { + ac.abort(); + clearTimeout(timeout); + resolve(type); + } + const evtMethod = target instanceof EventBus ? "_on" : "addEventListener"; + target[evtMethod](name, handler.bind(null, WaitOnType.EVENT), { + signal: ac.signal + }); + const timeout = setTimeout(handler.bind(null, WaitOnType.TIMEOUT), delay); + return promise; +} +class EventBus { + #listeners = Object.create(null); + on(eventName, listener, options = null) { + this._on(eventName, listener, { + external: true, + once: options?.once, + signal: options?.signal + }); + } + off(eventName, listener, options = null) { + this._off(eventName, listener); + } + dispatch(eventName, data) { + const eventListeners = this.#listeners[eventName]; + if (!eventListeners || eventListeners.length === 0) { + return; + } + let externalListeners; + for (const { + listener, + external, + once + } of eventListeners.slice(0)) { + if (once) { + this._off(eventName, listener); + } + if (external) { + (externalListeners ||= []).push(listener); + continue; + } + listener(data); + } + if (externalListeners) { + for (const listener of externalListeners) { + listener(data); + } + externalListeners = null; + } + } + _on(eventName, listener, options = null) { + let rmAbort = null; + if (options?.signal instanceof AbortSignal) { + const { + signal + } = options; + if (signal.aborted) { + console.error("Cannot use an `aborted` signal."); + return; + } + const onAbort = () => this._off(eventName, listener); + rmAbort = () => signal.removeEventListener("abort", onAbort); + signal.addEventListener("abort", onAbort); + } + const eventListeners = this.#listeners[eventName] ||= []; + eventListeners.push({ + listener, + external: options?.external === true, + once: options?.once === true, + rmAbort + }); + } + _off(eventName, listener, options = null) { + const eventListeners = this.#listeners[eventName]; + if (!eventListeners) { + return; + } + for (let i = 0, ii = eventListeners.length; i < ii; i++) { + const evt = eventListeners[i]; + if (evt.listener === listener) { + evt.rmAbort?.(); + eventListeners.splice(i, 1); + return; + } + } + } +} +class FirefoxEventBus extends EventBus { + #externalServices; + #globalEventNames; + #isInAutomation; + constructor(globalEventNames, externalServices, isInAutomation) { + super(); + this.#globalEventNames = globalEventNames; + this.#externalServices = externalServices; + this.#isInAutomation = isInAutomation; + } + dispatch(eventName, data) { + throw new Error("Not implemented: FirefoxEventBus.dispatch"); + } +} + +;// ./web/external_services.js +class BaseExternalServices { + updateFindControlState(data) {} + updateFindMatchesCount(data) {} + initPassiveLoading() {} + reportTelemetry(data) {} + async createL10n() { + throw new Error("Not implemented: createL10n"); + } + createScripting() { + throw new Error("Not implemented: createScripting"); + } + updateEditorStates(data) { + throw new Error("Not implemented: updateEditorStates"); + } + dispatchGlobalEvent(_event) {} +} + +;// ./web/preferences.js + +class BasePreferences { + #defaults = Object.freeze({ + altTextLearnMoreUrl: "", + annotationEditorMode: 0, + annotationMode: 2, + cursorToolOnLoad: 0, + defaultZoomDelay: 400, + defaultZoomValue: "", + disablePageLabels: false, + enableAltText: false, + enableAltTextModelDownload: true, + enableGuessAltText: true, + enableHighlightFloatingButton: false, + enableNewAltTextWhenAddingImage: true, + enablePermissions: false, + enablePrintAutoRotate: true, + enableScripting: true, + enableUpdatedAddImage: false, + externalLinkTarget: 0, + highlightEditorColors: "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F", + historyUpdateUrl: false, + ignoreDestinationZoom: false, + forcePageColors: false, + pageColorsBackground: "Canvas", + pageColorsForeground: "CanvasText", + pdfBugEnabled: false, + sidebarViewOnLoad: -1, + scrollModeOnLoad: -1, + spreadModeOnLoad: -1, + textLayerMode: 1, + viewOnLoad: 0, + disableAutoFetch: false, + disableFontFace: false, + disableRange: false, + disableStream: false, + enableHWA: true, + enableXfa: true, + viewerCssTheme: 0 + }); + #initializedPromise = null; + constructor() { + this.#initializedPromise = this._readFromStorage(this.#defaults).then(({ + browserPrefs, + prefs + }) => { + if (AppOptions._checkDisablePreferences()) { + return; + } + AppOptions.setAll({ + ...browserPrefs, + ...prefs + }, true); + }); + } + async _writeToStorage(prefObj) { + throw new Error("Not implemented: _writeToStorage"); + } + async _readFromStorage(prefObj) { + throw new Error("Not implemented: _readFromStorage"); + } + async reset() { + await this.#initializedPromise; + AppOptions.setAll(this.#defaults, true); + await this._writeToStorage(this.#defaults); + } + async set(name, value) { + await this.#initializedPromise; + AppOptions.setAll({ + [name]: value + }, true); + await this._writeToStorage(AppOptions.getAll(OptionKind.PREFERENCE)); + } + async get(name) { + await this.#initializedPromise; + return AppOptions.get(name); + } + get initializedPromise() { + return this.#initializedPromise; + } +} + +;// ./node_modules/@fluent/bundle/esm/types.js +class FluentType { + constructor(value) { + this.value = value; + } + valueOf() { + return this.value; + } +} +class FluentNone extends FluentType { + constructor(value = "???") { + super(value); + } + toString(scope) { + return `{${this.value}}`; + } +} +class FluentNumber extends FluentType { + constructor(value, opts = {}) { + super(value); + this.opts = opts; + } + toString(scope) { + try { + const nf = scope.memoizeIntlObject(Intl.NumberFormat, this.opts); + return nf.format(this.value); + } catch (err) { + scope.reportError(err); + return this.value.toString(10); + } + } +} +class FluentDateTime extends FluentType { + constructor(value, opts = {}) { + super(value); + this.opts = opts; + } + toString(scope) { + try { + const dtf = scope.memoizeIntlObject(Intl.DateTimeFormat, this.opts); + return dtf.format(this.value); + } catch (err) { + scope.reportError(err); + return new Date(this.value).toISOString(); + } + } +} +;// ./node_modules/@fluent/bundle/esm/resolver.js + +const MAX_PLACEABLES = 100; +const FSI = "\u2068"; +const PDI = "\u2069"; +function match(scope, selector, key) { + if (key === selector) { + return true; + } + if (key instanceof FluentNumber && selector instanceof FluentNumber && key.value === selector.value) { + return true; + } + if (selector instanceof FluentNumber && typeof key === "string") { + let category = scope.memoizeIntlObject(Intl.PluralRules, selector.opts).select(selector.value); + if (key === category) { + return true; + } + } + return false; +} +function getDefault(scope, variants, star) { + if (variants[star]) { + return resolvePattern(scope, variants[star].value); + } + scope.reportError(new RangeError("No default")); + return new FluentNone(); +} +function getArguments(scope, args) { + const positional = []; + const named = Object.create(null); + for (const arg of args) { + if (arg.type === "narg") { + named[arg.name] = resolveExpression(scope, arg.value); + } else { + positional.push(resolveExpression(scope, arg)); + } + } + return { + positional, + named + }; +} +function resolveExpression(scope, expr) { + switch (expr.type) { + case "str": + return expr.value; + case "num": + return new FluentNumber(expr.value, { + minimumFractionDigits: expr.precision + }); + case "var": + return resolveVariableReference(scope, expr); + case "mesg": + return resolveMessageReference(scope, expr); + case "term": + return resolveTermReference(scope, expr); + case "func": + return resolveFunctionReference(scope, expr); + case "select": + return resolveSelectExpression(scope, expr); + default: + return new FluentNone(); + } +} +function resolveVariableReference(scope, { + name +}) { + let arg; + if (scope.params) { + if (Object.prototype.hasOwnProperty.call(scope.params, name)) { + arg = scope.params[name]; + } else { + return new FluentNone(`$${name}`); + } + } else if (scope.args && Object.prototype.hasOwnProperty.call(scope.args, name)) { + arg = scope.args[name]; + } else { + scope.reportError(new ReferenceError(`Unknown variable: $${name}`)); + return new FluentNone(`$${name}`); + } + if (arg instanceof FluentType) { + return arg; + } + switch (typeof arg) { + case "string": + return arg; + case "number": + return new FluentNumber(arg); + case "object": + if (arg instanceof Date) { + return new FluentDateTime(arg.getTime()); + } + default: + scope.reportError(new TypeError(`Variable type not supported: $${name}, ${typeof arg}`)); + return new FluentNone(`$${name}`); + } +} +function resolveMessageReference(scope, { + name, + attr +}) { + const message = scope.bundle._messages.get(name); + if (!message) { + scope.reportError(new ReferenceError(`Unknown message: ${name}`)); + return new FluentNone(name); + } + if (attr) { + const attribute = message.attributes[attr]; + if (attribute) { + return resolvePattern(scope, attribute); + } + scope.reportError(new ReferenceError(`Unknown attribute: ${attr}`)); + return new FluentNone(`${name}.${attr}`); + } + if (message.value) { + return resolvePattern(scope, message.value); + } + scope.reportError(new ReferenceError(`No value: ${name}`)); + return new FluentNone(name); +} +function resolveTermReference(scope, { + name, + attr, + args +}) { + const id = `-${name}`; + const term = scope.bundle._terms.get(id); + if (!term) { + scope.reportError(new ReferenceError(`Unknown term: ${id}`)); + return new FluentNone(id); + } + if (attr) { + const attribute = term.attributes[attr]; + if (attribute) { + scope.params = getArguments(scope, args).named; + const resolved = resolvePattern(scope, attribute); + scope.params = null; + return resolved; + } + scope.reportError(new ReferenceError(`Unknown attribute: ${attr}`)); + return new FluentNone(`${id}.${attr}`); + } + scope.params = getArguments(scope, args).named; + const resolved = resolvePattern(scope, term.value); + scope.params = null; + return resolved; +} +function resolveFunctionReference(scope, { + name, + args +}) { + let func = scope.bundle._functions[name]; + if (!func) { + scope.reportError(new ReferenceError(`Unknown function: ${name}()`)); + return new FluentNone(`${name}()`); + } + if (typeof func !== "function") { + scope.reportError(new TypeError(`Function ${name}() is not callable`)); + return new FluentNone(`${name}()`); + } + try { + let resolved = getArguments(scope, args); + return func(resolved.positional, resolved.named); + } catch (err) { + scope.reportError(err); + return new FluentNone(`${name}()`); + } +} +function resolveSelectExpression(scope, { + selector, + variants, + star +}) { + let sel = resolveExpression(scope, selector); + if (sel instanceof FluentNone) { + return getDefault(scope, variants, star); + } + for (const variant of variants) { + const key = resolveExpression(scope, variant.key); + if (match(scope, sel, key)) { + return resolvePattern(scope, variant.value); + } + } + return getDefault(scope, variants, star); +} +function resolveComplexPattern(scope, ptn) { + if (scope.dirty.has(ptn)) { + scope.reportError(new RangeError("Cyclic reference")); + return new FluentNone(); + } + scope.dirty.add(ptn); + const result = []; + const useIsolating = scope.bundle._useIsolating && ptn.length > 1; + for (const elem of ptn) { + if (typeof elem === "string") { + result.push(scope.bundle._transform(elem)); + continue; + } + scope.placeables++; + if (scope.placeables > MAX_PLACEABLES) { + scope.dirty.delete(ptn); + throw new RangeError(`Too many placeables expanded: ${scope.placeables}, ` + `max allowed is ${MAX_PLACEABLES}`); + } + if (useIsolating) { + result.push(FSI); + } + result.push(resolveExpression(scope, elem).toString(scope)); + if (useIsolating) { + result.push(PDI); + } + } + scope.dirty.delete(ptn); + return result.join(""); +} +function resolvePattern(scope, value) { + if (typeof value === "string") { + return scope.bundle._transform(value); + } + return resolveComplexPattern(scope, value); +} +;// ./node_modules/@fluent/bundle/esm/scope.js +class Scope { + constructor(bundle, errors, args) { + this.dirty = new WeakSet(); + this.params = null; + this.placeables = 0; + this.bundle = bundle; + this.errors = errors; + this.args = args; + } + reportError(error) { + if (!this.errors || !(error instanceof Error)) { + throw error; + } + this.errors.push(error); + } + memoizeIntlObject(ctor, opts) { + let cache = this.bundle._intls.get(ctor); + if (!cache) { + cache = {}; + this.bundle._intls.set(ctor, cache); + } + let id = JSON.stringify(opts); + if (!cache[id]) { + cache[id] = new ctor(this.bundle.locales, opts); + } + return cache[id]; + } +} +;// ./node_modules/@fluent/bundle/esm/builtins.js + +function values(opts, allowed) { + const unwrapped = Object.create(null); + for (const [name, opt] of Object.entries(opts)) { + if (allowed.includes(name)) { + unwrapped[name] = opt.valueOf(); + } + } + return unwrapped; +} +const NUMBER_ALLOWED = ["unitDisplay", "currencyDisplay", "useGrouping", "minimumIntegerDigits", "minimumFractionDigits", "maximumFractionDigits", "minimumSignificantDigits", "maximumSignificantDigits"]; +function NUMBER(args, opts) { + let arg = args[0]; + if (arg instanceof FluentNone) { + return new FluentNone(`NUMBER(${arg.valueOf()})`); + } + if (arg instanceof FluentNumber) { + return new FluentNumber(arg.valueOf(), { + ...arg.opts, + ...values(opts, NUMBER_ALLOWED) + }); + } + if (arg instanceof FluentDateTime) { + return new FluentNumber(arg.valueOf(), { + ...values(opts, NUMBER_ALLOWED) + }); + } + throw new TypeError("Invalid argument to NUMBER"); +} +const DATETIME_ALLOWED = ["dateStyle", "timeStyle", "fractionalSecondDigits", "dayPeriod", "hour12", "weekday", "era", "year", "month", "day", "hour", "minute", "second", "timeZoneName"]; +function DATETIME(args, opts) { + let arg = args[0]; + if (arg instanceof FluentNone) { + return new FluentNone(`DATETIME(${arg.valueOf()})`); + } + if (arg instanceof FluentDateTime) { + return new FluentDateTime(arg.valueOf(), { + ...arg.opts, + ...values(opts, DATETIME_ALLOWED) + }); + } + if (arg instanceof FluentNumber) { + return new FluentDateTime(arg.valueOf(), { + ...values(opts, DATETIME_ALLOWED) + }); + } + throw new TypeError("Invalid argument to DATETIME"); +} +;// ./node_modules/@fluent/bundle/esm/memoizer.js +const cache = new Map(); +function getMemoizerForLocale(locales) { + const stringLocale = Array.isArray(locales) ? locales.join(" ") : locales; + let memoizer = cache.get(stringLocale); + if (memoizer === undefined) { + memoizer = new Map(); + cache.set(stringLocale, memoizer); + } + return memoizer; +} +;// ./node_modules/@fluent/bundle/esm/bundle.js + + + + + +class FluentBundle { + constructor(locales, { + functions, + useIsolating = true, + transform = v => v + } = {}) { + this._terms = new Map(); + this._messages = new Map(); + this.locales = Array.isArray(locales) ? locales : [locales]; + this._functions = { + NUMBER: NUMBER, + DATETIME: DATETIME, + ...functions + }; + this._useIsolating = useIsolating; + this._transform = transform; + this._intls = getMemoizerForLocale(locales); + } + hasMessage(id) { + return this._messages.has(id); + } + getMessage(id) { + return this._messages.get(id); + } + addResource(res, { + allowOverrides = false + } = {}) { + const errors = []; + for (let i = 0; i < res.body.length; i++) { + let entry = res.body[i]; + if (entry.id.startsWith("-")) { + if (allowOverrides === false && this._terms.has(entry.id)) { + errors.push(new Error(`Attempt to override an existing term: "${entry.id}"`)); + continue; + } + this._terms.set(entry.id, entry); + } else { + if (allowOverrides === false && this._messages.has(entry.id)) { + errors.push(new Error(`Attempt to override an existing message: "${entry.id}"`)); + continue; + } + this._messages.set(entry.id, entry); + } + } + return errors; + } + formatPattern(pattern, args = null, errors = null) { + if (typeof pattern === "string") { + return this._transform(pattern); + } + let scope = new Scope(this, errors, args); + try { + let value = resolveComplexPattern(scope, pattern); + return value.toString(scope); + } catch (err) { + if (scope.errors && err instanceof Error) { + scope.errors.push(err); + return new FluentNone().toString(scope); + } + throw err; + } + } +} +;// ./node_modules/@fluent/bundle/esm/resource.js +const RE_MESSAGE_START = /^(-?[a-zA-Z][\w-]*) *= */gm; +const RE_ATTRIBUTE_START = /\.([a-zA-Z][\w-]*) *= */y; +const RE_VARIANT_START = /\*?\[/y; +const RE_NUMBER_LITERAL = /(-?[0-9]+(?:\.([0-9]+))?)/y; +const RE_IDENTIFIER = /([a-zA-Z][\w-]*)/y; +const RE_REFERENCE = /([$-])?([a-zA-Z][\w-]*)(?:\.([a-zA-Z][\w-]*))?/y; +const RE_FUNCTION_NAME = /^[A-Z][A-Z0-9_-]*$/; +const RE_TEXT_RUN = /([^{}\n\r]+)/y; +const RE_STRING_RUN = /([^\\"\n\r]*)/y; +const RE_STRING_ESCAPE = /\\([\\"])/y; +const RE_UNICODE_ESCAPE = /\\u([a-fA-F0-9]{4})|\\U([a-fA-F0-9]{6})/y; +const RE_LEADING_NEWLINES = /^\n+/; +const RE_TRAILING_SPACES = / +$/; +const RE_BLANK_LINES = / *\r?\n/g; +const RE_INDENT = /( *)$/; +const TOKEN_BRACE_OPEN = /{\s*/y; +const TOKEN_BRACE_CLOSE = /\s*}/y; +const TOKEN_BRACKET_OPEN = /\[\s*/y; +const TOKEN_BRACKET_CLOSE = /\s*] */y; +const TOKEN_PAREN_OPEN = /\s*\(\s*/y; +const TOKEN_ARROW = /\s*->\s*/y; +const TOKEN_COLON = /\s*:\s*/y; +const TOKEN_COMMA = /\s*,?\s*/y; +const TOKEN_BLANK = /\s+/y; +class FluentResource { + constructor(source) { + this.body = []; + RE_MESSAGE_START.lastIndex = 0; + let cursor = 0; + while (true) { + let next = RE_MESSAGE_START.exec(source); + if (next === null) { + break; + } + cursor = RE_MESSAGE_START.lastIndex; + try { + this.body.push(parseMessage(next[1])); + } catch (err) { + if (err instanceof SyntaxError) { + continue; + } + throw err; + } + } + function test(re) { + re.lastIndex = cursor; + return re.test(source); + } + function consumeChar(char, errorClass) { + if (source[cursor] === char) { + cursor++; + return true; + } + if (errorClass) { + throw new errorClass(`Expected ${char}`); + } + return false; + } + function consumeToken(re, errorClass) { + if (test(re)) { + cursor = re.lastIndex; + return true; + } + if (errorClass) { + throw new errorClass(`Expected ${re.toString()}`); + } + return false; + } + function match(re) { + re.lastIndex = cursor; + let result = re.exec(source); + if (result === null) { + throw new SyntaxError(`Expected ${re.toString()}`); + } + cursor = re.lastIndex; + return result; + } + function match1(re) { + return match(re)[1]; + } + function parseMessage(id) { + let value = parsePattern(); + let attributes = parseAttributes(); + if (value === null && Object.keys(attributes).length === 0) { + throw new SyntaxError("Expected message value or attributes"); + } + return { + id, + value, + attributes + }; + } + function parseAttributes() { + let attrs = Object.create(null); + while (test(RE_ATTRIBUTE_START)) { + let name = match1(RE_ATTRIBUTE_START); + let value = parsePattern(); + if (value === null) { + throw new SyntaxError("Expected attribute value"); + } + attrs[name] = value; + } + return attrs; + } + function parsePattern() { + let first; + if (test(RE_TEXT_RUN)) { + first = match1(RE_TEXT_RUN); + } + if (source[cursor] === "{" || source[cursor] === "}") { + return parsePatternElements(first ? [first] : [], Infinity); + } + let indent = parseIndent(); + if (indent) { + if (first) { + return parsePatternElements([first, indent], indent.length); + } + indent.value = trim(indent.value, RE_LEADING_NEWLINES); + return parsePatternElements([indent], indent.length); + } + if (first) { + return trim(first, RE_TRAILING_SPACES); + } + return null; + } + function parsePatternElements(elements = [], commonIndent) { + while (true) { + if (test(RE_TEXT_RUN)) { + elements.push(match1(RE_TEXT_RUN)); + continue; + } + if (source[cursor] === "{") { + elements.push(parsePlaceable()); + continue; + } + if (source[cursor] === "}") { + throw new SyntaxError("Unbalanced closing brace"); + } + let indent = parseIndent(); + if (indent) { + elements.push(indent); + commonIndent = Math.min(commonIndent, indent.length); + continue; + } + break; + } + let lastIndex = elements.length - 1; + let lastElement = elements[lastIndex]; + if (typeof lastElement === "string") { + elements[lastIndex] = trim(lastElement, RE_TRAILING_SPACES); + } + let baked = []; + for (let element of elements) { + if (element instanceof Indent) { + element = element.value.slice(0, element.value.length - commonIndent); + } + if (element) { + baked.push(element); + } + } + return baked; + } + function parsePlaceable() { + consumeToken(TOKEN_BRACE_OPEN, SyntaxError); + let selector = parseInlineExpression(); + if (consumeToken(TOKEN_BRACE_CLOSE)) { + return selector; + } + if (consumeToken(TOKEN_ARROW)) { + let variants = parseVariants(); + consumeToken(TOKEN_BRACE_CLOSE, SyntaxError); + return { + type: "select", + selector, + ...variants + }; + } + throw new SyntaxError("Unclosed placeable"); + } + function parseInlineExpression() { + if (source[cursor] === "{") { + return parsePlaceable(); + } + if (test(RE_REFERENCE)) { + let [, sigil, name, attr = null] = match(RE_REFERENCE); + if (sigil === "$") { + return { + type: "var", + name + }; + } + if (consumeToken(TOKEN_PAREN_OPEN)) { + let args = parseArguments(); + if (sigil === "-") { + return { + type: "term", + name, + attr, + args + }; + } + if (RE_FUNCTION_NAME.test(name)) { + return { + type: "func", + name, + args + }; + } + throw new SyntaxError("Function names must be all upper-case"); + } + if (sigil === "-") { + return { + type: "term", + name, + attr, + args: [] + }; + } + return { + type: "mesg", + name, + attr + }; + } + return parseLiteral(); + } + function parseArguments() { + let args = []; + while (true) { + switch (source[cursor]) { + case ")": + cursor++; + return args; + case undefined: + throw new SyntaxError("Unclosed argument list"); + } + args.push(parseArgument()); + consumeToken(TOKEN_COMMA); + } + } + function parseArgument() { + let expr = parseInlineExpression(); + if (expr.type !== "mesg") { + return expr; + } + if (consumeToken(TOKEN_COLON)) { + return { + type: "narg", + name: expr.name, + value: parseLiteral() + }; + } + return expr; + } + function parseVariants() { + let variants = []; + let count = 0; + let star; + while (test(RE_VARIANT_START)) { + if (consumeChar("*")) { + star = count; + } + let key = parseVariantKey(); + let value = parsePattern(); + if (value === null) { + throw new SyntaxError("Expected variant value"); + } + variants[count++] = { + key, + value + }; + } + if (count === 0) { + return null; + } + if (star === undefined) { + throw new SyntaxError("Expected default variant"); + } + return { + variants, + star + }; + } + function parseVariantKey() { + consumeToken(TOKEN_BRACKET_OPEN, SyntaxError); + let key; + if (test(RE_NUMBER_LITERAL)) { + key = parseNumberLiteral(); + } else { + key = { + type: "str", + value: match1(RE_IDENTIFIER) + }; + } + consumeToken(TOKEN_BRACKET_CLOSE, SyntaxError); + return key; + } + function parseLiteral() { + if (test(RE_NUMBER_LITERAL)) { + return parseNumberLiteral(); + } + if (source[cursor] === '"') { + return parseStringLiteral(); + } + throw new SyntaxError("Invalid expression"); + } + function parseNumberLiteral() { + let [, value, fraction = ""] = match(RE_NUMBER_LITERAL); + let precision = fraction.length; + return { + type: "num", + value: parseFloat(value), + precision + }; + } + function parseStringLiteral() { + consumeChar('"', SyntaxError); + let value = ""; + while (true) { + value += match1(RE_STRING_RUN); + if (source[cursor] === "\\") { + value += parseEscapeSequence(); + continue; + } + if (consumeChar('"')) { + return { + type: "str", + value + }; + } + throw new SyntaxError("Unclosed string literal"); + } + } + function parseEscapeSequence() { + if (test(RE_STRING_ESCAPE)) { + return match1(RE_STRING_ESCAPE); + } + if (test(RE_UNICODE_ESCAPE)) { + let [, codepoint4, codepoint6] = match(RE_UNICODE_ESCAPE); + let codepoint = parseInt(codepoint4 || codepoint6, 16); + return codepoint <= 0xd7ff || 0xe000 <= codepoint ? String.fromCodePoint(codepoint) : "�"; + } + throw new SyntaxError("Unknown escape sequence"); + } + function parseIndent() { + let start = cursor; + consumeToken(TOKEN_BLANK); + switch (source[cursor]) { + case ".": + case "[": + case "*": + case "}": + case undefined: + return false; + case "{": + return makeIndent(source.slice(start, cursor)); + } + if (source[cursor - 1] === " ") { + return makeIndent(source.slice(start, cursor)); + } + return false; + } + function trim(text, re) { + return text.replace(re, ""); + } + function makeIndent(blank) { + let value = blank.replace(RE_BLANK_LINES, "\n"); + let length = RE_INDENT.exec(blank)[1].length; + return new Indent(value, length); + } + } +} +class Indent { + constructor(value, length) { + this.value = value; + this.length = length; + } +} +;// ./node_modules/@fluent/bundle/esm/index.js + + + +;// ./node_modules/@fluent/dom/esm/overlay.js +const reOverlay = /<|&#?\w+;/; +const TEXT_LEVEL_ELEMENTS = { + "http://www.w3.org/1999/xhtml": ["em", "strong", "small", "s", "cite", "q", "dfn", "abbr", "data", "time", "code", "var", "samp", "kbd", "sub", "sup", "i", "b", "u", "mark", "bdi", "bdo", "span", "br", "wbr"] +}; +const LOCALIZABLE_ATTRIBUTES = { + "http://www.w3.org/1999/xhtml": { + global: ["title", "aria-label", "aria-valuetext"], + a: ["download"], + area: ["download", "alt"], + input: ["alt", "placeholder"], + menuitem: ["label"], + menu: ["label"], + optgroup: ["label"], + option: ["label"], + track: ["label"], + img: ["alt"], + textarea: ["placeholder"], + th: ["abbr"] + }, + "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul": { + global: ["accesskey", "aria-label", "aria-valuetext", "label", "title", "tooltiptext"], + description: ["value"], + key: ["key", "keycode"], + label: ["value"], + textbox: ["placeholder", "value"] + } +}; +function translateElement(element, translation) { + const { + value + } = translation; + if (typeof value === "string") { + if (element.localName === "title" && element.namespaceURI === "http://www.w3.org/1999/xhtml") { + element.textContent = value; + } else if (!reOverlay.test(value)) { + element.textContent = value; + } else { + const templateElement = element.ownerDocument.createElementNS("http://www.w3.org/1999/xhtml", "template"); + templateElement.innerHTML = value; + overlayChildNodes(templateElement.content, element); + } + } + overlayAttributes(translation, element); +} +function overlayChildNodes(fromFragment, toElement) { + for (const childNode of fromFragment.childNodes) { + if (childNode.nodeType === childNode.TEXT_NODE) { + continue; + } + if (childNode.hasAttribute("data-l10n-name")) { + const sanitized = getNodeForNamedElement(toElement, childNode); + fromFragment.replaceChild(sanitized, childNode); + continue; + } + if (isElementAllowed(childNode)) { + const sanitized = createSanitizedElement(childNode); + fromFragment.replaceChild(sanitized, childNode); + continue; + } + console.warn(`An element of forbidden type "${childNode.localName}" was found in ` + "the translation. Only safe text-level elements and elements with " + "data-l10n-name are allowed."); + fromFragment.replaceChild(createTextNodeFromTextContent(childNode), childNode); + } + toElement.textContent = ""; + toElement.appendChild(fromFragment); +} +function hasAttribute(attributes, name) { + if (!attributes) { + return false; + } + for (let attr of attributes) { + if (attr.name === name) { + return true; + } + } + return false; +} +function overlayAttributes(fromElement, toElement) { + const explicitlyAllowed = toElement.hasAttribute("data-l10n-attrs") ? toElement.getAttribute("data-l10n-attrs").split(",").map(i => i.trim()) : null; + for (const attr of Array.from(toElement.attributes)) { + if (isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed) && !hasAttribute(fromElement.attributes, attr.name)) { + toElement.removeAttribute(attr.name); + } + } + if (!fromElement.attributes) { + return; + } + for (const attr of Array.from(fromElement.attributes)) { + if (isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed) && toElement.getAttribute(attr.name) !== attr.value) { + toElement.setAttribute(attr.name, attr.value); + } + } +} +function getNodeForNamedElement(sourceElement, translatedChild) { + const childName = translatedChild.getAttribute("data-l10n-name"); + const sourceChild = sourceElement.querySelector(`[data-l10n-name="${childName}"]`); + if (!sourceChild) { + console.warn(`An element named "${childName}" wasn't found in the source.`); + return createTextNodeFromTextContent(translatedChild); + } + if (sourceChild.localName !== translatedChild.localName) { + console.warn(`An element named "${childName}" was found in the translation ` + `but its type ${translatedChild.localName} didn't match the ` + `element found in the source (${sourceChild.localName}).`); + return createTextNodeFromTextContent(translatedChild); + } + sourceElement.removeChild(sourceChild); + const clone = sourceChild.cloneNode(false); + return shallowPopulateUsing(translatedChild, clone); +} +function createSanitizedElement(element) { + const clone = element.ownerDocument.createElement(element.localName); + return shallowPopulateUsing(element, clone); +} +function createTextNodeFromTextContent(element) { + return element.ownerDocument.createTextNode(element.textContent); +} +function isElementAllowed(element) { + const allowed = TEXT_LEVEL_ELEMENTS[element.namespaceURI]; + return allowed && allowed.includes(element.localName); +} +function isAttrNameLocalizable(name, element, explicitlyAllowed = null) { + if (explicitlyAllowed && explicitlyAllowed.includes(name)) { + return true; + } + const allowed = LOCALIZABLE_ATTRIBUTES[element.namespaceURI]; + if (!allowed) { + return false; + } + const attrName = name.toLowerCase(); + const elemName = element.localName; + if (allowed.global.includes(attrName)) { + return true; + } + if (!allowed[elemName]) { + return false; + } + if (allowed[elemName].includes(attrName)) { + return true; + } + if (element.namespaceURI === "http://www.w3.org/1999/xhtml" && elemName === "input" && attrName === "value") { + const type = element.type.toLowerCase(); + if (type === "submit" || type === "button" || type === "reset") { + return true; + } + } + return false; +} +function shallowPopulateUsing(fromElement, toElement) { + toElement.textContent = fromElement.textContent; + overlayAttributes(fromElement, toElement); + return toElement; +} +;// ./node_modules/cached-iterable/src/cached_iterable.mjs +class CachedIterable extends Array { + static from(iterable) { + if (iterable instanceof this) { + return iterable; + } + return new this(iterable); + } +} +;// ./node_modules/cached-iterable/src/cached_sync_iterable.mjs + +class CachedSyncIterable extends CachedIterable { + constructor(iterable) { + super(); + if (Symbol.iterator in Object(iterable)) { + this.iterator = iterable[Symbol.iterator](); + } else { + throw new TypeError("Argument must implement the iteration protocol."); + } + } + [Symbol.iterator]() { + const cached = this; + let cur = 0; + return { + next() { + if (cached.length <= cur) { + cached.push(cached.iterator.next()); + } + return cached[cur++]; + } + }; + } + touchNext(count = 1) { + let idx = 0; + while (idx++ < count) { + const last = this[this.length - 1]; + if (last && last.done) { + break; + } + this.push(this.iterator.next()); + } + return this[this.length - 1]; + } +} +;// ./node_modules/cached-iterable/src/cached_async_iterable.mjs + +class CachedAsyncIterable extends CachedIterable { + constructor(iterable) { + super(); + if (Symbol.asyncIterator in Object(iterable)) { + this.iterator = iterable[Symbol.asyncIterator](); + } else if (Symbol.iterator in Object(iterable)) { + this.iterator = iterable[Symbol.iterator](); + } else { + throw new TypeError("Argument must implement the iteration protocol."); + } + } + [Symbol.asyncIterator]() { + const cached = this; + let cur = 0; + return { + async next() { + if (cached.length <= cur) { + cached.push(cached.iterator.next()); + } + return cached[cur++]; + } + }; + } + async touchNext(count = 1) { + let idx = 0; + while (idx++ < count) { + const last = this[this.length - 1]; + if (last && (await last).done) { + break; + } + this.push(this.iterator.next()); + } + return this[this.length - 1]; + } +} +;// ./node_modules/cached-iterable/src/index.mjs + + +;// ./node_modules/@fluent/dom/esm/localization.js + +class Localization { + constructor(resourceIds = [], generateBundles) { + this.resourceIds = resourceIds; + this.generateBundles = generateBundles; + this.onChange(true); + } + addResourceIds(resourceIds, eager = false) { + this.resourceIds.push(...resourceIds); + this.onChange(eager); + return this.resourceIds.length; + } + removeResourceIds(resourceIds) { + this.resourceIds = this.resourceIds.filter(r => !resourceIds.includes(r)); + this.onChange(); + return this.resourceIds.length; + } + async formatWithFallback(keys, method) { + const translations = []; + let hasAtLeastOneBundle = false; + for await (const bundle of this.bundles) { + hasAtLeastOneBundle = true; + const missingIds = keysFromBundle(method, bundle, keys, translations); + if (missingIds.size === 0) { + break; + } + if (typeof console !== "undefined") { + const locale = bundle.locales[0]; + const ids = Array.from(missingIds).join(", "); + console.warn(`[fluent] Missing translations in ${locale}: ${ids}`); + } + } + if (!hasAtLeastOneBundle && typeof console !== "undefined") { + console.warn(`[fluent] Request for keys failed because no resource bundles got generated. + keys: ${JSON.stringify(keys)}. + resourceIds: ${JSON.stringify(this.resourceIds)}.`); + } + return translations; + } + formatMessages(keys) { + return this.formatWithFallback(keys, messageFromBundle); + } + formatValues(keys) { + return this.formatWithFallback(keys, valueFromBundle); + } + async formatValue(id, args) { + const [val] = await this.formatValues([{ + id, + args + }]); + return val; + } + handleEvent() { + this.onChange(); + } + onChange(eager = false) { + this.bundles = CachedAsyncIterable.from(this.generateBundles(this.resourceIds)); + if (eager) { + this.bundles.touchNext(2); + } + } +} +function valueFromBundle(bundle, errors, message, args) { + if (message.value) { + return bundle.formatPattern(message.value, args, errors); + } + return null; +} +function messageFromBundle(bundle, errors, message, args) { + const formatted = { + value: null, + attributes: null + }; + if (message.value) { + formatted.value = bundle.formatPattern(message.value, args, errors); + } + let attrNames = Object.keys(message.attributes); + if (attrNames.length > 0) { + formatted.attributes = new Array(attrNames.length); + for (let [i, name] of attrNames.entries()) { + let value = bundle.formatPattern(message.attributes[name], args, errors); + formatted.attributes[i] = { + name, + value + }; + } + } + return formatted; +} +function keysFromBundle(method, bundle, keys, translations) { + const messageErrors = []; + const missingIds = new Set(); + keys.forEach(({ + id, + args + }, i) => { + if (translations[i] !== undefined) { + return; + } + let message = bundle.getMessage(id); + if (message) { + messageErrors.length = 0; + translations[i] = method(bundle, messageErrors, message, args); + if (messageErrors.length > 0 && typeof console !== "undefined") { + const locale = bundle.locales[0]; + const errors = messageErrors.join(", "); + console.warn(`[fluent][resolver] errors in ${locale}/${id}: ${errors}.`); + } + } else { + missingIds.add(id); + } + }); + return missingIds; +} +;// ./node_modules/@fluent/dom/esm/dom_localization.js + + +const L10NID_ATTR_NAME = "data-l10n-id"; +const L10NARGS_ATTR_NAME = "data-l10n-args"; +const L10N_ELEMENT_QUERY = `[${L10NID_ATTR_NAME}]`; +class DOMLocalization extends Localization { + constructor(resourceIds, generateBundles) { + super(resourceIds, generateBundles); + this.roots = new Set(); + this.pendingrAF = null; + this.pendingElements = new Set(); + this.windowElement = null; + this.mutationObserver = null; + this.observerConfig = { + attributes: true, + characterData: false, + childList: true, + subtree: true, + attributeFilter: [L10NID_ATTR_NAME, L10NARGS_ATTR_NAME] + }; + } + onChange(eager = false) { + super.onChange(eager); + if (this.roots) { + this.translateRoots(); + } + } + setAttributes(element, id, args) { + element.setAttribute(L10NID_ATTR_NAME, id); + if (args) { + element.setAttribute(L10NARGS_ATTR_NAME, JSON.stringify(args)); + } else { + element.removeAttribute(L10NARGS_ATTR_NAME); + } + return element; + } + getAttributes(element) { + return { + id: element.getAttribute(L10NID_ATTR_NAME), + args: JSON.parse(element.getAttribute(L10NARGS_ATTR_NAME) || null) + }; + } + connectRoot(newRoot) { + for (const root of this.roots) { + if (root === newRoot || root.contains(newRoot) || newRoot.contains(root)) { + throw new Error("Cannot add a root that overlaps with existing root."); + } + } + if (this.windowElement) { + if (this.windowElement !== newRoot.ownerDocument.defaultView) { + throw new Error(`Cannot connect a root: + DOMLocalization already has a root from a different window.`); + } + } else { + this.windowElement = newRoot.ownerDocument.defaultView; + this.mutationObserver = new this.windowElement.MutationObserver(mutations => this.translateMutations(mutations)); + } + this.roots.add(newRoot); + this.mutationObserver.observe(newRoot, this.observerConfig); + } + disconnectRoot(root) { + this.roots.delete(root); + this.pauseObserving(); + if (this.roots.size === 0) { + this.mutationObserver = null; + if (this.windowElement && this.pendingrAF) { + this.windowElement.cancelAnimationFrame(this.pendingrAF); + } + this.windowElement = null; + this.pendingrAF = null; + this.pendingElements.clear(); + return true; + } + this.resumeObserving(); + return false; + } + translateRoots() { + const roots = Array.from(this.roots); + return Promise.all(roots.map(root => this.translateFragment(root))); + } + pauseObserving() { + if (!this.mutationObserver) { + return; + } + this.translateMutations(this.mutationObserver.takeRecords()); + this.mutationObserver.disconnect(); + } + resumeObserving() { + if (!this.mutationObserver) { + return; + } + for (const root of this.roots) { + this.mutationObserver.observe(root, this.observerConfig); + } + } + translateMutations(mutations) { + for (const mutation of mutations) { + switch (mutation.type) { + case "attributes": + if (mutation.target.hasAttribute("data-l10n-id")) { + this.pendingElements.add(mutation.target); + } + break; + case "childList": + for (const addedNode of mutation.addedNodes) { + if (addedNode.nodeType === addedNode.ELEMENT_NODE) { + if (addedNode.childElementCount) { + for (const element of this.getTranslatables(addedNode)) { + this.pendingElements.add(element); + } + } else if (addedNode.hasAttribute(L10NID_ATTR_NAME)) { + this.pendingElements.add(addedNode); + } + } + } + break; + } + } + if (this.pendingElements.size > 0) { + if (this.pendingrAF === null) { + this.pendingrAF = this.windowElement.requestAnimationFrame(() => { + this.translateElements(Array.from(this.pendingElements)); + this.pendingElements.clear(); + this.pendingrAF = null; + }); + } + } + } + translateFragment(frag) { + return this.translateElements(this.getTranslatables(frag)); + } + async translateElements(elements) { + if (!elements.length) { + return undefined; + } + const keys = elements.map(this.getKeysForElement); + const translations = await this.formatMessages(keys); + return this.applyTranslations(elements, translations); + } + applyTranslations(elements, translations) { + this.pauseObserving(); + for (let i = 0; i < elements.length; i++) { + if (translations[i] !== undefined) { + translateElement(elements[i], translations[i]); + } + } + this.resumeObserving(); + } + getTranslatables(element) { + const nodes = Array.from(element.querySelectorAll(L10N_ELEMENT_QUERY)); + if (typeof element.hasAttribute === "function" && element.hasAttribute(L10NID_ATTR_NAME)) { + nodes.push(element); + } + return nodes; + } + getKeysForElement(element) { + return { + id: element.getAttribute(L10NID_ATTR_NAME), + args: JSON.parse(element.getAttribute(L10NARGS_ATTR_NAME) || null) + }; + } +} +;// ./node_modules/@fluent/dom/esm/index.js + + +;// ./web/l10n.js +class L10n { + #dir; + #elements; + #lang; + #l10n; + constructor({ + lang, + isRTL + }, l10n = null) { + this.#lang = L10n.#fixupLangCode(lang); + this.#l10n = l10n; + this.#dir = isRTL ?? L10n.#isRTL(this.#lang) ? "rtl" : "ltr"; + } + _setL10n(l10n) { + this.#l10n = l10n; + } + getLanguage() { + return this.#lang; + } + getDirection() { + return this.#dir; + } + async get(ids, args = null, fallback) { + if (Array.isArray(ids)) { + ids = ids.map(id => ({ + id + })); + const messages = await this.#l10n.formatMessages(ids); + return messages.map(message => message.value); + } + const messages = await this.#l10n.formatMessages([{ + id: ids, + args + }]); + return messages[0]?.value || fallback; + } + async translate(element) { + (this.#elements ||= new Set()).add(element); + try { + this.#l10n.connectRoot(element); + await this.#l10n.translateRoots(); + } catch {} + } + async translateOnce(element) { + try { + await this.#l10n.translateElements([element]); + } catch (ex) { + console.error("translateOnce:", ex); + } + } + async destroy() { + if (this.#elements) { + for (const element of this.#elements) { + this.#l10n.disconnectRoot(element); + } + this.#elements.clear(); + this.#elements = null; + } + this.#l10n.pauseObserving(); + } + pause() { + this.#l10n.pauseObserving(); + } + resume() { + this.#l10n.resumeObserving(); + } + static #fixupLangCode(langCode) { + langCode = langCode?.toLowerCase() || "en-us"; + const PARTIAL_LANG_CODES = { + en: "en-us", + es: "es-es", + fy: "fy-nl", + ga: "ga-ie", + gu: "gu-in", + hi: "hi-in", + hy: "hy-am", + nb: "nb-no", + ne: "ne-np", + nn: "nn-no", + pa: "pa-in", + pt: "pt-pt", + sv: "sv-se", + zh: "zh-cn" + }; + return PARTIAL_LANG_CODES[langCode] || langCode; + } + static #isRTL(lang) { + const shortCode = lang.split("-", 1)[0]; + return ["ar", "he", "fa", "ps", "ur"].includes(shortCode); + } +} +const GenericL10n = null; + +;// ./web/genericl10n.js + + + + +function createBundle(lang, text) { + const resource = new FluentResource(text); + const bundle = new FluentBundle(lang); + const errors = bundle.addResource(resource); + if (errors.length) { + console.error("L10n errors", errors); + } + return bundle; +} +class genericl10n_GenericL10n extends L10n { + constructor(lang) { + super({ + lang + }); + const generateBundles = !lang ? genericl10n_GenericL10n.#generateBundlesFallback.bind(genericl10n_GenericL10n, this.getLanguage()) : genericl10n_GenericL10n.#generateBundles.bind(genericl10n_GenericL10n, "en-us", this.getLanguage()); + this._setL10n(new DOMLocalization([], generateBundles)); + } + static async *#generateBundles(defaultLang, baseLang) { + const { + baseURL, + paths + } = await this.#getPaths(); + const langs = [baseLang]; + if (defaultLang !== baseLang) { + const shortLang = baseLang.split("-", 1)[0]; + if (shortLang !== baseLang) { + langs.push(shortLang); + } + langs.push(defaultLang); + } + for (const lang of langs) { + const bundle = await this.#createBundle(lang, baseURL, paths); + if (bundle) { + yield bundle; + } else if (lang === "en-us") { + yield this.#createBundleFallback(lang); + } + } + } + static async #createBundle(lang, baseURL, paths) { + const path = paths[lang]; + if (!path) { + return null; + } + const url = new URL(path, baseURL); + const text = await fetchData(url, "text"); + return createBundle(lang, text); + } + static async #getPaths() { + try { + const { + href + } = document.querySelector(`link[type="application/l10n"]`); + const paths = await fetchData(href, "json"); + return { + baseURL: href.replace(/[^/]*$/, "") || "./", + paths + }; + } catch {} + return { + baseURL: "./", + paths: Object.create(null) + }; + } + static async *#generateBundlesFallback(lang) { + yield this.#createBundleFallback(lang); + } + static async #createBundleFallback(lang) { + const text = "pdfjs-previous-button =\n .title = Previous Page\npdfjs-previous-button-label = Previous\npdfjs-next-button =\n .title = Next Page\npdfjs-next-button-label = Next\npdfjs-page-input =\n .title = Page\npdfjs-of-pages = of { $pagesCount }\npdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount })\npdfjs-zoom-out-button =\n .title = Zoom Out\npdfjs-zoom-out-button-label = Zoom Out\npdfjs-zoom-in-button =\n .title = Zoom In\npdfjs-zoom-in-button-label = Zoom In\npdfjs-zoom-select =\n .title = Zoom\npdfjs-presentation-mode-button =\n .title = Switch to Presentation Mode\npdfjs-presentation-mode-button-label = Presentation Mode\npdfjs-open-file-button =\n .title = Open File\npdfjs-open-file-button-label = Open\npdfjs-print-button =\n .title = Print\npdfjs-print-button-label = Print\npdfjs-save-button =\n .title = Save\npdfjs-save-button-label = Save\npdfjs-download-button =\n .title = Download\npdfjs-download-button-label = Download\npdfjs-bookmark-button =\n .title = Current Page (View URL from Current Page)\npdfjs-bookmark-button-label = Current Page\npdfjs-tools-button =\n .title = Tools\npdfjs-tools-button-label = Tools\npdfjs-first-page-button =\n .title = Go to First Page\npdfjs-first-page-button-label = Go to First Page\npdfjs-last-page-button =\n .title = Go to Last Page\npdfjs-last-page-button-label = Go to Last Page\npdfjs-page-rotate-cw-button =\n .title = Rotate Clockwise\npdfjs-page-rotate-cw-button-label = Rotate Clockwise\npdfjs-page-rotate-ccw-button =\n .title = Rotate Counterclockwise\npdfjs-page-rotate-ccw-button-label = Rotate Counterclockwise\npdfjs-cursor-text-select-tool-button =\n .title = Enable Text Selection Tool\npdfjs-cursor-text-select-tool-button-label = Text Selection Tool\npdfjs-cursor-hand-tool-button =\n .title = Enable Hand Tool\npdfjs-cursor-hand-tool-button-label = Hand Tool\npdfjs-scroll-page-button =\n .title = Use Page Scrolling\npdfjs-scroll-page-button-label = Page Scrolling\npdfjs-scroll-vertical-button =\n .title = Use Vertical Scrolling\npdfjs-scroll-vertical-button-label = Vertical Scrolling\npdfjs-scroll-horizontal-button =\n .title = Use Horizontal Scrolling\npdfjs-scroll-horizontal-button-label = Horizontal Scrolling\npdfjs-scroll-wrapped-button =\n .title = Use Wrapped Scrolling\npdfjs-scroll-wrapped-button-label = Wrapped Scrolling\npdfjs-spread-none-button =\n .title = Do not join page spreads\npdfjs-spread-none-button-label = No Spreads\npdfjs-spread-odd-button =\n .title = Join page spreads starting with odd-numbered pages\npdfjs-spread-odd-button-label = Odd Spreads\npdfjs-spread-even-button =\n .title = Join page spreads starting with even-numbered pages\npdfjs-spread-even-button-label = Even Spreads\npdfjs-document-properties-button =\n .title = Document Properties\u2026\npdfjs-document-properties-button-label = Document Properties\u2026\npdfjs-document-properties-file-name = File name:\npdfjs-document-properties-file-size = File size:\npdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes)\npdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes)\npdfjs-document-properties-title = Title:\npdfjs-document-properties-author = Author:\npdfjs-document-properties-subject = Subject:\npdfjs-document-properties-keywords = Keywords:\npdfjs-document-properties-creation-date = Creation Date:\npdfjs-document-properties-modification-date = Modification Date:\npdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: \"short\", timeStyle: \"medium\") }\npdfjs-document-properties-creator = Creator:\npdfjs-document-properties-producer = PDF Producer:\npdfjs-document-properties-version = PDF Version:\npdfjs-document-properties-page-count = Page Count:\npdfjs-document-properties-page-size = Page Size:\npdfjs-document-properties-page-size-unit-inches = in\npdfjs-document-properties-page-size-unit-millimeters = mm\npdfjs-document-properties-page-size-orientation-portrait = portrait\npdfjs-document-properties-page-size-orientation-landscape = landscape\npdfjs-document-properties-page-size-name-a-three = A3\npdfjs-document-properties-page-size-name-a-four = A4\npdfjs-document-properties-page-size-name-letter = Letter\npdfjs-document-properties-page-size-name-legal = Legal\npdfjs-document-properties-page-size-dimension-string = { $width } \xD7 { $height } { $unit } ({ $orientation })\npdfjs-document-properties-page-size-dimension-name-string = { $width } \xD7 { $height } { $unit } ({ $name }, { $orientation })\npdfjs-document-properties-linearized = Fast Web View:\npdfjs-document-properties-linearized-yes = Yes\npdfjs-document-properties-linearized-no = No\npdfjs-document-properties-close-button = Close\npdfjs-print-progress-message = Preparing document for printing\u2026\npdfjs-print-progress-percent = { $progress }%\npdfjs-print-progress-close-button = Cancel\npdfjs-printing-not-supported = Warning: Printing is not fully supported by this browser.\npdfjs-printing-not-ready = Warning: The PDF is not fully loaded for printing.\npdfjs-toggle-sidebar-button =\n .title = Toggle Sidebar\npdfjs-toggle-sidebar-notification-button =\n .title = Toggle Sidebar (document contains outline/attachments/layers)\npdfjs-toggle-sidebar-button-label = Toggle Sidebar\npdfjs-document-outline-button =\n .title = Show Document Outline (double-click to expand/collapse all items)\npdfjs-document-outline-button-label = Document Outline\npdfjs-attachments-button =\n .title = Show Attachments\npdfjs-attachments-button-label = Attachments\npdfjs-layers-button =\n .title = Show Layers (double-click to reset all layers to the default state)\npdfjs-layers-button-label = Layers\npdfjs-thumbs-button =\n .title = Show Thumbnails\npdfjs-thumbs-button-label = Thumbnails\npdfjs-current-outline-item-button =\n .title = Find Current Outline Item\npdfjs-current-outline-item-button-label = Current Outline Item\npdfjs-findbar-button =\n .title = Find in Document\npdfjs-findbar-button-label = Find\npdfjs-additional-layers = Additional Layers\npdfjs-thumb-page-title =\n .title = Page { $page }\npdfjs-thumb-page-canvas =\n .aria-label = Thumbnail of Page { $page }\npdfjs-find-input =\n .title = Find\n .placeholder = Find in document\u2026\npdfjs-find-previous-button =\n .title = Find the previous occurrence of the phrase\npdfjs-find-previous-button-label = Previous\npdfjs-find-next-button =\n .title = Find the next occurrence of the phrase\npdfjs-find-next-button-label = Next\npdfjs-find-highlight-checkbox = Highlight All\npdfjs-find-match-case-checkbox-label = Match Case\npdfjs-find-match-diacritics-checkbox-label = Match Diacritics\npdfjs-find-entire-word-checkbox-label = Whole Words\npdfjs-find-reached-top = Reached top of document, continued from bottom\npdfjs-find-reached-bottom = Reached end of document, continued from top\npdfjs-find-match-count =\n { $total ->\n [one] { $current } of { $total } match\n *[other] { $current } of { $total } matches\n }\npdfjs-find-match-count-limit =\n { $limit ->\n [one] More than { $limit } match\n *[other] More than { $limit } matches\n }\npdfjs-find-not-found = Phrase not found\npdfjs-page-scale-width = Page Width\npdfjs-page-scale-fit = Page Fit\npdfjs-page-scale-auto = Automatic Zoom\npdfjs-page-scale-actual = Actual Size\npdfjs-page-scale-percent = { $scale }%\npdfjs-page-landmark =\n .aria-label = Page { $page }\npdfjs-loading-error = An error occurred while loading the PDF.\npdfjs-invalid-file-error = Invalid or corrupted PDF file.\npdfjs-missing-file-error = Missing PDF file.\npdfjs-unexpected-response-error = Unexpected server response.\npdfjs-rendering-error = An error occurred while rendering the page.\npdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: \"short\", timeStyle: \"medium\") }\npdfjs-text-annotation-type =\n .alt = [{ $type } Annotation]\npdfjs-password-label = Enter the password to open this PDF file.\npdfjs-password-invalid = Invalid password. Please try again.\npdfjs-password-ok-button = OK\npdfjs-password-cancel-button = Cancel\npdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts.\npdfjs-editor-free-text-button =\n .title = Text\npdfjs-editor-free-text-button-label = Text\npdfjs-editor-ink-button =\n .title = Draw\npdfjs-editor-ink-button-label = Draw\npdfjs-editor-stamp-button =\n .title = Add or edit images\npdfjs-editor-stamp-button-label = Add or edit images\npdfjs-editor-highlight-button =\n .title = Highlight\npdfjs-editor-highlight-button-label = Highlight\npdfjs-highlight-floating-button1 =\n .title = Highlight\n .aria-label = Highlight\npdfjs-highlight-floating-button-label = Highlight\npdfjs-editor-remove-ink-button =\n .title = Remove drawing\npdfjs-editor-remove-freetext-button =\n .title = Remove text\npdfjs-editor-remove-stamp-button =\n .title = Remove image\npdfjs-editor-remove-highlight-button =\n .title = Remove highlight\npdfjs-editor-free-text-color-input = Color\npdfjs-editor-free-text-size-input = Size\npdfjs-editor-ink-color-input = Color\npdfjs-editor-ink-thickness-input = Thickness\npdfjs-editor-ink-opacity-input = Opacity\npdfjs-editor-stamp-add-image-button =\n .title = Add image\npdfjs-editor-stamp-add-image-button-label = Add image\npdfjs-editor-free-highlight-thickness-input = Thickness\npdfjs-editor-free-highlight-thickness-title =\n .title = Change thickness when highlighting items other than text\npdfjs-free-text2 =\n .aria-label = Text Editor\n .default-content = Start typing\u2026\npdfjs-ink =\n .aria-label = Draw Editor\npdfjs-ink-canvas =\n .aria-label = User-created image\npdfjs-editor-alt-text-button =\n .aria-label = Alt text\npdfjs-editor-alt-text-button-label = Alt text\npdfjs-editor-alt-text-edit-button =\n .aria-label = Edit alt text\npdfjs-editor-alt-text-dialog-label = Choose an option\npdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people can\u2019t see the image or when it doesn\u2019t load.\npdfjs-editor-alt-text-add-description-label = Add a description\npdfjs-editor-alt-text-add-description-description = Aim for 1-2 sentences that describe the subject, setting, or actions.\npdfjs-editor-alt-text-mark-decorative-label = Mark as decorative\npdfjs-editor-alt-text-mark-decorative-description = This is used for ornamental images, like borders or watermarks.\npdfjs-editor-alt-text-cancel-button = Cancel\npdfjs-editor-alt-text-save-button = Save\npdfjs-editor-alt-text-decorative-tooltip = Marked as decorative\npdfjs-editor-alt-text-textarea =\n .placeholder = For example, \u201CA young man sits down at a table to eat a meal\u201D\npdfjs-editor-resizer-top-left =\n .aria-label = Top left corner \u2014 resize\npdfjs-editor-resizer-top-middle =\n .aria-label = Top middle \u2014 resize\npdfjs-editor-resizer-top-right =\n .aria-label = Top right corner \u2014 resize\npdfjs-editor-resizer-middle-right =\n .aria-label = Middle right \u2014 resize\npdfjs-editor-resizer-bottom-right =\n .aria-label = Bottom right corner \u2014 resize\npdfjs-editor-resizer-bottom-middle =\n .aria-label = Bottom middle \u2014 resize\npdfjs-editor-resizer-bottom-left =\n .aria-label = Bottom left corner \u2014 resize\npdfjs-editor-resizer-middle-left =\n .aria-label = Middle left \u2014 resize\npdfjs-editor-highlight-colorpicker-label = Highlight color\npdfjs-editor-colorpicker-button =\n .title = Change color\npdfjs-editor-colorpicker-dropdown =\n .aria-label = Color choices\npdfjs-editor-colorpicker-yellow =\n .title = Yellow\npdfjs-editor-colorpicker-green =\n .title = Green\npdfjs-editor-colorpicker-blue =\n .title = Blue\npdfjs-editor-colorpicker-pink =\n .title = Pink\npdfjs-editor-colorpicker-red =\n .title = Red\npdfjs-editor-highlight-show-all-button-label = Show all\npdfjs-editor-highlight-show-all-button =\n .title = Show all\npdfjs-editor-new-alt-text-dialog-edit-label = Edit alt text (image description)\npdfjs-editor-new-alt-text-dialog-add-label = Add alt text (image description)\npdfjs-editor-new-alt-text-textarea =\n .placeholder = Write your description here\u2026\npdfjs-editor-new-alt-text-description = Short description for people who can\u2019t see the image or when the image doesn\u2019t load.\npdfjs-editor-new-alt-text-disclaimer1 = This alt text was created automatically and may be inaccurate.\npdfjs-editor-new-alt-text-disclaimer-learn-more-url = Learn more\npdfjs-editor-new-alt-text-create-automatically-button-label = Create alt text automatically\npdfjs-editor-new-alt-text-not-now-button = Not now\npdfjs-editor-new-alt-text-error-title = Couldn\u2019t create alt text automatically\npdfjs-editor-new-alt-text-error-description = Please write your own alt text or try again later.\npdfjs-editor-new-alt-text-error-close-button = Close\npdfjs-editor-new-alt-text-ai-model-downloading-progress = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB)\n .aria-valuetext = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB)\npdfjs-editor-new-alt-text-added-button =\n .aria-label = Alt text added\npdfjs-editor-new-alt-text-added-button-label = Alt text added\npdfjs-editor-new-alt-text-missing-button =\n .aria-label = Missing alt text\npdfjs-editor-new-alt-text-missing-button-label = Missing alt text\npdfjs-editor-new-alt-text-to-review-button =\n .aria-label = Review alt text\npdfjs-editor-new-alt-text-to-review-button-label = Review alt text\npdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Created automatically: { $generatedAltText }\npdfjs-image-alt-text-settings-button =\n .title = Image alt text settings\npdfjs-image-alt-text-settings-button-label = Image alt text settings\npdfjs-editor-alt-text-settings-dialog-label = Image alt text settings\npdfjs-editor-alt-text-settings-automatic-title = Automatic alt text\npdfjs-editor-alt-text-settings-create-model-button-label = Create alt text automatically\npdfjs-editor-alt-text-settings-create-model-description = Suggests descriptions to help people who can\u2019t see the image or when the image doesn\u2019t load.\npdfjs-editor-alt-text-settings-download-model-label = Alt text AI model ({ $totalSize } MB)\npdfjs-editor-alt-text-settings-ai-model-description = Runs locally on your device so your data stays private. Required for automatic alt text.\npdfjs-editor-alt-text-settings-delete-model-button = Delete\npdfjs-editor-alt-text-settings-download-model-button = Download\npdfjs-editor-alt-text-settings-downloading-model-button = Downloading\u2026\npdfjs-editor-alt-text-settings-editor-title = Alt text editor\npdfjs-editor-alt-text-settings-show-dialog-button-label = Show alt text editor right away when adding an image\npdfjs-editor-alt-text-settings-show-dialog-description = Helps you make sure all your images have alt text.\npdfjs-editor-alt-text-settings-close-button = Close\npdfjs-editor-undo-bar-message-highlight = Highlight removed\npdfjs-editor-undo-bar-message-freetext = Text removed\npdfjs-editor-undo-bar-message-ink = Drawing removed\npdfjs-editor-undo-bar-message-stamp = Image removed\npdfjs-editor-undo-bar-message-multiple =\n { $count ->\n [one] { $count } annotation removed\n *[other] { $count } annotations removed\n }\npdfjs-editor-undo-bar-undo-button =\n .title = Undo\npdfjs-editor-undo-bar-undo-button-label = Undo\npdfjs-editor-undo-bar-close-button =\n .title = Close\npdfjs-editor-undo-bar-close-button-label = Close"; + return createBundle(lang, text); + } +} + +;// ./web/generic_scripting.js + +async function docProperties(pdfDocument) { + const url = "", + baseUrl = url.split("#", 1)[0]; + let { + info, + metadata, + contentDispositionFilename, + contentLength + } = await pdfDocument.getMetadata(); + if (!contentLength) { + const { + length + } = await pdfDocument.getDownloadInfo(); + contentLength = length; + } + return { + ...info, + baseURL: baseUrl, + filesize: contentLength, + filename: contentDispositionFilename || getPdfFilenameFromUrl(url), + metadata: metadata?.getRaw(), + authors: metadata?.get("dc:creator"), + numPages: pdfDocument.numPages, + URL: url + }; +} +class GenericScripting { + constructor(sandboxBundleSrc) { + this._ready = new Promise((resolve, reject) => { + const sandbox = import(/*webpackIgnore: true*/sandboxBundleSrc); + sandbox.then(pdfjsSandbox => { + resolve(pdfjsSandbox.QuickJSSandbox()); + }).catch(reject); + }); + } + async createSandbox(data) { + const sandbox = await this._ready; + sandbox.create(data); + } + async dispatchEventInSandbox(event) { + const sandbox = await this._ready; + setTimeout(() => sandbox.dispatchEvent(event), 0); + } + async destroySandbox() { + const sandbox = await this._ready; + sandbox.nukeSandbox(); + } +} + +;// ./web/genericcom.js + + + + + +function initCom(app) {} +class Preferences extends BasePreferences { + async _writeToStorage(prefObj) { + localStorage.setItem("pdfjs.preferences", JSON.stringify(prefObj)); + } + async _readFromStorage(prefObj) { + return { + prefs: JSON.parse(localStorage.getItem("pdfjs.preferences")) + }; + } +} +class ExternalServices extends BaseExternalServices { + async createL10n() { + return new genericl10n_GenericL10n(AppOptions.get("localeProperties")?.lang); + } + createScripting() { + return new GenericScripting(AppOptions.get("sandboxBundleSrc")); + } +} +class MLManager { + async isEnabledFor(_name) { + return false; + } + async deleteModel(_service) { + return null; + } + isReady(_name) { + return false; + } + guess(_data) {} + toggleService(_name, _enabled) {} + static getFakeMLManager(options) { + return new FakeMLManager(options); + } +} +class FakeMLManager { + eventBus = null; + hasProgress = false; + constructor({ + enableGuessAltText, + enableAltTextModelDownload + }) { + this.enableGuessAltText = enableGuessAltText; + this.enableAltTextModelDownload = enableAltTextModelDownload; + } + setEventBus(eventBus, abortSignal) { + this.eventBus = eventBus; + } + async isEnabledFor(_name) { + return this.enableGuessAltText; + } + async deleteModel(_name) { + this.enableAltTextModelDownload = false; + return null; + } + async loadModel(_name) {} + async downloadModel(_name) { + this.hasProgress = true; + const { + promise, + resolve + } = Promise.withResolvers(); + const total = 1e8; + const end = 1.5 * total; + const increment = 5e6; + let loaded = 0; + const id = setInterval(() => { + loaded += increment; + if (loaded <= end) { + this.eventBus.dispatch("loadaiengineprogress", { + source: this, + detail: { + total, + totalLoaded: loaded, + finished: loaded + increment >= end + } + }); + return; + } + clearInterval(id); + this.hasProgress = false; + this.enableAltTextModelDownload = true; + resolve(true); + }, 900); + return promise; + } + isReady(_name) { + return this.enableAltTextModelDownload; + } + guess({ + request: { + data + } + }) { + return new Promise(resolve => { + setTimeout(() => { + resolve(data ? { + output: "Fake alt text." + } : { + error: true + }); + }, 3000); + }); + } + toggleService(_name, enabled) { + this.enableGuessAltText = enabled; + } +} + +;// ./web/new_alt_text_manager.js + +class NewAltTextManager { + #boundCancel = this.#cancel.bind(this); + #createAutomaticallyButton; + #currentEditor = null; + #cancelButton; + #descriptionContainer; + #dialog; + #disclaimer; + #downloadModel; + #downloadModelDescription; + #eventBus; + #firstTime = false; + #guessedAltText; + #hasAI = null; + #isEditing = null; + #imagePreview; + #imageData; + #isAILoading = false; + #wasAILoading = false; + #learnMore; + #notNowButton; + #overlayManager; + #textarea; + #title; + #uiManager; + #previousAltText = null; + constructor({ + descriptionContainer, + dialog, + imagePreview, + cancelButton, + disclaimer, + notNowButton, + saveButton, + textarea, + learnMore, + errorCloseButton, + createAutomaticallyButton, + downloadModel, + downloadModelDescription, + title + }, overlayManager, eventBus) { + this.#cancelButton = cancelButton; + this.#createAutomaticallyButton = createAutomaticallyButton; + this.#descriptionContainer = descriptionContainer; + this.#dialog = dialog; + this.#disclaimer = disclaimer; + this.#notNowButton = notNowButton; + this.#imagePreview = imagePreview; + this.#textarea = textarea; + this.#learnMore = learnMore; + this.#title = title; + this.#downloadModel = downloadModel; + this.#downloadModelDescription = downloadModelDescription; + this.#overlayManager = overlayManager; + this.#eventBus = eventBus; + dialog.addEventListener("close", this.#close.bind(this)); + dialog.addEventListener("contextmenu", event => { + if (event.target !== this.#textarea) { + event.preventDefault(); + } + }); + cancelButton.addEventListener("click", this.#boundCancel); + notNowButton.addEventListener("click", this.#boundCancel); + saveButton.addEventListener("click", this.#save.bind(this)); + errorCloseButton.addEventListener("click", () => { + this.#toggleError(false); + }); + createAutomaticallyButton.addEventListener("click", async () => { + const checked = createAutomaticallyButton.getAttribute("aria-pressed") !== "true"; + this.#currentEditor._reportTelemetry({ + action: "pdfjs.image.alt_text.ai_generation_check", + data: { + status: checked + } + }); + if (this.#uiManager) { + this.#uiManager.setPreference("enableGuessAltText", checked); + await this.#uiManager.mlManager.toggleService("altText", checked); + } + this.#toggleGuessAltText(checked, false); + }); + textarea.addEventListener("focus", () => { + this.#wasAILoading = this.#isAILoading; + this.#toggleLoading(false); + this.#toggleTitleAndDisclaimer(); + }); + textarea.addEventListener("blur", () => { + if (!textarea.value) { + this.#toggleLoading(this.#wasAILoading); + } + this.#toggleTitleAndDisclaimer(); + }); + textarea.addEventListener("input", () => { + this.#toggleTitleAndDisclaimer(); + }); + eventBus._on("enableguessalttext", ({ + value + }) => { + this.#toggleGuessAltText(value, false); + }); + this.#overlayManager.register(dialog); + this.#learnMore.addEventListener("click", () => { + this.#currentEditor._reportTelemetry({ + action: "pdfjs.image.alt_text.info", + data: { + topic: "alt_text" + } + }); + }); + } + #toggleLoading(value) { + if (!this.#uiManager || this.#isAILoading === value) { + return; + } + this.#isAILoading = value; + this.#descriptionContainer.classList.toggle("loading", value); + } + #toggleError(value) { + if (!this.#uiManager) { + return; + } + this.#dialog.classList.toggle("error", value); + } + async #toggleGuessAltText(value, isInitial = false) { + if (!this.#uiManager) { + return; + } + this.#dialog.classList.toggle("aiDisabled", !value); + this.#createAutomaticallyButton.setAttribute("aria-pressed", value); + if (value) { + const { + altTextLearnMoreUrl + } = this.#uiManager.mlManager; + if (altTextLearnMoreUrl) { + this.#learnMore.href = altTextLearnMoreUrl; + } + this.#mlGuessAltText(isInitial); + } else { + this.#toggleLoading(false); + this.#isAILoading = false; + this.#toggleTitleAndDisclaimer(); + } + } + #toggleNotNow() { + this.#notNowButton.classList.toggle("hidden", !this.#firstTime); + this.#cancelButton.classList.toggle("hidden", this.#firstTime); + } + #toggleAI(value) { + if (!this.#uiManager || this.#hasAI === value) { + return; + } + this.#hasAI = value; + this.#dialog.classList.toggle("noAi", !value); + this.#toggleTitleAndDisclaimer(); + } + #toggleTitleAndDisclaimer() { + const visible = this.#isAILoading || this.#guessedAltText && this.#guessedAltText === this.#textarea.value; + this.#disclaimer.hidden = !visible; + const isEditing = this.#isAILoading || !!this.#textarea.value; + if (this.#isEditing === isEditing) { + return; + } + this.#isEditing = isEditing; + this.#title.setAttribute("data-l10n-id", isEditing ? "pdfjs-editor-new-alt-text-dialog-edit-label" : "pdfjs-editor-new-alt-text-dialog-add-label"); + } + async #mlGuessAltText(isInitial) { + if (this.#isAILoading) { + return; + } + if (this.#textarea.value) { + return; + } + if (isInitial && this.#previousAltText !== null) { + return; + } + this.#guessedAltText = this.#currentEditor.guessedAltText; + if (this.#previousAltText === null && this.#guessedAltText) { + this.#addAltText(this.#guessedAltText); + return; + } + this.#toggleLoading(true); + this.#toggleTitleAndDisclaimer(); + let hasError = false; + try { + const altText = await this.#currentEditor.mlGuessAltText(this.#imageData, false); + if (altText) { + this.#guessedAltText = altText; + this.#wasAILoading = this.#isAILoading; + if (this.#isAILoading) { + this.#addAltText(altText); + } + } + } catch (e) { + console.error(e); + hasError = true; + } + this.#toggleLoading(false); + this.#toggleTitleAndDisclaimer(); + if (hasError && this.#uiManager) { + this.#toggleError(true); + } + } + #addAltText(altText) { + if (!this.#uiManager || this.#textarea.value) { + return; + } + this.#textarea.value = altText; + this.#toggleTitleAndDisclaimer(); + } + #setProgress() { + this.#downloadModel.classList.toggle("hidden", false); + const callback = async ({ + detail: { + finished, + total, + totalLoaded + } + }) => { + const ONE_MEGA_BYTES = 1e6; + totalLoaded = Math.min(0.99 * total, totalLoaded); + const totalSize = this.#downloadModelDescription.ariaValueMax = Math.round(total / ONE_MEGA_BYTES); + const downloadedSize = this.#downloadModelDescription.ariaValueNow = Math.round(totalLoaded / ONE_MEGA_BYTES); + this.#downloadModelDescription.setAttribute("data-l10n-args", JSON.stringify({ + totalSize, + downloadedSize + })); + if (!finished) { + return; + } + this.#eventBus._off("loadaiengineprogress", callback); + this.#downloadModel.classList.toggle("hidden", true); + this.#toggleAI(true); + if (!this.#uiManager) { + return; + } + const { + mlManager + } = this.#uiManager; + mlManager.toggleService("altText", true); + this.#toggleGuessAltText(await mlManager.isEnabledFor("altText"), true); + }; + this.#eventBus._on("loadaiengineprogress", callback); + } + async editAltText(uiManager, editor, firstTime) { + if (this.#currentEditor || !editor) { + return; + } + if (firstTime && editor.hasAltTextData()) { + editor.altTextFinish(); + return; + } + this.#firstTime = firstTime; + let { + mlManager + } = uiManager; + let hasAI = !!mlManager; + this.#toggleTitleAndDisclaimer(); + if (mlManager && !mlManager.isReady("altText")) { + hasAI = false; + if (mlManager.hasProgress) { + this.#setProgress(); + } else { + mlManager = null; + } + } else { + this.#downloadModel.classList.toggle("hidden", true); + } + const isAltTextEnabledPromise = mlManager?.isEnabledFor("altText"); + this.#currentEditor = editor; + this.#uiManager = uiManager; + this.#uiManager.removeEditListeners(); + ({ + altText: this.#previousAltText + } = editor.altTextData); + this.#textarea.value = this.#previousAltText ?? ""; + const AI_MAX_IMAGE_DIMENSION = 224; + const MAX_PREVIEW_DIMENSION = 180; + let canvas, width, height; + if (mlManager) { + ({ + canvas, + width, + height, + imageData: this.#imageData + } = editor.copyCanvas(AI_MAX_IMAGE_DIMENSION, MAX_PREVIEW_DIMENSION, true)); + if (hasAI) { + this.#toggleGuessAltText(await isAltTextEnabledPromise, true); + } + } else { + ({ + canvas, + width, + height + } = editor.copyCanvas(AI_MAX_IMAGE_DIMENSION, MAX_PREVIEW_DIMENSION, false)); + } + canvas.setAttribute("role", "presentation"); + const { + style + } = canvas; + style.width = `${width}px`; + style.height = `${height}px`; + this.#imagePreview.append(canvas); + this.#toggleNotNow(); + this.#toggleAI(hasAI); + this.#toggleError(false); + try { + await this.#overlayManager.open(this.#dialog); + } catch (ex) { + this.#close(); + throw ex; + } + } + #cancel() { + this.#currentEditor.altTextData = { + cancel: true + }; + const altText = this.#textarea.value.trim(); + this.#currentEditor._reportTelemetry({ + action: "pdfjs.image.alt_text.dismiss", + data: { + alt_text_type: altText ? "present" : "empty", + flow: this.#firstTime ? "image_add" : "alt_text_edit" + } + }); + this.#currentEditor._reportTelemetry({ + action: "pdfjs.image.image_added", + data: { + alt_text_modal: true, + alt_text_type: "skipped" + } + }); + this.#finish(); + } + #finish() { + if (this.#overlayManager.active === this.#dialog) { + this.#overlayManager.close(this.#dialog); + } + } + #close() { + const canvas = this.#imagePreview.firstChild; + canvas.remove(); + canvas.width = canvas.height = 0; + this.#imageData = null; + this.#toggleLoading(false); + this.#uiManager?.addEditListeners(); + this.#currentEditor.altTextFinish(); + this.#uiManager?.setSelected(this.#currentEditor); + this.#currentEditor = null; + this.#uiManager = null; + } + #extractWords(text) { + return new Set(text.toLowerCase().split(/[^\p{L}\p{N}]+/gu).filter(x => !!x)); + } + #save() { + const altText = this.#textarea.value.trim(); + this.#currentEditor.altTextData = { + altText, + decorative: false + }; + this.#currentEditor.altTextData.guessedAltText = this.#guessedAltText; + if (this.#guessedAltText && this.#guessedAltText !== altText) { + const guessedWords = this.#extractWords(this.#guessedAltText); + const words = this.#extractWords(altText); + this.#currentEditor._reportTelemetry({ + action: "pdfjs.image.alt_text.user_edit", + data: { + total_words: guessedWords.size, + words_removed: guessedWords.difference(words).size, + words_added: words.difference(guessedWords).size + } + }); + } + this.#currentEditor._reportTelemetry({ + action: "pdfjs.image.image_added", + data: { + alt_text_modal: true, + alt_text_type: altText ? "present" : "empty" + } + }); + this.#currentEditor._reportTelemetry({ + action: "pdfjs.image.alt_text.save", + data: { + alt_text_type: altText ? "present" : "empty", + flow: this.#firstTime ? "image_add" : "alt_text_edit" + } + }); + this.#finish(); + } + destroy() { + this.#uiManager = null; + this.#finish(); + } +} +class ImageAltTextSettings { + #aiModelSettings; + #createModelButton; + #downloadModelButton; + #dialog; + #eventBus; + #mlManager; + #overlayManager; + #showAltTextDialogButton; + constructor({ + dialog, + createModelButton, + aiModelSettings, + learnMore, + closeButton, + deleteModelButton, + downloadModelButton, + showAltTextDialogButton + }, overlayManager, eventBus, mlManager) { + this.#dialog = dialog; + this.#aiModelSettings = aiModelSettings; + this.#createModelButton = createModelButton; + this.#downloadModelButton = downloadModelButton; + this.#showAltTextDialogButton = showAltTextDialogButton; + this.#overlayManager = overlayManager; + this.#eventBus = eventBus; + this.#mlManager = mlManager; + const { + altTextLearnMoreUrl + } = mlManager; + if (altTextLearnMoreUrl) { + learnMore.href = altTextLearnMoreUrl; + } + dialog.addEventListener("contextmenu", noContextMenu); + createModelButton.addEventListener("click", async e => { + const checked = this.#togglePref("enableGuessAltText", e); + await mlManager.toggleService("altText", checked); + this.#reportTelemetry({ + type: "stamp", + action: "pdfjs.image.alt_text.settings_ai_generation_check", + data: { + status: checked + } + }); + }); + showAltTextDialogButton.addEventListener("click", e => { + const checked = this.#togglePref("enableNewAltTextWhenAddingImage", e); + this.#reportTelemetry({ + type: "stamp", + action: "pdfjs.image.alt_text.settings_edit_alt_text_check", + data: { + status: checked + } + }); + }); + deleteModelButton.addEventListener("click", this.#delete.bind(this, true)); + downloadModelButton.addEventListener("click", this.#download.bind(this, true)); + closeButton.addEventListener("click", this.#finish.bind(this)); + learnMore.addEventListener("click", () => { + this.#reportTelemetry({ + type: "stamp", + action: "pdfjs.image.alt_text.info", + data: { + topic: "ai_generation" + } + }); + }); + eventBus._on("enablealttextmodeldownload", ({ + value + }) => { + if (value) { + this.#download(false); + } else { + this.#delete(false); + } + }); + this.#overlayManager.register(dialog); + } + #reportTelemetry(data) { + this.#eventBus.dispatch("reporttelemetry", { + source: this, + details: { + type: "editing", + data + } + }); + } + async #download(isFromUI = false) { + if (isFromUI) { + this.#downloadModelButton.disabled = true; + const span = this.#downloadModelButton.firstChild; + span.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-settings-downloading-model-button"); + await this.#mlManager.downloadModel("altText"); + span.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-settings-download-model-button"); + this.#createModelButton.disabled = false; + this.#setPref("enableGuessAltText", true); + this.#mlManager.toggleService("altText", true); + this.#setPref("enableAltTextModelDownload", true); + this.#downloadModelButton.disabled = false; + } + this.#aiModelSettings.classList.toggle("download", false); + this.#createModelButton.setAttribute("aria-pressed", true); + } + async #delete(isFromUI = false) { + if (isFromUI) { + await this.#mlManager.deleteModel("altText"); + this.#setPref("enableGuessAltText", false); + this.#setPref("enableAltTextModelDownload", false); + } + this.#aiModelSettings.classList.toggle("download", true); + this.#createModelButton.disabled = true; + this.#createModelButton.setAttribute("aria-pressed", false); + } + async open({ + enableGuessAltText, + enableNewAltTextWhenAddingImage + }) { + const { + enableAltTextModelDownload + } = this.#mlManager; + this.#createModelButton.disabled = !enableAltTextModelDownload; + this.#createModelButton.setAttribute("aria-pressed", enableAltTextModelDownload && enableGuessAltText); + this.#showAltTextDialogButton.setAttribute("aria-pressed", enableNewAltTextWhenAddingImage); + this.#aiModelSettings.classList.toggle("download", !enableAltTextModelDownload); + await this.#overlayManager.open(this.#dialog); + this.#reportTelemetry({ + type: "stamp", + action: "pdfjs.image.alt_text.settings_displayed" + }); + } + #togglePref(name, { + target + }) { + const checked = target.getAttribute("aria-pressed") !== "true"; + this.#setPref(name, checked); + target.setAttribute("aria-pressed", checked); + return checked; + } + #setPref(name, value) { + this.#eventBus.dispatch("setpreference", { + source: this, + name, + value + }); + } + #finish() { + if (this.#overlayManager.active === this.#dialog) { + this.#overlayManager.close(this.#dialog); + } + } +} + +;// ./web/alt_text_manager.js + +class AltTextManager { + #clickAC = null; + #currentEditor = null; + #cancelButton; + #dialog; + #eventBus; + #hasUsedPointer = false; + #optionDescription; + #optionDecorative; + #overlayManager; + #saveButton; + #textarea; + #uiManager; + #previousAltText = null; + #resizeAC = null; + #svgElement = null; + #rectElement = null; + #container; + #telemetryData = null; + constructor({ + dialog, + optionDescription, + optionDecorative, + textarea, + cancelButton, + saveButton + }, container, overlayManager, eventBus) { + this.#dialog = dialog; + this.#optionDescription = optionDescription; + this.#optionDecorative = optionDecorative; + this.#textarea = textarea; + this.#cancelButton = cancelButton; + this.#saveButton = saveButton; + this.#overlayManager = overlayManager; + this.#eventBus = eventBus; + this.#container = container; + const onUpdateUIState = this.#updateUIState.bind(this); + dialog.addEventListener("close", this.#close.bind(this)); + dialog.addEventListener("contextmenu", event => { + if (event.target !== this.#textarea) { + event.preventDefault(); + } + }); + cancelButton.addEventListener("click", this.#finish.bind(this)); + saveButton.addEventListener("click", this.#save.bind(this)); + optionDescription.addEventListener("change", onUpdateUIState); + optionDecorative.addEventListener("change", onUpdateUIState); + this.#overlayManager.register(dialog); + } + #createSVGElement() { + if (this.#svgElement) { + return; + } + const svgFactory = new DOMSVGFactory(); + const svg = this.#svgElement = svgFactory.createElement("svg"); + svg.setAttribute("width", "0"); + svg.setAttribute("height", "0"); + const defs = svgFactory.createElement("defs"); + svg.append(defs); + const mask = svgFactory.createElement("mask"); + defs.append(mask); + mask.setAttribute("id", "alttext-manager-mask"); + mask.setAttribute("maskContentUnits", "objectBoundingBox"); + let rect = svgFactory.createElement("rect"); + mask.append(rect); + rect.setAttribute("fill", "white"); + rect.setAttribute("width", "1"); + rect.setAttribute("height", "1"); + rect.setAttribute("x", "0"); + rect.setAttribute("y", "0"); + rect = this.#rectElement = svgFactory.createElement("rect"); + mask.append(rect); + rect.setAttribute("fill", "black"); + this.#dialog.append(svg); + } + async editAltText(uiManager, editor) { + if (this.#currentEditor || !editor) { + return; + } + this.#createSVGElement(); + this.#hasUsedPointer = false; + this.#clickAC = new AbortController(); + const clickOpts = { + signal: this.#clickAC.signal + }, + onClick = this.#onClick.bind(this); + for (const element of [this.#optionDescription, this.#optionDecorative, this.#textarea, this.#saveButton, this.#cancelButton]) { + element.addEventListener("click", onClick, clickOpts); + } + const { + altText, + decorative + } = editor.altTextData; + if (decorative === true) { + this.#optionDecorative.checked = true; + this.#optionDescription.checked = false; + } else { + this.#optionDecorative.checked = false; + this.#optionDescription.checked = true; + } + this.#previousAltText = this.#textarea.value = altText?.trim() || ""; + this.#updateUIState(); + this.#currentEditor = editor; + this.#uiManager = uiManager; + this.#uiManager.removeEditListeners(); + this.#resizeAC = new AbortController(); + this.#eventBus._on("resize", this.#setPosition.bind(this), { + signal: this.#resizeAC.signal + }); + try { + await this.#overlayManager.open(this.#dialog); + this.#setPosition(); + } catch (ex) { + this.#close(); + throw ex; + } + } + #setPosition() { + if (!this.#currentEditor) { + return; + } + const dialog = this.#dialog; + const { + style + } = dialog; + const { + x: containerX, + y: containerY, + width: containerW, + height: containerH + } = this.#container.getBoundingClientRect(); + const { + innerWidth: windowW, + innerHeight: windowH + } = window; + const { + width: dialogW, + height: dialogH + } = dialog.getBoundingClientRect(); + const { + x, + y, + width, + height + } = this.#currentEditor.getClientDimensions(); + const MARGIN = 10; + const isLTR = this.#uiManager.direction === "ltr"; + const xs = Math.max(x, containerX); + const xe = Math.min(x + width, containerX + containerW); + const ys = Math.max(y, containerY); + const ye = Math.min(y + height, containerY + containerH); + this.#rectElement.setAttribute("width", `${(xe - xs) / windowW}`); + this.#rectElement.setAttribute("height", `${(ye - ys) / windowH}`); + this.#rectElement.setAttribute("x", `${xs / windowW}`); + this.#rectElement.setAttribute("y", `${ys / windowH}`); + let left = null; + let top = Math.max(y, 0); + top += Math.min(windowH - (top + dialogH), 0); + if (isLTR) { + if (x + width + MARGIN + dialogW < windowW) { + left = x + width + MARGIN; + } else if (x > dialogW + MARGIN) { + left = x - dialogW - MARGIN; + } + } else if (x > dialogW + MARGIN) { + left = x - dialogW - MARGIN; + } else if (x + width + MARGIN + dialogW < windowW) { + left = x + width + MARGIN; + } + if (left === null) { + top = null; + left = Math.max(x, 0); + left += Math.min(windowW - (left + dialogW), 0); + if (y > dialogH + MARGIN) { + top = y - dialogH - MARGIN; + } else if (y + height + MARGIN + dialogH < windowH) { + top = y + height + MARGIN; + } + } + if (top !== null) { + dialog.classList.add("positioned"); + if (isLTR) { + style.left = `${left}px`; + } else { + style.right = `${windowW - left - dialogW}px`; + } + style.top = `${top}px`; + } else { + dialog.classList.remove("positioned"); + style.left = ""; + style.top = ""; + } + } + #finish() { + if (this.#overlayManager.active === this.#dialog) { + this.#overlayManager.close(this.#dialog); + } + } + #close() { + this.#currentEditor._reportTelemetry(this.#telemetryData || { + action: "alt_text_cancel", + alt_text_keyboard: !this.#hasUsedPointer + }); + this.#telemetryData = null; + this.#removeOnClickListeners(); + this.#uiManager?.addEditListeners(); + this.#resizeAC?.abort(); + this.#resizeAC = null; + this.#currentEditor.altTextFinish(); + this.#currentEditor = null; + this.#uiManager = null; + } + #updateUIState() { + this.#textarea.disabled = this.#optionDecorative.checked; + } + #save() { + const altText = this.#textarea.value.trim(); + const decorative = this.#optionDecorative.checked; + this.#currentEditor.altTextData = { + altText, + decorative + }; + this.#telemetryData = { + action: "alt_text_save", + alt_text_description: !!altText, + alt_text_edit: !!this.#previousAltText && this.#previousAltText !== altText, + alt_text_decorative: decorative, + alt_text_keyboard: !this.#hasUsedPointer + }; + this.#finish(); + } + #onClick(evt) { + if (evt.detail === 0) { + return; + } + this.#hasUsedPointer = true; + this.#removeOnClickListeners(); + } + #removeOnClickListeners() { + this.#clickAC?.abort(); + this.#clickAC = null; + } + destroy() { + this.#uiManager = null; + this.#finish(); + this.#svgElement?.remove(); + this.#svgElement = this.#rectElement = null; + } +} + +;// ./web/annotation_editor_params.js + +class AnnotationEditorParams { + constructor(options, eventBus) { + this.eventBus = eventBus; + this.#bindListeners(options); + } + #bindListeners({ + editorFreeTextFontSize, + editorFreeTextColor, + editorInkColor, + editorInkThickness, + editorInkOpacity, + editorStampAddImage, + editorFreeHighlightThickness, + editorHighlightShowAll + }) { + const dispatchEvent = (typeStr, value) => { + this.eventBus.dispatch("switchannotationeditorparams", { + source: this, + type: AnnotationEditorParamsType[typeStr], + value + }); + }; + editorFreeTextFontSize.addEventListener("input", function () { + dispatchEvent("FREETEXT_SIZE", this.valueAsNumber); + }); + editorFreeTextColor.addEventListener("input", function () { + dispatchEvent("FREETEXT_COLOR", this.value); + }); + editorInkColor.addEventListener("input", function () { + dispatchEvent("INK_COLOR", this.value); + }); + editorInkThickness.addEventListener("input", function () { + dispatchEvent("INK_THICKNESS", this.valueAsNumber); + }); + editorInkOpacity.addEventListener("input", function () { + dispatchEvent("INK_OPACITY", this.valueAsNumber); + }); + editorStampAddImage.addEventListener("click", () => { + this.eventBus.dispatch("reporttelemetry", { + source: this, + details: { + type: "editing", + data: { + action: "pdfjs.image.add_image_click" + } + } + }); + dispatchEvent("CREATE"); + }); + editorFreeHighlightThickness.addEventListener("input", function () { + dispatchEvent("HIGHLIGHT_THICKNESS", this.valueAsNumber); + }); + editorHighlightShowAll.addEventListener("click", function () { + const checked = this.getAttribute("aria-pressed") === "true"; + this.setAttribute("aria-pressed", !checked); + dispatchEvent("HIGHLIGHT_SHOW_ALL", !checked); + }); + this.eventBus._on("annotationeditorparamschanged", evt => { + for (const [type, value] of evt.details) { + switch (type) { + case AnnotationEditorParamsType.FREETEXT_SIZE: + editorFreeTextFontSize.value = value; + break; + case AnnotationEditorParamsType.FREETEXT_COLOR: + editorFreeTextColor.value = value; + break; + case AnnotationEditorParamsType.INK_COLOR: + editorInkColor.value = value; + break; + case AnnotationEditorParamsType.INK_THICKNESS: + editorInkThickness.value = value; + break; + case AnnotationEditorParamsType.INK_OPACITY: + editorInkOpacity.value = value; + break; + case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS: + editorFreeHighlightThickness.value = value; + break; + case AnnotationEditorParamsType.HIGHLIGHT_FREE: + editorFreeHighlightThickness.disabled = !value; + break; + case AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL: + editorHighlightShowAll.setAttribute("aria-pressed", value); + break; + } + } + }); + } +} + +;// ./web/caret_browsing.js +const PRECISION = 1e-1; +class CaretBrowsingMode { + #mainContainer; + #toolBarHeight = 0; + #viewerContainer; + constructor(abortSignal, mainContainer, viewerContainer, toolbarContainer) { + this.#mainContainer = mainContainer; + this.#viewerContainer = viewerContainer; + if (!toolbarContainer) { + return; + } + this.#toolBarHeight = toolbarContainer.getBoundingClientRect().height; + const toolbarObserver = new ResizeObserver(entries => { + for (const entry of entries) { + if (entry.target === toolbarContainer) { + this.#toolBarHeight = Math.floor(entry.borderBoxSize[0].blockSize); + break; + } + } + }); + toolbarObserver.observe(toolbarContainer); + abortSignal.addEventListener("abort", () => toolbarObserver.disconnect(), { + once: true + }); + } + #isOnSameLine(rect1, rect2) { + const top1 = rect1.y; + const bot1 = rect1.bottom; + const mid1 = rect1.y + rect1.height / 2; + const top2 = rect2.y; + const bot2 = rect2.bottom; + const mid2 = rect2.y + rect2.height / 2; + return top1 <= mid2 && mid2 <= bot1 || top2 <= mid1 && mid1 <= bot2; + } + #isUnderOver(rect, x, y, isUp) { + const midY = rect.y + rect.height / 2; + return (isUp ? y >= midY : y <= midY) && rect.x - PRECISION <= x && x <= rect.right + PRECISION; + } + #isVisible(rect) { + return rect.top >= this.#toolBarHeight && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth); + } + #getCaretPosition(selection, isUp) { + const { + focusNode, + focusOffset + } = selection; + const range = document.createRange(); + range.setStart(focusNode, focusOffset); + range.setEnd(focusNode, focusOffset); + const rect = range.getBoundingClientRect(); + return [rect.x, isUp ? rect.top : rect.bottom]; + } + static #caretPositionFromPoint(x, y) { + if (!document.caretPositionFromPoint) { + const { + startContainer: offsetNode, + startOffset: offset + } = document.caretRangeFromPoint(x, y); + return { + offsetNode, + offset + }; + } + return document.caretPositionFromPoint(x, y); + } + #setCaretPositionHelper(selection, caretX, select, element, rect) { + rect ||= element.getBoundingClientRect(); + if (caretX <= rect.x + PRECISION) { + if (select) { + selection.extend(element.firstChild, 0); + } else { + selection.setPosition(element.firstChild, 0); + } + return; + } + if (rect.right - PRECISION <= caretX) { + const { + lastChild + } = element; + if (select) { + selection.extend(lastChild, lastChild.length); + } else { + selection.setPosition(lastChild, lastChild.length); + } + return; + } + const midY = rect.y + rect.height / 2; + let caretPosition = CaretBrowsingMode.#caretPositionFromPoint(caretX, midY); + let parentElement = caretPosition.offsetNode?.parentElement; + if (parentElement && parentElement !== element) { + const elementsAtPoint = document.elementsFromPoint(caretX, midY); + const savedVisibilities = []; + for (const el of elementsAtPoint) { + if (el === element) { + break; + } + const { + style + } = el; + savedVisibilities.push([el, style.visibility]); + style.visibility = "hidden"; + } + caretPosition = CaretBrowsingMode.#caretPositionFromPoint(caretX, midY); + parentElement = caretPosition.offsetNode?.parentElement; + for (const [el, visibility] of savedVisibilities) { + el.style.visibility = visibility; + } + } + if (parentElement !== element) { + if (select) { + selection.extend(element.firstChild, 0); + } else { + selection.setPosition(element.firstChild, 0); + } + return; + } + if (select) { + selection.extend(caretPosition.offsetNode, caretPosition.offset); + } else { + selection.setPosition(caretPosition.offsetNode, caretPosition.offset); + } + } + #setCaretPosition(select, selection, newLineElement, newLineElementRect, caretX) { + if (this.#isVisible(newLineElementRect)) { + this.#setCaretPositionHelper(selection, caretX, select, newLineElement, newLineElementRect); + return; + } + this.#mainContainer.addEventListener("scrollend", this.#setCaretPositionHelper.bind(this, selection, caretX, select, newLineElement, null), { + once: true + }); + newLineElement.scrollIntoView(); + } + #getNodeOnNextPage(textLayer, isUp) { + while (true) { + const page = textLayer.closest(".page"); + const pageNumber = parseInt(page.getAttribute("data-page-number")); + const nextPage = isUp ? pageNumber - 1 : pageNumber + 1; + textLayer = this.#viewerContainer.querySelector(`.page[data-page-number="${nextPage}"] .textLayer`); + if (!textLayer) { + return null; + } + const walker = document.createTreeWalker(textLayer, NodeFilter.SHOW_TEXT); + const node = isUp ? walker.lastChild() : walker.firstChild(); + if (node) { + return node; + } + } + } + moveCaret(isUp, select) { + const selection = document.getSelection(); + if (selection.rangeCount === 0) { + return; + } + const { + focusNode + } = selection; + const focusElement = focusNode.nodeType !== Node.ELEMENT_NODE ? focusNode.parentElement : focusNode; + const root = focusElement.closest(".textLayer"); + if (!root) { + return; + } + const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT); + walker.currentNode = focusNode; + const focusRect = focusElement.getBoundingClientRect(); + let newLineElement = null; + const nodeIterator = (isUp ? walker.previousSibling : walker.nextSibling).bind(walker); + while (nodeIterator()) { + const element = walker.currentNode.parentElement; + if (!this.#isOnSameLine(focusRect, element.getBoundingClientRect())) { + newLineElement = element; + break; + } + } + if (!newLineElement) { + const node = this.#getNodeOnNextPage(root, isUp); + if (!node) { + return; + } + if (select) { + const lastNode = (isUp ? walker.firstChild() : walker.lastChild()) || focusNode; + selection.extend(lastNode, isUp ? 0 : lastNode.length); + const range = document.createRange(); + range.setStart(node, isUp ? node.length : 0); + range.setEnd(node, isUp ? node.length : 0); + selection.addRange(range); + return; + } + const [caretX] = this.#getCaretPosition(selection, isUp); + const { + parentElement + } = node; + this.#setCaretPosition(select, selection, parentElement, parentElement.getBoundingClientRect(), caretX); + return; + } + const [caretX, caretY] = this.#getCaretPosition(selection, isUp); + const newLineElementRect = newLineElement.getBoundingClientRect(); + if (this.#isUnderOver(newLineElementRect, caretX, caretY, isUp)) { + this.#setCaretPosition(select, selection, newLineElement, newLineElementRect, caretX); + return; + } + while (nodeIterator()) { + const element = walker.currentNode.parentElement; + const elementRect = element.getBoundingClientRect(); + if (!this.#isOnSameLine(newLineElementRect, elementRect)) { + break; + } + if (this.#isUnderOver(elementRect, caretX, caretY, isUp)) { + this.#setCaretPosition(select, selection, element, elementRect, caretX); + return; + } + } + this.#setCaretPosition(select, selection, newLineElement, newLineElementRect, caretX); + } +} + +;// ./web/download_manager.js + +function download(blobUrl, filename) { + const a = document.createElement("a"); + if (!a.click) { + throw new Error('DownloadManager: "a.click()" is not supported.'); + } + a.href = blobUrl; + a.target = "_parent"; + if ("download" in a) { + a.download = filename; + } + (document.body || document.documentElement).append(a); + a.click(); + a.remove(); +} +class DownloadManager { + #openBlobUrls = new WeakMap(); + downloadData(data, filename, contentType) { + const blobUrl = URL.createObjectURL(new Blob([data], { + type: contentType + })); + download(blobUrl, filename); + } + openOrDownloadData(data, filename, dest = null) { + const isPdfData = isPdfFile(filename); + const contentType = isPdfData ? "application/pdf" : ""; + if (isPdfData) { + let blobUrl = this.#openBlobUrls.get(data); + if (!blobUrl) { + blobUrl = URL.createObjectURL(new Blob([data], { + type: contentType + })); + this.#openBlobUrls.set(data, blobUrl); + } + let viewerUrl; + viewerUrl = "?file=" + encodeURIComponent(blobUrl + "#" + filename); + if (dest) { + viewerUrl += `#${escape(dest)}`; + } + try { + window.open(viewerUrl); + return true; + } catch (ex) { + console.error("openOrDownloadData:", ex); + URL.revokeObjectURL(blobUrl); + this.#openBlobUrls.delete(data); + } + } + this.downloadData(data, filename, contentType); + return false; + } + download(data, url, filename) { + let blobUrl; + if (data) { + blobUrl = URL.createObjectURL(new Blob([data], { + type: "application/pdf" + })); + } else { + if (!createValidAbsoluteUrl(url, "http://example.com")) { + console.error(`download - not a valid URL: ${url}`); + return; + } + blobUrl = url + "#pdfjs.action=download"; + } + download(blobUrl, filename); + } +} + +;// ./web/editor_undo_bar.js + +class EditorUndoBar { + #closeButton = null; + #container; + #eventBus = null; + #focusTimeout = null; + #initController = null; + isOpen = false; + #message; + #showController = null; + #undoButton; + static #l10nMessages = Object.freeze({ + highlight: "pdfjs-editor-undo-bar-message-highlight", + freetext: "pdfjs-editor-undo-bar-message-freetext", + stamp: "pdfjs-editor-undo-bar-message-stamp", + ink: "pdfjs-editor-undo-bar-message-ink", + _multiple: "pdfjs-editor-undo-bar-message-multiple" + }); + constructor({ + container, + message, + undoButton, + closeButton + }, eventBus) { + this.#container = container; + this.#message = message; + this.#undoButton = undoButton; + this.#closeButton = closeButton; + this.#eventBus = eventBus; + } + destroy() { + this.#initController?.abort(); + this.#initController = null; + this.hide(); + } + show(undoAction, messageData) { + if (!this.#initController) { + this.#initController = new AbortController(); + const opts = { + signal: this.#initController.signal + }; + const boundHide = this.hide.bind(this); + this.#container.addEventListener("contextmenu", noContextMenu, opts); + this.#closeButton.addEventListener("click", boundHide, opts); + this.#eventBus._on("beforeprint", boundHide, opts); + this.#eventBus._on("download", boundHide, opts); + } + this.hide(); + if (typeof messageData === "string") { + this.#message.setAttribute("data-l10n-id", EditorUndoBar.#l10nMessages[messageData]); + } else { + this.#message.setAttribute("data-l10n-id", EditorUndoBar.#l10nMessages._multiple); + this.#message.setAttribute("data-l10n-args", JSON.stringify({ + count: messageData + })); + } + this.isOpen = true; + this.#container.hidden = false; + this.#showController = new AbortController(); + this.#undoButton.addEventListener("click", () => { + undoAction(); + this.hide(); + }, { + signal: this.#showController.signal + }); + this.#focusTimeout = setTimeout(() => { + this.#container.focus(); + this.#focusTimeout = null; + }, 100); + } + hide() { + if (!this.isOpen) { + return; + } + this.isOpen = false; + this.#container.hidden = true; + this.#showController?.abort(); + this.#showController = null; + if (this.#focusTimeout) { + clearTimeout(this.#focusTimeout); + this.#focusTimeout = null; + } + } +} + +;// ./web/overlay_manager.js +class OverlayManager { + #overlays = new WeakMap(); + #active = null; + get active() { + return this.#active; + } + async register(dialog, canForceClose = false) { + if (typeof dialog !== "object") { + throw new Error("Not enough parameters."); + } else if (this.#overlays.has(dialog)) { + throw new Error("The overlay is already registered."); + } + this.#overlays.set(dialog, { + canForceClose + }); + dialog.addEventListener("cancel", evt => { + this.#active = null; + }); + } + async open(dialog) { + if (!this.#overlays.has(dialog)) { + throw new Error("The overlay does not exist."); + } else if (this.#active) { + if (this.#active === dialog) { + throw new Error("The overlay is already active."); + } else if (this.#overlays.get(dialog).canForceClose) { + await this.close(); + } else { + throw new Error("Another overlay is currently active."); + } + } + this.#active = dialog; + dialog.showModal(); + } + async close(dialog = this.#active) { + if (!this.#overlays.has(dialog)) { + throw new Error("The overlay does not exist."); + } else if (!this.#active) { + throw new Error("The overlay is currently not active."); + } else if (this.#active !== dialog) { + throw new Error("Another overlay is currently active."); + } + dialog.close(); + this.#active = null; + } +} + +;// ./web/password_prompt.js + +class PasswordPrompt { + #activeCapability = null; + #updateCallback = null; + #reason = null; + constructor(options, overlayManager, isViewerEmbedded = false) { + this.dialog = options.dialog; + this.label = options.label; + this.input = options.input; + this.submitButton = options.submitButton; + this.cancelButton = options.cancelButton; + this.overlayManager = overlayManager; + this._isViewerEmbedded = isViewerEmbedded; + this.submitButton.addEventListener("click", this.#verify.bind(this)); + this.cancelButton.addEventListener("click", this.close.bind(this)); + this.input.addEventListener("keydown", e => { + if (e.keyCode === 13) { + this.#verify(); + } + }); + this.overlayManager.register(this.dialog, true); + this.dialog.addEventListener("close", this.#cancel.bind(this)); + } + async open() { + await this.#activeCapability?.promise; + this.#activeCapability = Promise.withResolvers(); + try { + await this.overlayManager.open(this.dialog); + } catch (ex) { + this.#activeCapability.resolve(); + throw ex; + } + const passwordIncorrect = this.#reason === PasswordResponses.INCORRECT_PASSWORD; + if (!this._isViewerEmbedded || passwordIncorrect) { + this.input.focus(); + } + this.label.setAttribute("data-l10n-id", passwordIncorrect ? "pdfjs-password-invalid" : "pdfjs-password-label"); + } + async close() { + if (this.overlayManager.active === this.dialog) { + this.overlayManager.close(this.dialog); + } + } + #verify() { + const password = this.input.value; + if (password?.length > 0) { + this.#invokeCallback(password); + } + } + #cancel() { + this.#invokeCallback(new Error("PasswordPrompt cancelled.")); + this.#activeCapability.resolve(); + } + #invokeCallback(password) { + if (!this.#updateCallback) { + return; + } + this.close(); + this.input.value = ""; + this.#updateCallback(password); + this.#updateCallback = null; + } + async setUpdateCallback(updateCallback, reason) { + if (this.#activeCapability) { + await this.#activeCapability.promise; + } + this.#updateCallback = updateCallback; + this.#reason = reason; + } +} + +;// ./web/base_tree_viewer.js + +const TREEITEM_OFFSET_TOP = -100; +const TREEITEM_SELECTED_CLASS = "selected"; +class BaseTreeViewer { + constructor(options) { + this.container = options.container; + this.eventBus = options.eventBus; + this._l10n = options.l10n; + this.reset(); + } + reset() { + this._pdfDocument = null; + this._lastToggleIsShow = true; + this._currentTreeItem = null; + this.container.textContent = ""; + this.container.classList.remove("treeWithDeepNesting"); + } + _dispatchEvent(count) { + throw new Error("Not implemented: _dispatchEvent"); + } + _bindLink(element, params) { + throw new Error("Not implemented: _bindLink"); + } + _normalizeTextContent(str) { + return removeNullCharacters(str, true) || "\u2013"; + } + _addToggleButton(div, hidden = false) { + const toggler = document.createElement("div"); + toggler.className = "treeItemToggler"; + if (hidden) { + toggler.classList.add("treeItemsHidden"); + } + toggler.onclick = evt => { + evt.stopPropagation(); + toggler.classList.toggle("treeItemsHidden"); + if (evt.shiftKey) { + const shouldShowAll = !toggler.classList.contains("treeItemsHidden"); + this._toggleTreeItem(div, shouldShowAll); + } + }; + div.prepend(toggler); + } + _toggleTreeItem(root, show = false) { + this._l10n.pause(); + this._lastToggleIsShow = show; + for (const toggler of root.querySelectorAll(".treeItemToggler")) { + toggler.classList.toggle("treeItemsHidden", !show); + } + this._l10n.resume(); + } + _toggleAllTreeItems() { + this._toggleTreeItem(this.container, !this._lastToggleIsShow); + } + _finishRendering(fragment, count, hasAnyNesting = false) { + if (hasAnyNesting) { + this.container.classList.add("treeWithDeepNesting"); + this._lastToggleIsShow = !fragment.querySelector(".treeItemsHidden"); + } + this._l10n.pause(); + this.container.append(fragment); + this._l10n.resume(); + this._dispatchEvent(count); + } + render(params) { + throw new Error("Not implemented: render"); + } + _updateCurrentTreeItem(treeItem = null) { + if (this._currentTreeItem) { + this._currentTreeItem.classList.remove(TREEITEM_SELECTED_CLASS); + this._currentTreeItem = null; + } + if (treeItem) { + treeItem.classList.add(TREEITEM_SELECTED_CLASS); + this._currentTreeItem = treeItem; + } + } + _scrollToCurrentTreeItem(treeItem) { + if (!treeItem) { + return; + } + this._l10n.pause(); + let currentNode = treeItem.parentNode; + while (currentNode && currentNode !== this.container) { + if (currentNode.classList.contains("treeItem")) { + const toggler = currentNode.firstElementChild; + toggler?.classList.remove("treeItemsHidden"); + } + currentNode = currentNode.parentNode; + } + this._l10n.resume(); + this._updateCurrentTreeItem(treeItem); + this.container.scrollTo(treeItem.offsetLeft, treeItem.offsetTop + TREEITEM_OFFSET_TOP); + } +} + +;// ./web/pdf_attachment_viewer.js + + +class PDFAttachmentViewer extends BaseTreeViewer { + constructor(options) { + super(options); + this.downloadManager = options.downloadManager; + this.eventBus._on("fileattachmentannotation", this.#appendAttachment.bind(this)); + } + reset(keepRenderedCapability = false) { + super.reset(); + this._attachments = null; + if (!keepRenderedCapability) { + this._renderedCapability = Promise.withResolvers(); + } + this._pendingDispatchEvent = false; + } + async _dispatchEvent(attachmentsCount) { + this._renderedCapability.resolve(); + if (attachmentsCount === 0 && !this._pendingDispatchEvent) { + this._pendingDispatchEvent = true; + await waitOnEventOrTimeout({ + target: this.eventBus, + name: "annotationlayerrendered", + delay: 1000 + }); + if (!this._pendingDispatchEvent) { + return; + } + } + this._pendingDispatchEvent = false; + this.eventBus.dispatch("attachmentsloaded", { + source: this, + attachmentsCount + }); + } + _bindLink(element, { + content, + description, + filename + }) { + if (description) { + element.title = description; + } + element.onclick = () => { + this.downloadManager.openOrDownloadData(content, filename); + return false; + }; + } + render({ + attachments, + keepRenderedCapability = false + }) { + if (this._attachments) { + this.reset(keepRenderedCapability); + } + this._attachments = attachments || null; + if (!attachments) { + this._dispatchEvent(0); + return; + } + const fragment = document.createDocumentFragment(); + let attachmentsCount = 0; + for (const name in attachments) { + const item = attachments[name]; + const div = document.createElement("div"); + div.className = "treeItem"; + const element = document.createElement("a"); + this._bindLink(element, item); + element.textContent = this._normalizeTextContent(item.filename); + div.append(element); + fragment.append(div); + attachmentsCount++; + } + this._finishRendering(fragment, attachmentsCount); + } + #appendAttachment(item) { + const renderedPromise = this._renderedCapability.promise; + renderedPromise.then(() => { + if (renderedPromise !== this._renderedCapability.promise) { + return; + } + const attachments = this._attachments || Object.create(null); + for (const name in attachments) { + if (item.filename === name) { + return; + } + } + attachments[item.filename] = item; + this.render({ + attachments, + keepRenderedCapability: true + }); + }); + } +} + +;// ./web/grab_to_pan.js + +const CSS_CLASS_GRAB = "grab-to-pan-grab"; +class GrabToPan { + #activateAC = null; + #mouseDownAC = null; + #scrollAC = null; + constructor({ + element + }) { + this.element = element; + this.document = element.ownerDocument; + const overlay = this.overlay = document.createElement("div"); + overlay.className = "grab-to-pan-grabbing"; + } + activate() { + if (!this.#activateAC) { + this.#activateAC = new AbortController(); + this.element.addEventListener("mousedown", this.#onMouseDown.bind(this), { + capture: true, + signal: this.#activateAC.signal + }); + this.element.classList.add(CSS_CLASS_GRAB); + } + } + deactivate() { + if (this.#activateAC) { + this.#activateAC.abort(); + this.#activateAC = null; + this.#endPan(); + this.element.classList.remove(CSS_CLASS_GRAB); + } + } + toggle() { + if (this.#activateAC) { + this.deactivate(); + } else { + this.activate(); + } + } + ignoreTarget(node) { + return node.matches("a[href], a[href] *, input, textarea, button, button *, select, option"); + } + #onMouseDown(event) { + if (event.button !== 0 || this.ignoreTarget(event.target)) { + return; + } + if (event.originalTarget) { + try { + event.originalTarget.tagName; + } catch { + return; + } + } + this.scrollLeftStart = this.element.scrollLeft; + this.scrollTopStart = this.element.scrollTop; + this.clientXStart = event.clientX; + this.clientYStart = event.clientY; + this.#mouseDownAC = new AbortController(); + const boundEndPan = this.#endPan.bind(this), + mouseOpts = { + capture: true, + signal: this.#mouseDownAC.signal + }; + this.document.addEventListener("mousemove", this.#onMouseMove.bind(this), mouseOpts); + this.document.addEventListener("mouseup", boundEndPan, mouseOpts); + this.#scrollAC = new AbortController(); + this.element.addEventListener("scroll", boundEndPan, { + capture: true, + signal: this.#scrollAC.signal + }); + stopEvent(event); + const focusedElement = document.activeElement; + if (focusedElement && !focusedElement.contains(event.target)) { + focusedElement.blur(); + } + } + #onMouseMove(event) { + this.#scrollAC?.abort(); + this.#scrollAC = null; + if (!(event.buttons & 1)) { + this.#endPan(); + return; + } + const xDiff = event.clientX - this.clientXStart; + const yDiff = event.clientY - this.clientYStart; + this.element.scrollTo({ + top: this.scrollTopStart - yDiff, + left: this.scrollLeftStart - xDiff, + behavior: "instant" + }); + if (!this.overlay.parentNode) { + document.body.append(this.overlay); + } + } + #endPan() { + this.#mouseDownAC?.abort(); + this.#mouseDownAC = null; + this.#scrollAC?.abort(); + this.#scrollAC = null; + this.overlay.remove(); + } +} + +;// ./web/pdf_cursor_tools.js + + + +class PDFCursorTools { + #active = CursorTool.SELECT; + #prevActive = null; + constructor({ + container, + eventBus, + cursorToolOnLoad = CursorTool.SELECT + }) { + this.container = container; + this.eventBus = eventBus; + this.#addEventListeners(); + Promise.resolve().then(() => { + this.switchTool(cursorToolOnLoad); + }); + } + get activeTool() { + return this.#active; + } + switchTool(tool) { + if (this.#prevActive !== null) { + return; + } + this.#switchTool(tool); + } + #switchTool(tool, disabled = false) { + if (tool === this.#active) { + if (this.#prevActive !== null) { + this.eventBus.dispatch("cursortoolchanged", { + source: this, + tool, + disabled + }); + } + return; + } + const disableActiveTool = () => { + switch (this.#active) { + case CursorTool.SELECT: + break; + case CursorTool.HAND: + this._handTool.deactivate(); + break; + case CursorTool.ZOOM: + } + }; + switch (tool) { + case CursorTool.SELECT: + disableActiveTool(); + break; + case CursorTool.HAND: + disableActiveTool(); + this._handTool.activate(); + break; + case CursorTool.ZOOM: + default: + console.error(`switchTool: "${tool}" is an unsupported value.`); + return; + } + this.#active = tool; + this.eventBus.dispatch("cursortoolchanged", { + source: this, + tool, + disabled + }); + } + #addEventListeners() { + this.eventBus._on("switchcursortool", evt => { + if (!evt.reset) { + this.switchTool(evt.tool); + } else if (this.#prevActive !== null) { + annotationEditorMode = AnnotationEditorType.NONE; + presentationModeState = PresentationModeState.NORMAL; + enableActive(); + } + }); + let annotationEditorMode = AnnotationEditorType.NONE, + presentationModeState = PresentationModeState.NORMAL; + const disableActive = () => { + this.#prevActive ??= this.#active; + this.#switchTool(CursorTool.SELECT, true); + }; + const enableActive = () => { + if (this.#prevActive !== null && annotationEditorMode === AnnotationEditorType.NONE && presentationModeState === PresentationModeState.NORMAL) { + this.#switchTool(this.#prevActive); + this.#prevActive = null; + } + }; + this.eventBus._on("annotationeditormodechanged", ({ + mode + }) => { + annotationEditorMode = mode; + if (mode === AnnotationEditorType.NONE) { + enableActive(); + } else { + disableActive(); + } + }); + this.eventBus._on("presentationmodechanged", ({ + state + }) => { + presentationModeState = state; + if (state === PresentationModeState.NORMAL) { + enableActive(); + } else if (state === PresentationModeState.FULLSCREEN) { + disableActive(); + } + }); + } + get _handTool() { + return shadow(this, "_handTool", new GrabToPan({ + element: this.container + })); + } +} + +;// ./web/pdf_document_properties.js + + +const NON_METRIC_LOCALES = ["en-us", "en-lr", "my"]; +const US_PAGE_NAMES = { + "8.5x11": "pdfjs-document-properties-page-size-name-letter", + "8.5x14": "pdfjs-document-properties-page-size-name-legal" +}; +const METRIC_PAGE_NAMES = { + "297x420": "pdfjs-document-properties-page-size-name-a-three", + "210x297": "pdfjs-document-properties-page-size-name-a-four" +}; +function getPageName(size, isPortrait, pageNames) { + const width = isPortrait ? size.width : size.height; + const height = isPortrait ? size.height : size.width; + return pageNames[`${width}x${height}`]; +} +class PDFDocumentProperties { + #fieldData = null; + constructor({ + dialog, + fields, + closeButton + }, overlayManager, eventBus, l10n, fileNameLookup) { + this.dialog = dialog; + this.fields = fields; + this.overlayManager = overlayManager; + this.l10n = l10n; + this._fileNameLookup = fileNameLookup; + this.#reset(); + closeButton.addEventListener("click", this.close.bind(this)); + this.overlayManager.register(this.dialog); + eventBus._on("pagechanging", evt => { + this._currentPageNumber = evt.pageNumber; + }); + eventBus._on("rotationchanging", evt => { + this._pagesRotation = evt.pagesRotation; + }); + } + async open() { + await Promise.all([this.overlayManager.open(this.dialog), this._dataAvailableCapability.promise]); + const currentPageNumber = this._currentPageNumber; + const pagesRotation = this._pagesRotation; + if (this.#fieldData && currentPageNumber === this.#fieldData._currentPageNumber && pagesRotation === this.#fieldData._pagesRotation) { + this.#updateUI(); + return; + } + const [{ + info, + contentLength + }, pdfPage] = await Promise.all([this.pdfDocument.getMetadata(), this.pdfDocument.getPage(currentPageNumber)]); + const [fileName, fileSize, creationDate, modificationDate, pageSize, isLinearized] = await Promise.all([this._fileNameLookup(), this.#parseFileSize(contentLength), this.#parseDate(info.CreationDate), this.#parseDate(info.ModDate), this.#parsePageSize(getPageSizeInches(pdfPage), pagesRotation), this.#parseLinearization(info.IsLinearized)]); + this.#fieldData = Object.freeze({ + fileName, + fileSize, + title: info.Title, + author: info.Author, + subject: info.Subject, + keywords: info.Keywords, + creationDate, + modificationDate, + creator: info.Creator, + producer: info.Producer, + version: info.PDFFormatVersion, + pageCount: this.pdfDocument.numPages, + pageSize, + linearized: isLinearized, + _currentPageNumber: currentPageNumber, + _pagesRotation: pagesRotation + }); + this.#updateUI(); + const { + length + } = await this.pdfDocument.getDownloadInfo(); + if (contentLength === length) { + return; + } + const data = Object.assign(Object.create(null), this.#fieldData); + data.fileSize = await this.#parseFileSize(length); + this.#fieldData = Object.freeze(data); + this.#updateUI(); + } + async close() { + this.overlayManager.close(this.dialog); + } + setDocument(pdfDocument) { + if (this.pdfDocument) { + this.#reset(); + this.#updateUI(); + } + if (!pdfDocument) { + return; + } + this.pdfDocument = pdfDocument; + this._dataAvailableCapability.resolve(); + } + #reset() { + this.pdfDocument = null; + this.#fieldData = null; + this._dataAvailableCapability = Promise.withResolvers(); + this._currentPageNumber = 1; + this._pagesRotation = 0; + } + #updateUI() { + if (this.#fieldData && this.overlayManager.active !== this.dialog) { + return; + } + for (const id in this.fields) { + const content = this.#fieldData?.[id]; + this.fields[id].textContent = content || content === 0 ? content : "-"; + } + } + async #parseFileSize(b = 0) { + const kb = b / 1024, + mb = kb / 1024; + return kb ? this.l10n.get(mb >= 1 ? "pdfjs-document-properties-size-mb" : "pdfjs-document-properties-size-kb", { + mb, + kb, + b + }) : undefined; + } + async #parsePageSize(pageSizeInches, pagesRotation) { + if (!pageSizeInches) { + return undefined; + } + if (pagesRotation % 180 !== 0) { + pageSizeInches = { + width: pageSizeInches.height, + height: pageSizeInches.width + }; + } + const isPortrait = isPortraitOrientation(pageSizeInches), + nonMetric = NON_METRIC_LOCALES.includes(this.l10n.getLanguage()); + let sizeInches = { + width: Math.round(pageSizeInches.width * 100) / 100, + height: Math.round(pageSizeInches.height * 100) / 100 + }; + let sizeMillimeters = { + width: Math.round(pageSizeInches.width * 25.4 * 10) / 10, + height: Math.round(pageSizeInches.height * 25.4 * 10) / 10 + }; + let nameId = getPageName(sizeInches, isPortrait, US_PAGE_NAMES) || getPageName(sizeMillimeters, isPortrait, METRIC_PAGE_NAMES); + if (!nameId && !(Number.isInteger(sizeMillimeters.width) && Number.isInteger(sizeMillimeters.height))) { + const exactMillimeters = { + width: pageSizeInches.width * 25.4, + height: pageSizeInches.height * 25.4 + }; + const intMillimeters = { + width: Math.round(sizeMillimeters.width), + height: Math.round(sizeMillimeters.height) + }; + if (Math.abs(exactMillimeters.width - intMillimeters.width) < 0.1 && Math.abs(exactMillimeters.height - intMillimeters.height) < 0.1) { + nameId = getPageName(intMillimeters, isPortrait, METRIC_PAGE_NAMES); + if (nameId) { + sizeInches = { + width: Math.round(intMillimeters.width / 25.4 * 100) / 100, + height: Math.round(intMillimeters.height / 25.4 * 100) / 100 + }; + sizeMillimeters = intMillimeters; + } + } + } + const [{ + width, + height + }, unit, name, orientation] = await Promise.all([nonMetric ? sizeInches : sizeMillimeters, this.l10n.get(nonMetric ? "pdfjs-document-properties-page-size-unit-inches" : "pdfjs-document-properties-page-size-unit-millimeters"), nameId && this.l10n.get(nameId), this.l10n.get(isPortrait ? "pdfjs-document-properties-page-size-orientation-portrait" : "pdfjs-document-properties-page-size-orientation-landscape")]); + return this.l10n.get(name ? "pdfjs-document-properties-page-size-dimension-name-string" : "pdfjs-document-properties-page-size-dimension-string", { + width, + height, + unit, + name, + orientation + }); + } + async #parseDate(inputDate) { + const dateObj = PDFDateString.toDateObject(inputDate); + return dateObj ? this.l10n.get("pdfjs-document-properties-date-time-string", { + dateObj: dateObj.valueOf() + }) : undefined; + } + #parseLinearization(isLinearized) { + return this.l10n.get(isLinearized ? "pdfjs-document-properties-linearized-yes" : "pdfjs-document-properties-linearized-no"); + } +} + +;// ./web/pdf_find_utils.js +const CharacterType = { + SPACE: 0, + ALPHA_LETTER: 1, + PUNCT: 2, + HAN_LETTER: 3, + KATAKANA_LETTER: 4, + HIRAGANA_LETTER: 5, + HALFWIDTH_KATAKANA_LETTER: 6, + THAI_LETTER: 7 +}; +function isAlphabeticalScript(charCode) { + return charCode < 0x2e80; +} +function isAscii(charCode) { + return (charCode & 0xff80) === 0; +} +function isAsciiAlpha(charCode) { + return charCode >= 0x61 && charCode <= 0x7a || charCode >= 0x41 && charCode <= 0x5a; +} +function isAsciiDigit(charCode) { + return charCode >= 0x30 && charCode <= 0x39; +} +function isAsciiSpace(charCode) { + return charCode === 0x20 || charCode === 0x09 || charCode === 0x0d || charCode === 0x0a; +} +function isHan(charCode) { + return charCode >= 0x3400 && charCode <= 0x9fff || charCode >= 0xf900 && charCode <= 0xfaff; +} +function isKatakana(charCode) { + return charCode >= 0x30a0 && charCode <= 0x30ff; +} +function isHiragana(charCode) { + return charCode >= 0x3040 && charCode <= 0x309f; +} +function isHalfwidthKatakana(charCode) { + return charCode >= 0xff60 && charCode <= 0xff9f; +} +function isThai(charCode) { + return (charCode & 0xff80) === 0x0e00; +} +function getCharacterType(charCode) { + if (isAlphabeticalScript(charCode)) { + if (isAscii(charCode)) { + if (isAsciiSpace(charCode)) { + return CharacterType.SPACE; + } else if (isAsciiAlpha(charCode) || isAsciiDigit(charCode) || charCode === 0x5f) { + return CharacterType.ALPHA_LETTER; + } + return CharacterType.PUNCT; + } else if (isThai(charCode)) { + return CharacterType.THAI_LETTER; + } else if (charCode === 0xa0) { + return CharacterType.SPACE; + } + return CharacterType.ALPHA_LETTER; + } + if (isHan(charCode)) { + return CharacterType.HAN_LETTER; + } else if (isKatakana(charCode)) { + return CharacterType.KATAKANA_LETTER; + } else if (isHiragana(charCode)) { + return CharacterType.HIRAGANA_LETTER; + } else if (isHalfwidthKatakana(charCode)) { + return CharacterType.HALFWIDTH_KATAKANA_LETTER; + } + return CharacterType.ALPHA_LETTER; +} +let NormalizeWithNFKC; +function getNormalizeWithNFKC() { + NormalizeWithNFKC ||= ` ¨ª¯²-µ¸-º¼-¾IJ-ijĿ-ŀʼnſDŽ-njDZ-dzʰ-ʸ˘-˝ˠ-ˤʹͺ;΄-΅·ϐ-ϖϰ-ϲϴ-ϵϹևٵ-ٸक़-य़ড়-ঢ়য়ਲ਼ਸ਼ਖ਼-ਜ਼ਫ਼ଡ଼-ଢ଼ำຳໜ-ໝ༌གྷཌྷདྷབྷཛྷཀྵჼᴬ-ᴮᴰ-ᴺᴼ-ᵍᵏ-ᵪᵸᶛ-ᶿẚ-ẛάέήίόύώΆ᾽-῁ΈΉ῍-῏ΐΊ῝-῟ΰΎ῭-`ΌΏ´-῾ - ‑‗․-… ″-‴‶-‷‼‾⁇-⁉⁗ ⁰-ⁱ⁴-₎ₐ-ₜ₨℀-℃℅-ℇ℉-ℓℕ-№ℙ-ℝ℠-™ℤΩℨK-ℭℯ-ℱℳ-ℹ℻-⅀ⅅ-ⅉ⅐-ⅿ↉∬-∭∯-∰〈-〉①-⓪⨌⩴-⩶⫝̸ⱼ-ⱽⵯ⺟⻳⼀-⿕ 〶〸-〺゛-゜ゟヿㄱ-ㆎ㆒-㆟㈀-㈞㈠-㉇㉐-㉾㊀-㏿ꚜ-ꚝꝰꟲ-ꟴꟸ-ꟹꭜ-ꭟꭩ豈-嗀塚晴凞-羽蘒諸逸-都飯-舘並-龎ff-stﬓ-ﬗיִײַ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-﷼︐-︙︰-﹄﹇-﹒﹔-﹦﹨-﹫ﹰ-ﹲﹴﹶ-ﻼ!-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ¢-₩`; + return NormalizeWithNFKC; +} + +;// ./web/pdf_find_controller.js + + +const FindState = { + FOUND: 0, + NOT_FOUND: 1, + WRAPPED: 2, + PENDING: 3 +}; +const FIND_TIMEOUT = 250; +const MATCH_SCROLL_OFFSET_TOP = -50; +const MATCH_SCROLL_OFFSET_LEFT = -400; +const CHARACTERS_TO_NORMALIZE = { + "\u2010": "-", + "\u2018": "'", + "\u2019": "'", + "\u201A": "'", + "\u201B": "'", + "\u201C": '"', + "\u201D": '"', + "\u201E": '"', + "\u201F": '"', + "\u00BC": "1/4", + "\u00BD": "1/2", + "\u00BE": "3/4" +}; +const DIACRITICS_EXCEPTION = new Set([0x3099, 0x309a, 0x094d, 0x09cd, 0x0a4d, 0x0acd, 0x0b4d, 0x0bcd, 0x0c4d, 0x0ccd, 0x0d3b, 0x0d3c, 0x0d4d, 0x0dca, 0x0e3a, 0x0eba, 0x0f84, 0x1039, 0x103a, 0x1714, 0x1734, 0x17d2, 0x1a60, 0x1b44, 0x1baa, 0x1bab, 0x1bf2, 0x1bf3, 0x2d7f, 0xa806, 0xa82c, 0xa8c4, 0xa953, 0xa9c0, 0xaaf6, 0xabed, 0x0c56, 0x0f71, 0x0f72, 0x0f7a, 0x0f7b, 0x0f7c, 0x0f7d, 0x0f80, 0x0f74]); +let DIACRITICS_EXCEPTION_STR; +const DIACRITICS_REG_EXP = /\p{M}+/gu; +const SPECIAL_CHARS_REG_EXP = /([.*+?^${}()|[\]\\])|(\p{P})|(\s+)|(\p{M})|(\p{L})/gu; +const NOT_DIACRITIC_FROM_END_REG_EXP = /([^\p{M}])\p{M}*$/u; +const NOT_DIACRITIC_FROM_START_REG_EXP = /^\p{M}*([^\p{M}])/u; +const SYLLABLES_REG_EXP = /[\uAC00-\uD7AF\uFA6C\uFACF-\uFAD1\uFAD5-\uFAD7]+/g; +const SYLLABLES_LENGTHS = new Map(); +const FIRST_CHAR_SYLLABLES_REG_EXP = "[\\u1100-\\u1112\\ud7a4-\\ud7af\\ud84a\\ud84c\\ud850\\ud854\\ud857\\ud85f]"; +const NFKC_CHARS_TO_NORMALIZE = new Map(); +let noSyllablesRegExp = null; +let withSyllablesRegExp = null; +function normalize(text) { + const syllablePositions = []; + let m; + while ((m = SYLLABLES_REG_EXP.exec(text)) !== null) { + let { + index + } = m; + for (const char of m[0]) { + let len = SYLLABLES_LENGTHS.get(char); + if (!len) { + len = char.normalize("NFD").length; + SYLLABLES_LENGTHS.set(char, len); + } + syllablePositions.push([len, index++]); + } + } + let normalizationRegex; + if (syllablePositions.length === 0 && noSyllablesRegExp) { + normalizationRegex = noSyllablesRegExp; + } else if (syllablePositions.length > 0 && withSyllablesRegExp) { + normalizationRegex = withSyllablesRegExp; + } else { + const replace = Object.keys(CHARACTERS_TO_NORMALIZE).join(""); + const toNormalizeWithNFKC = getNormalizeWithNFKC(); + const CJK = "(?:\\p{Ideographic}|[\u3040-\u30FF])"; + const HKDiacritics = "(?:\u3099|\u309A)"; + const CompoundWord = "\\p{Ll}-\\n\\p{Lu}"; + const regexp = `([${replace}])|([${toNormalizeWithNFKC}])|(${HKDiacritics}\\n)|(\\p{M}+(?:-\\n)?)|(${CompoundWord})|(\\S-\\n)|(${CJK}\\n)|(\\n)`; + if (syllablePositions.length === 0) { + normalizationRegex = noSyllablesRegExp = new RegExp(regexp + "|(\\u0000)", "gum"); + } else { + normalizationRegex = withSyllablesRegExp = new RegExp(regexp + `|(${FIRST_CHAR_SYLLABLES_REG_EXP})`, "gum"); + } + } + const rawDiacriticsPositions = []; + while ((m = DIACRITICS_REG_EXP.exec(text)) !== null) { + rawDiacriticsPositions.push([m[0].length, m.index]); + } + let normalized = text.normalize("NFD"); + const positions = [0, 0]; + let rawDiacriticsIndex = 0; + let syllableIndex = 0; + let shift = 0; + let shiftOrigin = 0; + let eol = 0; + let hasDiacritics = false; + normalized = normalized.replace(normalizationRegex, (match, p1, p2, p3, p4, p5, p6, p7, p8, p9, i) => { + i -= shiftOrigin; + if (p1) { + const replacement = CHARACTERS_TO_NORMALIZE[p1]; + const jj = replacement.length; + for (let j = 1; j < jj; j++) { + positions.push(i - shift + j, shift - j); + } + shift -= jj - 1; + return replacement; + } + if (p2) { + let replacement = NFKC_CHARS_TO_NORMALIZE.get(p2); + if (!replacement) { + replacement = p2.normalize("NFKC"); + NFKC_CHARS_TO_NORMALIZE.set(p2, replacement); + } + const jj = replacement.length; + for (let j = 1; j < jj; j++) { + positions.push(i - shift + j, shift - j); + } + shift -= jj - 1; + return replacement; + } + if (p3) { + hasDiacritics = true; + if (i + eol === rawDiacriticsPositions[rawDiacriticsIndex]?.[1]) { + ++rawDiacriticsIndex; + } else { + positions.push(i - 1 - shift + 1, shift - 1); + shift -= 1; + shiftOrigin += 1; + } + positions.push(i - shift + 1, shift); + shiftOrigin += 1; + eol += 1; + return p3.charAt(0); + } + if (p4) { + const hasTrailingDashEOL = p4.endsWith("\n"); + const len = hasTrailingDashEOL ? p4.length - 2 : p4.length; + hasDiacritics = true; + let jj = len; + if (i + eol === rawDiacriticsPositions[rawDiacriticsIndex]?.[1]) { + jj -= rawDiacriticsPositions[rawDiacriticsIndex][0]; + ++rawDiacriticsIndex; + } + for (let j = 1; j <= jj; j++) { + positions.push(i - 1 - shift + j, shift - j); + } + shift -= jj; + shiftOrigin += jj; + if (hasTrailingDashEOL) { + i += len - 1; + positions.push(i - shift + 1, 1 + shift); + shift += 1; + shiftOrigin += 1; + eol += 1; + return p4.slice(0, len); + } + return p4; + } + if (p5) { + shiftOrigin += 1; + eol += 1; + return p5.replace("\n", ""); + } + if (p6) { + const len = p6.length - 2; + positions.push(i - shift + len, 1 + shift); + shift += 1; + shiftOrigin += 1; + eol += 1; + return p6.slice(0, -2); + } + if (p7) { + const len = p7.length - 1; + positions.push(i - shift + len, shift); + shiftOrigin += 1; + eol += 1; + return p7.slice(0, -1); + } + if (p8) { + positions.push(i - shift + 1, shift - 1); + shift -= 1; + shiftOrigin += 1; + eol += 1; + return " "; + } + if (i + eol === syllablePositions[syllableIndex]?.[1]) { + const newCharLen = syllablePositions[syllableIndex][0] - 1; + ++syllableIndex; + for (let j = 1; j <= newCharLen; j++) { + positions.push(i - (shift - j), shift - j); + } + shift -= newCharLen; + shiftOrigin += newCharLen; + } + return p9; + }); + positions.push(normalized.length, shift); + const starts = new Uint32Array(positions.length >> 1); + const shifts = new Int32Array(positions.length >> 1); + for (let i = 0, ii = positions.length; i < ii; i += 2) { + starts[i >> 1] = positions[i]; + shifts[i >> 1] = positions[i + 1]; + } + return [normalized, [starts, shifts], hasDiacritics]; +} +function getOriginalIndex(diffs, pos, len) { + if (!diffs) { + return [pos, len]; + } + const [starts, shifts] = diffs; + const start = pos; + const end = pos + len - 1; + let i = binarySearchFirstItem(starts, x => x >= start); + if (starts[i] > start) { + --i; + } + let j = binarySearchFirstItem(starts, x => x >= end, i); + if (starts[j] > end) { + --j; + } + const oldStart = start + shifts[i]; + const oldEnd = end + shifts[j]; + const oldLen = oldEnd + 1 - oldStart; + return [oldStart, oldLen]; +} +class PDFFindController { + #state = null; + #updateMatchesCountOnProgress = true; + #visitedPagesCount = 0; + constructor({ + linkService, + eventBus, + updateMatchesCountOnProgress = true + }) { + this._linkService = linkService; + this._eventBus = eventBus; + this.#updateMatchesCountOnProgress = updateMatchesCountOnProgress; + this.onIsPageVisible = null; + this.#reset(); + eventBus._on("find", this.#onFind.bind(this)); + eventBus._on("findbarclose", this.#onFindBarClose.bind(this)); + } + get highlightMatches() { + return this._highlightMatches; + } + get pageMatches() { + return this._pageMatches; + } + get pageMatchesLength() { + return this._pageMatchesLength; + } + get selected() { + return this._selected; + } + get state() { + return this.#state; + } + setDocument(pdfDocument) { + if (this._pdfDocument) { + this.#reset(); + } + if (!pdfDocument) { + return; + } + this._pdfDocument = pdfDocument; + this._firstPageCapability.resolve(); + } + #onFind(state) { + if (!state) { + return; + } + const pdfDocument = this._pdfDocument; + const { + type + } = state; + if (this.#state === null || this.#shouldDirtyMatch(state)) { + this._dirtyMatch = true; + } + this.#state = state; + if (type !== "highlightallchange") { + this.#updateUIState(FindState.PENDING); + } + this._firstPageCapability.promise.then(() => { + if (!this._pdfDocument || pdfDocument && this._pdfDocument !== pdfDocument) { + return; + } + this.#extractText(); + const findbarClosed = !this._highlightMatches; + const pendingTimeout = !!this._findTimeout; + if (this._findTimeout) { + clearTimeout(this._findTimeout); + this._findTimeout = null; + } + if (!type) { + this._findTimeout = setTimeout(() => { + this.#nextMatch(); + this._findTimeout = null; + }, FIND_TIMEOUT); + } else if (this._dirtyMatch) { + this.#nextMatch(); + } else if (type === "again") { + this.#nextMatch(); + if (findbarClosed && this.#state.highlightAll) { + this.#updateAllPages(); + } + } else if (type === "highlightallchange") { + if (pendingTimeout) { + this.#nextMatch(); + } else { + this._highlightMatches = true; + } + this.#updateAllPages(); + } else { + this.#nextMatch(); + } + }); + } + scrollMatchIntoView({ + element = null, + selectedLeft = 0, + pageIndex = -1, + matchIndex = -1 + }) { + if (!this._scrollMatches || !element) { + return; + } else if (matchIndex === -1 || matchIndex !== this._selected.matchIdx) { + return; + } else if (pageIndex === -1 || pageIndex !== this._selected.pageIdx) { + return; + } + this._scrollMatches = false; + const spot = { + top: MATCH_SCROLL_OFFSET_TOP, + left: selectedLeft + MATCH_SCROLL_OFFSET_LEFT + }; + scrollIntoView(element, spot, true); + } + #reset() { + this._highlightMatches = false; + this._scrollMatches = false; + this._pdfDocument = null; + this._pageMatches = []; + this._pageMatchesLength = []; + this.#visitedPagesCount = 0; + this.#state = null; + this._selected = { + pageIdx: -1, + matchIdx: -1 + }; + this._offset = { + pageIdx: null, + matchIdx: null, + wrapped: false + }; + this._extractTextPromises = []; + this._pageContents = []; + this._pageDiffs = []; + this._hasDiacritics = []; + this._matchesCountTotal = 0; + this._pagesToSearch = null; + this._pendingFindMatches = new Set(); + this._resumePageIdx = null; + this._dirtyMatch = false; + clearTimeout(this._findTimeout); + this._findTimeout = null; + this._firstPageCapability = Promise.withResolvers(); + } + get #query() { + const { + query + } = this.#state; + if (typeof query === "string") { + if (query !== this._rawQuery) { + this._rawQuery = query; + [this._normalizedQuery] = normalize(query); + } + return this._normalizedQuery; + } + return (query || []).filter(q => !!q).map(q => normalize(q)[0]); + } + #shouldDirtyMatch(state) { + const newQuery = state.query, + prevQuery = this.#state.query; + const newType = typeof newQuery, + prevType = typeof prevQuery; + if (newType !== prevType) { + return true; + } + if (newType === "string") { + if (newQuery !== prevQuery) { + return true; + } + } else if (JSON.stringify(newQuery) !== JSON.stringify(prevQuery)) { + return true; + } + switch (state.type) { + case "again": + const pageNumber = this._selected.pageIdx + 1; + const linkService = this._linkService; + return pageNumber >= 1 && pageNumber <= linkService.pagesCount && pageNumber !== linkService.page && !(this.onIsPageVisible?.(pageNumber) ?? true); + case "highlightallchange": + return false; + } + return true; + } + #isEntireWord(content, startIdx, length) { + let match = content.slice(0, startIdx).match(NOT_DIACRITIC_FROM_END_REG_EXP); + if (match) { + const first = content.charCodeAt(startIdx); + const limit = match[1].charCodeAt(0); + if (getCharacterType(first) === getCharacterType(limit)) { + return false; + } + } + match = content.slice(startIdx + length).match(NOT_DIACRITIC_FROM_START_REG_EXP); + if (match) { + const last = content.charCodeAt(startIdx + length - 1); + const limit = match[1].charCodeAt(0); + if (getCharacterType(last) === getCharacterType(limit)) { + return false; + } + } + return true; + } + #convertToRegExpString(query, hasDiacritics) { + const { + matchDiacritics + } = this.#state; + let isUnicode = false; + query = query.replaceAll(SPECIAL_CHARS_REG_EXP, (match, p1, p2, p3, p4, p5) => { + if (p1) { + return `[ ]*\\${p1}[ ]*`; + } + if (p2) { + return `[ ]*${p2}[ ]*`; + } + if (p3) { + return "[ ]+"; + } + if (matchDiacritics) { + return p4 || p5; + } + if (p4) { + return DIACRITICS_EXCEPTION.has(p4.charCodeAt(0)) ? p4 : ""; + } + if (hasDiacritics) { + isUnicode = true; + return `${p5}\\p{M}*`; + } + return p5; + }); + const trailingSpaces = "[ ]*"; + if (query.endsWith(trailingSpaces)) { + query = query.slice(0, query.length - trailingSpaces.length); + } + if (matchDiacritics) { + if (hasDiacritics) { + DIACRITICS_EXCEPTION_STR ||= String.fromCharCode(...DIACRITICS_EXCEPTION); + isUnicode = true; + query = `${query}(?=[${DIACRITICS_EXCEPTION_STR}]|[^\\p{M}]|$)`; + } + } + return [isUnicode, query]; + } + #calculateMatch(pageIndex) { + const query = this.#query; + if (query.length === 0) { + return; + } + const pageContent = this._pageContents[pageIndex]; + const matcherResult = this.match(query, pageContent, pageIndex); + const matches = this._pageMatches[pageIndex] = []; + const matchesLength = this._pageMatchesLength[pageIndex] = []; + const diffs = this._pageDiffs[pageIndex]; + matcherResult?.forEach(({ + index, + length + }) => { + const [matchPos, matchLen] = getOriginalIndex(diffs, index, length); + if (matchLen) { + matches.push(matchPos); + matchesLength.push(matchLen); + } + }); + if (this.#state.highlightAll) { + this.#updatePage(pageIndex); + } + if (this._resumePageIdx === pageIndex) { + this._resumePageIdx = null; + this.#nextPageMatch(); + } + const pageMatchesCount = matches.length; + this._matchesCountTotal += pageMatchesCount; + if (this.#updateMatchesCountOnProgress) { + if (pageMatchesCount > 0) { + this.#updateUIResultsCount(); + } + } else if (++this.#visitedPagesCount === this._linkService.pagesCount) { + this.#updateUIResultsCount(); + } + } + match(query, pageContent, pageIndex) { + const hasDiacritics = this._hasDiacritics[pageIndex]; + let isUnicode = false; + if (typeof query === "string") { + [isUnicode, query] = this.#convertToRegExpString(query, hasDiacritics); + } else { + query = query.sort().reverse().map(q => { + const [isUnicodePart, queryPart] = this.#convertToRegExpString(q, hasDiacritics); + isUnicode ||= isUnicodePart; + return `(${queryPart})`; + }).join("|"); + } + if (!query) { + return undefined; + } + const { + caseSensitive, + entireWord + } = this.#state; + const flags = `g${isUnicode ? "u" : ""}${caseSensitive ? "" : "i"}`; + query = new RegExp(query, flags); + const matches = []; + let match; + while ((match = query.exec(pageContent)) !== null) { + if (entireWord && !this.#isEntireWord(pageContent, match.index, match[0].length)) { + continue; + } + matches.push({ + index: match.index, + length: match[0].length + }); + } + return matches; + } + #extractText() { + if (this._extractTextPromises.length > 0) { + return; + } + let deferred = Promise.resolve(); + const textOptions = { + disableNormalization: true + }; + for (let i = 0, ii = this._linkService.pagesCount; i < ii; i++) { + const { + promise, + resolve + } = Promise.withResolvers(); + this._extractTextPromises[i] = promise; + deferred = deferred.then(() => { + return this._pdfDocument.getPage(i + 1).then(pdfPage => pdfPage.getTextContent(textOptions)).then(textContent => { + const strBuf = []; + for (const textItem of textContent.items) { + strBuf.push(textItem.str); + if (textItem.hasEOL) { + strBuf.push("\n"); + } + } + [this._pageContents[i], this._pageDiffs[i], this._hasDiacritics[i]] = normalize(strBuf.join("")); + resolve(); + }, reason => { + console.error(`Unable to get text content for page ${i + 1}`, reason); + this._pageContents[i] = ""; + this._pageDiffs[i] = null; + this._hasDiacritics[i] = false; + resolve(); + }); + }); + } + } + #updatePage(index) { + if (this._scrollMatches && this._selected.pageIdx === index) { + this._linkService.page = index + 1; + } + this._eventBus.dispatch("updatetextlayermatches", { + source: this, + pageIndex: index + }); + } + #updateAllPages() { + this._eventBus.dispatch("updatetextlayermatches", { + source: this, + pageIndex: -1 + }); + } + #nextMatch() { + const previous = this.#state.findPrevious; + const currentPageIndex = this._linkService.page - 1; + const numPages = this._linkService.pagesCount; + this._highlightMatches = true; + if (this._dirtyMatch) { + this._dirtyMatch = false; + this._selected.pageIdx = this._selected.matchIdx = -1; + this._offset.pageIdx = currentPageIndex; + this._offset.matchIdx = null; + this._offset.wrapped = false; + this._resumePageIdx = null; + this._pageMatches.length = 0; + this._pageMatchesLength.length = 0; + this.#visitedPagesCount = 0; + this._matchesCountTotal = 0; + this.#updateAllPages(); + for (let i = 0; i < numPages; i++) { + if (this._pendingFindMatches.has(i)) { + continue; + } + this._pendingFindMatches.add(i); + this._extractTextPromises[i].then(() => { + this._pendingFindMatches.delete(i); + this.#calculateMatch(i); + }); + } + } + const query = this.#query; + if (query.length === 0) { + this.#updateUIState(FindState.FOUND); + return; + } + if (this._resumePageIdx) { + return; + } + const offset = this._offset; + this._pagesToSearch = numPages; + if (offset.matchIdx !== null) { + const numPageMatches = this._pageMatches[offset.pageIdx].length; + if (!previous && offset.matchIdx + 1 < numPageMatches || previous && offset.matchIdx > 0) { + offset.matchIdx = previous ? offset.matchIdx - 1 : offset.matchIdx + 1; + this.#updateMatch(true); + return; + } + this.#advanceOffsetPage(previous); + } + this.#nextPageMatch(); + } + #matchesReady(matches) { + const offset = this._offset; + const numMatches = matches.length; + const previous = this.#state.findPrevious; + if (numMatches) { + offset.matchIdx = previous ? numMatches - 1 : 0; + this.#updateMatch(true); + return true; + } + this.#advanceOffsetPage(previous); + if (offset.wrapped) { + offset.matchIdx = null; + if (this._pagesToSearch < 0) { + this.#updateMatch(false); + return true; + } + } + return false; + } + #nextPageMatch() { + if (this._resumePageIdx !== null) { + console.error("There can only be one pending page."); + } + let matches = null; + do { + const pageIdx = this._offset.pageIdx; + matches = this._pageMatches[pageIdx]; + if (!matches) { + this._resumePageIdx = pageIdx; + break; + } + } while (!this.#matchesReady(matches)); + } + #advanceOffsetPage(previous) { + const offset = this._offset; + const numPages = this._linkService.pagesCount; + offset.pageIdx = previous ? offset.pageIdx - 1 : offset.pageIdx + 1; + offset.matchIdx = null; + this._pagesToSearch--; + if (offset.pageIdx >= numPages || offset.pageIdx < 0) { + offset.pageIdx = previous ? numPages - 1 : 0; + offset.wrapped = true; + } + } + #updateMatch(found = false) { + let state = FindState.NOT_FOUND; + const wrapped = this._offset.wrapped; + this._offset.wrapped = false; + if (found) { + const previousPage = this._selected.pageIdx; + this._selected.pageIdx = this._offset.pageIdx; + this._selected.matchIdx = this._offset.matchIdx; + state = wrapped ? FindState.WRAPPED : FindState.FOUND; + if (previousPage !== -1 && previousPage !== this._selected.pageIdx) { + this.#updatePage(previousPage); + } + } + this.#updateUIState(state, this.#state.findPrevious); + if (this._selected.pageIdx !== -1) { + this._scrollMatches = true; + this.#updatePage(this._selected.pageIdx); + } + } + #onFindBarClose(evt) { + const pdfDocument = this._pdfDocument; + this._firstPageCapability.promise.then(() => { + if (!this._pdfDocument || pdfDocument && this._pdfDocument !== pdfDocument) { + return; + } + if (this._findTimeout) { + clearTimeout(this._findTimeout); + this._findTimeout = null; + } + if (this._resumePageIdx) { + this._resumePageIdx = null; + this._dirtyMatch = true; + } + this.#updateUIState(FindState.FOUND); + this._highlightMatches = false; + this.#updateAllPages(); + }); + } + #requestMatchesCount() { + const { + pageIdx, + matchIdx + } = this._selected; + let current = 0, + total = this._matchesCountTotal; + if (matchIdx !== -1) { + for (let i = 0; i < pageIdx; i++) { + current += this._pageMatches[i]?.length || 0; + } + current += matchIdx + 1; + } + if (current < 1 || current > total) { + current = total = 0; + } + return { + current, + total + }; + } + #updateUIResultsCount() { + this._eventBus.dispatch("updatefindmatchescount", { + source: this, + matchesCount: this.#requestMatchesCount() + }); + } + #updateUIState(state, previous = false) { + if (!this.#updateMatchesCountOnProgress && (this.#visitedPagesCount !== this._linkService.pagesCount || state === FindState.PENDING)) { + return; + } + this._eventBus.dispatch("updatefindcontrolstate", { + source: this, + state, + previous, + entireWord: this.#state?.entireWord ?? null, + matchesCount: this.#requestMatchesCount(), + rawQuery: this.#state?.query ?? null + }); + } +} + +;// ./web/pdf_find_bar.js + + +const MATCHES_COUNT_LIMIT = 1000; +class PDFFindBar { + #mainContainer; + #resizeObserver = new ResizeObserver(this.#resizeObserverCallback.bind(this)); + constructor(options, mainContainer, eventBus) { + this.opened = false; + this.bar = options.bar; + this.toggleButton = options.toggleButton; + this.findField = options.findField; + this.highlightAll = options.highlightAllCheckbox; + this.caseSensitive = options.caseSensitiveCheckbox; + this.matchDiacritics = options.matchDiacriticsCheckbox; + this.entireWord = options.entireWordCheckbox; + this.findMsg = options.findMsg; + this.findResultsCount = options.findResultsCount; + this.findPreviousButton = options.findPreviousButton; + this.findNextButton = options.findNextButton; + this.eventBus = eventBus; + this.#mainContainer = mainContainer; + const checkedInputs = new Map([[this.highlightAll, "highlightallchange"], [this.caseSensitive, "casesensitivitychange"], [this.entireWord, "entirewordchange"], [this.matchDiacritics, "diacriticmatchingchange"]]); + this.toggleButton.addEventListener("click", () => { + this.toggle(); + }); + this.findField.addEventListener("input", () => { + this.dispatchEvent(""); + }); + this.bar.addEventListener("keydown", ({ + keyCode, + shiftKey, + target + }) => { + switch (keyCode) { + case 13: + if (target === this.findField) { + this.dispatchEvent("again", shiftKey); + } else if (checkedInputs.has(target)) { + target.checked = !target.checked; + this.dispatchEvent(checkedInputs.get(target)); + } + break; + case 27: + this.close(); + break; + } + }); + this.findPreviousButton.addEventListener("click", () => { + this.dispatchEvent("again", true); + }); + this.findNextButton.addEventListener("click", () => { + this.dispatchEvent("again", false); + }); + for (const [elem, evtName] of checkedInputs) { + elem.addEventListener("click", () => { + this.dispatchEvent(evtName); + }); + } + } + reset() { + this.updateUIState(); + } + dispatchEvent(type, findPrev = false) { + this.eventBus.dispatch("find", { + source: this, + type, + query: this.findField.value, + caseSensitive: this.caseSensitive.checked, + entireWord: this.entireWord.checked, + highlightAll: this.highlightAll.checked, + findPrevious: findPrev, + matchDiacritics: this.matchDiacritics.checked + }); + } + updateUIState(state, previous, matchesCount) { + const { + findField, + findMsg + } = this; + let findMsgId = "", + status = ""; + switch (state) { + case FindState.FOUND: + break; + case FindState.PENDING: + status = "pending"; + break; + case FindState.NOT_FOUND: + findMsgId = "pdfjs-find-not-found"; + status = "notFound"; + break; + case FindState.WRAPPED: + findMsgId = previous ? "pdfjs-find-reached-top" : "pdfjs-find-reached-bottom"; + break; + } + findField.setAttribute("data-status", status); + findField.setAttribute("aria-invalid", state === FindState.NOT_FOUND); + findMsg.setAttribute("data-status", status); + if (findMsgId) { + findMsg.setAttribute("data-l10n-id", findMsgId); + } else { + findMsg.removeAttribute("data-l10n-id"); + findMsg.textContent = ""; + } + this.updateResultsCount(matchesCount); + } + updateResultsCount({ + current = 0, + total = 0 + } = {}) { + const { + findResultsCount + } = this; + if (total > 0) { + const limit = MATCHES_COUNT_LIMIT; + findResultsCount.setAttribute("data-l10n-id", total > limit ? "pdfjs-find-match-count-limit" : "pdfjs-find-match-count"); + findResultsCount.setAttribute("data-l10n-args", JSON.stringify({ + limit, + current, + total + })); + } else { + findResultsCount.removeAttribute("data-l10n-id"); + findResultsCount.textContent = ""; + } + } + open() { + if (!this.opened) { + this.#resizeObserver.observe(this.#mainContainer); + this.#resizeObserver.observe(this.bar); + this.opened = true; + toggleExpandedBtn(this.toggleButton, true, this.bar); + } + this.findField.select(); + this.findField.focus(); + } + close() { + if (!this.opened) { + return; + } + this.#resizeObserver.disconnect(); + this.opened = false; + toggleExpandedBtn(this.toggleButton, false, this.bar); + this.eventBus.dispatch("findbarclose", { + source: this + }); + } + toggle() { + if (this.opened) { + this.close(); + } else { + this.open(); + } + } + #resizeObserverCallback() { + const { + bar + } = this; + bar.classList.remove("wrapContainers"); + const findbarHeight = bar.clientHeight; + const inputContainerHeight = bar.firstElementChild.clientHeight; + if (findbarHeight > inputContainerHeight) { + bar.classList.add("wrapContainers"); + } + } +} + +;// ./web/pdf_history.js + + +const HASH_CHANGE_TIMEOUT = 1000; +const POSITION_UPDATED_THRESHOLD = 50; +const UPDATE_VIEWAREA_TIMEOUT = 1000; +function getCurrentHash() { + return document.location.hash; +} +class PDFHistory { + #eventAbortController = null; + constructor({ + linkService, + eventBus + }) { + this.linkService = linkService; + this.eventBus = eventBus; + this._initialized = false; + this._fingerprint = ""; + this.reset(); + this.eventBus._on("pagesinit", () => { + this._isPagesLoaded = false; + this.eventBus._on("pagesloaded", evt => { + this._isPagesLoaded = !!evt.pagesCount; + }, { + once: true + }); + }); + } + initialize({ + fingerprint, + resetHistory = false, + updateUrl = false + }) { + if (!fingerprint || typeof fingerprint !== "string") { + console.error('PDFHistory.initialize: The "fingerprint" must be a non-empty string.'); + return; + } + if (this._initialized) { + this.reset(); + } + const reInitialized = this._fingerprint !== "" && this._fingerprint !== fingerprint; + this._fingerprint = fingerprint; + this._updateUrl = updateUrl === true; + this._initialized = true; + this.#bindEvents(); + const state = window.history.state; + this._popStateInProgress = false; + this._blockHashChange = 0; + this._currentHash = getCurrentHash(); + this._numPositionUpdates = 0; + this._uid = this._maxUid = 0; + this._destination = null; + this._position = null; + if (!this.#isValidState(state, true) || resetHistory) { + const { + hash, + page, + rotation + } = this.#parseCurrentHash(true); + if (!hash || reInitialized || resetHistory) { + this.#pushOrReplaceState(null, true); + return; + } + this.#pushOrReplaceState({ + hash, + page, + rotation + }, true); + return; + } + const destination = state.destination; + this.#updateInternalState(destination, state.uid, true); + if (destination.rotation !== undefined) { + this._initialRotation = destination.rotation; + } + if (destination.dest) { + this._initialBookmark = JSON.stringify(destination.dest); + this._destination.page = null; + } else if (destination.hash) { + this._initialBookmark = destination.hash; + } else if (destination.page) { + this._initialBookmark = `page=${destination.page}`; + } + } + reset() { + if (this._initialized) { + this.#pageHide(); + this._initialized = false; + this.#unbindEvents(); + } + if (this._updateViewareaTimeout) { + clearTimeout(this._updateViewareaTimeout); + this._updateViewareaTimeout = null; + } + this._initialBookmark = null; + this._initialRotation = null; + } + push({ + namedDest = null, + explicitDest, + pageNumber + }) { + if (!this._initialized) { + return; + } + if (namedDest && typeof namedDest !== "string") { + console.error("PDFHistory.push: " + `"${namedDest}" is not a valid namedDest parameter.`); + return; + } else if (!Array.isArray(explicitDest)) { + console.error("PDFHistory.push: " + `"${explicitDest}" is not a valid explicitDest parameter.`); + return; + } else if (!this.#isValidPage(pageNumber)) { + if (pageNumber !== null || this._destination) { + console.error("PDFHistory.push: " + `"${pageNumber}" is not a valid pageNumber parameter.`); + return; + } + } + const hash = namedDest || JSON.stringify(explicitDest); + if (!hash) { + return; + } + let forceReplace = false; + if (this._destination && (isDestHashesEqual(this._destination.hash, hash) || isDestArraysEqual(this._destination.dest, explicitDest))) { + if (this._destination.page) { + return; + } + forceReplace = true; + } + if (this._popStateInProgress && !forceReplace) { + return; + } + this.#pushOrReplaceState({ + dest: explicitDest, + hash, + page: pageNumber, + rotation: this.linkService.rotation + }, forceReplace); + if (!this._popStateInProgress) { + this._popStateInProgress = true; + Promise.resolve().then(() => { + this._popStateInProgress = false; + }); + } + } + pushPage(pageNumber) { + if (!this._initialized) { + return; + } + if (!this.#isValidPage(pageNumber)) { + console.error(`PDFHistory.pushPage: "${pageNumber}" is not a valid page number.`); + return; + } + if (this._destination?.page === pageNumber) { + return; + } + if (this._popStateInProgress) { + return; + } + this.#pushOrReplaceState({ + dest: null, + hash: `page=${pageNumber}`, + page: pageNumber, + rotation: this.linkService.rotation + }); + if (!this._popStateInProgress) { + this._popStateInProgress = true; + Promise.resolve().then(() => { + this._popStateInProgress = false; + }); + } + } + pushCurrentPosition() { + if (!this._initialized || this._popStateInProgress) { + return; + } + this.#tryPushCurrentPosition(); + } + back() { + if (!this._initialized || this._popStateInProgress) { + return; + } + const state = window.history.state; + if (this.#isValidState(state) && state.uid > 0) { + window.history.back(); + } + } + forward() { + if (!this._initialized || this._popStateInProgress) { + return; + } + const state = window.history.state; + if (this.#isValidState(state) && state.uid < this._maxUid) { + window.history.forward(); + } + } + get popStateInProgress() { + return this._initialized && (this._popStateInProgress || this._blockHashChange > 0); + } + get initialBookmark() { + return this._initialized ? this._initialBookmark : null; + } + get initialRotation() { + return this._initialized ? this._initialRotation : null; + } + #pushOrReplaceState(destination, forceReplace = false) { + const shouldReplace = forceReplace || !this._destination; + const newState = { + fingerprint: this._fingerprint, + uid: shouldReplace ? this._uid : this._uid + 1, + destination + }; + this.#updateInternalState(destination, newState.uid); + let newUrl; + if (this._updateUrl && destination?.hash) { + const baseUrl = document.location.href.split("#", 1)[0]; + if (!baseUrl.startsWith("file://")) { + newUrl = `${baseUrl}#${destination.hash}`; + } + } + if (shouldReplace) { + window.history.replaceState(newState, "", newUrl); + } else { + window.history.pushState(newState, "", newUrl); + } + } + #tryPushCurrentPosition(temporary = false) { + if (!this._position) { + return; + } + let position = this._position; + if (temporary) { + position = Object.assign(Object.create(null), this._position); + position.temporary = true; + } + if (!this._destination) { + this.#pushOrReplaceState(position); + return; + } + if (this._destination.temporary) { + this.#pushOrReplaceState(position, true); + return; + } + if (this._destination.hash === position.hash) { + return; + } + if (!this._destination.page && (POSITION_UPDATED_THRESHOLD <= 0 || this._numPositionUpdates <= POSITION_UPDATED_THRESHOLD)) { + return; + } + let forceReplace = false; + if (this._destination.page >= position.first && this._destination.page <= position.page) { + if (this._destination.dest !== undefined || !this._destination.first) { + return; + } + forceReplace = true; + } + this.#pushOrReplaceState(position, forceReplace); + } + #isValidPage(val) { + return Number.isInteger(val) && val > 0 && val <= this.linkService.pagesCount; + } + #isValidState(state, checkReload = false) { + if (!state) { + return false; + } + if (state.fingerprint !== this._fingerprint) { + if (checkReload) { + if (typeof state.fingerprint !== "string" || state.fingerprint.length !== this._fingerprint.length) { + return false; + } + const [perfEntry] = performance.getEntriesByType("navigation"); + if (perfEntry?.type !== "reload") { + return false; + } + } else { + return false; + } + } + if (!Number.isInteger(state.uid) || state.uid < 0) { + return false; + } + if (state.destination === null || typeof state.destination !== "object") { + return false; + } + return true; + } + #updateInternalState(destination, uid, removeTemporary = false) { + if (this._updateViewareaTimeout) { + clearTimeout(this._updateViewareaTimeout); + this._updateViewareaTimeout = null; + } + if (removeTemporary && destination?.temporary) { + delete destination.temporary; + } + this._destination = destination; + this._uid = uid; + this._maxUid = Math.max(this._maxUid, uid); + this._numPositionUpdates = 0; + } + #parseCurrentHash(checkNameddest = false) { + const hash = unescape(getCurrentHash()).substring(1); + const params = parseQueryString(hash); + const nameddest = params.get("nameddest") || ""; + let page = params.get("page") | 0; + if (!this.#isValidPage(page) || checkNameddest && nameddest.length > 0) { + page = null; + } + return { + hash, + page, + rotation: this.linkService.rotation + }; + } + #updateViewarea({ + location + }) { + if (this._updateViewareaTimeout) { + clearTimeout(this._updateViewareaTimeout); + this._updateViewareaTimeout = null; + } + this._position = { + hash: location.pdfOpenParams.substring(1), + page: this.linkService.page, + first: location.pageNumber, + rotation: location.rotation + }; + if (this._popStateInProgress) { + return; + } + if (POSITION_UPDATED_THRESHOLD > 0 && this._isPagesLoaded && this._destination && !this._destination.page) { + this._numPositionUpdates++; + } + if (UPDATE_VIEWAREA_TIMEOUT > 0) { + this._updateViewareaTimeout = setTimeout(() => { + if (!this._popStateInProgress) { + this.#tryPushCurrentPosition(true); + } + this._updateViewareaTimeout = null; + }, UPDATE_VIEWAREA_TIMEOUT); + } + } + #popState({ + state + }) { + const newHash = getCurrentHash(), + hashChanged = this._currentHash !== newHash; + this._currentHash = newHash; + if (!state) { + this._uid++; + const { + hash, + page, + rotation + } = this.#parseCurrentHash(); + this.#pushOrReplaceState({ + hash, + page, + rotation + }, true); + return; + } + if (!this.#isValidState(state)) { + return; + } + this._popStateInProgress = true; + if (hashChanged) { + this._blockHashChange++; + waitOnEventOrTimeout({ + target: window, + name: "hashchange", + delay: HASH_CHANGE_TIMEOUT + }).then(() => { + this._blockHashChange--; + }); + } + const destination = state.destination; + this.#updateInternalState(destination, state.uid, true); + if (isValidRotation(destination.rotation)) { + this.linkService.rotation = destination.rotation; + } + if (destination.dest) { + this.linkService.goToDestination(destination.dest); + } else if (destination.hash) { + this.linkService.setHash(destination.hash); + } else if (destination.page) { + this.linkService.page = destination.page; + } + Promise.resolve().then(() => { + this._popStateInProgress = false; + }); + } + #pageHide() { + if (!this._destination || this._destination.temporary) { + this.#tryPushCurrentPosition(); + } + } + #bindEvents() { + if (this.#eventAbortController) { + return; + } + this.#eventAbortController = new AbortController(); + const { + signal + } = this.#eventAbortController; + this.eventBus._on("updateviewarea", this.#updateViewarea.bind(this), { + signal + }); + window.addEventListener("popstate", this.#popState.bind(this), { + signal + }); + window.addEventListener("pagehide", this.#pageHide.bind(this), { + signal + }); + } + #unbindEvents() { + this.#eventAbortController?.abort(); + this.#eventAbortController = null; + } +} +function isDestHashesEqual(destHash, pushHash) { + if (typeof destHash !== "string" || typeof pushHash !== "string") { + return false; + } + if (destHash === pushHash) { + return true; + } + const nameddest = parseQueryString(destHash).get("nameddest"); + if (nameddest === pushHash) { + return true; + } + return false; +} +function isDestArraysEqual(firstDest, secondDest) { + function isEntryEqual(first, second) { + if (typeof first !== typeof second) { + return false; + } + if (Array.isArray(first) || Array.isArray(second)) { + return false; + } + if (first !== null && typeof first === "object" && second !== null) { + if (Object.keys(first).length !== Object.keys(second).length) { + return false; + } + for (const key in first) { + if (!isEntryEqual(first[key], second[key])) { + return false; + } + } + return true; + } + return first === second || Number.isNaN(first) && Number.isNaN(second); + } + if (!(Array.isArray(firstDest) && Array.isArray(secondDest))) { + return false; + } + if (firstDest.length !== secondDest.length) { + return false; + } + for (let i = 0, ii = firstDest.length; i < ii; i++) { + if (!isEntryEqual(firstDest[i], secondDest[i])) { + return false; + } + } + return true; +} + +;// ./web/pdf_layer_viewer.js + +class PDFLayerViewer extends BaseTreeViewer { + constructor(options) { + super(options); + this.eventBus._on("optionalcontentconfigchanged", evt => { + this.#updateLayers(evt.promise); + }); + this.eventBus._on("resetlayers", () => { + this.#updateLayers(); + }); + this.eventBus._on("togglelayerstree", this._toggleAllTreeItems.bind(this)); + } + reset() { + super.reset(); + this._optionalContentConfig = null; + this._optionalContentVisibility?.clear(); + this._optionalContentVisibility = null; + } + _dispatchEvent(layersCount) { + this.eventBus.dispatch("layersloaded", { + source: this, + layersCount + }); + } + _bindLink(element, { + groupId, + input + }) { + const setVisibility = () => { + const visible = input.checked; + this._optionalContentConfig.setVisibility(groupId, visible); + const cached = this._optionalContentVisibility.get(groupId); + if (cached) { + cached.visible = visible; + } + this.eventBus.dispatch("optionalcontentconfig", { + source: this, + promise: Promise.resolve(this._optionalContentConfig) + }); + }; + element.onclick = evt => { + if (evt.target === input) { + setVisibility(); + return true; + } else if (evt.target !== element) { + return true; + } + input.checked = !input.checked; + setVisibility(); + return false; + }; + } + _setNestedName(element, { + name = null + }) { + if (typeof name === "string") { + element.textContent = this._normalizeTextContent(name); + return; + } + element.setAttribute("data-l10n-id", "pdfjs-additional-layers"); + element.style.fontStyle = "italic"; + this._l10n.translateOnce(element); + } + _addToggleButton(div, { + name = null + }) { + super._addToggleButton(div, name === null); + } + _toggleAllTreeItems() { + if (!this._optionalContentConfig) { + return; + } + super._toggleAllTreeItems(); + } + render({ + optionalContentConfig, + pdfDocument + }) { + if (this._optionalContentConfig) { + this.reset(); + } + this._optionalContentConfig = optionalContentConfig || null; + this._pdfDocument = pdfDocument || null; + const groups = optionalContentConfig?.getOrder(); + if (!groups) { + this._dispatchEvent(0); + return; + } + this._optionalContentVisibility = new Map(); + const fragment = document.createDocumentFragment(), + queue = [{ + parent: fragment, + groups + }]; + let layersCount = 0, + hasAnyNesting = false; + while (queue.length > 0) { + const levelData = queue.shift(); + for (const groupId of levelData.groups) { + const div = document.createElement("div"); + div.className = "treeItem"; + const element = document.createElement("a"); + div.append(element); + if (typeof groupId === "object") { + hasAnyNesting = true; + this._addToggleButton(div, groupId); + this._setNestedName(element, groupId); + const itemsDiv = document.createElement("div"); + itemsDiv.className = "treeItems"; + div.append(itemsDiv); + queue.push({ + parent: itemsDiv, + groups: groupId.order + }); + } else { + const group = optionalContentConfig.getGroup(groupId); + const input = document.createElement("input"); + this._bindLink(element, { + groupId, + input + }); + input.type = "checkbox"; + input.checked = group.visible; + this._optionalContentVisibility.set(groupId, { + input, + visible: input.checked + }); + const label = document.createElement("label"); + label.textContent = this._normalizeTextContent(group.name); + label.append(input); + element.append(label); + layersCount++; + } + levelData.parent.append(div); + } + } + this._finishRendering(fragment, layersCount, hasAnyNesting); + } + async #updateLayers(promise = null) { + if (!this._optionalContentConfig) { + return; + } + const pdfDocument = this._pdfDocument; + const optionalContentConfig = await (promise || pdfDocument.getOptionalContentConfig({ + intent: "display" + })); + if (pdfDocument !== this._pdfDocument) { + return; + } + if (promise) { + for (const [groupId, cached] of this._optionalContentVisibility) { + const group = optionalContentConfig.getGroup(groupId); + if (group && cached.visible !== group.visible) { + cached.input.checked = cached.visible = !cached.visible; + } + } + return; + } + this.eventBus.dispatch("optionalcontentconfig", { + source: this, + promise: Promise.resolve(optionalContentConfig) + }); + this.render({ + optionalContentConfig, + pdfDocument: this._pdfDocument + }); + } +} + +;// ./web/pdf_outline_viewer.js + + +class PDFOutlineViewer extends BaseTreeViewer { + constructor(options) { + super(options); + this.linkService = options.linkService; + this.downloadManager = options.downloadManager; + this.eventBus._on("toggleoutlinetree", this._toggleAllTreeItems.bind(this)); + this.eventBus._on("currentoutlineitem", this._currentOutlineItem.bind(this)); + this.eventBus._on("pagechanging", evt => { + this._currentPageNumber = evt.pageNumber; + }); + this.eventBus._on("pagesloaded", evt => { + this._isPagesLoaded = !!evt.pagesCount; + this._currentOutlineItemCapability?.resolve(this._isPagesLoaded); + }); + this.eventBus._on("sidebarviewchanged", evt => { + this._sidebarView = evt.view; + }); + } + reset() { + super.reset(); + this._outline = null; + this._pageNumberToDestHashCapability = null; + this._currentPageNumber = 1; + this._isPagesLoaded = null; + this._currentOutlineItemCapability?.resolve(false); + this._currentOutlineItemCapability = null; + } + _dispatchEvent(outlineCount) { + this._currentOutlineItemCapability = Promise.withResolvers(); + if (outlineCount === 0 || this._pdfDocument?.loadingParams.disableAutoFetch) { + this._currentOutlineItemCapability.resolve(false); + } else if (this._isPagesLoaded !== null) { + this._currentOutlineItemCapability.resolve(this._isPagesLoaded); + } + this.eventBus.dispatch("outlineloaded", { + source: this, + outlineCount, + currentOutlineItemPromise: this._currentOutlineItemCapability.promise + }); + } + _bindLink(element, { + url, + newWindow, + action, + attachment, + dest, + setOCGState + }) { + const { + linkService + } = this; + if (url) { + linkService.addLinkAttributes(element, url, newWindow); + return; + } + if (action) { + element.href = linkService.getAnchorUrl(""); + element.onclick = () => { + linkService.executeNamedAction(action); + return false; + }; + return; + } + if (attachment) { + element.href = linkService.getAnchorUrl(""); + element.onclick = () => { + this.downloadManager.openOrDownloadData(attachment.content, attachment.filename); + return false; + }; + return; + } + if (setOCGState) { + element.href = linkService.getAnchorUrl(""); + element.onclick = () => { + linkService.executeSetOCGState(setOCGState); + return false; + }; + return; + } + element.href = linkService.getDestinationHash(dest); + element.onclick = evt => { + this._updateCurrentTreeItem(evt.target.parentNode); + if (dest) { + linkService.goToDestination(dest); + } + return false; + }; + } + _setStyles(element, { + bold, + italic + }) { + if (bold) { + element.style.fontWeight = "bold"; + } + if (italic) { + element.style.fontStyle = "italic"; + } + } + _addToggleButton(div, { + count, + items + }) { + let hidden = false; + if (count < 0) { + let totalCount = items.length; + if (totalCount > 0) { + const queue = [...items]; + while (queue.length > 0) { + const { + count: nestedCount, + items: nestedItems + } = queue.shift(); + if (nestedCount > 0 && nestedItems.length > 0) { + totalCount += nestedItems.length; + queue.push(...nestedItems); + } + } + } + if (Math.abs(count) === totalCount) { + hidden = true; + } + } + super._addToggleButton(div, hidden); + } + _toggleAllTreeItems() { + if (!this._outline) { + return; + } + super._toggleAllTreeItems(); + } + render({ + outline, + pdfDocument + }) { + if (this._outline) { + this.reset(); + } + this._outline = outline || null; + this._pdfDocument = pdfDocument || null; + if (!outline) { + this._dispatchEvent(0); + return; + } + const fragment = document.createDocumentFragment(); + const queue = [{ + parent: fragment, + items: outline + }]; + let outlineCount = 0, + hasAnyNesting = false; + while (queue.length > 0) { + const levelData = queue.shift(); + for (const item of levelData.items) { + const div = document.createElement("div"); + div.className = "treeItem"; + const element = document.createElement("a"); + this._bindLink(element, item); + this._setStyles(element, item); + element.textContent = this._normalizeTextContent(item.title); + div.append(element); + if (item.items.length > 0) { + hasAnyNesting = true; + this._addToggleButton(div, item); + const itemsDiv = document.createElement("div"); + itemsDiv.className = "treeItems"; + div.append(itemsDiv); + queue.push({ + parent: itemsDiv, + items: item.items + }); + } + levelData.parent.append(div); + outlineCount++; + } + } + this._finishRendering(fragment, outlineCount, hasAnyNesting); + } + async _currentOutlineItem() { + if (!this._isPagesLoaded) { + throw new Error("_currentOutlineItem: All pages have not been loaded."); + } + if (!this._outline || !this._pdfDocument) { + return; + } + const pageNumberToDestHash = await this._getPageNumberToDestHash(this._pdfDocument); + if (!pageNumberToDestHash) { + return; + } + this._updateCurrentTreeItem(null); + if (this._sidebarView !== SidebarView.OUTLINE) { + return; + } + for (let i = this._currentPageNumber; i > 0; i--) { + const destHash = pageNumberToDestHash.get(i); + if (!destHash) { + continue; + } + const linkElement = this.container.querySelector(`a[href="${destHash}"]`); + if (!linkElement) { + continue; + } + this._scrollToCurrentTreeItem(linkElement.parentNode); + break; + } + } + async _getPageNumberToDestHash(pdfDocument) { + if (this._pageNumberToDestHashCapability) { + return this._pageNumberToDestHashCapability.promise; + } + this._pageNumberToDestHashCapability = Promise.withResolvers(); + const pageNumberToDestHash = new Map(), + pageNumberNesting = new Map(); + const queue = [{ + nesting: 0, + items: this._outline + }]; + while (queue.length > 0) { + const levelData = queue.shift(), + currentNesting = levelData.nesting; + for (const { + dest, + items + } of levelData.items) { + let explicitDest, pageNumber; + if (typeof dest === "string") { + explicitDest = await pdfDocument.getDestination(dest); + if (pdfDocument !== this._pdfDocument) { + return null; + } + } else { + explicitDest = dest; + } + if (Array.isArray(explicitDest)) { + const [destRef] = explicitDest; + if (destRef && typeof destRef === "object") { + pageNumber = pdfDocument.cachedPageNumber(destRef); + } else if (Number.isInteger(destRef)) { + pageNumber = destRef + 1; + } + if (Number.isInteger(pageNumber) && (!pageNumberToDestHash.has(pageNumber) || currentNesting > pageNumberNesting.get(pageNumber))) { + const destHash = this.linkService.getDestinationHash(dest); + pageNumberToDestHash.set(pageNumber, destHash); + pageNumberNesting.set(pageNumber, currentNesting); + } + } + if (items.length > 0) { + queue.push({ + nesting: currentNesting + 1, + items + }); + } + } + } + this._pageNumberToDestHashCapability.resolve(pageNumberToDestHash.size > 0 ? pageNumberToDestHash : null); + return this._pageNumberToDestHashCapability.promise; + } +} + +;// ./web/pdf_presentation_mode.js + + +const DELAY_BEFORE_HIDING_CONTROLS = 3000; +const ACTIVE_SELECTOR = "pdfPresentationMode"; +const CONTROLS_SELECTOR = "pdfPresentationModeControls"; +const MOUSE_SCROLL_COOLDOWN_TIME = 50; +const PAGE_SWITCH_THRESHOLD = 0.1; +const SWIPE_MIN_DISTANCE_THRESHOLD = 50; +const SWIPE_ANGLE_THRESHOLD = Math.PI / 6; +class PDFPresentationMode { + #state = PresentationModeState.UNKNOWN; + #args = null; + #fullscreenChangeAbortController = null; + #windowAbortController = null; + constructor({ + container, + pdfViewer, + eventBus + }) { + this.container = container; + this.pdfViewer = pdfViewer; + this.eventBus = eventBus; + this.contextMenuOpen = false; + this.mouseScrollTimeStamp = 0; + this.mouseScrollDelta = 0; + this.touchSwipeState = null; + } + async request() { + const { + container, + pdfViewer + } = this; + if (this.active || !pdfViewer.pagesCount || !container.requestFullscreen) { + return false; + } + this.#addFullscreenChangeListeners(); + this.#notifyStateChange(PresentationModeState.CHANGING); + const promise = container.requestFullscreen(); + this.#args = { + pageNumber: pdfViewer.currentPageNumber, + scaleValue: pdfViewer.currentScaleValue, + scrollMode: pdfViewer.scrollMode, + spreadMode: null, + annotationEditorMode: null + }; + if (pdfViewer.spreadMode !== SpreadMode.NONE && !(pdfViewer.pageViewsReady && pdfViewer.hasEqualPageSizes)) { + console.warn("Ignoring Spread modes when entering PresentationMode, " + "since the document may contain varying page sizes."); + this.#args.spreadMode = pdfViewer.spreadMode; + } + if (pdfViewer.annotationEditorMode !== AnnotationEditorType.DISABLE) { + this.#args.annotationEditorMode = pdfViewer.annotationEditorMode; + } + try { + await promise; + pdfViewer.focus(); + return true; + } catch { + this.#removeFullscreenChangeListeners(); + this.#notifyStateChange(PresentationModeState.NORMAL); + } + return false; + } + get active() { + return this.#state === PresentationModeState.CHANGING || this.#state === PresentationModeState.FULLSCREEN; + } + #mouseWheel(evt) { + if (!this.active) { + return; + } + evt.preventDefault(); + const delta = normalizeWheelEventDelta(evt); + const currentTime = Date.now(); + const storedTime = this.mouseScrollTimeStamp; + if (currentTime > storedTime && currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) { + return; + } + if (this.mouseScrollDelta > 0 && delta < 0 || this.mouseScrollDelta < 0 && delta > 0) { + this.#resetMouseScrollState(); + } + this.mouseScrollDelta += delta; + if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) { + const totalDelta = this.mouseScrollDelta; + this.#resetMouseScrollState(); + const success = totalDelta > 0 ? this.pdfViewer.previousPage() : this.pdfViewer.nextPage(); + if (success) { + this.mouseScrollTimeStamp = currentTime; + } + } + } + #notifyStateChange(state) { + this.#state = state; + this.eventBus.dispatch("presentationmodechanged", { + source: this, + state + }); + } + #enter() { + this.#notifyStateChange(PresentationModeState.FULLSCREEN); + this.container.classList.add(ACTIVE_SELECTOR); + setTimeout(() => { + this.pdfViewer.scrollMode = ScrollMode.PAGE; + if (this.#args.spreadMode !== null) { + this.pdfViewer.spreadMode = SpreadMode.NONE; + } + this.pdfViewer.currentPageNumber = this.#args.pageNumber; + this.pdfViewer.currentScaleValue = "page-fit"; + if (this.#args.annotationEditorMode !== null) { + this.pdfViewer.annotationEditorMode = { + mode: AnnotationEditorType.NONE + }; + } + }, 0); + this.#addWindowListeners(); + this.#showControls(); + this.contextMenuOpen = false; + document.getSelection().empty(); + } + #exit() { + const pageNumber = this.pdfViewer.currentPageNumber; + this.container.classList.remove(ACTIVE_SELECTOR); + setTimeout(() => { + this.#removeFullscreenChangeListeners(); + this.#notifyStateChange(PresentationModeState.NORMAL); + this.pdfViewer.scrollMode = this.#args.scrollMode; + if (this.#args.spreadMode !== null) { + this.pdfViewer.spreadMode = this.#args.spreadMode; + } + this.pdfViewer.currentScaleValue = this.#args.scaleValue; + this.pdfViewer.currentPageNumber = pageNumber; + if (this.#args.annotationEditorMode !== null) { + this.pdfViewer.annotationEditorMode = { + mode: this.#args.annotationEditorMode + }; + } + this.#args = null; + }, 0); + this.#removeWindowListeners(); + this.#hideControls(); + this.#resetMouseScrollState(); + this.contextMenuOpen = false; + } + #mouseDown(evt) { + if (this.contextMenuOpen) { + this.contextMenuOpen = false; + evt.preventDefault(); + return; + } + if (evt.button !== 0) { + return; + } + if (evt.target.href && evt.target.parentNode?.hasAttribute("data-internal-link")) { + return; + } + evt.preventDefault(); + if (evt.shiftKey) { + this.pdfViewer.previousPage(); + } else { + this.pdfViewer.nextPage(); + } + } + #contextMenu() { + this.contextMenuOpen = true; + } + #showControls() { + if (this.controlsTimeout) { + clearTimeout(this.controlsTimeout); + } else { + this.container.classList.add(CONTROLS_SELECTOR); + } + this.controlsTimeout = setTimeout(() => { + this.container.classList.remove(CONTROLS_SELECTOR); + delete this.controlsTimeout; + }, DELAY_BEFORE_HIDING_CONTROLS); + } + #hideControls() { + if (!this.controlsTimeout) { + return; + } + clearTimeout(this.controlsTimeout); + this.container.classList.remove(CONTROLS_SELECTOR); + delete this.controlsTimeout; + } + #resetMouseScrollState() { + this.mouseScrollTimeStamp = 0; + this.mouseScrollDelta = 0; + } + #touchSwipe(evt) { + if (!this.active) { + return; + } + if (evt.touches.length > 1) { + this.touchSwipeState = null; + return; + } + switch (evt.type) { + case "touchstart": + this.touchSwipeState = { + startX: evt.touches[0].pageX, + startY: evt.touches[0].pageY, + endX: evt.touches[0].pageX, + endY: evt.touches[0].pageY + }; + break; + case "touchmove": + if (this.touchSwipeState === null) { + return; + } + this.touchSwipeState.endX = evt.touches[0].pageX; + this.touchSwipeState.endY = evt.touches[0].pageY; + evt.preventDefault(); + break; + case "touchend": + if (this.touchSwipeState === null) { + return; + } + let delta = 0; + const dx = this.touchSwipeState.endX - this.touchSwipeState.startX; + const dy = this.touchSwipeState.endY - this.touchSwipeState.startY; + const absAngle = Math.abs(Math.atan2(dy, dx)); + if (Math.abs(dx) > SWIPE_MIN_DISTANCE_THRESHOLD && (absAngle <= SWIPE_ANGLE_THRESHOLD || absAngle >= Math.PI - SWIPE_ANGLE_THRESHOLD)) { + delta = dx; + } else if (Math.abs(dy) > SWIPE_MIN_DISTANCE_THRESHOLD && Math.abs(absAngle - Math.PI / 2) <= SWIPE_ANGLE_THRESHOLD) { + delta = dy; + } + if (delta > 0) { + this.pdfViewer.previousPage(); + } else if (delta < 0) { + this.pdfViewer.nextPage(); + } + break; + } + } + #addWindowListeners() { + if (this.#windowAbortController) { + return; + } + this.#windowAbortController = new AbortController(); + const { + signal + } = this.#windowAbortController; + const touchSwipeBind = this.#touchSwipe.bind(this); + window.addEventListener("mousemove", this.#showControls.bind(this), { + signal + }); + window.addEventListener("mousedown", this.#mouseDown.bind(this), { + signal + }); + window.addEventListener("wheel", this.#mouseWheel.bind(this), { + passive: false, + signal + }); + window.addEventListener("keydown", this.#resetMouseScrollState.bind(this), { + signal + }); + window.addEventListener("contextmenu", this.#contextMenu.bind(this), { + signal + }); + window.addEventListener("touchstart", touchSwipeBind, { + signal + }); + window.addEventListener("touchmove", touchSwipeBind, { + signal + }); + window.addEventListener("touchend", touchSwipeBind, { + signal + }); + } + #removeWindowListeners() { + this.#windowAbortController?.abort(); + this.#windowAbortController = null; + } + #addFullscreenChangeListeners() { + if (this.#fullscreenChangeAbortController) { + return; + } + this.#fullscreenChangeAbortController = new AbortController(); + window.addEventListener("fullscreenchange", () => { + if (document.fullscreenElement) { + this.#enter(); + } else { + this.#exit(); + } + }, { + signal: this.#fullscreenChangeAbortController.signal + }); + } + #removeFullscreenChangeListeners() { + this.#fullscreenChangeAbortController?.abort(); + this.#fullscreenChangeAbortController = null; + } +} + +;// ./web/xfa_layer_builder.js + +class XfaLayerBuilder { + constructor({ + pdfPage, + annotationStorage = null, + linkService, + xfaHtml = null + }) { + this.pdfPage = pdfPage; + this.annotationStorage = annotationStorage; + this.linkService = linkService; + this.xfaHtml = xfaHtml; + this.div = null; + this._cancelled = false; + } + async render(viewport, intent = "display") { + if (intent === "print") { + const parameters = { + viewport: viewport.clone({ + dontFlip: true + }), + div: this.div, + xfaHtml: this.xfaHtml, + annotationStorage: this.annotationStorage, + linkService: this.linkService, + intent + }; + this.div = document.createElement("div"); + parameters.div = this.div; + return XfaLayer.render(parameters); + } + const xfaHtml = await this.pdfPage.getXfa(); + if (this._cancelled || !xfaHtml) { + return { + textDivs: [] + }; + } + const parameters = { + viewport: viewport.clone({ + dontFlip: true + }), + div: this.div, + xfaHtml, + annotationStorage: this.annotationStorage, + linkService: this.linkService, + intent + }; + if (this.div) { + return XfaLayer.update(parameters); + } + this.div = document.createElement("div"); + parameters.div = this.div; + return XfaLayer.render(parameters); + } + cancel() { + this._cancelled = true; + } + hide() { + if (!this.div) { + return; + } + this.div.hidden = true; + } +} + +;// ./web/print_utils.js + + + +function getXfaHtmlForPrinting(printContainer, pdfDocument) { + const xfaHtml = pdfDocument.allXfaHtml; + const linkService = new SimpleLinkService(); + const scale = Math.round(PixelsPerInch.PDF_TO_CSS_UNITS * 100) / 100; + for (const xfaPage of xfaHtml.children) { + const page = document.createElement("div"); + page.className = "xfaPrintedPage"; + printContainer.append(page); + const builder = new XfaLayerBuilder({ + pdfPage: null, + annotationStorage: pdfDocument.annotationStorage, + linkService, + xfaHtml: xfaPage + }); + const viewport = getXfaPageViewport(xfaPage, { + scale + }); + builder.render(viewport, "print"); + page.append(builder.div); + } +} + +;// ./web/pdf_print_service.js + + +let activeService = null; +let dialog = null; +let overlayManager = null; +let viewerApp = { + initialized: false +}; +function renderPage(activeServiceOnEntry, pdfDocument, pageNumber, size, printResolution, optionalContentConfigPromise, printAnnotationStoragePromise) { + const scratchCanvas = activeService.scratchCanvas; + const PRINT_UNITS = printResolution / PixelsPerInch.PDF; + scratchCanvas.width = Math.floor(size.width * PRINT_UNITS); + scratchCanvas.height = Math.floor(size.height * PRINT_UNITS); + const ctx = scratchCanvas.getContext("2d"); + ctx.save(); + ctx.fillStyle = "rgb(255, 255, 255)"; + ctx.fillRect(0, 0, scratchCanvas.width, scratchCanvas.height); + ctx.restore(); + return Promise.all([pdfDocument.getPage(pageNumber), printAnnotationStoragePromise]).then(function ([pdfPage, printAnnotationStorage]) { + const renderContext = { + canvasContext: ctx, + transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0], + viewport: pdfPage.getViewport({ + scale: 1, + rotation: size.rotation + }), + intent: "print", + annotationMode: AnnotationMode.ENABLE_STORAGE, + optionalContentConfigPromise, + printAnnotationStorage + }; + const renderTask = pdfPage.render(renderContext); + return renderTask.promise.catch(reason => { + if (!(reason instanceof RenderingCancelledException)) { + console.error(reason); + } + throw reason; + }); + }); +} +class PDFPrintService { + constructor({ + pdfDocument, + pagesOverview, + printContainer, + printResolution, + printAnnotationStoragePromise = null + }) { + this.pdfDocument = pdfDocument; + this.pagesOverview = pagesOverview; + this.printContainer = printContainer; + this._printResolution = printResolution || 150; + this._optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({ + intent: "print" + }); + this._printAnnotationStoragePromise = printAnnotationStoragePromise || Promise.resolve(); + this.currentPage = -1; + this.scratchCanvas = document.createElement("canvas"); + } + layout() { + this.throwIfInactive(); + const body = document.querySelector("body"); + body.setAttribute("data-pdfjsprinting", true); + const { + width, + height + } = this.pagesOverview[0]; + const hasEqualPageSizes = this.pagesOverview.every(size => size.width === width && size.height === height); + if (!hasEqualPageSizes) { + console.warn("Not all pages have the same size. The printed result may be incorrect!"); + } + this.pageStyleSheet = document.createElement("style"); + this.pageStyleSheet.textContent = `@page { size: ${width}pt ${height}pt;}`; + body.append(this.pageStyleSheet); + } + destroy() { + if (activeService !== this) { + return; + } + this.printContainer.textContent = ""; + const body = document.querySelector("body"); + body.removeAttribute("data-pdfjsprinting"); + if (this.pageStyleSheet) { + this.pageStyleSheet.remove(); + this.pageStyleSheet = null; + } + this.scratchCanvas.width = this.scratchCanvas.height = 0; + this.scratchCanvas = null; + activeService = null; + ensureOverlay().then(function () { + if (overlayManager.active === dialog) { + overlayManager.close(dialog); + } + }); + } + renderPages() { + if (this.pdfDocument.isPureXfa) { + getXfaHtmlForPrinting(this.printContainer, this.pdfDocument); + return Promise.resolve(); + } + const pageCount = this.pagesOverview.length; + const renderNextPage = (resolve, reject) => { + this.throwIfInactive(); + if (++this.currentPage >= pageCount) { + renderProgress(pageCount, pageCount); + resolve(); + return; + } + const index = this.currentPage; + renderProgress(index, pageCount); + renderPage(this, this.pdfDocument, index + 1, this.pagesOverview[index], this._printResolution, this._optionalContentConfigPromise, this._printAnnotationStoragePromise).then(this.useRenderedPage.bind(this)).then(function () { + renderNextPage(resolve, reject); + }, reject); + }; + return new Promise(renderNextPage); + } + useRenderedPage() { + this.throwIfInactive(); + const img = document.createElement("img"); + this.scratchCanvas.toBlob(blob => { + img.src = URL.createObjectURL(blob); + }); + const wrapper = document.createElement("div"); + wrapper.className = "printedPage"; + wrapper.append(img); + this.printContainer.append(wrapper); + const { + promise, + resolve, + reject + } = Promise.withResolvers(); + img.onload = resolve; + img.onerror = reject; + promise.catch(() => {}).then(() => { + URL.revokeObjectURL(img.src); + }); + return promise; + } + performPrint() { + this.throwIfInactive(); + return new Promise(resolve => { + setTimeout(() => { + if (!this.active) { + resolve(); + return; + } + print.call(window); + setTimeout(resolve, 20); + }, 0); + }); + } + get active() { + return this === activeService; + } + throwIfInactive() { + if (!this.active) { + throw new Error("This print request was cancelled or completed."); + } + } +} +const print = window.print; +window.print = function () { + if (activeService) { + console.warn("Ignored window.print() because of a pending print job."); + return; + } + ensureOverlay().then(function () { + if (activeService) { + overlayManager.open(dialog); + } + }); + try { + dispatchEvent("beforeprint"); + } finally { + if (!activeService) { + console.error("Expected print service to be initialized."); + ensureOverlay().then(function () { + if (overlayManager.active === dialog) { + overlayManager.close(dialog); + } + }); + return; + } + const activeServiceOnEntry = activeService; + activeService.renderPages().then(function () { + return activeServiceOnEntry.performPrint(); + }).catch(function () {}).then(function () { + if (activeServiceOnEntry.active) { + abort(); + } + }); + } +}; +function dispatchEvent(eventType) { + const event = new CustomEvent(eventType, { + bubbles: false, + cancelable: false, + detail: "custom" + }); + window.dispatchEvent(event); +} +function abort() { + if (activeService) { + activeService.destroy(); + dispatchEvent("afterprint"); + } +} +function renderProgress(index, total) { + dialog ||= document.getElementById("printServiceDialog"); + const progress = Math.round(100 * index / total); + const progressBar = dialog.querySelector("progress"); + const progressPerc = dialog.querySelector(".relative-progress"); + progressBar.value = progress; + progressPerc.setAttribute("data-l10n-args", JSON.stringify({ + progress + })); +} +window.addEventListener("keydown", function (event) { + if (event.keyCode === 80 && (event.ctrlKey || event.metaKey) && !event.altKey && (!event.shiftKey || window.chrome || window.opera)) { + window.print(); + event.preventDefault(); + event.stopImmediatePropagation(); + } +}, true); +if ("onbeforeprint" in window) { + const stopPropagationIfNeeded = function (event) { + if (event.detail !== "custom") { + event.stopImmediatePropagation(); + } + }; + window.addEventListener("beforeprint", stopPropagationIfNeeded); + window.addEventListener("afterprint", stopPropagationIfNeeded); +} +let overlayPromise; +function ensureOverlay() { + if (!overlayPromise) { + overlayManager = viewerApp.overlayManager; + if (!overlayManager) { + throw new Error("The overlay manager has not yet been initialized."); + } + dialog ||= document.getElementById("printServiceDialog"); + overlayPromise = overlayManager.register(dialog, true); + document.getElementById("printCancel").onclick = abort; + dialog.addEventListener("close", abort); + } + return overlayPromise; +} +class PDFPrintServiceFactory { + static initGlobals(app) { + viewerApp = app; + } + static get supportsPrinting() { + return shadow(this, "supportsPrinting", true); + } + static createPrintService(params) { + if (activeService) { + throw new Error("The print service is created and active."); + } + return activeService = new PDFPrintService(params); + } +} + +;// ./web/pdf_rendering_queue.js + + +const CLEANUP_TIMEOUT = 30000; +class PDFRenderingQueue { + constructor() { + this.pdfViewer = null; + this.pdfThumbnailViewer = null; + this.onIdle = null; + this.highestPriorityPage = null; + this.idleTimeout = null; + this.printing = false; + this.isThumbnailViewEnabled = false; + Object.defineProperty(this, "hasViewer", { + value: () => !!this.pdfViewer + }); + } + setViewer(pdfViewer) { + this.pdfViewer = pdfViewer; + } + setThumbnailViewer(pdfThumbnailViewer) { + this.pdfThumbnailViewer = pdfThumbnailViewer; + } + isHighestPriority(view) { + return this.highestPriorityPage === view.renderingId; + } + renderHighestPriority(currentlyVisiblePages) { + if (this.idleTimeout) { + clearTimeout(this.idleTimeout); + this.idleTimeout = null; + } + if (this.pdfViewer.forceRendering(currentlyVisiblePages)) { + return; + } + if (this.isThumbnailViewEnabled && this.pdfThumbnailViewer?.forceRendering()) { + return; + } + if (this.printing) { + return; + } + if (this.onIdle) { + this.idleTimeout = setTimeout(this.onIdle.bind(this), CLEANUP_TIMEOUT); + } + } + getHighestPriority(visible, views, scrolledDown, preRenderExtra = false) { + const visibleViews = visible.views, + numVisible = visibleViews.length; + if (numVisible === 0) { + return null; + } + for (let i = 0; i < numVisible; i++) { + const view = visibleViews[i].view; + if (!this.isViewFinished(view)) { + return view; + } + } + const firstId = visible.first.id, + lastId = visible.last.id; + if (lastId - firstId + 1 > numVisible) { + const visibleIds = visible.ids; + for (let i = 1, ii = lastId - firstId; i < ii; i++) { + const holeId = scrolledDown ? firstId + i : lastId - i; + if (visibleIds.has(holeId)) { + continue; + } + const holeView = views[holeId - 1]; + if (!this.isViewFinished(holeView)) { + return holeView; + } + } + } + let preRenderIndex = scrolledDown ? lastId : firstId - 2; + let preRenderView = views[preRenderIndex]; + if (preRenderView && !this.isViewFinished(preRenderView)) { + return preRenderView; + } + if (preRenderExtra) { + preRenderIndex += scrolledDown ? 1 : -1; + preRenderView = views[preRenderIndex]; + if (preRenderView && !this.isViewFinished(preRenderView)) { + return preRenderView; + } + } + return null; + } + isViewFinished(view) { + return view.renderingState === RenderingStates.FINISHED; + } + renderView(view) { + switch (view.renderingState) { + case RenderingStates.FINISHED: + return false; + case RenderingStates.PAUSED: + this.highestPriorityPage = view.renderingId; + view.resume(); + break; + case RenderingStates.RUNNING: + this.highestPriorityPage = view.renderingId; + break; + case RenderingStates.INITIAL: + this.highestPriorityPage = view.renderingId; + view.draw().finally(() => { + this.renderHighestPriority(); + }).catch(reason => { + if (reason instanceof RenderingCancelledException) { + return; + } + console.error("renderView:", reason); + }); + break; + } + return true; + } +} + +;// ./web/pdf_scripting_manager.js + + +class PDFScriptingManager { + #closeCapability = null; + #destroyCapability = null; + #docProperties = null; + #eventAbortController = null; + #eventBus = null; + #externalServices = null; + #pdfDocument = null; + #pdfViewer = null; + #ready = false; + #scripting = null; + #willPrintCapability = null; + constructor({ + eventBus, + externalServices = null, + docProperties = null + }) { + this.#eventBus = eventBus; + this.#externalServices = externalServices; + this.#docProperties = docProperties; + } + setViewer(pdfViewer) { + this.#pdfViewer = pdfViewer; + } + async setDocument(pdfDocument) { + if (this.#pdfDocument) { + await this.#destroyScripting(); + } + this.#pdfDocument = pdfDocument; + if (!pdfDocument) { + return; + } + const [objects, calculationOrder, docActions] = await Promise.all([pdfDocument.getFieldObjects(), pdfDocument.getCalculationOrderIds(), pdfDocument.getJSActions()]); + if (!objects && !docActions) { + await this.#destroyScripting(); + return; + } + if (pdfDocument !== this.#pdfDocument) { + return; + } + try { + this.#scripting = this.#initScripting(); + } catch (error) { + console.error("setDocument:", error); + await this.#destroyScripting(); + return; + } + const eventBus = this.#eventBus; + this.#eventAbortController = new AbortController(); + const { + signal + } = this.#eventAbortController; + eventBus._on("updatefromsandbox", event => { + if (event?.source === window) { + this.#updateFromSandbox(event.detail); + } + }, { + signal + }); + eventBus._on("dispatcheventinsandbox", event => { + this.#scripting?.dispatchEventInSandbox(event.detail); + }, { + signal + }); + eventBus._on("pagechanging", ({ + pageNumber, + previous + }) => { + if (pageNumber === previous) { + return; + } + this.#dispatchPageClose(previous); + this.#dispatchPageOpen(pageNumber); + }, { + signal + }); + eventBus._on("pagerendered", ({ + pageNumber + }) => { + if (!this._pageOpenPending.has(pageNumber)) { + return; + } + if (pageNumber !== this.#pdfViewer.currentPageNumber) { + return; + } + this.#dispatchPageOpen(pageNumber); + }, { + signal + }); + eventBus._on("pagesdestroy", async () => { + await this.#dispatchPageClose(this.#pdfViewer.currentPageNumber); + await this.#scripting?.dispatchEventInSandbox({ + id: "doc", + name: "WillClose" + }); + this.#closeCapability?.resolve(); + }, { + signal + }); + try { + const docProperties = await this.#docProperties(pdfDocument); + if (pdfDocument !== this.#pdfDocument) { + return; + } + await this.#scripting.createSandbox({ + objects, + calculationOrder, + appInfo: { + platform: navigator.platform, + language: navigator.language + }, + docInfo: { + ...docProperties, + actions: docActions + } + }); + eventBus.dispatch("sandboxcreated", { + source: this + }); + } catch (error) { + console.error("setDocument:", error); + await this.#destroyScripting(); + return; + } + await this.#scripting?.dispatchEventInSandbox({ + id: "doc", + name: "Open" + }); + await this.#dispatchPageOpen(this.#pdfViewer.currentPageNumber, true); + Promise.resolve().then(() => { + if (pdfDocument === this.#pdfDocument) { + this.#ready = true; + } + }); + } + async dispatchWillSave() { + return this.#scripting?.dispatchEventInSandbox({ + id: "doc", + name: "WillSave" + }); + } + async dispatchDidSave() { + return this.#scripting?.dispatchEventInSandbox({ + id: "doc", + name: "DidSave" + }); + } + async dispatchWillPrint() { + if (!this.#scripting) { + return; + } + await this.#willPrintCapability?.promise; + this.#willPrintCapability = Promise.withResolvers(); + try { + await this.#scripting.dispatchEventInSandbox({ + id: "doc", + name: "WillPrint" + }); + } catch (ex) { + this.#willPrintCapability.resolve(); + this.#willPrintCapability = null; + throw ex; + } + await this.#willPrintCapability.promise; + } + async dispatchDidPrint() { + return this.#scripting?.dispatchEventInSandbox({ + id: "doc", + name: "DidPrint" + }); + } + get destroyPromise() { + return this.#destroyCapability?.promise || null; + } + get ready() { + return this.#ready; + } + get _pageOpenPending() { + return shadow(this, "_pageOpenPending", new Set()); + } + get _visitedPages() { + return shadow(this, "_visitedPages", new Map()); + } + async #updateFromSandbox(detail) { + const pdfViewer = this.#pdfViewer; + const isInPresentationMode = pdfViewer.isInPresentationMode || pdfViewer.isChangingPresentationMode; + const { + id, + siblings, + command, + value + } = detail; + if (!id) { + switch (command) { + case "clear": + console.clear(); + break; + case "error": + console.error(value); + break; + case "layout": + if (!isInPresentationMode) { + const modes = apiPageLayoutToViewerModes(value); + pdfViewer.spreadMode = modes.spreadMode; + } + break; + case "page-num": + pdfViewer.currentPageNumber = value + 1; + break; + case "print": + await pdfViewer.pagesPromise; + this.#eventBus.dispatch("print", { + source: this + }); + break; + case "println": + console.log(value); + break; + case "zoom": + if (!isInPresentationMode) { + pdfViewer.currentScaleValue = value; + } + break; + case "SaveAs": + this.#eventBus.dispatch("download", { + source: this + }); + break; + case "FirstPage": + pdfViewer.currentPageNumber = 1; + break; + case "LastPage": + pdfViewer.currentPageNumber = pdfViewer.pagesCount; + break; + case "NextPage": + pdfViewer.nextPage(); + break; + case "PrevPage": + pdfViewer.previousPage(); + break; + case "ZoomViewIn": + if (!isInPresentationMode) { + pdfViewer.increaseScale(); + } + break; + case "ZoomViewOut": + if (!isInPresentationMode) { + pdfViewer.decreaseScale(); + } + break; + case "WillPrintFinished": + this.#willPrintCapability?.resolve(); + this.#willPrintCapability = null; + break; + } + return; + } + if (isInPresentationMode && detail.focus) { + return; + } + delete detail.id; + delete detail.siblings; + const ids = siblings ? [id, ...siblings] : [id]; + for (const elementId of ids) { + const element = document.querySelector(`[data-element-id="${elementId}"]`); + if (element) { + element.dispatchEvent(new CustomEvent("updatefromsandbox", { + detail + })); + } else { + this.#pdfDocument?.annotationStorage.setValue(elementId, detail); + } + } + } + async #dispatchPageOpen(pageNumber, initialize = false) { + const pdfDocument = this.#pdfDocument, + visitedPages = this._visitedPages; + if (initialize) { + this.#closeCapability = Promise.withResolvers(); + } + if (!this.#closeCapability) { + return; + } + const pageView = this.#pdfViewer.getPageView(pageNumber - 1); + if (pageView?.renderingState !== RenderingStates.FINISHED) { + this._pageOpenPending.add(pageNumber); + return; + } + this._pageOpenPending.delete(pageNumber); + const actionsPromise = (async () => { + const actions = await (!visitedPages.has(pageNumber) ? pageView.pdfPage?.getJSActions() : null); + if (pdfDocument !== this.#pdfDocument) { + return; + } + await this.#scripting?.dispatchEventInSandbox({ + id: "page", + name: "PageOpen", + pageNumber, + actions + }); + })(); + visitedPages.set(pageNumber, actionsPromise); + } + async #dispatchPageClose(pageNumber) { + const pdfDocument = this.#pdfDocument, + visitedPages = this._visitedPages; + if (!this.#closeCapability) { + return; + } + if (this._pageOpenPending.has(pageNumber)) { + return; + } + const actionsPromise = visitedPages.get(pageNumber); + if (!actionsPromise) { + return; + } + visitedPages.set(pageNumber, null); + await actionsPromise; + if (pdfDocument !== this.#pdfDocument) { + return; + } + await this.#scripting?.dispatchEventInSandbox({ + id: "page", + name: "PageClose", + pageNumber + }); + } + #initScripting() { + this.#destroyCapability = Promise.withResolvers(); + if (this.#scripting) { + throw new Error("#initScripting: Scripting already exists."); + } + return this.#externalServices.createScripting(); + } + async #destroyScripting() { + if (!this.#scripting) { + this.#pdfDocument = null; + this.#destroyCapability?.resolve(); + return; + } + if (this.#closeCapability) { + await Promise.race([this.#closeCapability.promise, new Promise(resolve => { + setTimeout(resolve, 1000); + })]).catch(() => {}); + this.#closeCapability = null; + } + this.#pdfDocument = null; + try { + await this.#scripting.destroySandbox(); + } catch {} + this.#willPrintCapability?.reject(new Error("Scripting destroyed.")); + this.#willPrintCapability = null; + this.#eventAbortController?.abort(); + this.#eventAbortController = null; + this._pageOpenPending.clear(); + this._visitedPages.clear(); + this.#scripting = null; + this.#ready = false; + this.#destroyCapability?.resolve(); + } +} + +;// ./web/pdf_sidebar.js + +const SIDEBAR_WIDTH_VAR = "--sidebar-width"; +const SIDEBAR_MIN_WIDTH = 200; +const SIDEBAR_RESIZING_CLASS = "sidebarResizing"; +const UI_NOTIFICATION_CLASS = "pdfSidebarNotification"; +class PDFSidebar { + #isRTL = false; + #mouseAC = null; + #outerContainerWidth = null; + #width = null; + constructor({ + elements, + eventBus, + l10n + }) { + this.isOpen = false; + this.active = SidebarView.THUMBS; + this.isInitialViewSet = false; + this.isInitialEventDispatched = false; + this.onToggled = null; + this.onUpdateThumbnails = null; + this.outerContainer = elements.outerContainer; + this.sidebarContainer = elements.sidebarContainer; + this.toggleButton = elements.toggleButton; + this.resizer = elements.resizer; + this.thumbnailButton = elements.thumbnailButton; + this.outlineButton = elements.outlineButton; + this.attachmentsButton = elements.attachmentsButton; + this.layersButton = elements.layersButton; + this.thumbnailView = elements.thumbnailView; + this.outlineView = elements.outlineView; + this.attachmentsView = elements.attachmentsView; + this.layersView = elements.layersView; + this._currentOutlineItemButton = elements.currentOutlineItemButton; + this.eventBus = eventBus; + this.#isRTL = l10n.getDirection() === "rtl"; + this.#addEventListeners(); + } + reset() { + this.isInitialViewSet = false; + this.isInitialEventDispatched = false; + this.#hideUINotification(true); + this.switchView(SidebarView.THUMBS); + this.outlineButton.disabled = false; + this.attachmentsButton.disabled = false; + this.layersButton.disabled = false; + this._currentOutlineItemButton.disabled = true; + } + get visibleView() { + return this.isOpen ? this.active : SidebarView.NONE; + } + setInitialView(view = SidebarView.NONE) { + if (this.isInitialViewSet) { + return; + } + this.isInitialViewSet = true; + if (view === SidebarView.NONE || view === SidebarView.UNKNOWN) { + this.#dispatchEvent(); + return; + } + this.switchView(view, true); + if (!this.isInitialEventDispatched) { + this.#dispatchEvent(); + } + } + switchView(view, forceOpen = false) { + const isViewChanged = view !== this.active; + let forceRendering = false; + switch (view) { + case SidebarView.NONE: + if (this.isOpen) { + this.close(); + } + return; + case SidebarView.THUMBS: + if (this.isOpen && isViewChanged) { + forceRendering = true; + } + break; + case SidebarView.OUTLINE: + if (this.outlineButton.disabled) { + return; + } + break; + case SidebarView.ATTACHMENTS: + if (this.attachmentsButton.disabled) { + return; + } + break; + case SidebarView.LAYERS: + if (this.layersButton.disabled) { + return; + } + break; + default: + console.error(`PDFSidebar.switchView: "${view}" is not a valid view.`); + return; + } + this.active = view; + toggleCheckedBtn(this.thumbnailButton, view === SidebarView.THUMBS, this.thumbnailView); + toggleCheckedBtn(this.outlineButton, view === SidebarView.OUTLINE, this.outlineView); + toggleCheckedBtn(this.attachmentsButton, view === SidebarView.ATTACHMENTS, this.attachmentsView); + toggleCheckedBtn(this.layersButton, view === SidebarView.LAYERS, this.layersView); + if (forceOpen && !this.isOpen) { + this.open(); + return; + } + if (forceRendering) { + this.onUpdateThumbnails(); + this.onToggled(); + } + if (isViewChanged) { + this.#dispatchEvent(); + } + } + open() { + if (this.isOpen) { + return; + } + this.isOpen = true; + toggleExpandedBtn(this.toggleButton, true); + this.outerContainer.classList.add("sidebarMoving", "sidebarOpen"); + if (this.active === SidebarView.THUMBS) { + this.onUpdateThumbnails(); + } + this.onToggled(); + this.#dispatchEvent(); + this.#hideUINotification(); + } + close(evt = null) { + if (!this.isOpen) { + return; + } + this.isOpen = false; + toggleExpandedBtn(this.toggleButton, false); + this.outerContainer.classList.add("sidebarMoving"); + this.outerContainer.classList.remove("sidebarOpen"); + this.onToggled(); + this.#dispatchEvent(); + if (evt?.detail > 0) { + this.toggleButton.blur(); + } + } + toggle(evt = null) { + if (this.isOpen) { + this.close(evt); + } else { + this.open(); + } + } + #dispatchEvent() { + if (this.isInitialViewSet) { + this.isInitialEventDispatched ||= true; + } + this.eventBus.dispatch("sidebarviewchanged", { + source: this, + view: this.visibleView + }); + } + #showUINotification() { + this.toggleButton.setAttribute("data-l10n-id", "pdfjs-toggle-sidebar-notification-button"); + if (!this.isOpen) { + this.toggleButton.classList.add(UI_NOTIFICATION_CLASS); + } + } + #hideUINotification(reset = false) { + if (this.isOpen || reset) { + this.toggleButton.classList.remove(UI_NOTIFICATION_CLASS); + } + if (reset) { + this.toggleButton.setAttribute("data-l10n-id", "pdfjs-toggle-sidebar-button"); + } + } + #addEventListeners() { + const { + eventBus, + outerContainer + } = this; + this.sidebarContainer.addEventListener("transitionend", evt => { + if (evt.target === this.sidebarContainer) { + outerContainer.classList.remove("sidebarMoving"); + eventBus.dispatch("resize", { + source: this + }); + } + }); + this.toggleButton.addEventListener("click", evt => { + this.toggle(evt); + }); + this.thumbnailButton.addEventListener("click", () => { + this.switchView(SidebarView.THUMBS); + }); + this.outlineButton.addEventListener("click", () => { + this.switchView(SidebarView.OUTLINE); + }); + this.outlineButton.addEventListener("dblclick", () => { + eventBus.dispatch("toggleoutlinetree", { + source: this + }); + }); + this.attachmentsButton.addEventListener("click", () => { + this.switchView(SidebarView.ATTACHMENTS); + }); + this.layersButton.addEventListener("click", () => { + this.switchView(SidebarView.LAYERS); + }); + this.layersButton.addEventListener("dblclick", () => { + eventBus.dispatch("resetlayers", { + source: this + }); + }); + this._currentOutlineItemButton.addEventListener("click", () => { + eventBus.dispatch("currentoutlineitem", { + source: this + }); + }); + const onTreeLoaded = (count, button, view) => { + button.disabled = !count; + if (count) { + this.#showUINotification(); + } else if (this.active === view) { + this.switchView(SidebarView.THUMBS); + } + }; + eventBus._on("outlineloaded", evt => { + onTreeLoaded(evt.outlineCount, this.outlineButton, SidebarView.OUTLINE); + evt.currentOutlineItemPromise.then(enabled => { + if (!this.isInitialViewSet) { + return; + } + this._currentOutlineItemButton.disabled = !enabled; + }); + }); + eventBus._on("attachmentsloaded", evt => { + onTreeLoaded(evt.attachmentsCount, this.attachmentsButton, SidebarView.ATTACHMENTS); + }); + eventBus._on("layersloaded", evt => { + onTreeLoaded(evt.layersCount, this.layersButton, SidebarView.LAYERS); + }); + eventBus._on("presentationmodechanged", evt => { + if (evt.state === PresentationModeState.NORMAL && this.visibleView === SidebarView.THUMBS) { + this.onUpdateThumbnails(); + } + }); + this.resizer.addEventListener("mousedown", evt => { + if (evt.button !== 0) { + return; + } + outerContainer.classList.add(SIDEBAR_RESIZING_CLASS); + this.#mouseAC = new AbortController(); + const opts = { + signal: this.#mouseAC.signal + }; + window.addEventListener("mousemove", this.#mouseMove.bind(this), opts); + window.addEventListener("mouseup", this.#mouseUp.bind(this), opts); + window.addEventListener("blur", this.#mouseUp.bind(this), opts); + }); + eventBus._on("resize", evt => { + if (evt.source !== window) { + return; + } + this.#outerContainerWidth = null; + if (!this.#width) { + return; + } + if (!this.isOpen) { + this.#updateWidth(this.#width); + return; + } + outerContainer.classList.add(SIDEBAR_RESIZING_CLASS); + const updated = this.#updateWidth(this.#width); + Promise.resolve().then(() => { + outerContainer.classList.remove(SIDEBAR_RESIZING_CLASS); + if (updated) { + eventBus.dispatch("resize", { + source: this + }); + } + }); + }); + } + get outerContainerWidth() { + return this.#outerContainerWidth ||= this.outerContainer.clientWidth; + } + #updateWidth(width = 0) { + const maxWidth = Math.floor(this.outerContainerWidth / 2); + if (width > maxWidth) { + width = maxWidth; + } + if (width < SIDEBAR_MIN_WIDTH) { + width = SIDEBAR_MIN_WIDTH; + } + if (width === this.#width) { + return false; + } + this.#width = width; + docStyle.setProperty(SIDEBAR_WIDTH_VAR, `${width}px`); + return true; + } + #mouseMove(evt) { + let width = evt.clientX; + if (this.#isRTL) { + width = this.outerContainerWidth - width; + } + this.#updateWidth(width); + } + #mouseUp(evt) { + this.outerContainer.classList.remove(SIDEBAR_RESIZING_CLASS); + this.eventBus.dispatch("resize", { + source: this + }); + this.#mouseAC?.abort(); + this.#mouseAC = null; + } +} + +;// ./web/pdf_thumbnail_view.js + + +const DRAW_UPSCALE_FACTOR = 2; +const MAX_NUM_SCALING_STEPS = 3; +const THUMBNAIL_WIDTH = 98; +class TempImageFactory { + static #tempCanvas = null; + static getCanvas(width, height) { + const tempCanvas = this.#tempCanvas ||= document.createElement("canvas"); + tempCanvas.width = width; + tempCanvas.height = height; + const ctx = tempCanvas.getContext("2d", { + alpha: false + }); + ctx.save(); + ctx.fillStyle = "rgb(255, 255, 255)"; + ctx.fillRect(0, 0, width, height); + ctx.restore(); + return [tempCanvas, tempCanvas.getContext("2d")]; + } + static destroyCanvas() { + const tempCanvas = this.#tempCanvas; + if (tempCanvas) { + tempCanvas.width = 0; + tempCanvas.height = 0; + } + this.#tempCanvas = null; + } +} +class PDFThumbnailView { + constructor({ + container, + eventBus, + id, + defaultViewport, + optionalContentConfigPromise, + linkService, + renderingQueue, + pageColors, + enableHWA + }) { + this.id = id; + this.renderingId = "thumbnail" + id; + this.pageLabel = null; + this.pdfPage = null; + this.rotation = 0; + this.viewport = defaultViewport; + this.pdfPageRotate = defaultViewport.rotation; + this._optionalContentConfigPromise = optionalContentConfigPromise || null; + this.pageColors = pageColors || null; + this.enableHWA = enableHWA || false; + this.eventBus = eventBus; + this.linkService = linkService; + this.renderingQueue = renderingQueue; + this.renderTask = null; + this.renderingState = RenderingStates.INITIAL; + this.resume = null; + const anchor = document.createElement("a"); + anchor.href = linkService.getAnchorUrl("#page=" + id); + anchor.setAttribute("data-l10n-id", "pdfjs-thumb-page-title"); + anchor.setAttribute("data-l10n-args", this.#pageL10nArgs); + anchor.onclick = function () { + linkService.goToPage(id); + return false; + }; + this.anchor = anchor; + const div = document.createElement("div"); + div.className = "thumbnail"; + div.setAttribute("data-page-number", this.id); + this.div = div; + this.#updateDims(); + const img = document.createElement("div"); + img.className = "thumbnailImage"; + this._placeholderImg = img; + div.append(img); + anchor.append(div); + container.append(anchor); + } + #updateDims() { + const { + width, + height + } = this.viewport; + const ratio = width / height; + this.canvasWidth = THUMBNAIL_WIDTH; + this.canvasHeight = this.canvasWidth / ratio | 0; + this.scale = this.canvasWidth / width; + const { + style + } = this.div; + style.setProperty("--thumbnail-width", `${this.canvasWidth}px`); + style.setProperty("--thumbnail-height", `${this.canvasHeight}px`); + } + setPdfPage(pdfPage) { + this.pdfPage = pdfPage; + this.pdfPageRotate = pdfPage.rotate; + const totalRotation = (this.rotation + this.pdfPageRotate) % 360; + this.viewport = pdfPage.getViewport({ + scale: 1, + rotation: totalRotation + }); + this.reset(); + } + reset() { + this.cancelRendering(); + this.renderingState = RenderingStates.INITIAL; + this.div.removeAttribute("data-loaded"); + this.image?.replaceWith(this._placeholderImg); + this.#updateDims(); + if (this.image) { + this.image.removeAttribute("src"); + delete this.image; + } + } + update({ + rotation = null + }) { + if (typeof rotation === "number") { + this.rotation = rotation; + } + const totalRotation = (this.rotation + this.pdfPageRotate) % 360; + this.viewport = this.viewport.clone({ + scale: 1, + rotation: totalRotation + }); + this.reset(); + } + cancelRendering() { + if (this.renderTask) { + this.renderTask.cancel(); + this.renderTask = null; + } + this.resume = null; + } + #getPageDrawContext(upscaleFactor = 1, enableHWA = this.enableHWA) { + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d", { + alpha: false, + willReadFrequently: !enableHWA + }); + const outputScale = new OutputScale(); + canvas.width = upscaleFactor * this.canvasWidth * outputScale.sx | 0; + canvas.height = upscaleFactor * this.canvasHeight * outputScale.sy | 0; + const transform = outputScale.scaled ? [outputScale.sx, 0, 0, outputScale.sy, 0, 0] : null; + return { + ctx, + canvas, + transform + }; + } + #convertCanvasToImage(canvas) { + if (this.renderingState !== RenderingStates.FINISHED) { + throw new Error("#convertCanvasToImage: Rendering has not finished."); + } + const reducedCanvas = this.#reduceImage(canvas); + const image = document.createElement("img"); + image.className = "thumbnailImage"; + image.setAttribute("data-l10n-id", "pdfjs-thumb-page-canvas"); + image.setAttribute("data-l10n-args", this.#pageL10nArgs); + image.src = reducedCanvas.toDataURL(); + this.image = image; + this.div.setAttribute("data-loaded", true); + this._placeholderImg.replaceWith(image); + reducedCanvas.width = 0; + reducedCanvas.height = 0; + } + async #finishRenderTask(renderTask, canvas, error = null) { + if (renderTask === this.renderTask) { + this.renderTask = null; + } + if (error instanceof RenderingCancelledException) { + return; + } + this.renderingState = RenderingStates.FINISHED; + this.#convertCanvasToImage(canvas); + if (error) { + throw error; + } + } + async draw() { + if (this.renderingState !== RenderingStates.INITIAL) { + console.error("Must be in new state before drawing"); + return undefined; + } + const { + pdfPage + } = this; + if (!pdfPage) { + this.renderingState = RenderingStates.FINISHED; + throw new Error("pdfPage is not loaded"); + } + this.renderingState = RenderingStates.RUNNING; + const { + ctx, + canvas, + transform + } = this.#getPageDrawContext(DRAW_UPSCALE_FACTOR); + const drawViewport = this.viewport.clone({ + scale: DRAW_UPSCALE_FACTOR * this.scale + }); + const renderContinueCallback = cont => { + if (!this.renderingQueue.isHighestPriority(this)) { + this.renderingState = RenderingStates.PAUSED; + this.resume = () => { + this.renderingState = RenderingStates.RUNNING; + cont(); + }; + return; + } + cont(); + }; + const renderContext = { + canvasContext: ctx, + transform, + viewport: drawViewport, + optionalContentConfigPromise: this._optionalContentConfigPromise, + pageColors: this.pageColors + }; + const renderTask = this.renderTask = pdfPage.render(renderContext); + renderTask.onContinue = renderContinueCallback; + const resultPromise = renderTask.promise.then(() => this.#finishRenderTask(renderTask, canvas), error => this.#finishRenderTask(renderTask, canvas, error)); + resultPromise.finally(() => { + canvas.width = 0; + canvas.height = 0; + this.eventBus.dispatch("thumbnailrendered", { + source: this, + pageNumber: this.id, + pdfPage: this.pdfPage + }); + }); + return resultPromise; + } + setImage(pageView) { + if (this.renderingState !== RenderingStates.INITIAL) { + return; + } + const { + thumbnailCanvas: canvas, + pdfPage, + scale + } = pageView; + if (!canvas) { + return; + } + if (!this.pdfPage) { + this.setPdfPage(pdfPage); + } + if (scale < this.scale) { + return; + } + this.renderingState = RenderingStates.FINISHED; + this.#convertCanvasToImage(canvas); + } + #reduceImage(img) { + const { + ctx, + canvas + } = this.#getPageDrawContext(1, true); + if (img.width <= 2 * canvas.width) { + ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height); + return canvas; + } + let reducedWidth = canvas.width << MAX_NUM_SCALING_STEPS; + let reducedHeight = canvas.height << MAX_NUM_SCALING_STEPS; + const [reducedImage, reducedImageCtx] = TempImageFactory.getCanvas(reducedWidth, reducedHeight); + while (reducedWidth > img.width || reducedHeight > img.height) { + reducedWidth >>= 1; + reducedHeight >>= 1; + } + reducedImageCtx.drawImage(img, 0, 0, img.width, img.height, 0, 0, reducedWidth, reducedHeight); + while (reducedWidth > 2 * canvas.width) { + reducedImageCtx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, reducedWidth >> 1, reducedHeight >> 1); + reducedWidth >>= 1; + reducedHeight >>= 1; + } + ctx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, canvas.width, canvas.height); + return canvas; + } + get #pageL10nArgs() { + return JSON.stringify({ + page: this.pageLabel ?? this.id + }); + } + setPageLabel(label) { + this.pageLabel = typeof label === "string" ? label : null; + this.anchor.setAttribute("data-l10n-args", this.#pageL10nArgs); + if (this.renderingState !== RenderingStates.FINISHED) { + return; + } + this.image?.setAttribute("data-l10n-args", this.#pageL10nArgs); + } +} + +;// ./web/pdf_thumbnail_viewer.js + + +const THUMBNAIL_SCROLL_MARGIN = -19; +const THUMBNAIL_SELECTED_CLASS = "selected"; +class PDFThumbnailViewer { + constructor({ + container, + eventBus, + linkService, + renderingQueue, + pageColors, + abortSignal, + enableHWA + }) { + this.container = container; + this.eventBus = eventBus; + this.linkService = linkService; + this.renderingQueue = renderingQueue; + this.pageColors = pageColors || null; + this.enableHWA = enableHWA || false; + this.scroll = watchScroll(this.container, this.#scrollUpdated.bind(this), abortSignal); + this.#resetView(); + } + #scrollUpdated() { + this.renderingQueue.renderHighestPriority(); + } + getThumbnail(index) { + return this._thumbnails[index]; + } + #getVisibleThumbs() { + return getVisibleElements({ + scrollEl: this.container, + views: this._thumbnails + }); + } + scrollThumbnailIntoView(pageNumber) { + if (!this.pdfDocument) { + return; + } + const thumbnailView = this._thumbnails[pageNumber - 1]; + if (!thumbnailView) { + console.error('scrollThumbnailIntoView: Invalid "pageNumber" parameter.'); + return; + } + if (pageNumber !== this._currentPageNumber) { + const prevThumbnailView = this._thumbnails[this._currentPageNumber - 1]; + prevThumbnailView.div.classList.remove(THUMBNAIL_SELECTED_CLASS); + thumbnailView.div.classList.add(THUMBNAIL_SELECTED_CLASS); + } + const { + first, + last, + views + } = this.#getVisibleThumbs(); + if (views.length > 0) { + let shouldScroll = false; + if (pageNumber <= first.id || pageNumber >= last.id) { + shouldScroll = true; + } else { + for (const { + id, + percent + } of views) { + if (id !== pageNumber) { + continue; + } + shouldScroll = percent < 100; + break; + } + } + if (shouldScroll) { + scrollIntoView(thumbnailView.div, { + top: THUMBNAIL_SCROLL_MARGIN + }); + } + } + this._currentPageNumber = pageNumber; + } + get pagesRotation() { + return this._pagesRotation; + } + set pagesRotation(rotation) { + if (!isValidRotation(rotation)) { + throw new Error("Invalid thumbnails rotation angle."); + } + if (!this.pdfDocument) { + return; + } + if (this._pagesRotation === rotation) { + return; + } + this._pagesRotation = rotation; + const updateArgs = { + rotation + }; + for (const thumbnail of this._thumbnails) { + thumbnail.update(updateArgs); + } + } + cleanup() { + for (const thumbnail of this._thumbnails) { + if (thumbnail.renderingState !== RenderingStates.FINISHED) { + thumbnail.reset(); + } + } + TempImageFactory.destroyCanvas(); + } + #resetView() { + this._thumbnails = []; + this._currentPageNumber = 1; + this._pageLabels = null; + this._pagesRotation = 0; + this.container.textContent = ""; + } + setDocument(pdfDocument) { + if (this.pdfDocument) { + this.#cancelRendering(); + this.#resetView(); + } + this.pdfDocument = pdfDocument; + if (!pdfDocument) { + return; + } + const firstPagePromise = pdfDocument.getPage(1); + const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({ + intent: "display" + }); + firstPagePromise.then(firstPdfPage => { + const pagesCount = pdfDocument.numPages; + const viewport = firstPdfPage.getViewport({ + scale: 1 + }); + for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) { + const thumbnail = new PDFThumbnailView({ + container: this.container, + eventBus: this.eventBus, + id: pageNum, + defaultViewport: viewport.clone(), + optionalContentConfigPromise, + linkService: this.linkService, + renderingQueue: this.renderingQueue, + pageColors: this.pageColors, + enableHWA: this.enableHWA + }); + this._thumbnails.push(thumbnail); + } + this._thumbnails[0]?.setPdfPage(firstPdfPage); + const thumbnailView = this._thumbnails[this._currentPageNumber - 1]; + thumbnailView.div.classList.add(THUMBNAIL_SELECTED_CLASS); + }).catch(reason => { + console.error("Unable to initialize thumbnail viewer", reason); + }); + } + #cancelRendering() { + for (const thumbnail of this._thumbnails) { + thumbnail.cancelRendering(); + } + } + setPageLabels(labels) { + if (!this.pdfDocument) { + return; + } + if (!labels) { + this._pageLabels = null; + } else if (!(Array.isArray(labels) && this.pdfDocument.numPages === labels.length)) { + this._pageLabels = null; + console.error("PDFThumbnailViewer_setPageLabels: Invalid page labels."); + } else { + this._pageLabels = labels; + } + for (let i = 0, ii = this._thumbnails.length; i < ii; i++) { + this._thumbnails[i].setPageLabel(this._pageLabels?.[i] ?? null); + } + } + async #ensurePdfPageLoaded(thumbView) { + if (thumbView.pdfPage) { + return thumbView.pdfPage; + } + try { + const pdfPage = await this.pdfDocument.getPage(thumbView.id); + if (!thumbView.pdfPage) { + thumbView.setPdfPage(pdfPage); + } + return pdfPage; + } catch (reason) { + console.error("Unable to get page for thumb view", reason); + return null; + } + } + #getScrollAhead(visible) { + if (visible.first?.id === 1) { + return true; + } else if (visible.last?.id === this._thumbnails.length) { + return false; + } + return this.scroll.down; + } + forceRendering() { + const visibleThumbs = this.#getVisibleThumbs(); + const scrollAhead = this.#getScrollAhead(visibleThumbs); + const thumbView = this.renderingQueue.getHighestPriority(visibleThumbs, this._thumbnails, scrollAhead); + if (thumbView) { + this.#ensurePdfPageLoaded(thumbView).then(() => { + this.renderingQueue.renderView(thumbView); + }); + return true; + } + return false; + } +} + +;// ./web/annotation_editor_layer_builder.js + + +class AnnotationEditorLayerBuilder { + #annotationLayer = null; + #drawLayer = null; + #onAppend = null; + #structTreeLayer = null; + #textLayer = null; + #uiManager; + constructor(options) { + this.pdfPage = options.pdfPage; + this.accessibilityManager = options.accessibilityManager; + this.l10n = options.l10n; + this.l10n ||= new genericl10n_GenericL10n(); + this.annotationEditorLayer = null; + this.div = null; + this._cancelled = false; + this.#uiManager = options.uiManager; + this.#annotationLayer = options.annotationLayer || null; + this.#textLayer = options.textLayer || null; + this.#drawLayer = options.drawLayer || null; + this.#onAppend = options.onAppend || null; + this.#structTreeLayer = options.structTreeLayer || null; + } + async render(viewport, intent = "display") { + if (intent !== "display") { + return; + } + if (this._cancelled) { + return; + } + const clonedViewport = viewport.clone({ + dontFlip: true + }); + if (this.div) { + this.annotationEditorLayer.update({ + viewport: clonedViewport + }); + this.show(); + return; + } + const div = this.div = document.createElement("div"); + div.className = "annotationEditorLayer"; + div.hidden = true; + div.dir = this.#uiManager.direction; + this.#onAppend?.(div); + this.annotationEditorLayer = new AnnotationEditorLayer({ + uiManager: this.#uiManager, + div, + structTreeLayer: this.#structTreeLayer, + accessibilityManager: this.accessibilityManager, + pageIndex: this.pdfPage.pageNumber - 1, + l10n: this.l10n, + viewport: clonedViewport, + annotationLayer: this.#annotationLayer, + textLayer: this.#textLayer, + drawLayer: this.#drawLayer + }); + const parameters = { + viewport: clonedViewport, + div, + annotations: null, + intent + }; + this.annotationEditorLayer.render(parameters); + this.show(); + } + cancel() { + this._cancelled = true; + if (!this.div) { + return; + } + this.annotationEditorLayer.destroy(); + } + hide() { + if (!this.div) { + return; + } + this.annotationEditorLayer.pause(true); + this.div.hidden = true; + } + show() { + if (!this.div || this.annotationEditorLayer.isInvisible) { + return; + } + this.div.hidden = false; + this.annotationEditorLayer.pause(false); + } +} + +;// ./web/annotation_layer_builder.js + + +class AnnotationLayerBuilder { + #onAppend = null; + #eventAbortController = null; + constructor({ + pdfPage, + linkService, + downloadManager, + annotationStorage = null, + imageResourcesPath = "", + renderForms = true, + enableScripting = false, + hasJSActionsPromise = null, + fieldObjectsPromise = null, + annotationCanvasMap = null, + accessibilityManager = null, + annotationEditorUIManager = null, + onAppend = null + }) { + this.pdfPage = pdfPage; + this.linkService = linkService; + this.downloadManager = downloadManager; + this.imageResourcesPath = imageResourcesPath; + this.renderForms = renderForms; + this.annotationStorage = annotationStorage; + this.enableScripting = enableScripting; + this._hasJSActionsPromise = hasJSActionsPromise || Promise.resolve(false); + this._fieldObjectsPromise = fieldObjectsPromise || Promise.resolve(null); + this._annotationCanvasMap = annotationCanvasMap; + this._accessibilityManager = accessibilityManager; + this._annotationEditorUIManager = annotationEditorUIManager; + this.#onAppend = onAppend; + this.annotationLayer = null; + this.div = null; + this._cancelled = false; + this._eventBus = linkService.eventBus; + } + async render(viewport, options, intent = "display") { + if (this.div) { + if (this._cancelled || !this.annotationLayer) { + return; + } + this.annotationLayer.update({ + viewport: viewport.clone({ + dontFlip: true + }) + }); + return; + } + const [annotations, hasJSActions, fieldObjects] = await Promise.all([this.pdfPage.getAnnotations({ + intent + }), this._hasJSActionsPromise, this._fieldObjectsPromise]); + if (this._cancelled) { + return; + } + const div = this.div = document.createElement("div"); + div.className = "annotationLayer"; + this.#onAppend?.(div); + if (annotations.length === 0) { + this.hide(); + return; + } + this.annotationLayer = new AnnotationLayer({ + div, + accessibilityManager: this._accessibilityManager, + annotationCanvasMap: this._annotationCanvasMap, + annotationEditorUIManager: this._annotationEditorUIManager, + page: this.pdfPage, + viewport: viewport.clone({ + dontFlip: true + }), + structTreeLayer: options?.structTreeLayer || null + }); + await this.annotationLayer.render({ + annotations, + imageResourcesPath: this.imageResourcesPath, + renderForms: this.renderForms, + linkService: this.linkService, + downloadManager: this.downloadManager, + annotationStorage: this.annotationStorage, + enableScripting: this.enableScripting, + hasJSActions, + fieldObjects + }); + if (this.linkService.isInPresentationMode) { + this.#updatePresentationModeState(PresentationModeState.FULLSCREEN); + } + if (!this.#eventAbortController) { + this.#eventAbortController = new AbortController(); + this._eventBus?._on("presentationmodechanged", evt => { + this.#updatePresentationModeState(evt.state); + }, { + signal: this.#eventAbortController.signal + }); + } + } + cancel() { + this._cancelled = true; + this.#eventAbortController?.abort(); + this.#eventAbortController = null; + } + hide() { + if (!this.div) { + return; + } + this.div.hidden = true; + } + hasEditableAnnotations() { + return !!this.annotationLayer?.hasEditableAnnotations(); + } + #updatePresentationModeState(state) { + if (!this.div) { + return; + } + let disableFormElements = false; + switch (state) { + case PresentationModeState.FULLSCREEN: + disableFormElements = true; + break; + case PresentationModeState.NORMAL: + break; + default: + return; + } + for (const section of this.div.childNodes) { + if (section.hasAttribute("data-internal-link")) { + continue; + } + section.inert = disableFormElements; + } + } +} + +;// ./web/draw_layer_builder.js + +class DrawLayerBuilder { + #drawLayer = null; + constructor(options) { + this.pageIndex = options.pageIndex; + } + async render(intent = "display") { + if (intent !== "display" || this.#drawLayer || this._cancelled) { + return; + } + this.#drawLayer = new DrawLayer({ + pageIndex: this.pageIndex + }); + } + cancel() { + this._cancelled = true; + if (!this.#drawLayer) { + return; + } + this.#drawLayer.destroy(); + this.#drawLayer = null; + } + setParent(parent) { + this.#drawLayer?.setParent(parent); + } + getDrawLayer() { + return this.#drawLayer; + } +} + +;// ./web/struct_tree_layer_builder.js + +const PDF_ROLE_TO_HTML_ROLE = { + Document: null, + DocumentFragment: null, + Part: "group", + Sect: "group", + Div: "group", + Aside: "note", + NonStruct: "none", + P: null, + H: "heading", + Title: null, + FENote: "note", + Sub: "group", + Lbl: null, + Span: null, + Em: null, + Strong: null, + Link: "link", + Annot: "note", + Form: "form", + Ruby: null, + RB: null, + RT: null, + RP: null, + Warichu: null, + WT: null, + WP: null, + L: "list", + LI: "listitem", + LBody: null, + Table: "table", + TR: "row", + TH: "columnheader", + TD: "cell", + THead: "columnheader", + TBody: null, + TFoot: null, + Caption: null, + Figure: "figure", + Formula: null, + Artifact: null +}; +const HEADING_PATTERN = /^H(\d+)$/; +class StructTreeLayerBuilder { + #promise; + #treeDom = null; + #treePromise; + #elementAttributes = new Map(); + #rawDims; + #elementsToAddToTextLayer = null; + constructor(pdfPage, rawDims) { + this.#promise = pdfPage.getStructTree(); + this.#rawDims = rawDims; + } + async render() { + if (this.#treePromise) { + return this.#treePromise; + } + const { + promise, + resolve, + reject + } = Promise.withResolvers(); + this.#treePromise = promise; + try { + this.#treeDom = this.#walk(await this.#promise); + } catch (ex) { + reject(ex); + } + this.#promise = null; + this.#treeDom?.classList.add("structTree"); + resolve(this.#treeDom); + return promise; + } + async getAriaAttributes(annotationId) { + try { + await this.render(); + return this.#elementAttributes.get(annotationId); + } catch {} + return null; + } + hide() { + if (this.#treeDom && !this.#treeDom.hidden) { + this.#treeDom.hidden = true; + } + } + show() { + if (this.#treeDom?.hidden) { + this.#treeDom.hidden = false; + } + } + #setAttributes(structElement, htmlElement) { + const { + alt, + id, + lang + } = structElement; + if (alt !== undefined) { + let added = false; + const label = removeNullCharacters(alt); + for (const child of structElement.children) { + if (child.type === "annotation") { + let attrs = this.#elementAttributes.get(child.id); + if (!attrs) { + attrs = new Map(); + this.#elementAttributes.set(child.id, attrs); + } + attrs.set("aria-label", label); + added = true; + } + } + if (!added) { + htmlElement.setAttribute("aria-label", label); + } + } + if (id !== undefined) { + htmlElement.setAttribute("aria-owns", id); + } + if (lang !== undefined) { + htmlElement.setAttribute("lang", removeNullCharacters(lang, true)); + } + } + #addImageInTextLayer(node, element) { + const { + alt, + bbox, + children + } = node; + const child = children?.[0]; + if (!this.#rawDims || !alt || !bbox || child?.type !== "content") { + return false; + } + const { + id + } = child; + if (!id) { + return false; + } + element.setAttribute("aria-owns", id); + const img = document.createElement("span"); + (this.#elementsToAddToTextLayer ||= new Map()).set(id, img); + img.setAttribute("role", "img"); + img.setAttribute("aria-label", removeNullCharacters(alt)); + const { + pageHeight, + pageX, + pageY + } = this.#rawDims; + const calc = "calc(var(--scale-factor)*"; + const { + style + } = img; + style.width = `${calc}${bbox[2] - bbox[0]}px)`; + style.height = `${calc}${bbox[3] - bbox[1]}px)`; + style.left = `${calc}${bbox[0] - pageX}px)`; + style.top = `${calc}${pageHeight - bbox[3] + pageY}px)`; + return true; + } + addElementsToTextLayer() { + if (!this.#elementsToAddToTextLayer) { + return; + } + for (const [id, img] of this.#elementsToAddToTextLayer) { + document.getElementById(id)?.append(img); + } + this.#elementsToAddToTextLayer.clear(); + this.#elementsToAddToTextLayer = null; + } + #walk(node) { + if (!node) { + return null; + } + const element = document.createElement("span"); + if ("role" in node) { + const { + role + } = node; + const match = role.match(HEADING_PATTERN); + if (match) { + element.setAttribute("role", "heading"); + element.setAttribute("aria-level", match[1]); + } else if (PDF_ROLE_TO_HTML_ROLE[role]) { + element.setAttribute("role", PDF_ROLE_TO_HTML_ROLE[role]); + } + if (role === "Figure" && this.#addImageInTextLayer(node, element)) { + return element; + } + } + this.#setAttributes(node, element); + if (node.children) { + if (node.children.length === 1 && "id" in node.children[0]) { + this.#setAttributes(node.children[0], element); + } else { + for (const kid of node.children) { + element.append(this.#walk(kid)); + } + } + } + return element; + } +} + +;// ./web/text_accessibility.js + +class TextAccessibilityManager { + #enabled = false; + #textChildren = null; + #textNodes = new Map(); + #waitingElements = new Map(); + setTextMapping(textDivs) { + this.#textChildren = textDivs; + } + static #compareElementPositions(e1, e2) { + const rect1 = e1.getBoundingClientRect(); + const rect2 = e2.getBoundingClientRect(); + if (rect1.width === 0 && rect1.height === 0) { + return +1; + } + if (rect2.width === 0 && rect2.height === 0) { + return -1; + } + const top1 = rect1.y; + const bot1 = rect1.y + rect1.height; + const mid1 = rect1.y + rect1.height / 2; + const top2 = rect2.y; + const bot2 = rect2.y + rect2.height; + const mid2 = rect2.y + rect2.height / 2; + if (mid1 <= top2 && mid2 >= bot1) { + return -1; + } + if (mid2 <= top1 && mid1 >= bot2) { + return +1; + } + const centerX1 = rect1.x + rect1.width / 2; + const centerX2 = rect2.x + rect2.width / 2; + return centerX1 - centerX2; + } + enable() { + if (this.#enabled) { + throw new Error("TextAccessibilityManager is already enabled."); + } + if (!this.#textChildren) { + throw new Error("Text divs and strings have not been set."); + } + this.#enabled = true; + this.#textChildren = this.#textChildren.slice(); + this.#textChildren.sort(TextAccessibilityManager.#compareElementPositions); + if (this.#textNodes.size > 0) { + const textChildren = this.#textChildren; + for (const [id, nodeIndex] of this.#textNodes) { + const element = document.getElementById(id); + if (!element) { + this.#textNodes.delete(id); + continue; + } + this.#addIdToAriaOwns(id, textChildren[nodeIndex]); + } + } + for (const [element, isRemovable] of this.#waitingElements) { + this.addPointerInTextLayer(element, isRemovable); + } + this.#waitingElements.clear(); + } + disable() { + if (!this.#enabled) { + return; + } + this.#waitingElements.clear(); + this.#textChildren = null; + this.#enabled = false; + } + removePointerInTextLayer(element) { + if (!this.#enabled) { + this.#waitingElements.delete(element); + return; + } + const children = this.#textChildren; + if (!children || children.length === 0) { + return; + } + const { + id + } = element; + const nodeIndex = this.#textNodes.get(id); + if (nodeIndex === undefined) { + return; + } + const node = children[nodeIndex]; + this.#textNodes.delete(id); + let owns = node.getAttribute("aria-owns"); + if (owns?.includes(id)) { + owns = owns.split(" ").filter(x => x !== id).join(" "); + if (owns) { + node.setAttribute("aria-owns", owns); + } else { + node.removeAttribute("aria-owns"); + node.setAttribute("role", "presentation"); + } + } + } + #addIdToAriaOwns(id, node) { + const owns = node.getAttribute("aria-owns"); + if (!owns?.includes(id)) { + node.setAttribute("aria-owns", owns ? `${owns} ${id}` : id); + } + node.removeAttribute("role"); + } + addPointerInTextLayer(element, isRemovable) { + const { + id + } = element; + if (!id) { + return null; + } + if (!this.#enabled) { + this.#waitingElements.set(element, isRemovable); + return null; + } + if (isRemovable) { + this.removePointerInTextLayer(element); + } + const children = this.#textChildren; + if (!children || children.length === 0) { + return null; + } + const index = binarySearchFirstItem(children, node => TextAccessibilityManager.#compareElementPositions(element, node) < 0); + const nodeIndex = Math.max(0, index - 1); + const child = children[nodeIndex]; + this.#addIdToAriaOwns(id, child); + this.#textNodes.set(id, nodeIndex); + const parent = child.parentNode; + return parent?.classList.contains("markedContent") ? parent.id : null; + } + moveElementInDOM(container, element, contentElement, isRemovable) { + const id = this.addPointerInTextLayer(contentElement, isRemovable); + if (!container.hasChildNodes()) { + container.append(element); + return id; + } + const children = Array.from(container.childNodes).filter(node => node !== element); + if (children.length === 0) { + return id; + } + const elementToCompare = contentElement || element; + const index = binarySearchFirstItem(children, node => TextAccessibilityManager.#compareElementPositions(elementToCompare, node) < 0); + if (index === 0) { + children[0].before(element); + } else { + children[index - 1].after(element); + } + return id; + } +} + +;// ./web/text_highlighter.js +class TextHighlighter { + #eventAbortController = null; + constructor({ + findController, + eventBus, + pageIndex + }) { + this.findController = findController; + this.matches = []; + this.eventBus = eventBus; + this.pageIdx = pageIndex; + this.textDivs = null; + this.textContentItemsStr = null; + this.enabled = false; + } + setTextMapping(divs, texts) { + this.textDivs = divs; + this.textContentItemsStr = texts; + } + enable() { + if (!this.textDivs || !this.textContentItemsStr) { + throw new Error("Text divs and strings have not been set."); + } + if (this.enabled) { + throw new Error("TextHighlighter is already enabled."); + } + this.enabled = true; + if (!this.#eventAbortController) { + this.#eventAbortController = new AbortController(); + this.eventBus._on("updatetextlayermatches", evt => { + if (evt.pageIndex === this.pageIdx || evt.pageIndex === -1) { + this._updateMatches(); + } + }, { + signal: this.#eventAbortController.signal + }); + } + this._updateMatches(); + } + disable() { + if (!this.enabled) { + return; + } + this.enabled = false; + this.#eventAbortController?.abort(); + this.#eventAbortController = null; + this._updateMatches(true); + } + _convertMatches(matches, matchesLength) { + if (!matches) { + return []; + } + const { + textContentItemsStr + } = this; + let i = 0, + iIndex = 0; + const end = textContentItemsStr.length - 1; + const result = []; + for (let m = 0, mm = matches.length; m < mm; m++) { + let matchIdx = matches[m]; + while (i !== end && matchIdx >= iIndex + textContentItemsStr[i].length) { + iIndex += textContentItemsStr[i].length; + i++; + } + if (i === textContentItemsStr.length) { + console.error("Could not find a matching mapping"); + } + const match = { + begin: { + divIdx: i, + offset: matchIdx - iIndex + } + }; + matchIdx += matchesLength[m]; + while (i !== end && matchIdx > iIndex + textContentItemsStr[i].length) { + iIndex += textContentItemsStr[i].length; + i++; + } + match.end = { + divIdx: i, + offset: matchIdx - iIndex + }; + result.push(match); + } + return result; + } + _renderMatches(matches) { + if (matches.length === 0) { + return; + } + const { + findController, + pageIdx + } = this; + const { + textContentItemsStr, + textDivs + } = this; + const isSelectedPage = pageIdx === findController.selected.pageIdx; + const selectedMatchIdx = findController.selected.matchIdx; + const highlightAll = findController.state.highlightAll; + let prevEnd = null; + const infinity = { + divIdx: -1, + offset: undefined + }; + function beginText(begin, className) { + const divIdx = begin.divIdx; + textDivs[divIdx].textContent = ""; + return appendTextToDiv(divIdx, 0, begin.offset, className); + } + function appendTextToDiv(divIdx, fromOffset, toOffset, className) { + let div = textDivs[divIdx]; + if (div.nodeType === Node.TEXT_NODE) { + const span = document.createElement("span"); + div.before(span); + span.append(div); + textDivs[divIdx] = span; + div = span; + } + const content = textContentItemsStr[divIdx].substring(fromOffset, toOffset); + const node = document.createTextNode(content); + if (className) { + const span = document.createElement("span"); + span.className = `${className} appended`; + span.append(node); + div.append(span); + if (className.includes("selected")) { + const { + left + } = span.getClientRects()[0]; + const parentLeft = div.getBoundingClientRect().left; + return left - parentLeft; + } + return 0; + } + div.append(node); + return 0; + } + let i0 = selectedMatchIdx, + i1 = i0 + 1; + if (highlightAll) { + i0 = 0; + i1 = matches.length; + } else if (!isSelectedPage) { + return; + } + let lastDivIdx = -1; + let lastOffset = -1; + for (let i = i0; i < i1; i++) { + const match = matches[i]; + const begin = match.begin; + if (begin.divIdx === lastDivIdx && begin.offset === lastOffset) { + continue; + } + lastDivIdx = begin.divIdx; + lastOffset = begin.offset; + const end = match.end; + const isSelected = isSelectedPage && i === selectedMatchIdx; + const highlightSuffix = isSelected ? " selected" : ""; + let selectedLeft = 0; + if (!prevEnd || begin.divIdx !== prevEnd.divIdx) { + if (prevEnd !== null) { + appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset); + } + beginText(begin); + } else { + appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset); + } + if (begin.divIdx === end.divIdx) { + selectedLeft = appendTextToDiv(begin.divIdx, begin.offset, end.offset, "highlight" + highlightSuffix); + } else { + selectedLeft = appendTextToDiv(begin.divIdx, begin.offset, infinity.offset, "highlight begin" + highlightSuffix); + for (let n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) { + textDivs[n0].className = "highlight middle" + highlightSuffix; + } + beginText(end, "highlight end" + highlightSuffix); + } + prevEnd = end; + if (isSelected) { + findController.scrollMatchIntoView({ + element: textDivs[begin.divIdx], + selectedLeft, + pageIndex: pageIdx, + matchIndex: selectedMatchIdx + }); + } + } + if (prevEnd) { + appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset); + } + } + _updateMatches(reset = false) { + if (!this.enabled && !reset) { + return; + } + const { + findController, + matches, + pageIdx + } = this; + const { + textContentItemsStr, + textDivs + } = this; + let clearedUntilDivIdx = -1; + for (const match of matches) { + const begin = Math.max(clearedUntilDivIdx, match.begin.divIdx); + for (let n = begin, end = match.end.divIdx; n <= end; n++) { + const div = textDivs[n]; + div.textContent = textContentItemsStr[n]; + div.className = ""; + } + clearedUntilDivIdx = match.end.divIdx + 1; + } + if (!findController?.highlightMatches || reset) { + return; + } + const pageMatches = findController.pageMatches[pageIdx] || null; + const pageMatchesLength = findController.pageMatchesLength[pageIdx] || null; + this.matches = this._convertMatches(pageMatches, pageMatchesLength); + this._renderMatches(this.matches); + } +} + +;// ./web/text_layer_builder.js + + +class TextLayerBuilder { + #enablePermissions = false; + #onAppend = null; + #renderingDone = false; + #textLayer = null; + static #textLayers = new Map(); + static #selectionChangeAbortController = null; + constructor({ + pdfPage, + highlighter = null, + accessibilityManager = null, + enablePermissions = false, + onAppend = null + }) { + this.pdfPage = pdfPage; + this.highlighter = highlighter; + this.accessibilityManager = accessibilityManager; + this.#enablePermissions = enablePermissions === true; + this.#onAppend = onAppend; + this.div = document.createElement("div"); + this.div.tabIndex = 0; + this.div.className = "textLayer"; + } + async render(viewport, textContentParams = null) { + if (this.#renderingDone && this.#textLayer) { + this.#textLayer.update({ + viewport, + onBefore: this.hide.bind(this) + }); + this.show(); + return; + } + this.cancel(); + this.#textLayer = new TextLayer({ + textContentSource: this.pdfPage.streamTextContent(textContentParams || { + includeMarkedContent: true, + disableNormalization: true + }), + container: this.div, + viewport + }); + const { + textDivs, + textContentItemsStr + } = this.#textLayer; + this.highlighter?.setTextMapping(textDivs, textContentItemsStr); + this.accessibilityManager?.setTextMapping(textDivs); + await this.#textLayer.render(); + this.#renderingDone = true; + const endOfContent = document.createElement("div"); + endOfContent.className = "endOfContent"; + this.div.append(endOfContent); + this.#bindMouse(endOfContent); + this.#onAppend?.(this.div); + this.highlighter?.enable(); + this.accessibilityManager?.enable(); + } + hide() { + if (!this.div.hidden && this.#renderingDone) { + this.highlighter?.disable(); + this.div.hidden = true; + } + } + show() { + if (this.div.hidden && this.#renderingDone) { + this.div.hidden = false; + this.highlighter?.enable(); + } + } + cancel() { + this.#textLayer?.cancel(); + this.#textLayer = null; + this.highlighter?.disable(); + this.accessibilityManager?.disable(); + TextLayerBuilder.#removeGlobalSelectionListener(this.div); + } + #bindMouse(end) { + const { + div + } = this; + div.addEventListener("mousedown", () => { + div.classList.add("selecting"); + }); + div.addEventListener("copy", event => { + if (!this.#enablePermissions) { + const selection = document.getSelection(); + event.clipboardData.setData("text/plain", removeNullCharacters(normalizeUnicode(selection.toString()))); + } + stopEvent(event); + }); + TextLayerBuilder.#textLayers.set(div, end); + TextLayerBuilder.#enableGlobalSelectionListener(); + } + static #removeGlobalSelectionListener(textLayerDiv) { + this.#textLayers.delete(textLayerDiv); + if (this.#textLayers.size === 0) { + this.#selectionChangeAbortController?.abort(); + this.#selectionChangeAbortController = null; + } + } + static #enableGlobalSelectionListener() { + if (this.#selectionChangeAbortController) { + return; + } + this.#selectionChangeAbortController = new AbortController(); + const { + signal + } = this.#selectionChangeAbortController; + const reset = (end, textLayer) => { + textLayer.append(end); + end.style.width = ""; + end.style.height = ""; + textLayer.classList.remove("selecting"); + }; + let isPointerDown = false; + document.addEventListener("pointerdown", () => { + isPointerDown = true; + }, { + signal + }); + document.addEventListener("pointerup", () => { + isPointerDown = false; + this.#textLayers.forEach(reset); + }, { + signal + }); + window.addEventListener("blur", () => { + isPointerDown = false; + this.#textLayers.forEach(reset); + }, { + signal + }); + document.addEventListener("keyup", () => { + if (!isPointerDown) { + this.#textLayers.forEach(reset); + } + }, { + signal + }); + var isFirefox, prevRange; + document.addEventListener("selectionchange", () => { + const selection = document.getSelection(); + if (selection.rangeCount === 0) { + this.#textLayers.forEach(reset); + return; + } + const activeTextLayers = new Set(); + for (let i = 0; i < selection.rangeCount; i++) { + const range = selection.getRangeAt(i); + for (const textLayerDiv of this.#textLayers.keys()) { + if (!activeTextLayers.has(textLayerDiv) && range.intersectsNode(textLayerDiv)) { + activeTextLayers.add(textLayerDiv); + } + } + } + for (const [textLayerDiv, endDiv] of this.#textLayers) { + if (activeTextLayers.has(textLayerDiv)) { + textLayerDiv.classList.add("selecting"); + } else { + reset(endDiv, textLayerDiv); + } + } + isFirefox ??= getComputedStyle(this.#textLayers.values().next().value).getPropertyValue("-moz-user-select") === "none"; + if (isFirefox) { + return; + } + const range = selection.getRangeAt(0); + const modifyStart = prevRange && (range.compareBoundaryPoints(Range.END_TO_END, prevRange) === 0 || range.compareBoundaryPoints(Range.START_TO_END, prevRange) === 0); + let anchor = modifyStart ? range.startContainer : range.endContainer; + if (anchor.nodeType === Node.TEXT_NODE) { + anchor = anchor.parentNode; + } + const parentTextLayer = anchor.parentElement?.closest(".textLayer"); + const endDiv = this.#textLayers.get(parentTextLayer); + if (endDiv) { + endDiv.style.width = parentTextLayer.style.width; + endDiv.style.height = parentTextLayer.style.height; + anchor.parentElement.insertBefore(endDiv, modifyStart ? anchor : anchor.nextSibling); + } + prevRange = range.cloneRange(); + }, { + signal + }); + } +} + +;// ./web/pdf_page_view.js + + + + + + + + + + + + + +const DEFAULT_LAYER_PROPERTIES = null; +const LAYERS_ORDER = new Map([["canvasWrapper", 0], ["textLayer", 1], ["annotationLayer", 2], ["annotationEditorLayer", 3], ["xfaLayer", 3]]); +class PDFPageView { + #annotationMode = AnnotationMode.ENABLE_FORMS; + #canvasWrapper = null; + #enableHWA = false; + #hasRestrictedScaling = false; + #isEditing = false; + #layerProperties = null; + #loadingId = null; + #originalViewport = null; + #previousRotation = null; + #scaleRoundX = 1; + #scaleRoundY = 1; + #renderError = null; + #renderingState = RenderingStates.INITIAL; + #textLayerMode = TextLayerMode.ENABLE; + #useThumbnailCanvas = { + directDrawing: true, + initialOptionalContent: true, + regularAnnotations: true + }; + #layers = [null, null, null, null]; + constructor(options) { + const container = options.container; + const defaultViewport = options.defaultViewport; + this.id = options.id; + this.renderingId = "page" + this.id; + this.#layerProperties = options.layerProperties || DEFAULT_LAYER_PROPERTIES; + this.pdfPage = null; + this.pageLabel = null; + this.rotation = 0; + this.scale = options.scale || DEFAULT_SCALE; + this.viewport = defaultViewport; + this.pdfPageRotate = defaultViewport.rotation; + this._optionalContentConfigPromise = options.optionalContentConfigPromise || null; + this.#textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE; + this.#annotationMode = options.annotationMode ?? AnnotationMode.ENABLE_FORMS; + this.imageResourcesPath = options.imageResourcesPath || ""; + this.maxCanvasPixels = options.maxCanvasPixels ?? AppOptions.get("maxCanvasPixels"); + this.pageColors = options.pageColors || null; + this.#enableHWA = options.enableHWA || false; + this.eventBus = options.eventBus; + this.renderingQueue = options.renderingQueue; + this.l10n = options.l10n; + this.l10n ||= new genericl10n_GenericL10n(); + this.renderTask = null; + this.resume = null; + this._isStandalone = !this.renderingQueue?.hasViewer(); + this._container = container; + this._annotationCanvasMap = null; + this.annotationLayer = null; + this.annotationEditorLayer = null; + this.textLayer = null; + this.xfaLayer = null; + this.structTreeLayer = null; + this.drawLayer = null; + const div = document.createElement("div"); + div.className = "page"; + div.setAttribute("data-page-number", this.id); + div.setAttribute("role", "region"); + div.setAttribute("data-l10n-id", "pdfjs-page-landmark"); + div.setAttribute("data-l10n-args", JSON.stringify({ + page: this.id + })); + this.div = div; + this.#setDimensions(); + container?.append(div); + if (this._isStandalone) { + container?.style.setProperty("--scale-factor", this.scale * PixelsPerInch.PDF_TO_CSS_UNITS); + if (this.pageColors?.background) { + container?.style.setProperty("--page-bg-color", this.pageColors.background); + } + const { + optionalContentConfigPromise + } = options; + if (optionalContentConfigPromise) { + optionalContentConfigPromise.then(optionalContentConfig => { + if (optionalContentConfigPromise !== this._optionalContentConfigPromise) { + return; + } + this.#useThumbnailCanvas.initialOptionalContent = optionalContentConfig.hasInitialVisibility; + }); + } + if (!options.l10n) { + this.l10n.translate(this.div); + } + } + } + #addLayer(div, name) { + const pos = LAYERS_ORDER.get(name); + const oldDiv = this.#layers[pos]; + this.#layers[pos] = div; + if (oldDiv) { + oldDiv.replaceWith(div); + return; + } + for (let i = pos - 1; i >= 0; i--) { + const layer = this.#layers[i]; + if (layer) { + layer.after(div); + return; + } + } + this.div.prepend(div); + } + get renderingState() { + return this.#renderingState; + } + set renderingState(state) { + if (state === this.#renderingState) { + return; + } + this.#renderingState = state; + if (this.#loadingId) { + clearTimeout(this.#loadingId); + this.#loadingId = null; + } + switch (state) { + case RenderingStates.PAUSED: + this.div.classList.remove("loading"); + break; + case RenderingStates.RUNNING: + this.div.classList.add("loadingIcon"); + this.#loadingId = setTimeout(() => { + this.div.classList.add("loading"); + this.#loadingId = null; + }, 0); + break; + case RenderingStates.INITIAL: + case RenderingStates.FINISHED: + this.div.classList.remove("loadingIcon", "loading"); + break; + } + } + #setDimensions() { + const { + viewport + } = this; + if (this.pdfPage) { + if (this.#previousRotation === viewport.rotation) { + return; + } + this.#previousRotation = viewport.rotation; + } + setLayerDimensions(this.div, viewport, true, false); + } + setPdfPage(pdfPage) { + if (this._isStandalone && (this.pageColors?.foreground === "CanvasText" || this.pageColors?.background === "Canvas")) { + this._container?.style.setProperty("--hcm-highlight-filter", pdfPage.filterFactory.addHighlightHCMFilter("highlight", "CanvasText", "Canvas", "HighlightText", "Highlight")); + this._container?.style.setProperty("--hcm-highlight-selected-filter", pdfPage.filterFactory.addHighlightHCMFilter("highlight_selected", "CanvasText", "Canvas", "HighlightText", "Highlight")); + } + this.pdfPage = pdfPage; + this.pdfPageRotate = pdfPage.rotate; + const totalRotation = (this.rotation + this.pdfPageRotate) % 360; + this.viewport = pdfPage.getViewport({ + scale: this.scale * PixelsPerInch.PDF_TO_CSS_UNITS, + rotation: totalRotation + }); + this.#setDimensions(); + this.reset(); + } + destroy() { + this.reset(); + this.pdfPage?.cleanup(); + } + hasEditableAnnotations() { + return !!this.annotationLayer?.hasEditableAnnotations(); + } + get _textHighlighter() { + return shadow(this, "_textHighlighter", new TextHighlighter({ + pageIndex: this.id - 1, + eventBus: this.eventBus, + findController: this.#layerProperties.findController + })); + } + #dispatchLayerRendered(name, error) { + this.eventBus.dispatch(name, { + source: this, + pageNumber: this.id, + error + }); + } + async #renderAnnotationLayer() { + let error = null; + try { + await this.annotationLayer.render(this.viewport, { + structTreeLayer: this.structTreeLayer + }, "display"); + } catch (ex) { + console.error("#renderAnnotationLayer:", ex); + error = ex; + } finally { + this.#dispatchLayerRendered("annotationlayerrendered", error); + } + } + async #renderAnnotationEditorLayer() { + let error = null; + try { + await this.annotationEditorLayer.render(this.viewport, "display"); + } catch (ex) { + console.error("#renderAnnotationEditorLayer:", ex); + error = ex; + } finally { + this.#dispatchLayerRendered("annotationeditorlayerrendered", error); + } + } + async #renderDrawLayer() { + try { + await this.drawLayer.render("display"); + } catch (ex) { + console.error("#renderDrawLayer:", ex); + } + } + async #renderXfaLayer() { + let error = null; + try { + const result = await this.xfaLayer.render(this.viewport, "display"); + if (result?.textDivs && this._textHighlighter) { + this.#buildXfaTextContentItems(result.textDivs); + } + } catch (ex) { + console.error("#renderXfaLayer:", ex); + error = ex; + } finally { + if (this.xfaLayer?.div) { + this.l10n.pause(); + this.#addLayer(this.xfaLayer.div, "xfaLayer"); + this.l10n.resume(); + } + this.#dispatchLayerRendered("xfalayerrendered", error); + } + } + async #renderTextLayer() { + if (!this.textLayer) { + return; + } + let error = null; + try { + await this.textLayer.render(this.viewport); + } catch (ex) { + if (ex instanceof AbortException) { + return; + } + console.error("#renderTextLayer:", ex); + error = ex; + } + this.#dispatchLayerRendered("textlayerrendered", error); + this.#renderStructTreeLayer(); + } + async #renderStructTreeLayer() { + if (!this.textLayer) { + return; + } + const treeDom = await this.structTreeLayer?.render(); + if (treeDom) { + this.l10n.pause(); + this.structTreeLayer?.addElementsToTextLayer(); + if (this.canvas && treeDom.parentNode !== this.canvas) { + this.canvas.append(treeDom); + } + this.l10n.resume(); + } + this.structTreeLayer?.show(); + } + async #buildXfaTextContentItems(textDivs) { + const text = await this.pdfPage.getTextContent(); + const items = []; + for (const item of text.items) { + items.push(item.str); + } + this._textHighlighter.setTextMapping(textDivs, items); + this._textHighlighter.enable(); + } + #resetCanvas() { + const { + canvas + } = this; + if (!canvas) { + return; + } + canvas.remove(); + canvas.width = canvas.height = 0; + this.canvas = null; + this.#originalViewport = null; + } + reset({ + keepAnnotationLayer = false, + keepAnnotationEditorLayer = false, + keepXfaLayer = false, + keepTextLayer = false, + keepCanvasWrapper = false + } = {}) { + this.cancelRendering({ + keepAnnotationLayer, + keepAnnotationEditorLayer, + keepXfaLayer, + keepTextLayer + }); + this.renderingState = RenderingStates.INITIAL; + const div = this.div; + const childNodes = div.childNodes, + annotationLayerNode = keepAnnotationLayer && this.annotationLayer?.div || null, + annotationEditorLayerNode = keepAnnotationEditorLayer && this.annotationEditorLayer?.div || null, + xfaLayerNode = keepXfaLayer && this.xfaLayer?.div || null, + textLayerNode = keepTextLayer && this.textLayer?.div || null, + canvasWrapperNode = keepCanvasWrapper && this.#canvasWrapper || null; + for (let i = childNodes.length - 1; i >= 0; i--) { + const node = childNodes[i]; + switch (node) { + case annotationLayerNode: + case annotationEditorLayerNode: + case xfaLayerNode: + case textLayerNode: + case canvasWrapperNode: + continue; + } + node.remove(); + const layerIndex = this.#layers.indexOf(node); + if (layerIndex >= 0) { + this.#layers[layerIndex] = null; + } + } + div.removeAttribute("data-loaded"); + if (annotationLayerNode) { + this.annotationLayer.hide(); + } + if (annotationEditorLayerNode) { + this.annotationEditorLayer.hide(); + } + if (xfaLayerNode) { + this.xfaLayer.hide(); + } + if (textLayerNode) { + this.textLayer.hide(); + } + this.structTreeLayer?.hide(); + if (!keepCanvasWrapper && this.#canvasWrapper) { + this.#canvasWrapper = null; + this.#resetCanvas(); + } + } + toggleEditingMode(isEditing) { + if (!this.hasEditableAnnotations()) { + return; + } + this.#isEditing = isEditing; + this.reset({ + keepAnnotationLayer: true, + keepAnnotationEditorLayer: true, + keepXfaLayer: true, + keepTextLayer: true, + keepCanvasWrapper: true + }); + } + update({ + scale = 0, + rotation = null, + optionalContentConfigPromise = null, + drawingDelay = -1 + }) { + this.scale = scale || this.scale; + if (typeof rotation === "number") { + this.rotation = rotation; + } + if (optionalContentConfigPromise instanceof Promise) { + this._optionalContentConfigPromise = optionalContentConfigPromise; + optionalContentConfigPromise.then(optionalContentConfig => { + if (optionalContentConfigPromise !== this._optionalContentConfigPromise) { + return; + } + this.#useThumbnailCanvas.initialOptionalContent = optionalContentConfig.hasInitialVisibility; + }); + } + this.#useThumbnailCanvas.directDrawing = true; + const totalRotation = (this.rotation + this.pdfPageRotate) % 360; + this.viewport = this.viewport.clone({ + scale: this.scale * PixelsPerInch.PDF_TO_CSS_UNITS, + rotation: totalRotation + }); + this.#setDimensions(); + if (this._isStandalone) { + this._container?.style.setProperty("--scale-factor", this.viewport.scale); + } + if (this.canvas) { + let onlyCssZoom = false; + if (this.#hasRestrictedScaling) { + if (this.maxCanvasPixels === 0) { + onlyCssZoom = true; + } else if (this.maxCanvasPixels > 0) { + const { + width, + height + } = this.viewport; + const { + sx, + sy + } = this.outputScale; + onlyCssZoom = (Math.floor(width) * sx | 0) * (Math.floor(height) * sy | 0) > this.maxCanvasPixels; + } + } + const postponeDrawing = drawingDelay >= 0 && drawingDelay < 1000; + if (postponeDrawing || onlyCssZoom) { + if (postponeDrawing && !onlyCssZoom && this.renderingState !== RenderingStates.FINISHED) { + this.cancelRendering({ + keepAnnotationLayer: true, + keepAnnotationEditorLayer: true, + keepXfaLayer: true, + keepTextLayer: true, + cancelExtraDelay: drawingDelay + }); + this.renderingState = RenderingStates.FINISHED; + this.#useThumbnailCanvas.directDrawing = false; + } + this.cssTransform({ + redrawAnnotationLayer: true, + redrawAnnotationEditorLayer: true, + redrawXfaLayer: true, + redrawTextLayer: !postponeDrawing, + hideTextLayer: postponeDrawing + }); + if (postponeDrawing) { + return; + } + this.eventBus.dispatch("pagerendered", { + source: this, + pageNumber: this.id, + cssTransform: true, + timestamp: performance.now(), + error: this.#renderError + }); + return; + } + } + this.cssTransform({}); + this.reset({ + keepAnnotationLayer: true, + keepAnnotationEditorLayer: true, + keepXfaLayer: true, + keepTextLayer: true, + keepCanvasWrapper: true + }); + } + cancelRendering({ + keepAnnotationLayer = false, + keepAnnotationEditorLayer = false, + keepXfaLayer = false, + keepTextLayer = false, + cancelExtraDelay = 0 + } = {}) { + if (this.renderTask) { + this.renderTask.cancel(cancelExtraDelay); + this.renderTask = null; + } + this.resume = null; + if (this.textLayer && (!keepTextLayer || !this.textLayer.div)) { + this.textLayer.cancel(); + this.textLayer = null; + } + if (this.annotationLayer && (!keepAnnotationLayer || !this.annotationLayer.div)) { + this.annotationLayer.cancel(); + this.annotationLayer = null; + this._annotationCanvasMap = null; + } + if (this.structTreeLayer && !this.textLayer) { + this.structTreeLayer = null; + } + if (this.annotationEditorLayer && (!keepAnnotationEditorLayer || !this.annotationEditorLayer.div)) { + if (this.drawLayer) { + this.drawLayer.cancel(); + this.drawLayer = null; + } + this.annotationEditorLayer.cancel(); + this.annotationEditorLayer = null; + } + if (this.xfaLayer && (!keepXfaLayer || !this.xfaLayer.div)) { + this.xfaLayer.cancel(); + this.xfaLayer = null; + this._textHighlighter?.disable(); + } + } + cssTransform({ + redrawAnnotationLayer = false, + redrawAnnotationEditorLayer = false, + redrawXfaLayer = false, + redrawTextLayer = false, + hideTextLayer = false + }) { + const { + canvas + } = this; + if (!canvas) { + return; + } + const originalViewport = this.#originalViewport; + if (this.viewport !== originalViewport) { + const relativeRotation = (360 + this.viewport.rotation - originalViewport.rotation) % 360; + if (relativeRotation === 90 || relativeRotation === 270) { + const { + width, + height + } = this.viewport; + const scaleX = height / width; + const scaleY = width / height; + canvas.style.transform = `rotate(${relativeRotation}deg) scale(${scaleX},${scaleY})`; + } else { + canvas.style.transform = relativeRotation === 0 ? "" : `rotate(${relativeRotation}deg)`; + } + } + if (redrawAnnotationLayer && this.annotationLayer) { + this.#renderAnnotationLayer(); + } + if (redrawAnnotationEditorLayer && this.annotationEditorLayer) { + if (this.drawLayer) { + this.#renderDrawLayer(); + } + this.#renderAnnotationEditorLayer(); + } + if (redrawXfaLayer && this.xfaLayer) { + this.#renderXfaLayer(); + } + if (this.textLayer) { + if (hideTextLayer) { + this.textLayer.hide(); + this.structTreeLayer?.hide(); + } else if (redrawTextLayer) { + this.#renderTextLayer(); + } + } + } + get width() { + return this.viewport.width; + } + get height() { + return this.viewport.height; + } + getPagePoint(x, y) { + return this.viewport.convertToPdfPoint(x, y); + } + async #finishRenderTask(renderTask, error = null) { + if (renderTask === this.renderTask) { + this.renderTask = null; + } + if (error instanceof RenderingCancelledException) { + this.#renderError = null; + return; + } + this.#renderError = error; + this.renderingState = RenderingStates.FINISHED; + this.#useThumbnailCanvas.regularAnnotations = !renderTask.separateAnnots; + this.eventBus.dispatch("pagerendered", { + source: this, + pageNumber: this.id, + cssTransform: false, + timestamp: performance.now(), + error: this.#renderError + }); + if (error) { + throw error; + } + } + async draw() { + if (this.renderingState !== RenderingStates.INITIAL) { + console.error("Must be in new state before drawing"); + this.reset(); + } + const { + div, + l10n, + pageColors, + pdfPage, + viewport + } = this; + if (!pdfPage) { + this.renderingState = RenderingStates.FINISHED; + throw new Error("pdfPage is not loaded"); + } + this.renderingState = RenderingStates.RUNNING; + let canvasWrapper = this.#canvasWrapper; + if (!canvasWrapper) { + canvasWrapper = this.#canvasWrapper = document.createElement("div"); + canvasWrapper.classList.add("canvasWrapper"); + this.#addLayer(canvasWrapper, "canvasWrapper"); + } + if (!this.textLayer && this.#textLayerMode !== TextLayerMode.DISABLE && !pdfPage.isPureXfa) { + this._accessibilityManager ||= new TextAccessibilityManager(); + this.textLayer = new TextLayerBuilder({ + pdfPage, + highlighter: this._textHighlighter, + accessibilityManager: this._accessibilityManager, + enablePermissions: this.#textLayerMode === TextLayerMode.ENABLE_PERMISSIONS, + onAppend: textLayerDiv => { + this.l10n.pause(); + this.#addLayer(textLayerDiv, "textLayer"); + this.l10n.resume(); + } + }); + } + if (!this.annotationLayer && this.#annotationMode !== AnnotationMode.DISABLE) { + const { + annotationStorage, + annotationEditorUIManager, + downloadManager, + enableScripting, + fieldObjectsPromise, + hasJSActionsPromise, + linkService + } = this.#layerProperties; + this._annotationCanvasMap ||= new Map(); + this.annotationLayer = new AnnotationLayerBuilder({ + pdfPage, + annotationStorage, + imageResourcesPath: this.imageResourcesPath, + renderForms: this.#annotationMode === AnnotationMode.ENABLE_FORMS, + linkService, + downloadManager, + enableScripting, + hasJSActionsPromise, + fieldObjectsPromise, + annotationCanvasMap: this._annotationCanvasMap, + accessibilityManager: this._accessibilityManager, + annotationEditorUIManager, + onAppend: annotationLayerDiv => { + this.#addLayer(annotationLayerDiv, "annotationLayer"); + } + }); + } + const renderContinueCallback = cont => { + showCanvas?.(false); + if (this.renderingQueue && !this.renderingQueue.isHighestPriority(this)) { + this.renderingState = RenderingStates.PAUSED; + this.resume = () => { + this.renderingState = RenderingStates.RUNNING; + cont(); + }; + return; + } + cont(); + }; + const { + width, + height + } = viewport; + const canvas = document.createElement("canvas"); + canvas.setAttribute("role", "presentation"); + const hasHCM = !!(pageColors?.background && pageColors?.foreground); + const prevCanvas = this.canvas; + const updateOnFirstShow = !prevCanvas && !hasHCM; + this.canvas = canvas; + this.#originalViewport = viewport; + let showCanvas = isLastShow => { + if (updateOnFirstShow) { + canvasWrapper.prepend(canvas); + showCanvas = null; + return; + } + if (!isLastShow) { + return; + } + if (prevCanvas) { + prevCanvas.replaceWith(canvas); + prevCanvas.width = prevCanvas.height = 0; + } else { + canvasWrapper.prepend(canvas); + } + showCanvas = null; + }; + const ctx = canvas.getContext("2d", { + alpha: false, + willReadFrequently: !this.#enableHWA + }); + const outputScale = this.outputScale = new OutputScale(); + if (this.maxCanvasPixels === 0) { + const invScale = 1 / this.scale; + outputScale.sx *= invScale; + outputScale.sy *= invScale; + this.#hasRestrictedScaling = true; + } else if (this.maxCanvasPixels > 0) { + const pixelsInViewport = width * height; + const maxScale = Math.sqrt(this.maxCanvasPixels / pixelsInViewport); + if (outputScale.sx > maxScale || outputScale.sy > maxScale) { + outputScale.sx = maxScale; + outputScale.sy = maxScale; + this.#hasRestrictedScaling = true; + } else { + this.#hasRestrictedScaling = false; + } + } + const sfx = approximateFraction(outputScale.sx); + const sfy = approximateFraction(outputScale.sy); + const canvasWidth = canvas.width = floorToDivide(calcRound(width * outputScale.sx), sfx[0]); + const canvasHeight = canvas.height = floorToDivide(calcRound(height * outputScale.sy), sfy[0]); + const pageWidth = floorToDivide(calcRound(width), sfx[1]); + const pageHeight = floorToDivide(calcRound(height), sfy[1]); + outputScale.sx = canvasWidth / pageWidth; + outputScale.sy = canvasHeight / pageHeight; + if (this.#scaleRoundX !== sfx[1]) { + div.style.setProperty("--scale-round-x", `${sfx[1]}px`); + this.#scaleRoundX = sfx[1]; + } + if (this.#scaleRoundY !== sfy[1]) { + div.style.setProperty("--scale-round-y", `${sfy[1]}px`); + this.#scaleRoundY = sfy[1]; + } + const transform = outputScale.scaled ? [outputScale.sx, 0, 0, outputScale.sy, 0, 0] : null; + const renderContext = { + canvasContext: ctx, + transform, + viewport, + annotationMode: this.#annotationMode, + optionalContentConfigPromise: this._optionalContentConfigPromise, + annotationCanvasMap: this._annotationCanvasMap, + pageColors, + isEditing: this.#isEditing + }; + const renderTask = this.renderTask = pdfPage.render(renderContext); + renderTask.onContinue = renderContinueCallback; + const resultPromise = renderTask.promise.then(async () => { + showCanvas?.(true); + await this.#finishRenderTask(renderTask); + this.structTreeLayer ||= new StructTreeLayerBuilder(pdfPage, viewport.rawDims); + this.#renderTextLayer(); + if (this.annotationLayer) { + await this.#renderAnnotationLayer(); + } + const { + annotationEditorUIManager + } = this.#layerProperties; + if (!annotationEditorUIManager) { + return; + } + this.drawLayer ||= new DrawLayerBuilder({ + pageIndex: this.id + }); + await this.#renderDrawLayer(); + this.drawLayer.setParent(canvasWrapper); + this.annotationEditorLayer ||= new AnnotationEditorLayerBuilder({ + uiManager: annotationEditorUIManager, + pdfPage, + l10n, + structTreeLayer: this.structTreeLayer, + accessibilityManager: this._accessibilityManager, + annotationLayer: this.annotationLayer?.annotationLayer, + textLayer: this.textLayer, + drawLayer: this.drawLayer.getDrawLayer(), + onAppend: annotationEditorLayerDiv => { + this.#addLayer(annotationEditorLayerDiv, "annotationEditorLayer"); + } + }); + this.#renderAnnotationEditorLayer(); + }, error => { + if (!(error instanceof RenderingCancelledException)) { + showCanvas?.(true); + } else { + prevCanvas?.remove(); + this.#resetCanvas(); + } + return this.#finishRenderTask(renderTask, error); + }); + if (pdfPage.isPureXfa) { + if (!this.xfaLayer) { + const { + annotationStorage, + linkService + } = this.#layerProperties; + this.xfaLayer = new XfaLayerBuilder({ + pdfPage, + annotationStorage, + linkService + }); + } + this.#renderXfaLayer(); + } + div.setAttribute("data-loaded", true); + this.eventBus.dispatch("pagerender", { + source: this, + pageNumber: this.id + }); + return resultPromise; + } + setPageLabel(label) { + this.pageLabel = typeof label === "string" ? label : null; + this.div.setAttribute("data-l10n-args", JSON.stringify({ + page: this.pageLabel ?? this.id + })); + if (this.pageLabel !== null) { + this.div.setAttribute("data-page-label", this.pageLabel); + } else { + this.div.removeAttribute("data-page-label"); + } + } + get thumbnailCanvas() { + const { + directDrawing, + initialOptionalContent, + regularAnnotations + } = this.#useThumbnailCanvas; + return directDrawing && initialOptionalContent && regularAnnotations ? this.canvas : null; + } +} + +;// ./web/pdf_viewer.js + + + + + + +const DEFAULT_CACHE_SIZE = 10; +const PagesCountLimit = { + FORCE_SCROLL_MODE_PAGE: 10000, + FORCE_LAZY_PAGE_INIT: 5000, + PAUSE_EAGER_PAGE_INIT: 250 +}; +function isValidAnnotationEditorMode(mode) { + return Object.values(AnnotationEditorType).includes(mode) && mode !== AnnotationEditorType.DISABLE; +} +class PDFPageViewBuffer { + #buf = new Set(); + #size = 0; + constructor(size) { + this.#size = size; + } + push(view) { + const buf = this.#buf; + if (buf.has(view)) { + buf.delete(view); + } + buf.add(view); + if (buf.size > this.#size) { + this.#destroyFirstView(); + } + } + resize(newSize, idsToKeep = null) { + this.#size = newSize; + const buf = this.#buf; + if (idsToKeep) { + const ii = buf.size; + let i = 1; + for (const view of buf) { + if (idsToKeep.has(view.id)) { + buf.delete(view); + buf.add(view); + } + if (++i > ii) { + break; + } + } + } + while (buf.size > this.#size) { + this.#destroyFirstView(); + } + } + has(view) { + return this.#buf.has(view); + } + [Symbol.iterator]() { + return this.#buf.keys(); + } + #destroyFirstView() { + const firstView = this.#buf.keys().next().value; + firstView?.destroy(); + this.#buf.delete(firstView); + } +} +class PDFViewer { + #buffer = null; + #altTextManager = null; + #annotationEditorHighlightColors = null; + #annotationEditorMode = AnnotationEditorType.NONE; + #annotationEditorUIManager = null; + #annotationMode = AnnotationMode.ENABLE_FORMS; + #containerTopLeft = null; + #editorUndoBar = null; + #enableHWA = false; + #enableHighlightFloatingButton = false; + #enablePermissions = false; + #enableUpdatedAddImage = false; + #enableNewAltTextWhenAddingImage = false; + #eventAbortController = null; + #mlManager = null; + #switchAnnotationEditorModeAC = null; + #switchAnnotationEditorModeTimeoutId = null; + #getAllTextInProgress = false; + #hiddenCopyElement = null; + #interruptCopyCondition = false; + #previousContainerHeight = 0; + #resizeObserver = new ResizeObserver(this.#resizeObserverCallback.bind(this)); + #scrollModePageState = null; + #scaleTimeoutId = null; + #supportsPinchToZoom = true; + #textLayerMode = TextLayerMode.ENABLE; + constructor(options) { + const viewerVersion = "4.10.38"; + if (version !== viewerVersion) { + throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`); + } + this.container = options.container; + this.viewer = options.viewer || options.container.firstElementChild; + if (this.container?.tagName !== "DIV" || this.viewer?.tagName !== "DIV") { + throw new Error("Invalid `container` and/or `viewer` option."); + } + if (this.container.offsetParent && getComputedStyle(this.container).position !== "absolute") { + throw new Error("The `container` must be absolutely positioned."); + } + this.#resizeObserver.observe(this.container); + this.eventBus = options.eventBus; + this.linkService = options.linkService || new SimpleLinkService(); + this.downloadManager = options.downloadManager || null; + this.findController = options.findController || null; + this.#altTextManager = options.altTextManager || null; + this.#editorUndoBar = options.editorUndoBar || null; + if (this.findController) { + this.findController.onIsPageVisible = pageNumber => this._getVisiblePages().ids.has(pageNumber); + } + this._scriptingManager = options.scriptingManager || null; + this.#textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE; + this.#annotationMode = options.annotationMode ?? AnnotationMode.ENABLE_FORMS; + this.#annotationEditorMode = options.annotationEditorMode ?? AnnotationEditorType.NONE; + this.#annotationEditorHighlightColors = options.annotationEditorHighlightColors || null; + this.#enableHighlightFloatingButton = options.enableHighlightFloatingButton === true; + this.#enableUpdatedAddImage = options.enableUpdatedAddImage === true; + this.#enableNewAltTextWhenAddingImage = options.enableNewAltTextWhenAddingImage === true; + this.imageResourcesPath = options.imageResourcesPath || ""; + this.enablePrintAutoRotate = options.enablePrintAutoRotate || false; + this.removePageBorders = options.removePageBorders || false; + this.maxCanvasPixels = options.maxCanvasPixels; + this.l10n = options.l10n; + this.l10n ||= new genericl10n_GenericL10n(); + this.#enablePermissions = options.enablePermissions || false; + this.pageColors = options.pageColors || null; + this.#mlManager = options.mlManager || null; + this.#enableHWA = options.enableHWA || false; + this.#supportsPinchToZoom = options.supportsPinchToZoom !== false; + this.defaultRenderingQueue = !options.renderingQueue; + if (this.defaultRenderingQueue) { + this.renderingQueue = new PDFRenderingQueue(); + this.renderingQueue.setViewer(this); + } else { + this.renderingQueue = options.renderingQueue; + } + const { + abortSignal + } = options; + abortSignal?.addEventListener("abort", () => { + this.#resizeObserver.disconnect(); + this.#resizeObserver = null; + }, { + once: true + }); + this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this), abortSignal); + this.presentationModeState = PresentationModeState.UNKNOWN; + this._resetView(); + if (this.removePageBorders) { + this.viewer.classList.add("removePageBorders"); + } + this.#updateContainerHeightCss(); + this.eventBus._on("thumbnailrendered", ({ + pageNumber, + pdfPage + }) => { + const pageView = this._pages[pageNumber - 1]; + if (!this.#buffer.has(pageView)) { + pdfPage?.cleanup(); + } + }); + if (!options.l10n) { + this.l10n.translate(this.container); + } + } + get pagesCount() { + return this._pages.length; + } + getPageView(index) { + return this._pages[index]; + } + getCachedPageViews() { + return new Set(this.#buffer); + } + get pageViewsReady() { + return this._pages.every(pageView => pageView?.pdfPage); + } + get renderForms() { + return this.#annotationMode === AnnotationMode.ENABLE_FORMS; + } + get enableScripting() { + return !!this._scriptingManager; + } + get currentPageNumber() { + return this._currentPageNumber; + } + set currentPageNumber(val) { + if (!Number.isInteger(val)) { + throw new Error("Invalid page number."); + } + if (!this.pdfDocument) { + return; + } + if (!this._setCurrentPageNumber(val, true)) { + console.error(`currentPageNumber: "${val}" is not a valid page.`); + } + } + _setCurrentPageNumber(val, resetCurrentPageView = false) { + if (this._currentPageNumber === val) { + if (resetCurrentPageView) { + this.#resetCurrentPageView(); + } + return true; + } + if (!(0 < val && val <= this.pagesCount)) { + return false; + } + const previous = this._currentPageNumber; + this._currentPageNumber = val; + this.eventBus.dispatch("pagechanging", { + source: this, + pageNumber: val, + pageLabel: this._pageLabels?.[val - 1] ?? null, + previous + }); + if (resetCurrentPageView) { + this.#resetCurrentPageView(); + } + return true; + } + get currentPageLabel() { + return this._pageLabels?.[this._currentPageNumber - 1] ?? null; + } + set currentPageLabel(val) { + if (!this.pdfDocument) { + return; + } + let page = val | 0; + if (this._pageLabels) { + const i = this._pageLabels.indexOf(val); + if (i >= 0) { + page = i + 1; + } + } + if (!this._setCurrentPageNumber(page, true)) { + console.error(`currentPageLabel: "${val}" is not a valid page.`); + } + } + get currentScale() { + return this._currentScale !== UNKNOWN_SCALE ? this._currentScale : DEFAULT_SCALE; + } + set currentScale(val) { + if (isNaN(val)) { + throw new Error("Invalid numeric scale."); + } + if (!this.pdfDocument) { + return; + } + this.#setScale(val, { + noScroll: false + }); + } + get currentScaleValue() { + return this._currentScaleValue; + } + set currentScaleValue(val) { + if (!this.pdfDocument) { + return; + } + this.#setScale(val, { + noScroll: false + }); + } + get pagesRotation() { + return this._pagesRotation; + } + set pagesRotation(rotation) { + if (!isValidRotation(rotation)) { + throw new Error("Invalid pages rotation angle."); + } + if (!this.pdfDocument) { + return; + } + rotation %= 360; + if (rotation < 0) { + rotation += 360; + } + if (this._pagesRotation === rotation) { + return; + } + this._pagesRotation = rotation; + const pageNumber = this._currentPageNumber; + this.refresh(true, { + rotation + }); + if (this._currentScaleValue) { + this.#setScale(this._currentScaleValue, { + noScroll: true + }); + } + this.eventBus.dispatch("rotationchanging", { + source: this, + pagesRotation: rotation, + pageNumber + }); + if (this.defaultRenderingQueue) { + this.update(); + } + } + get firstPagePromise() { + return this.pdfDocument ? this._firstPageCapability.promise : null; + } + get onePageRendered() { + return this.pdfDocument ? this._onePageRenderedCapability.promise : null; + } + get pagesPromise() { + return this.pdfDocument ? this._pagesCapability.promise : null; + } + get _layerProperties() { + const self = this; + return shadow(this, "_layerProperties", { + get annotationEditorUIManager() { + return self.#annotationEditorUIManager; + }, + get annotationStorage() { + return self.pdfDocument?.annotationStorage; + }, + get downloadManager() { + return self.downloadManager; + }, + get enableScripting() { + return !!self._scriptingManager; + }, + get fieldObjectsPromise() { + return self.pdfDocument?.getFieldObjects(); + }, + get findController() { + return self.findController; + }, + get hasJSActionsPromise() { + return self.pdfDocument?.hasJSActions(); + }, + get linkService() { + return self.linkService; + } + }); + } + #initializePermissions(permissions) { + const params = { + annotationEditorMode: this.#annotationEditorMode, + annotationMode: this.#annotationMode, + textLayerMode: this.#textLayerMode + }; + if (!permissions) { + return params; + } + if (!permissions.includes(PermissionFlag.COPY) && this.#textLayerMode === TextLayerMode.ENABLE) { + params.textLayerMode = TextLayerMode.ENABLE_PERMISSIONS; + } + if (!permissions.includes(PermissionFlag.MODIFY_CONTENTS)) { + params.annotationEditorMode = AnnotationEditorType.DISABLE; + } + if (!permissions.includes(PermissionFlag.MODIFY_ANNOTATIONS) && !permissions.includes(PermissionFlag.FILL_INTERACTIVE_FORMS) && this.#annotationMode === AnnotationMode.ENABLE_FORMS) { + params.annotationMode = AnnotationMode.ENABLE; + } + return params; + } + async #onePageRenderedOrForceFetch(signal) { + if (document.visibilityState === "hidden" || !this.container.offsetParent || this._getVisiblePages().views.length === 0) { + return; + } + const hiddenCapability = Promise.withResolvers(), + ac = new AbortController(); + document.addEventListener("visibilitychange", () => { + if (document.visibilityState === "hidden") { + hiddenCapability.resolve(); + } + }, { + signal: typeof AbortSignal.any === "function" ? AbortSignal.any([signal, ac.signal]) : signal + }); + await Promise.race([this._onePageRenderedCapability.promise, hiddenCapability.promise]); + ac.abort(); + } + async getAllText() { + const texts = []; + const buffer = []; + for (let pageNum = 1, pagesCount = this.pdfDocument.numPages; pageNum <= pagesCount; ++pageNum) { + if (this.#interruptCopyCondition) { + return null; + } + buffer.length = 0; + const page = await this.pdfDocument.getPage(pageNum); + const { + items + } = await page.getTextContent(); + for (const item of items) { + if (item.str) { + buffer.push(item.str); + } + if (item.hasEOL) { + buffer.push("\n"); + } + } + texts.push(removeNullCharacters(buffer.join(""))); + } + return texts.join("\n"); + } + #copyCallback(textLayerMode, event) { + const selection = document.getSelection(); + const { + focusNode, + anchorNode + } = selection; + if (anchorNode && focusNode && selection.containsNode(this.#hiddenCopyElement)) { + if (this.#getAllTextInProgress || textLayerMode === TextLayerMode.ENABLE_PERMISSIONS) { + stopEvent(event); + return; + } + this.#getAllTextInProgress = true; + const { + classList + } = this.viewer; + classList.add("copyAll"); + const ac = new AbortController(); + window.addEventListener("keydown", ev => this.#interruptCopyCondition = ev.key === "Escape", { + signal: ac.signal + }); + this.getAllText().then(async text => { + if (text !== null) { + await navigator.clipboard.writeText(text); + } + }).catch(reason => { + console.warn(`Something goes wrong when extracting the text: ${reason.message}`); + }).finally(() => { + this.#getAllTextInProgress = false; + this.#interruptCopyCondition = false; + ac.abort(); + classList.remove("copyAll"); + }); + stopEvent(event); + } + } + setDocument(pdfDocument) { + if (this.pdfDocument) { + this.eventBus.dispatch("pagesdestroy", { + source: this + }); + this._cancelRendering(); + this._resetView(); + this.findController?.setDocument(null); + this._scriptingManager?.setDocument(null); + this.#annotationEditorUIManager?.destroy(); + this.#annotationEditorUIManager = null; + } + this.pdfDocument = pdfDocument; + if (!pdfDocument) { + return; + } + const pagesCount = pdfDocument.numPages; + const firstPagePromise = pdfDocument.getPage(1); + const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({ + intent: "display" + }); + const permissionsPromise = this.#enablePermissions ? pdfDocument.getPermissions() : Promise.resolve(); + const { + eventBus, + pageColors, + viewer + } = this; + this.#eventAbortController = new AbortController(); + const { + signal + } = this.#eventAbortController; + if (pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE) { + console.warn("Forcing PAGE-scrolling for performance reasons, given the length of the document."); + const mode = this._scrollMode = ScrollMode.PAGE; + eventBus.dispatch("scrollmodechanged", { + source: this, + mode + }); + } + this._pagesCapability.promise.then(() => { + eventBus.dispatch("pagesloaded", { + source: this, + pagesCount + }); + }, () => {}); + const onBeforeDraw = evt => { + const pageView = this._pages[evt.pageNumber - 1]; + if (!pageView) { + return; + } + this.#buffer.push(pageView); + }; + eventBus._on("pagerender", onBeforeDraw, { + signal + }); + const onAfterDraw = evt => { + if (evt.cssTransform) { + return; + } + this._onePageRenderedCapability.resolve({ + timestamp: evt.timestamp + }); + eventBus._off("pagerendered", onAfterDraw); + }; + eventBus._on("pagerendered", onAfterDraw, { + signal + }); + Promise.all([firstPagePromise, permissionsPromise]).then(([firstPdfPage, permissions]) => { + if (pdfDocument !== this.pdfDocument) { + return; + } + this._firstPageCapability.resolve(firstPdfPage); + this._optionalContentConfigPromise = optionalContentConfigPromise; + const { + annotationEditorMode, + annotationMode, + textLayerMode + } = this.#initializePermissions(permissions); + if (textLayerMode !== TextLayerMode.DISABLE) { + const element = this.#hiddenCopyElement = document.createElement("div"); + element.id = "hiddenCopyElement"; + viewer.before(element); + } + if (typeof AbortSignal.any === "function" && annotationEditorMode !== AnnotationEditorType.DISABLE) { + const mode = annotationEditorMode; + if (pdfDocument.isPureXfa) { + console.warn("Warning: XFA-editing is not implemented."); + } else if (isValidAnnotationEditorMode(mode)) { + this.#annotationEditorUIManager = new AnnotationEditorUIManager(this.container, viewer, this.#altTextManager, eventBus, pdfDocument, pageColors, this.#annotationEditorHighlightColors, this.#enableHighlightFloatingButton, this.#enableUpdatedAddImage, this.#enableNewAltTextWhenAddingImage, this.#mlManager, this.#editorUndoBar, this.#supportsPinchToZoom); + eventBus.dispatch("annotationeditoruimanager", { + source: this, + uiManager: this.#annotationEditorUIManager + }); + if (mode !== AnnotationEditorType.NONE) { + if (mode === AnnotationEditorType.STAMP) { + this.#mlManager?.loadModel("altText"); + } + this.#annotationEditorUIManager.updateMode(mode); + } + } else { + console.error(`Invalid AnnotationEditor mode: ${mode}`); + } + } + const viewerElement = this._scrollMode === ScrollMode.PAGE ? null : viewer; + const scale = this.currentScale; + const viewport = firstPdfPage.getViewport({ + scale: scale * PixelsPerInch.PDF_TO_CSS_UNITS + }); + viewer.style.setProperty("--scale-factor", viewport.scale); + if (pageColors?.background) { + viewer.style.setProperty("--page-bg-color", pageColors.background); + } + if (pageColors?.foreground === "CanvasText" || pageColors?.background === "Canvas") { + viewer.style.setProperty("--hcm-highlight-filter", pdfDocument.filterFactory.addHighlightHCMFilter("highlight", "CanvasText", "Canvas", "HighlightText", "Highlight")); + viewer.style.setProperty("--hcm-highlight-selected-filter", pdfDocument.filterFactory.addHighlightHCMFilter("highlight_selected", "CanvasText", "Canvas", "HighlightText", "ButtonText")); + } + for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) { + const pageView = new PDFPageView({ + container: viewerElement, + eventBus, + id: pageNum, + scale, + defaultViewport: viewport.clone(), + optionalContentConfigPromise, + renderingQueue: this.renderingQueue, + textLayerMode, + annotationMode, + imageResourcesPath: this.imageResourcesPath, + maxCanvasPixels: this.maxCanvasPixels, + pageColors, + l10n: this.l10n, + layerProperties: this._layerProperties, + enableHWA: this.#enableHWA + }); + this._pages.push(pageView); + } + this._pages[0]?.setPdfPage(firstPdfPage); + if (this._scrollMode === ScrollMode.PAGE) { + this.#ensurePageViewVisible(); + } else if (this._spreadMode !== SpreadMode.NONE) { + this._updateSpreadMode(); + } + this.#onePageRenderedOrForceFetch(signal).then(async () => { + if (pdfDocument !== this.pdfDocument) { + return; + } + this.findController?.setDocument(pdfDocument); + this._scriptingManager?.setDocument(pdfDocument); + if (this.#hiddenCopyElement) { + document.addEventListener("copy", this.#copyCallback.bind(this, textLayerMode), { + signal + }); + } + if (this.#annotationEditorUIManager) { + eventBus.dispatch("annotationeditormodechanged", { + source: this, + mode: this.#annotationEditorMode + }); + } + if (pdfDocument.loadingParams.disableAutoFetch || pagesCount > PagesCountLimit.FORCE_LAZY_PAGE_INIT) { + this._pagesCapability.resolve(); + return; + } + let getPagesLeft = pagesCount - 1; + if (getPagesLeft <= 0) { + this._pagesCapability.resolve(); + return; + } + for (let pageNum = 2; pageNum <= pagesCount; ++pageNum) { + const promise = pdfDocument.getPage(pageNum).then(pdfPage => { + const pageView = this._pages[pageNum - 1]; + if (!pageView.pdfPage) { + pageView.setPdfPage(pdfPage); + } + if (--getPagesLeft === 0) { + this._pagesCapability.resolve(); + } + }, reason => { + console.error(`Unable to get page ${pageNum} to initialize viewer`, reason); + if (--getPagesLeft === 0) { + this._pagesCapability.resolve(); + } + }); + if (pageNum % PagesCountLimit.PAUSE_EAGER_PAGE_INIT === 0) { + await promise; + } + } + }); + eventBus.dispatch("pagesinit", { + source: this + }); + pdfDocument.getMetadata().then(({ + info + }) => { + if (pdfDocument !== this.pdfDocument) { + return; + } + if (info.Language) { + viewer.lang = info.Language; + } + }); + if (this.defaultRenderingQueue) { + this.update(); + } + }).catch(reason => { + console.error("Unable to initialize viewer", reason); + this._pagesCapability.reject(reason); + }); + } + setPageLabels(labels) { + if (!this.pdfDocument) { + return; + } + if (!labels) { + this._pageLabels = null; + } else if (!(Array.isArray(labels) && this.pdfDocument.numPages === labels.length)) { + this._pageLabels = null; + console.error(`setPageLabels: Invalid page labels.`); + } else { + this._pageLabels = labels; + } + for (let i = 0, ii = this._pages.length; i < ii; i++) { + this._pages[i].setPageLabel(this._pageLabels?.[i] ?? null); + } + } + _resetView() { + this._pages = []; + this._currentPageNumber = 1; + this._currentScale = UNKNOWN_SCALE; + this._currentScaleValue = null; + this._pageLabels = null; + this.#buffer = new PDFPageViewBuffer(DEFAULT_CACHE_SIZE); + this._location = null; + this._pagesRotation = 0; + this._optionalContentConfigPromise = null; + this._firstPageCapability = Promise.withResolvers(); + this._onePageRenderedCapability = Promise.withResolvers(); + this._pagesCapability = Promise.withResolvers(); + this._scrollMode = ScrollMode.VERTICAL; + this._previousScrollMode = ScrollMode.UNKNOWN; + this._spreadMode = SpreadMode.NONE; + this.#scrollModePageState = { + previousPageNumber: 1, + scrollDown: true, + pages: [] + }; + this.#eventAbortController?.abort(); + this.#eventAbortController = null; + this.viewer.textContent = ""; + this._updateScrollMode(); + this.viewer.removeAttribute("lang"); + this.#hiddenCopyElement?.remove(); + this.#hiddenCopyElement = null; + this.#cleanupSwitchAnnotationEditorMode(); + } + #ensurePageViewVisible() { + if (this._scrollMode !== ScrollMode.PAGE) { + throw new Error("#ensurePageViewVisible: Invalid scrollMode value."); + } + const pageNumber = this._currentPageNumber, + state = this.#scrollModePageState, + viewer = this.viewer; + viewer.textContent = ""; + state.pages.length = 0; + if (this._spreadMode === SpreadMode.NONE && !this.isInPresentationMode) { + const pageView = this._pages[pageNumber - 1]; + viewer.append(pageView.div); + state.pages.push(pageView); + } else { + const pageIndexSet = new Set(), + parity = this._spreadMode - 1; + if (parity === -1) { + pageIndexSet.add(pageNumber - 1); + } else if (pageNumber % 2 !== parity) { + pageIndexSet.add(pageNumber - 1); + pageIndexSet.add(pageNumber); + } else { + pageIndexSet.add(pageNumber - 2); + pageIndexSet.add(pageNumber - 1); + } + const spread = document.createElement("div"); + spread.className = "spread"; + if (this.isInPresentationMode) { + const dummyPage = document.createElement("div"); + dummyPage.className = "dummyPage"; + spread.append(dummyPage); + } + for (const i of pageIndexSet) { + const pageView = this._pages[i]; + if (!pageView) { + continue; + } + spread.append(pageView.div); + state.pages.push(pageView); + } + viewer.append(spread); + } + state.scrollDown = pageNumber >= state.previousPageNumber; + state.previousPageNumber = pageNumber; + } + _scrollUpdate() { + if (this.pagesCount === 0) { + return; + } + this.update(); + } + #scrollIntoView(pageView, pageSpot = null) { + const { + div, + id + } = pageView; + if (this._currentPageNumber !== id) { + this._setCurrentPageNumber(id); + } + if (this._scrollMode === ScrollMode.PAGE) { + this.#ensurePageViewVisible(); + this.update(); + } + if (!pageSpot && !this.isInPresentationMode) { + const left = div.offsetLeft + div.clientLeft, + right = left + div.clientWidth; + const { + scrollLeft, + clientWidth + } = this.container; + if (this._scrollMode === ScrollMode.HORIZONTAL || left < scrollLeft || right > scrollLeft + clientWidth) { + pageSpot = { + left: 0, + top: 0 + }; + } + } + scrollIntoView(div, pageSpot); + if (!this._currentScaleValue && this._location) { + this._location = null; + } + } + #isSameScale(newScale) { + return newScale === this._currentScale || Math.abs(newScale - this._currentScale) < 1e-15; + } + #setScaleUpdatePages(newScale, newValue, { + noScroll = false, + preset = false, + drawingDelay = -1, + origin = null + }) { + this._currentScaleValue = newValue.toString(); + if (this.#isSameScale(newScale)) { + if (preset) { + this.eventBus.dispatch("scalechanging", { + source: this, + scale: newScale, + presetValue: newValue + }); + } + return; + } + this.viewer.style.setProperty("--scale-factor", newScale * PixelsPerInch.PDF_TO_CSS_UNITS); + const postponeDrawing = drawingDelay >= 0 && drawingDelay < 1000; + this.refresh(true, { + scale: newScale, + drawingDelay: postponeDrawing ? drawingDelay : -1 + }); + if (postponeDrawing) { + this.#scaleTimeoutId = setTimeout(() => { + this.#scaleTimeoutId = null; + this.refresh(); + }, drawingDelay); + } + const previousScale = this._currentScale; + this._currentScale = newScale; + if (!noScroll) { + let page = this._currentPageNumber, + dest; + if (this._location && !(this.isInPresentationMode || this.isChangingPresentationMode)) { + page = this._location.pageNumber; + dest = [null, { + name: "XYZ" + }, this._location.left, this._location.top, null]; + } + this.scrollPageIntoView({ + pageNumber: page, + destArray: dest, + allowNegativeOffset: true + }); + if (Array.isArray(origin)) { + const scaleDiff = newScale / previousScale - 1; + const [top, left] = this.containerTopLeft; + this.container.scrollLeft += (origin[0] - left) * scaleDiff; + this.container.scrollTop += (origin[1] - top) * scaleDiff; + } + } + this.eventBus.dispatch("scalechanging", { + source: this, + scale: newScale, + presetValue: preset ? newValue : undefined + }); + if (this.defaultRenderingQueue) { + this.update(); + } + } + get #pageWidthScaleFactor() { + if (this._spreadMode !== SpreadMode.NONE && this._scrollMode !== ScrollMode.HORIZONTAL) { + return 2; + } + return 1; + } + #setScale(value, options) { + let scale = parseFloat(value); + if (scale > 0) { + options.preset = false; + this.#setScaleUpdatePages(scale, value, options); + } else { + const currentPage = this._pages[this._currentPageNumber - 1]; + if (!currentPage) { + return; + } + let hPadding = SCROLLBAR_PADDING, + vPadding = VERTICAL_PADDING; + if (this.isInPresentationMode) { + hPadding = vPadding = 4; + if (this._spreadMode !== SpreadMode.NONE) { + hPadding *= 2; + } + } else if (this.removePageBorders) { + hPadding = vPadding = 0; + } else if (this._scrollMode === ScrollMode.HORIZONTAL) { + [hPadding, vPadding] = [vPadding, hPadding]; + } + const pageWidthScale = (this.container.clientWidth - hPadding) / currentPage.width * currentPage.scale / this.#pageWidthScaleFactor; + const pageHeightScale = (this.container.clientHeight - vPadding) / currentPage.height * currentPage.scale; + switch (value) { + case "page-actual": + scale = 1; + break; + case "page-width": + scale = pageWidthScale; + break; + case "page-height": + scale = pageHeightScale; + break; + case "page-fit": + scale = Math.min(pageWidthScale, pageHeightScale); + break; + case "auto": + const horizontalScale = isPortraitOrientation(currentPage) ? pageWidthScale : Math.min(pageHeightScale, pageWidthScale); + scale = Math.min(MAX_AUTO_SCALE, horizontalScale); + break; + default: + console.error(`#setScale: "${value}" is an unknown zoom value.`); + return; + } + options.preset = true; + this.#setScaleUpdatePages(scale, value, options); + } + } + #resetCurrentPageView() { + const pageView = this._pages[this._currentPageNumber - 1]; + if (this.isInPresentationMode) { + this.#setScale(this._currentScaleValue, { + noScroll: true + }); + } + this.#scrollIntoView(pageView); + } + pageLabelToPageNumber(label) { + if (!this._pageLabels) { + return null; + } + const i = this._pageLabels.indexOf(label); + if (i < 0) { + return null; + } + return i + 1; + } + scrollPageIntoView({ + pageNumber, + destArray = null, + allowNegativeOffset = false, + ignoreDestinationZoom = false + }) { + if (!this.pdfDocument) { + return; + } + const pageView = Number.isInteger(pageNumber) && this._pages[pageNumber - 1]; + if (!pageView) { + console.error(`scrollPageIntoView: "${pageNumber}" is not a valid pageNumber parameter.`); + return; + } + if (this.isInPresentationMode || !destArray) { + this._setCurrentPageNumber(pageNumber, true); + return; + } + let x = 0, + y = 0; + let width = 0, + height = 0, + widthScale, + heightScale; + const changeOrientation = pageView.rotation % 180 !== 0; + const pageWidth = (changeOrientation ? pageView.height : pageView.width) / pageView.scale / PixelsPerInch.PDF_TO_CSS_UNITS; + const pageHeight = (changeOrientation ? pageView.width : pageView.height) / pageView.scale / PixelsPerInch.PDF_TO_CSS_UNITS; + let scale = 0; + switch (destArray[1].name) { + case "XYZ": + x = destArray[2]; + y = destArray[3]; + scale = destArray[4]; + x = x !== null ? x : 0; + y = y !== null ? y : pageHeight; + break; + case "Fit": + case "FitB": + scale = "page-fit"; + break; + case "FitH": + case "FitBH": + y = destArray[2]; + scale = "page-width"; + if (y === null && this._location) { + x = this._location.left; + y = this._location.top; + } else if (typeof y !== "number" || y < 0) { + y = pageHeight; + } + break; + case "FitV": + case "FitBV": + x = destArray[2]; + width = pageWidth; + height = pageHeight; + scale = "page-height"; + break; + case "FitR": + x = destArray[2]; + y = destArray[3]; + width = destArray[4] - x; + height = destArray[5] - y; + let hPadding = SCROLLBAR_PADDING, + vPadding = VERTICAL_PADDING; + if (this.removePageBorders) { + hPadding = vPadding = 0; + } + widthScale = (this.container.clientWidth - hPadding) / width / PixelsPerInch.PDF_TO_CSS_UNITS; + heightScale = (this.container.clientHeight - vPadding) / height / PixelsPerInch.PDF_TO_CSS_UNITS; + scale = Math.min(Math.abs(widthScale), Math.abs(heightScale)); + break; + default: + console.error(`scrollPageIntoView: "${destArray[1].name}" is not a valid destination type.`); + return; + } + if (!ignoreDestinationZoom) { + if (scale && scale !== this._currentScale) { + this.currentScaleValue = scale; + } else if (this._currentScale === UNKNOWN_SCALE) { + this.currentScaleValue = DEFAULT_SCALE_VALUE; + } + } + if (scale === "page-fit" && !destArray[4]) { + this.#scrollIntoView(pageView); + return; + } + const boundingRect = [pageView.viewport.convertToViewportPoint(x, y), pageView.viewport.convertToViewportPoint(x + width, y + height)]; + let left = Math.min(boundingRect[0][0], boundingRect[1][0]); + let top = Math.min(boundingRect[0][1], boundingRect[1][1]); + if (!allowNegativeOffset) { + left = Math.max(left, 0); + top = Math.max(top, 0); + } + this.#scrollIntoView(pageView, { + left, + top + }); + } + _updateLocation(firstPage) { + const currentScale = this._currentScale; + const currentScaleValue = this._currentScaleValue; + const normalizedScaleValue = parseFloat(currentScaleValue) === currentScale ? Math.round(currentScale * 10000) / 100 : currentScaleValue; + const pageNumber = firstPage.id; + const currentPageView = this._pages[pageNumber - 1]; + const container = this.container; + const topLeft = currentPageView.getPagePoint(container.scrollLeft - firstPage.x, container.scrollTop - firstPage.y); + const intLeft = Math.round(topLeft[0]); + const intTop = Math.round(topLeft[1]); + let pdfOpenParams = `#page=${pageNumber}`; + if (!this.isInPresentationMode) { + pdfOpenParams += `&zoom=${normalizedScaleValue},${intLeft},${intTop}`; + } + this._location = { + pageNumber, + scale: normalizedScaleValue, + top: intTop, + left: intLeft, + rotation: this._pagesRotation, + pdfOpenParams + }; + } + update() { + const visible = this._getVisiblePages(); + const visiblePages = visible.views, + numVisiblePages = visiblePages.length; + if (numVisiblePages === 0) { + return; + } + const newCacheSize = Math.max(DEFAULT_CACHE_SIZE, 2 * numVisiblePages + 1); + this.#buffer.resize(newCacheSize, visible.ids); + this.renderingQueue.renderHighestPriority(visible); + const isSimpleLayout = this._spreadMode === SpreadMode.NONE && (this._scrollMode === ScrollMode.PAGE || this._scrollMode === ScrollMode.VERTICAL); + const currentId = this._currentPageNumber; + let stillFullyVisible = false; + for (const page of visiblePages) { + if (page.percent < 100) { + break; + } + if (page.id === currentId && isSimpleLayout) { + stillFullyVisible = true; + break; + } + } + this._setCurrentPageNumber(stillFullyVisible ? currentId : visiblePages[0].id); + this._updateLocation(visible.first); + this.eventBus.dispatch("updateviewarea", { + source: this, + location: this._location + }); + } + #switchToEditAnnotationMode() { + const visible = this._getVisiblePages(); + const pagesToRefresh = []; + const { + ids, + views + } = visible; + for (const page of views) { + const { + view + } = page; + if (!view.hasEditableAnnotations()) { + ids.delete(view.id); + continue; + } + pagesToRefresh.push(page); + } + if (pagesToRefresh.length === 0) { + return null; + } + this.renderingQueue.renderHighestPriority({ + first: pagesToRefresh[0], + last: pagesToRefresh.at(-1), + views: pagesToRefresh, + ids + }); + return ids; + } + containsElement(element) { + return this.container.contains(element); + } + focus() { + this.container.focus(); + } + get _isContainerRtl() { + return getComputedStyle(this.container).direction === "rtl"; + } + get isInPresentationMode() { + return this.presentationModeState === PresentationModeState.FULLSCREEN; + } + get isChangingPresentationMode() { + return this.presentationModeState === PresentationModeState.CHANGING; + } + get isHorizontalScrollbarEnabled() { + return this.isInPresentationMode ? false : this.container.scrollWidth > this.container.clientWidth; + } + get isVerticalScrollbarEnabled() { + return this.isInPresentationMode ? false : this.container.scrollHeight > this.container.clientHeight; + } + _getVisiblePages() { + const views = this._scrollMode === ScrollMode.PAGE ? this.#scrollModePageState.pages : this._pages, + horizontal = this._scrollMode === ScrollMode.HORIZONTAL, + rtl = horizontal && this._isContainerRtl; + return getVisibleElements({ + scrollEl: this.container, + views, + sortByVisibility: true, + horizontal, + rtl + }); + } + cleanup() { + for (const pageView of this._pages) { + if (pageView.renderingState !== RenderingStates.FINISHED) { + pageView.reset(); + } + } + } + _cancelRendering() { + for (const pageView of this._pages) { + pageView.cancelRendering(); + } + } + async #ensurePdfPageLoaded(pageView) { + if (pageView.pdfPage) { + return pageView.pdfPage; + } + try { + const pdfPage = await this.pdfDocument.getPage(pageView.id); + if (!pageView.pdfPage) { + pageView.setPdfPage(pdfPage); + } + return pdfPage; + } catch (reason) { + console.error("Unable to get page for page view", reason); + return null; + } + } + #getScrollAhead(visible) { + if (visible.first?.id === 1) { + return true; + } else if (visible.last?.id === this.pagesCount) { + return false; + } + switch (this._scrollMode) { + case ScrollMode.PAGE: + return this.#scrollModePageState.scrollDown; + case ScrollMode.HORIZONTAL: + return this.scroll.right; + } + return this.scroll.down; + } + forceRendering(currentlyVisiblePages) { + const visiblePages = currentlyVisiblePages || this._getVisiblePages(); + const scrollAhead = this.#getScrollAhead(visiblePages); + const preRenderExtra = this._spreadMode !== SpreadMode.NONE && this._scrollMode !== ScrollMode.HORIZONTAL; + const pageView = this.renderingQueue.getHighestPriority(visiblePages, this._pages, scrollAhead, preRenderExtra); + if (pageView) { + this.#ensurePdfPageLoaded(pageView).then(() => { + this.renderingQueue.renderView(pageView); + }); + return true; + } + return false; + } + get hasEqualPageSizes() { + const firstPageView = this._pages[0]; + for (let i = 1, ii = this._pages.length; i < ii; ++i) { + const pageView = this._pages[i]; + if (pageView.width !== firstPageView.width || pageView.height !== firstPageView.height) { + return false; + } + } + return true; + } + getPagesOverview() { + let initialOrientation; + return this._pages.map(pageView => { + const viewport = pageView.pdfPage.getViewport({ + scale: 1 + }); + const orientation = isPortraitOrientation(viewport); + if (initialOrientation === undefined) { + initialOrientation = orientation; + } else if (this.enablePrintAutoRotate && orientation !== initialOrientation) { + return { + width: viewport.height, + height: viewport.width, + rotation: (viewport.rotation - 90) % 360 + }; + } + return { + width: viewport.width, + height: viewport.height, + rotation: viewport.rotation + }; + }); + } + get optionalContentConfigPromise() { + if (!this.pdfDocument) { + return Promise.resolve(null); + } + if (!this._optionalContentConfigPromise) { + console.error("optionalContentConfigPromise: Not initialized yet."); + return this.pdfDocument.getOptionalContentConfig({ + intent: "display" + }); + } + return this._optionalContentConfigPromise; + } + set optionalContentConfigPromise(promise) { + if (!(promise instanceof Promise)) { + throw new Error(`Invalid optionalContentConfigPromise: ${promise}`); + } + if (!this.pdfDocument) { + return; + } + if (!this._optionalContentConfigPromise) { + return; + } + this._optionalContentConfigPromise = promise; + this.refresh(false, { + optionalContentConfigPromise: promise + }); + this.eventBus.dispatch("optionalcontentconfigchanged", { + source: this, + promise + }); + } + get scrollMode() { + return this._scrollMode; + } + set scrollMode(mode) { + if (this._scrollMode === mode) { + return; + } + if (!isValidScrollMode(mode)) { + throw new Error(`Invalid scroll mode: ${mode}`); + } + if (this.pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE) { + return; + } + this._previousScrollMode = this._scrollMode; + this._scrollMode = mode; + this.eventBus.dispatch("scrollmodechanged", { + source: this, + mode + }); + this._updateScrollMode(this._currentPageNumber); + } + _updateScrollMode(pageNumber = null) { + const scrollMode = this._scrollMode, + viewer = this.viewer; + viewer.classList.toggle("scrollHorizontal", scrollMode === ScrollMode.HORIZONTAL); + viewer.classList.toggle("scrollWrapped", scrollMode === ScrollMode.WRAPPED); + if (!this.pdfDocument || !pageNumber) { + return; + } + if (scrollMode === ScrollMode.PAGE) { + this.#ensurePageViewVisible(); + } else if (this._previousScrollMode === ScrollMode.PAGE) { + this._updateSpreadMode(); + } + if (this._currentScaleValue && isNaN(this._currentScaleValue)) { + this.#setScale(this._currentScaleValue, { + noScroll: true + }); + } + this._setCurrentPageNumber(pageNumber, true); + this.update(); + } + get spreadMode() { + return this._spreadMode; + } + set spreadMode(mode) { + if (this._spreadMode === mode) { + return; + } + if (!isValidSpreadMode(mode)) { + throw new Error(`Invalid spread mode: ${mode}`); + } + this._spreadMode = mode; + this.eventBus.dispatch("spreadmodechanged", { + source: this, + mode + }); + this._updateSpreadMode(this._currentPageNumber); + } + _updateSpreadMode(pageNumber = null) { + if (!this.pdfDocument) { + return; + } + const viewer = this.viewer, + pages = this._pages; + if (this._scrollMode === ScrollMode.PAGE) { + this.#ensurePageViewVisible(); + } else { + viewer.textContent = ""; + if (this._spreadMode === SpreadMode.NONE) { + for (const pageView of this._pages) { + viewer.append(pageView.div); + } + } else { + const parity = this._spreadMode - 1; + let spread = null; + for (let i = 0, ii = pages.length; i < ii; ++i) { + if (spread === null) { + spread = document.createElement("div"); + spread.className = "spread"; + viewer.append(spread); + } else if (i % 2 === parity) { + spread = spread.cloneNode(false); + viewer.append(spread); + } + spread.append(pages[i].div); + } + } + } + if (!pageNumber) { + return; + } + if (this._currentScaleValue && isNaN(this._currentScaleValue)) { + this.#setScale(this._currentScaleValue, { + noScroll: true + }); + } + this._setCurrentPageNumber(pageNumber, true); + this.update(); + } + _getPageAdvance(currentPageNumber, previous = false) { + switch (this._scrollMode) { + case ScrollMode.WRAPPED: + { + const { + views + } = this._getVisiblePages(), + pageLayout = new Map(); + for (const { + id, + y, + percent, + widthPercent + } of views) { + if (percent === 0 || widthPercent < 100) { + continue; + } + let yArray = pageLayout.get(y); + if (!yArray) { + pageLayout.set(y, yArray ||= []); + } + yArray.push(id); + } + for (const yArray of pageLayout.values()) { + const currentIndex = yArray.indexOf(currentPageNumber); + if (currentIndex === -1) { + continue; + } + const numPages = yArray.length; + if (numPages === 1) { + break; + } + if (previous) { + for (let i = currentIndex - 1, ii = 0; i >= ii; i--) { + const currentId = yArray[i], + expectedId = yArray[i + 1] - 1; + if (currentId < expectedId) { + return currentPageNumber - expectedId; + } + } + } else { + for (let i = currentIndex + 1, ii = numPages; i < ii; i++) { + const currentId = yArray[i], + expectedId = yArray[i - 1] + 1; + if (currentId > expectedId) { + return expectedId - currentPageNumber; + } + } + } + if (previous) { + const firstId = yArray[0]; + if (firstId < currentPageNumber) { + return currentPageNumber - firstId + 1; + } + } else { + const lastId = yArray[numPages - 1]; + if (lastId > currentPageNumber) { + return lastId - currentPageNumber + 1; + } + } + break; + } + break; + } + case ScrollMode.HORIZONTAL: + { + break; + } + case ScrollMode.PAGE: + case ScrollMode.VERTICAL: + { + if (this._spreadMode === SpreadMode.NONE) { + break; + } + const parity = this._spreadMode - 1; + if (previous && currentPageNumber % 2 !== parity) { + break; + } else if (!previous && currentPageNumber % 2 === parity) { + break; + } + const { + views + } = this._getVisiblePages(), + expectedId = previous ? currentPageNumber - 1 : currentPageNumber + 1; + for (const { + id, + percent, + widthPercent + } of views) { + if (id !== expectedId) { + continue; + } + if (percent > 0 && widthPercent === 100) { + return 2; + } + break; + } + break; + } + } + return 1; + } + nextPage() { + const currentPageNumber = this._currentPageNumber, + pagesCount = this.pagesCount; + if (currentPageNumber >= pagesCount) { + return false; + } + const advance = this._getPageAdvance(currentPageNumber, false) || 1; + this.currentPageNumber = Math.min(currentPageNumber + advance, pagesCount); + return true; + } + previousPage() { + const currentPageNumber = this._currentPageNumber; + if (currentPageNumber <= 1) { + return false; + } + const advance = this._getPageAdvance(currentPageNumber, true) || 1; + this.currentPageNumber = Math.max(currentPageNumber - advance, 1); + return true; + } + updateScale({ + drawingDelay, + scaleFactor = null, + steps = null, + origin + }) { + if (steps === null && scaleFactor === null) { + throw new Error("Invalid updateScale options: either `steps` or `scaleFactor` must be provided."); + } + if (!this.pdfDocument) { + return; + } + let newScale = this._currentScale; + if (scaleFactor > 0 && scaleFactor !== 1) { + newScale = Math.round(newScale * scaleFactor * 100) / 100; + } else if (steps) { + const delta = steps > 0 ? DEFAULT_SCALE_DELTA : 1 / DEFAULT_SCALE_DELTA; + const round = steps > 0 ? Math.ceil : Math.floor; + steps = Math.abs(steps); + do { + newScale = round((newScale * delta).toFixed(2) * 10) / 10; + } while (--steps > 0); + } + newScale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, newScale)); + this.#setScale(newScale, { + noScroll: false, + drawingDelay, + origin + }); + } + increaseScale(options = {}) { + this.updateScale({ + ...options, + steps: options.steps ?? 1 + }); + } + decreaseScale(options = {}) { + this.updateScale({ + ...options, + steps: -(options.steps ?? 1) + }); + } + #updateContainerHeightCss(height = this.container.clientHeight) { + if (height !== this.#previousContainerHeight) { + this.#previousContainerHeight = height; + docStyle.setProperty("--viewer-container-height", `${height}px`); + } + } + #resizeObserverCallback(entries) { + for (const entry of entries) { + if (entry.target === this.container) { + this.#updateContainerHeightCss(Math.floor(entry.borderBoxSize[0].blockSize)); + this.#containerTopLeft = null; + break; + } + } + } + get containerTopLeft() { + return this.#containerTopLeft ||= [this.container.offsetTop, this.container.offsetLeft]; + } + #cleanupSwitchAnnotationEditorMode() { + this.#switchAnnotationEditorModeAC?.abort(); + this.#switchAnnotationEditorModeAC = null; + if (this.#switchAnnotationEditorModeTimeoutId !== null) { + clearTimeout(this.#switchAnnotationEditorModeTimeoutId); + this.#switchAnnotationEditorModeTimeoutId = null; + } + } + get annotationEditorMode() { + return this.#annotationEditorUIManager ? this.#annotationEditorMode : AnnotationEditorType.DISABLE; + } + set annotationEditorMode({ + mode, + editId = null, + isFromKeyboard = false + }) { + if (!this.#annotationEditorUIManager) { + throw new Error(`The AnnotationEditor is not enabled.`); + } + if (this.#annotationEditorMode === mode) { + return; + } + if (!isValidAnnotationEditorMode(mode)) { + throw new Error(`Invalid AnnotationEditor mode: ${mode}`); + } + if (!this.pdfDocument) { + return; + } + if (mode === AnnotationEditorType.STAMP) { + this.#mlManager?.loadModel("altText"); + } + const { + eventBus + } = this; + const updater = () => { + this.#cleanupSwitchAnnotationEditorMode(); + this.#annotationEditorMode = mode; + this.#annotationEditorUIManager.updateMode(mode, editId, isFromKeyboard); + eventBus.dispatch("annotationeditormodechanged", { + source: this, + mode + }); + }; + if (mode === AnnotationEditorType.NONE || this.#annotationEditorMode === AnnotationEditorType.NONE) { + const isEditing = mode !== AnnotationEditorType.NONE; + if (!isEditing) { + this.pdfDocument.annotationStorage.resetModifiedIds(); + } + for (const pageView of this._pages) { + pageView.toggleEditingMode(isEditing); + } + const idsToRefresh = this.#switchToEditAnnotationMode(); + if (isEditing && idsToRefresh) { + this.#cleanupSwitchAnnotationEditorMode(); + this.#switchAnnotationEditorModeAC = new AbortController(); + const signal = AbortSignal.any([this.#eventAbortController.signal, this.#switchAnnotationEditorModeAC.signal]); + eventBus._on("pagerendered", ({ + pageNumber + }) => { + idsToRefresh.delete(pageNumber); + if (idsToRefresh.size === 0) { + this.#switchAnnotationEditorModeTimeoutId = setTimeout(updater, 0); + } + }, { + signal + }); + return; + } + } + updater(); + } + refresh(noUpdate = false, updateArgs = Object.create(null)) { + if (!this.pdfDocument) { + return; + } + for (const pageView of this._pages) { + pageView.update(updateArgs); + } + if (this.#scaleTimeoutId !== null) { + clearTimeout(this.#scaleTimeoutId); + this.#scaleTimeoutId = null; + } + if (!noUpdate) { + this.update(); + } + } +} + +;// ./web/secondary_toolbar.js + + +class SecondaryToolbar { + #opts; + constructor(options, eventBus) { + this.#opts = options; + const buttons = [{ + element: options.presentationModeButton, + eventName: "presentationmode", + close: true + }, { + element: options.printButton, + eventName: "print", + close: true + }, { + element: options.downloadButton, + eventName: "download", + close: true + }, { + element: options.viewBookmarkButton, + eventName: null, + close: true + }, { + element: options.firstPageButton, + eventName: "firstpage", + close: true + }, { + element: options.lastPageButton, + eventName: "lastpage", + close: true + }, { + element: options.pageRotateCwButton, + eventName: "rotatecw", + close: false + }, { + element: options.pageRotateCcwButton, + eventName: "rotateccw", + close: false + }, { + element: options.cursorSelectToolButton, + eventName: "switchcursortool", + eventDetails: { + tool: CursorTool.SELECT + }, + close: true + }, { + element: options.cursorHandToolButton, + eventName: "switchcursortool", + eventDetails: { + tool: CursorTool.HAND + }, + close: true + }, { + element: options.scrollPageButton, + eventName: "switchscrollmode", + eventDetails: { + mode: ScrollMode.PAGE + }, + close: true + }, { + element: options.scrollVerticalButton, + eventName: "switchscrollmode", + eventDetails: { + mode: ScrollMode.VERTICAL + }, + close: true + }, { + element: options.scrollHorizontalButton, + eventName: "switchscrollmode", + eventDetails: { + mode: ScrollMode.HORIZONTAL + }, + close: true + }, { + element: options.scrollWrappedButton, + eventName: "switchscrollmode", + eventDetails: { + mode: ScrollMode.WRAPPED + }, + close: true + }, { + element: options.spreadNoneButton, + eventName: "switchspreadmode", + eventDetails: { + mode: SpreadMode.NONE + }, + close: true + }, { + element: options.spreadOddButton, + eventName: "switchspreadmode", + eventDetails: { + mode: SpreadMode.ODD + }, + close: true + }, { + element: options.spreadEvenButton, + eventName: "switchspreadmode", + eventDetails: { + mode: SpreadMode.EVEN + }, + close: true + }, { + element: options.imageAltTextSettingsButton, + eventName: "imagealttextsettings", + close: true + }, { + element: options.documentPropertiesButton, + eventName: "documentproperties", + close: true + }]; + buttons.push({ + element: options.openFileButton, + eventName: "openfile", + close: true + }); + this.eventBus = eventBus; + this.opened = false; + this.#bindListeners(buttons); + this.reset(); + } + get isOpen() { + return this.opened; + } + setPageNumber(pageNumber) { + this.pageNumber = pageNumber; + this.#updateUIState(); + } + setPagesCount(pagesCount) { + this.pagesCount = pagesCount; + this.#updateUIState(); + } + reset() { + this.pageNumber = 0; + this.pagesCount = 0; + this.#updateUIState(); + this.eventBus.dispatch("switchcursortool", { + source: this, + reset: true + }); + this.#scrollModeChanged({ + mode: ScrollMode.VERTICAL + }); + this.#spreadModeChanged({ + mode: SpreadMode.NONE + }); + } + #updateUIState() { + const { + firstPageButton, + lastPageButton, + pageRotateCwButton, + pageRotateCcwButton + } = this.#opts; + firstPageButton.disabled = this.pageNumber <= 1; + lastPageButton.disabled = this.pageNumber >= this.pagesCount; + pageRotateCwButton.disabled = this.pagesCount === 0; + pageRotateCcwButton.disabled = this.pagesCount === 0; + } + #bindListeners(buttons) { + const { + eventBus + } = this; + const { + toggleButton + } = this.#opts; + toggleButton.addEventListener("click", this.toggle.bind(this)); + for (const { + element, + eventName, + close, + eventDetails + } of buttons) { + element.addEventListener("click", evt => { + if (eventName !== null) { + eventBus.dispatch(eventName, { + source: this, + ...eventDetails + }); + } + if (close) { + this.close(); + } + eventBus.dispatch("reporttelemetry", { + source: this, + details: { + type: "buttons", + data: { + id: element.id + } + } + }); + }); + } + eventBus._on("cursortoolchanged", this.#cursorToolChanged.bind(this)); + eventBus._on("scrollmodechanged", this.#scrollModeChanged.bind(this)); + eventBus._on("spreadmodechanged", this.#spreadModeChanged.bind(this)); + } + #cursorToolChanged({ + tool, + disabled + }) { + const { + cursorSelectToolButton, + cursorHandToolButton + } = this.#opts; + toggleCheckedBtn(cursorSelectToolButton, tool === CursorTool.SELECT); + toggleCheckedBtn(cursorHandToolButton, tool === CursorTool.HAND); + cursorSelectToolButton.disabled = disabled; + cursorHandToolButton.disabled = disabled; + } + #scrollModeChanged({ + mode + }) { + const { + scrollPageButton, + scrollVerticalButton, + scrollHorizontalButton, + scrollWrappedButton, + spreadNoneButton, + spreadOddButton, + spreadEvenButton + } = this.#opts; + toggleCheckedBtn(scrollPageButton, mode === ScrollMode.PAGE); + toggleCheckedBtn(scrollVerticalButton, mode === ScrollMode.VERTICAL); + toggleCheckedBtn(scrollHorizontalButton, mode === ScrollMode.HORIZONTAL); + toggleCheckedBtn(scrollWrappedButton, mode === ScrollMode.WRAPPED); + const forceScrollModePage = this.pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE; + scrollPageButton.disabled = forceScrollModePage; + scrollVerticalButton.disabled = forceScrollModePage; + scrollHorizontalButton.disabled = forceScrollModePage; + scrollWrappedButton.disabled = forceScrollModePage; + const isHorizontal = mode === ScrollMode.HORIZONTAL; + spreadNoneButton.disabled = isHorizontal; + spreadOddButton.disabled = isHorizontal; + spreadEvenButton.disabled = isHorizontal; + } + #spreadModeChanged({ + mode + }) { + const { + spreadNoneButton, + spreadOddButton, + spreadEvenButton + } = this.#opts; + toggleCheckedBtn(spreadNoneButton, mode === SpreadMode.NONE); + toggleCheckedBtn(spreadOddButton, mode === SpreadMode.ODD); + toggleCheckedBtn(spreadEvenButton, mode === SpreadMode.EVEN); + } + open() { + if (this.opened) { + return; + } + this.opened = true; + const { + toggleButton, + toolbar + } = this.#opts; + toggleExpandedBtn(toggleButton, true, toolbar); + } + close() { + if (!this.opened) { + return; + } + this.opened = false; + const { + toggleButton, + toolbar + } = this.#opts; + toggleExpandedBtn(toggleButton, false, toolbar); + } + toggle() { + if (this.opened) { + this.close(); + } else { + this.open(); + } + } +} + +;// ./web/toolbar.js + + +class Toolbar { + #opts; + constructor(options, eventBus, toolbarDensity = 0) { + this.#opts = options; + this.eventBus = eventBus; + const buttons = [{ + element: options.previous, + eventName: "previouspage" + }, { + element: options.next, + eventName: "nextpage" + }, { + element: options.zoomIn, + eventName: "zoomin" + }, { + element: options.zoomOut, + eventName: "zoomout" + }, { + element: options.print, + eventName: "print" + }, { + element: options.download, + eventName: "download" + }, { + element: options.editorFreeTextButton, + eventName: "switchannotationeditormode", + eventDetails: { + get mode() { + const { + classList + } = options.editorFreeTextButton; + return classList.contains("toggled") ? AnnotationEditorType.NONE : AnnotationEditorType.FREETEXT; + } + } + }, { + element: options.editorHighlightButton, + eventName: "switchannotationeditormode", + eventDetails: { + get mode() { + const { + classList + } = options.editorHighlightButton; + return classList.contains("toggled") ? AnnotationEditorType.NONE : AnnotationEditorType.HIGHLIGHT; + } + } + }, { + element: options.editorInkButton, + eventName: "switchannotationeditormode", + eventDetails: { + get mode() { + const { + classList + } = options.editorInkButton; + return classList.contains("toggled") ? AnnotationEditorType.NONE : AnnotationEditorType.INK; + } + } + }, { + element: options.editorStampButton, + eventName: "switchannotationeditormode", + eventDetails: { + get mode() { + const { + classList + } = options.editorStampButton; + return classList.contains("toggled") ? AnnotationEditorType.NONE : AnnotationEditorType.STAMP; + } + }, + telemetry: { + type: "editing", + data: { + action: "pdfjs.image.icon_click" + } + } + }]; + this.#bindListeners(buttons); + this.#updateToolbarDensity({ + value: toolbarDensity + }); + this.reset(); + } + #updateToolbarDensity({ + value + }) { + let name = "normal"; + switch (value) { + case 1: + name = "compact"; + break; + case 2: + name = "touch"; + break; + } + document.documentElement.setAttribute("data-toolbar-density", name); + } + #setAnnotationEditorUIManager(uiManager, parentContainer) { + const colorPicker = new ColorPicker({ + uiManager + }); + uiManager.setMainHighlightColorPicker(colorPicker); + parentContainer.append(colorPicker.renderMainDropdown()); + } + setPageNumber(pageNumber, pageLabel) { + this.pageNumber = pageNumber; + this.pageLabel = pageLabel; + this.#updateUIState(false); + } + setPagesCount(pagesCount, hasPageLabels) { + this.pagesCount = pagesCount; + this.hasPageLabels = hasPageLabels; + this.#updateUIState(true); + } + setPageScale(pageScaleValue, pageScale) { + this.pageScaleValue = (pageScaleValue || pageScale).toString(); + this.pageScale = pageScale; + this.#updateUIState(false); + } + reset() { + this.pageNumber = 0; + this.pageLabel = null; + this.hasPageLabels = false; + this.pagesCount = 0; + this.pageScaleValue = DEFAULT_SCALE_VALUE; + this.pageScale = DEFAULT_SCALE; + this.#updateUIState(true); + this.updateLoadingIndicatorState(); + this.#editorModeChanged({ + mode: AnnotationEditorType.DISABLE + }); + } + #bindListeners(buttons) { + const { + eventBus + } = this; + const { + editorHighlightColorPicker, + editorHighlightButton, + pageNumber, + scaleSelect + } = this.#opts; + const self = this; + for (const { + element, + eventName, + eventDetails, + telemetry + } of buttons) { + element.addEventListener("click", evt => { + if (eventName !== null) { + eventBus.dispatch(eventName, { + source: this, + ...eventDetails, + isFromKeyboard: evt.detail === 0 + }); + } + if (telemetry) { + eventBus.dispatch("reporttelemetry", { + source: this, + details: telemetry + }); + } + }); + } + pageNumber.addEventListener("click", function () { + this.select(); + }); + pageNumber.addEventListener("change", function () { + eventBus.dispatch("pagenumberchanged", { + source: self, + value: this.value + }); + }); + scaleSelect.addEventListener("change", function () { + if (this.value === "custom") { + return; + } + eventBus.dispatch("scalechanged", { + source: self, + value: this.value + }); + }); + scaleSelect.addEventListener("click", function ({ + target + }) { + if (this.value === self.pageScaleValue && target.tagName.toUpperCase() === "OPTION") { + this.blur(); + } + }); + scaleSelect.oncontextmenu = noContextMenu; + eventBus._on("annotationeditormodechanged", this.#editorModeChanged.bind(this)); + eventBus._on("showannotationeditorui", ({ + mode + }) => { + switch (mode) { + case AnnotationEditorType.HIGHLIGHT: + editorHighlightButton.click(); + break; + } + }); + eventBus._on("toolbardensity", this.#updateToolbarDensity.bind(this)); + if (editorHighlightColorPicker) { + eventBus._on("annotationeditoruimanager", ({ + uiManager + }) => { + this.#setAnnotationEditorUIManager(uiManager, editorHighlightColorPicker); + }, { + once: true + }); + } + } + #editorModeChanged({ + mode + }) { + const { + editorFreeTextButton, + editorFreeTextParamsToolbar, + editorHighlightButton, + editorHighlightParamsToolbar, + editorInkButton, + editorInkParamsToolbar, + editorStampButton, + editorStampParamsToolbar + } = this.#opts; + toggleExpandedBtn(editorFreeTextButton, mode === AnnotationEditorType.FREETEXT, editorFreeTextParamsToolbar); + toggleExpandedBtn(editorHighlightButton, mode === AnnotationEditorType.HIGHLIGHT, editorHighlightParamsToolbar); + toggleExpandedBtn(editorInkButton, mode === AnnotationEditorType.INK, editorInkParamsToolbar); + toggleExpandedBtn(editorStampButton, mode === AnnotationEditorType.STAMP, editorStampParamsToolbar); + const isDisable = mode === AnnotationEditorType.DISABLE; + editorFreeTextButton.disabled = isDisable; + editorHighlightButton.disabled = isDisable; + editorInkButton.disabled = isDisable; + editorStampButton.disabled = isDisable; + } + #updateUIState(resetNumPages = false) { + const { + pageNumber, + pagesCount, + pageScaleValue, + pageScale + } = this; + const opts = this.#opts; + if (resetNumPages) { + if (this.hasPageLabels) { + opts.pageNumber.type = "text"; + opts.numPages.setAttribute("data-l10n-id", "pdfjs-page-of-pages"); + } else { + opts.pageNumber.type = "number"; + opts.numPages.setAttribute("data-l10n-id", "pdfjs-of-pages"); + opts.numPages.setAttribute("data-l10n-args", JSON.stringify({ + pagesCount + })); + } + opts.pageNumber.max = pagesCount; + } + if (this.hasPageLabels) { + opts.pageNumber.value = this.pageLabel; + opts.numPages.setAttribute("data-l10n-args", JSON.stringify({ + pageNumber, + pagesCount + })); + } else { + opts.pageNumber.value = pageNumber; + } + opts.previous.disabled = pageNumber <= 1; + opts.next.disabled = pageNumber >= pagesCount; + opts.zoomOut.disabled = pageScale <= MIN_SCALE; + opts.zoomIn.disabled = pageScale >= MAX_SCALE; + let predefinedValueFound = false; + for (const option of opts.scaleSelect.options) { + if (option.value !== pageScaleValue) { + option.selected = false; + continue; + } + option.selected = true; + predefinedValueFound = true; + } + if (!predefinedValueFound) { + opts.customScaleOption.selected = true; + opts.customScaleOption.setAttribute("data-l10n-args", JSON.stringify({ + scale: Math.round(pageScale * 10000) / 100 + })); + } + } + updateLoadingIndicatorState(loading = false) { + const { + pageNumber + } = this.#opts; + pageNumber.classList.toggle("loading", loading); + } +} + +;// ./web/view_history.js +const DEFAULT_VIEW_HISTORY_CACHE_SIZE = 20; +class ViewHistory { + constructor(fingerprint, cacheSize = DEFAULT_VIEW_HISTORY_CACHE_SIZE) { + this.fingerprint = fingerprint; + this.cacheSize = cacheSize; + this._initializedPromise = this._readFromStorage().then(databaseStr => { + const database = JSON.parse(databaseStr || "{}"); + let index = -1; + if (!Array.isArray(database.files)) { + database.files = []; + } else { + while (database.files.length >= this.cacheSize) { + database.files.shift(); + } + for (let i = 0, ii = database.files.length; i < ii; i++) { + const branch = database.files[i]; + if (branch.fingerprint === this.fingerprint) { + index = i; + break; + } + } + } + if (index === -1) { + index = database.files.push({ + fingerprint: this.fingerprint + }) - 1; + } + this.file = database.files[index]; + this.database = database; + }); + } + async _writeToStorage() { + const databaseStr = JSON.stringify(this.database); + localStorage.setItem("pdfjs.history", databaseStr); + } + async _readFromStorage() { + return localStorage.getItem("pdfjs.history"); + } + async set(name, val) { + await this._initializedPromise; + this.file[name] = val; + return this._writeToStorage(); + } + async setMultiple(properties) { + await this._initializedPromise; + for (const name in properties) { + this.file[name] = properties[name]; + } + return this._writeToStorage(); + } + async get(name, defaultValue) { + await this._initializedPromise; + const val = this.file[name]; + return val !== undefined ? val : defaultValue; + } + async getMultiple(properties) { + await this._initializedPromise; + const values = Object.create(null); + for (const name in properties) { + const val = this.file[name]; + values[name] = val !== undefined ? val : properties[name]; + } + return values; + } +} + +;// ./web/app.js + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +const FORCE_PAGES_LOADED_TIMEOUT = 10000; +const ViewOnLoad = { + UNKNOWN: -1, + PREVIOUS: 0, + INITIAL: 1 +}; +const PDFViewerApplication = { + initialBookmark: document.location.hash.substring(1), + _initializedCapability: { + ...Promise.withResolvers(), + settled: false + }, + appConfig: null, + pdfDocument: null, + pdfLoadingTask: null, + printService: null, + pdfViewer: null, + pdfThumbnailViewer: null, + pdfRenderingQueue: null, + pdfPresentationMode: null, + pdfDocumentProperties: null, + pdfLinkService: null, + pdfHistory: null, + pdfSidebar: null, + pdfOutlineViewer: null, + pdfAttachmentViewer: null, + pdfLayerViewer: null, + pdfCursorTools: null, + pdfScriptingManager: null, + store: null, + downloadManager: null, + overlayManager: null, + preferences: new Preferences(), + toolbar: null, + secondaryToolbar: null, + eventBus: null, + l10n: null, + annotationEditorParams: null, + imageAltTextSettings: null, + isInitialViewSet: false, + isViewerEmbedded: window.parent !== window, + url: "", + baseUrl: "", + mlManager: null, + _downloadUrl: "", + _eventBusAbortController: null, + _windowAbortController: null, + _globalAbortController: new AbortController(), + documentInfo: null, + metadata: null, + _contentDispositionFilename: null, + _contentLength: null, + _saveInProgress: false, + _wheelUnusedTicks: 0, + _wheelUnusedFactor: 1, + _touchManager: null, + _touchUnusedTicks: 0, + _touchUnusedFactor: 1, + _PDFBug: null, + _hasAnnotationEditors: false, + _title: document.title, + _printAnnotationStoragePromise: null, + _isCtrlKeyDown: false, + _caretBrowsing: null, + _isScrolling: false, + editorUndoBar: null, + async initialize(appConfig) { + this.appConfig = appConfig; + try { + await this.preferences.initializedPromise; + } catch (ex) { + console.error("initialize:", ex); + } + if (AppOptions.get("pdfBugEnabled")) { + await this._parseHashParams(); + } + let mode; + switch (AppOptions.get("viewerCssTheme")) { + case 1: + mode = "is-light"; + break; + case 2: + mode = "is-dark"; + break; + } + if (mode) { + document.documentElement.classList.add(mode); + } + this.l10n = await this.externalServices.createL10n(); + document.getElementsByTagName("html")[0].dir = this.l10n.getDirection(); + this.l10n.translate(appConfig.appContainer || document.documentElement); + if (this.isViewerEmbedded && AppOptions.get("externalLinkTarget") === LinkTarget.NONE) { + AppOptions.set("externalLinkTarget", LinkTarget.TOP); + } + await this._initializeViewerComponents(); + this.bindEvents(); + this.bindWindowEvents(); + this._initializedCapability.settled = true; + this._initializedCapability.resolve(); + }, + async _parseHashParams() { + const hash = document.location.hash.substring(1); + if (!hash) { + return; + } + const { + mainContainer, + viewerContainer + } = this.appConfig, + params = parseQueryString(hash); + const loadPDFBug = async () => { + if (this._PDFBug) { + return; + } + const { + PDFBug + } = await import(/*webpackIgnore: true*/AppOptions.get("debuggerSrc")); + this._PDFBug = PDFBug; + }; + if (params.get("disableworker") === "true") { + try { + GlobalWorkerOptions.workerSrc ||= AppOptions.get("workerSrc"); + await import(/*webpackIgnore: true*/PDFWorker.workerSrc); + } catch (ex) { + console.error("_parseHashParams:", ex); + } + } + if (params.has("textlayer")) { + switch (params.get("textlayer")) { + case "off": + AppOptions.set("textLayerMode", TextLayerMode.DISABLE); + break; + case "visible": + case "shadow": + case "hover": + viewerContainer.classList.add(`textLayer-${params.get("textlayer")}`); + try { + await loadPDFBug(); + this._PDFBug.loadCSS(); + } catch (ex) { + console.error("_parseHashParams:", ex); + } + break; + } + } + if (params.has("pdfbug")) { + AppOptions.setAll({ + pdfBug: true, + fontExtraProperties: true + }); + const enabled = params.get("pdfbug").split(","); + try { + await loadPDFBug(); + this._PDFBug.init(mainContainer, enabled); + } catch (ex) { + console.error("_parseHashParams:", ex); + } + } + if (params.has("locale")) { + AppOptions.set("localeProperties", { + lang: params.get("locale") + }); + } + const opts = { + disableAutoFetch: x => x === "true", + disableFontFace: x => x === "true", + disableHistory: x => x === "true", + disableRange: x => x === "true", + disableStream: x => x === "true", + verbosity: x => x | 0 + }; + for (const name in opts) { + const check = opts[name], + key = name.toLowerCase(); + if (params.has(key)) { + AppOptions.set(name, check(params.get(key))); + } + } + }, + async _initializeViewerComponents() { + const { + appConfig, + externalServices, + l10n + } = this; + const eventBus = new EventBus(); + this.eventBus = AppOptions.eventBus = eventBus; + this.mlManager?.setEventBus(eventBus, this._globalAbortController.signal); + this.overlayManager = new OverlayManager(); + const pdfRenderingQueue = new PDFRenderingQueue(); + pdfRenderingQueue.onIdle = this._cleanup.bind(this); + this.pdfRenderingQueue = pdfRenderingQueue; + const pdfLinkService = new PDFLinkService({ + eventBus, + externalLinkTarget: AppOptions.get("externalLinkTarget"), + externalLinkRel: AppOptions.get("externalLinkRel"), + ignoreDestinationZoom: AppOptions.get("ignoreDestinationZoom") + }); + this.pdfLinkService = pdfLinkService; + const downloadManager = this.downloadManager = new DownloadManager(); + const findController = new PDFFindController({ + linkService: pdfLinkService, + eventBus, + updateMatchesCountOnProgress: true + }); + this.findController = findController; + const pdfScriptingManager = new PDFScriptingManager({ + eventBus, + externalServices, + docProperties: this._scriptingDocProperties.bind(this) + }); + this.pdfScriptingManager = pdfScriptingManager; + const container = appConfig.mainContainer, + viewer = appConfig.viewerContainer; + const annotationEditorMode = AppOptions.get("annotationEditorMode"); + const pageColors = AppOptions.get("forcePageColors") || window.matchMedia("(forced-colors: active)").matches ? { + background: AppOptions.get("pageColorsBackground"), + foreground: AppOptions.get("pageColorsForeground") + } : null; + let altTextManager; + if (AppOptions.get("enableUpdatedAddImage")) { + altTextManager = appConfig.newAltTextDialog ? new NewAltTextManager(appConfig.newAltTextDialog, this.overlayManager, eventBus) : null; + } else { + altTextManager = appConfig.altTextDialog ? new AltTextManager(appConfig.altTextDialog, container, this.overlayManager, eventBus) : null; + } + if (appConfig.editorUndoBar) { + this.editorUndoBar = new EditorUndoBar(appConfig.editorUndoBar, eventBus); + } + const enableHWA = AppOptions.get("enableHWA"); + const pdfViewer = new PDFViewer({ + container, + viewer, + eventBus, + renderingQueue: pdfRenderingQueue, + linkService: pdfLinkService, + downloadManager, + altTextManager, + editorUndoBar: this.editorUndoBar, + findController, + scriptingManager: AppOptions.get("enableScripting") && pdfScriptingManager, + l10n, + textLayerMode: AppOptions.get("textLayerMode"), + annotationMode: AppOptions.get("annotationMode"), + annotationEditorMode, + annotationEditorHighlightColors: AppOptions.get("highlightEditorColors"), + enableHighlightFloatingButton: AppOptions.get("enableHighlightFloatingButton"), + enableUpdatedAddImage: AppOptions.get("enableUpdatedAddImage"), + enableNewAltTextWhenAddingImage: AppOptions.get("enableNewAltTextWhenAddingImage"), + imageResourcesPath: AppOptions.get("imageResourcesPath"), + enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"), + maxCanvasPixels: AppOptions.get("maxCanvasPixels"), + enablePermissions: AppOptions.get("enablePermissions"), + pageColors, + mlManager: this.mlManager, + abortSignal: this._globalAbortController.signal, + enableHWA, + supportsPinchToZoom: this.supportsPinchToZoom + }); + this.pdfViewer = pdfViewer; + pdfRenderingQueue.setViewer(pdfViewer); + pdfLinkService.setViewer(pdfViewer); + pdfScriptingManager.setViewer(pdfViewer); + if (appConfig.sidebar?.thumbnailView) { + this.pdfThumbnailViewer = new PDFThumbnailViewer({ + container: appConfig.sidebar.thumbnailView, + eventBus, + renderingQueue: pdfRenderingQueue, + linkService: pdfLinkService, + pageColors, + abortSignal: this._globalAbortController.signal, + enableHWA + }); + pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer); + } + if (!this.isViewerEmbedded && !AppOptions.get("disableHistory")) { + this.pdfHistory = new PDFHistory({ + linkService: pdfLinkService, + eventBus + }); + pdfLinkService.setHistory(this.pdfHistory); + } + if (!this.supportsIntegratedFind && appConfig.findBar) { + this.findBar = new PDFFindBar(appConfig.findBar, appConfig.principalContainer, eventBus); + } + if (appConfig.annotationEditorParams) { + if (typeof AbortSignal.any === "function" && annotationEditorMode !== AnnotationEditorType.DISABLE) { + this.annotationEditorParams = new AnnotationEditorParams(appConfig.annotationEditorParams, eventBus); + } else { + for (const id of ["editorModeButtons", "editorModeSeparator"]) { + document.getElementById(id)?.classList.add("hidden"); + } + } + } + if (this.mlManager && appConfig.secondaryToolbar?.imageAltTextSettingsButton) { + this.imageAltTextSettings = new ImageAltTextSettings(appConfig.altTextSettingsDialog, this.overlayManager, eventBus, this.mlManager); + } + if (appConfig.documentProperties) { + this.pdfDocumentProperties = new PDFDocumentProperties(appConfig.documentProperties, this.overlayManager, eventBus, l10n, () => this._docFilename); + } + if (appConfig.secondaryToolbar?.cursorHandToolButton) { + this.pdfCursorTools = new PDFCursorTools({ + container, + eventBus, + cursorToolOnLoad: AppOptions.get("cursorToolOnLoad") + }); + } + if (appConfig.toolbar) { + this.toolbar = new Toolbar(appConfig.toolbar, eventBus, AppOptions.get("toolbarDensity")); + } + if (appConfig.secondaryToolbar) { + if (AppOptions.get("enableAltText")) { + appConfig.secondaryToolbar.imageAltTextSettingsButton?.classList.remove("hidden"); + appConfig.secondaryToolbar.imageAltTextSettingsSeparator?.classList.remove("hidden"); + } + this.secondaryToolbar = new SecondaryToolbar(appConfig.secondaryToolbar, eventBus); + } + if (this.supportsFullscreen && appConfig.secondaryToolbar?.presentationModeButton) { + this.pdfPresentationMode = new PDFPresentationMode({ + container, + pdfViewer, + eventBus + }); + } + if (appConfig.passwordOverlay) { + this.passwordPrompt = new PasswordPrompt(appConfig.passwordOverlay, this.overlayManager, this.isViewerEmbedded); + } + if (appConfig.sidebar?.outlineView) { + this.pdfOutlineViewer = new PDFOutlineViewer({ + container: appConfig.sidebar.outlineView, + eventBus, + l10n, + linkService: pdfLinkService, + downloadManager + }); + } + if (appConfig.sidebar?.attachmentsView) { + this.pdfAttachmentViewer = new PDFAttachmentViewer({ + container: appConfig.sidebar.attachmentsView, + eventBus, + l10n, + downloadManager + }); + } + if (appConfig.sidebar?.layersView) { + this.pdfLayerViewer = new PDFLayerViewer({ + container: appConfig.sidebar.layersView, + eventBus, + l10n + }); + } + if (appConfig.sidebar) { + this.pdfSidebar = new PDFSidebar({ + elements: appConfig.sidebar, + eventBus, + l10n + }); + this.pdfSidebar.onToggled = this.forceRendering.bind(this); + this.pdfSidebar.onUpdateThumbnails = () => { + for (const pageView of pdfViewer.getCachedPageViews()) { + if (pageView.renderingState === RenderingStates.FINISHED) { + this.pdfThumbnailViewer.getThumbnail(pageView.id - 1)?.setImage(pageView); + } + } + this.pdfThumbnailViewer.scrollThumbnailIntoView(pdfViewer.currentPageNumber); + }; + } + }, + async run(config) { + await this.initialize(config); + const { + appConfig, + eventBus + } = this; + let file; + const queryString = document.location.search.substring(1); + const params = parseQueryString(queryString); + file = params.get("file") ?? AppOptions.get("defaultUrl"); + validateFileURL(file); + const fileInput = this._openFileInput = document.createElement("input"); + fileInput.id = "fileInput"; + fileInput.hidden = true; + fileInput.type = "file"; + fileInput.value = null; + document.body.append(fileInput); + fileInput.addEventListener("change", function (evt) { + const { + files + } = evt.target; + if (!files || files.length === 0) { + return; + } + eventBus.dispatch("fileinputchange", { + source: this, + fileInput: evt.target + }); + }); + appConfig.mainContainer.addEventListener("dragover", function (evt) { + for (const item of evt.dataTransfer.items) { + if (item.type === "application/pdf") { + evt.dataTransfer.dropEffect = evt.dataTransfer.effectAllowed === "copy" ? "copy" : "move"; + stopEvent(evt); + return; + } + } + }); + appConfig.mainContainer.addEventListener("drop", function (evt) { + if (evt.dataTransfer.files?.[0].type !== "application/pdf") { + return; + } + stopEvent(evt); + eventBus.dispatch("fileinputchange", { + source: this, + fileInput: evt.dataTransfer + }); + }); + if (!AppOptions.get("supportsDocumentFonts")) { + AppOptions.set("disableFontFace", true); + this.l10n.get("pdfjs-web-fonts-disabled").then(msg => { + console.warn(msg); + }); + } + if (!this.supportsPrinting) { + appConfig.toolbar?.print?.classList.add("hidden"); + appConfig.secondaryToolbar?.printButton.classList.add("hidden"); + } + if (!this.supportsFullscreen) { + appConfig.secondaryToolbar?.presentationModeButton.classList.add("hidden"); + } + if (this.supportsIntegratedFind) { + appConfig.findBar?.toggleButton?.classList.add("hidden"); + } + if (file) { + this.open({ + url: file + }); + } else { + this._hideViewBookmark(); + } + }, + get externalServices() { + return shadow(this, "externalServices", new ExternalServices()); + }, + get initialized() { + return this._initializedCapability.settled; + }, + get initializedPromise() { + return this._initializedCapability.promise; + }, + updateZoom(steps, scaleFactor, origin) { + if (this.pdfViewer.isInPresentationMode) { + return; + } + this.pdfViewer.updateScale({ + drawingDelay: AppOptions.get("defaultZoomDelay"), + steps, + scaleFactor, + origin + }); + }, + zoomIn() { + this.updateZoom(1); + }, + zoomOut() { + this.updateZoom(-1); + }, + zoomReset() { + if (this.pdfViewer.isInPresentationMode) { + return; + } + this.pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE; + }, + touchPinchCallback(origin, prevDistance, distance) { + if (this.supportsPinchToZoom) { + const newScaleFactor = this._accumulateFactor(this.pdfViewer.currentScale, distance / prevDistance, "_touchUnusedFactor"); + this.updateZoom(null, newScaleFactor, origin); + } else { + const PIXELS_PER_LINE_SCALE = 30; + const ticks = this._accumulateTicks((distance - prevDistance) / PIXELS_PER_LINE_SCALE, "_touchUnusedTicks"); + this.updateZoom(ticks, null, origin); + } + }, + touchPinchEndCallback() { + this._touchUnusedTicks = 0; + this._touchUnusedFactor = 1; + }, + get pagesCount() { + return this.pdfDocument ? this.pdfDocument.numPages : 0; + }, + get page() { + return this.pdfViewer.currentPageNumber; + }, + set page(val) { + this.pdfViewer.currentPageNumber = val; + }, + get supportsPrinting() { + return PDFPrintServiceFactory.supportsPrinting; + }, + get supportsFullscreen() { + return shadow(this, "supportsFullscreen", document.fullscreenEnabled); + }, + get supportsPinchToZoom() { + return shadow(this, "supportsPinchToZoom", AppOptions.get("supportsPinchToZoom")); + }, + get supportsIntegratedFind() { + return shadow(this, "supportsIntegratedFind", AppOptions.get("supportsIntegratedFind")); + }, + get loadingBar() { + const barElement = document.getElementById("loadingBar"); + const bar = barElement ? new ProgressBar(barElement) : null; + return shadow(this, "loadingBar", bar); + }, + get supportsMouseWheelZoomCtrlKey() { + return shadow(this, "supportsMouseWheelZoomCtrlKey", AppOptions.get("supportsMouseWheelZoomCtrlKey")); + }, + get supportsMouseWheelZoomMetaKey() { + return shadow(this, "supportsMouseWheelZoomMetaKey", AppOptions.get("supportsMouseWheelZoomMetaKey")); + }, + get supportsCaretBrowsingMode() { + return AppOptions.get("supportsCaretBrowsingMode"); + }, + moveCaret(isUp, select) { + this._caretBrowsing ||= new CaretBrowsingMode(this._globalAbortController.signal, this.appConfig.mainContainer, this.appConfig.viewerContainer, this.appConfig.toolbar?.container); + this._caretBrowsing.moveCaret(isUp, select); + }, + setTitleUsingUrl(url = "", downloadUrl = null) { + this.url = url; + this.baseUrl = url.split("#", 1)[0]; + if (downloadUrl) { + this._downloadUrl = downloadUrl === url ? this.baseUrl : downloadUrl.split("#", 1)[0]; + } + if (isDataScheme(url)) { + this._hideViewBookmark(); + } + let title = pdfjs_getPdfFilenameFromUrl(url, ""); + if (!title) { + try { + title = decodeURIComponent(getFilenameFromUrl(url)); + } catch {} + } + this.setTitle(title || url); + }, + setTitle(title = this._title) { + this._title = title; + if (this.isViewerEmbedded) { + return; + } + const editorIndicator = this._hasAnnotationEditors && !this.pdfRenderingQueue.printing; + document.title = `${editorIndicator ? "* " : ""}${title}`; + }, + get _docFilename() { + return this._contentDispositionFilename || pdfjs_getPdfFilenameFromUrl(this.url); + }, + _hideViewBookmark() { + const { + secondaryToolbar + } = this.appConfig; + secondaryToolbar?.viewBookmarkButton.classList.add("hidden"); + if (secondaryToolbar?.presentationModeButton.classList.contains("hidden")) { + document.getElementById("viewBookmarkSeparator")?.classList.add("hidden"); + } + }, + async close() { + this._unblockDocumentLoadEvent(); + this._hideViewBookmark(); + if (!this.pdfLoadingTask) { + return; + } + if (this.pdfDocument?.annotationStorage.size > 0 && this._annotationStorageModified) { + try { + await this.save(); + } catch {} + } + const promises = []; + promises.push(this.pdfLoadingTask.destroy()); + this.pdfLoadingTask = null; + if (this.pdfDocument) { + this.pdfDocument = null; + this.pdfThumbnailViewer?.setDocument(null); + this.pdfViewer.setDocument(null); + this.pdfLinkService.setDocument(null); + this.pdfDocumentProperties?.setDocument(null); + } + this.pdfLinkService.externalLinkEnabled = true; + this.store = null; + this.isInitialViewSet = false; + this.url = ""; + this.baseUrl = ""; + this._downloadUrl = ""; + this.documentInfo = null; + this.metadata = null; + this._contentDispositionFilename = null; + this._contentLength = null; + this._saveInProgress = false; + this._hasAnnotationEditors = false; + promises.push(this.pdfScriptingManager.destroyPromise, this.passwordPrompt.close()); + this.setTitle(); + this.pdfSidebar?.reset(); + this.pdfOutlineViewer?.reset(); + this.pdfAttachmentViewer?.reset(); + this.pdfLayerViewer?.reset(); + this.pdfHistory?.reset(); + this.findBar?.reset(); + this.toolbar?.reset(); + this.secondaryToolbar?.reset(); + this._PDFBug?.cleanup(); + await Promise.all(promises); + }, + async open(args) { + if (this.pdfLoadingTask) { + await this.close(); + } + const workerParams = AppOptions.getAll(OptionKind.WORKER); + Object.assign(GlobalWorkerOptions, workerParams); + if (args.url) { + this.setTitleUsingUrl(args.originalUrl || args.url, args.url); + } + const apiParams = AppOptions.getAll(OptionKind.API); + const loadingTask = getDocument({ + ...apiParams, + ...args + }); + this.pdfLoadingTask = loadingTask; + loadingTask.onPassword = (updateCallback, reason) => { + if (this.isViewerEmbedded) { + this._unblockDocumentLoadEvent(); + } + this.pdfLinkService.externalLinkEnabled = false; + this.passwordPrompt.setUpdateCallback(updateCallback, reason); + this.passwordPrompt.open(); + }; + loadingTask.onProgress = ({ + loaded, + total + }) => { + this.progress(loaded / total); + }; + return loadingTask.promise.then(pdfDocument => { + this.load(pdfDocument); + }, reason => { + if (loadingTask !== this.pdfLoadingTask) { + return undefined; + } + let key = "pdfjs-loading-error"; + if (reason instanceof InvalidPDFException) { + key = "pdfjs-invalid-file-error"; + } else if (reason instanceof MissingPDFException) { + key = "pdfjs-missing-file-error"; + } else if (reason instanceof UnexpectedResponseException) { + key = "pdfjs-unexpected-response-error"; + } + return this._documentError(key, { + message: reason.message + }).then(() => { + throw reason; + }); + }); + }, + async download() { + let data; + try { + data = await this.pdfDocument.getData(); + } catch {} + this.downloadManager.download(data, this._downloadUrl, this._docFilename); + }, + async save() { + if (this._saveInProgress) { + return; + } + this._saveInProgress = true; + await this.pdfScriptingManager.dispatchWillSave(); + try { + const data = await this.pdfDocument.saveDocument(); + this.downloadManager.download(data, this._downloadUrl, this._docFilename); + } catch (reason) { + console.error(`Error when saving the document:`, reason); + await this.download(); + } finally { + await this.pdfScriptingManager.dispatchDidSave(); + this._saveInProgress = false; + } + if (this._hasAnnotationEditors) { + this.externalServices.reportTelemetry({ + type: "editing", + data: { + type: "save", + stats: this.pdfDocument?.annotationStorage.editorStats + } + }); + } + }, + async downloadOrSave() { + const { + classList + } = this.appConfig.appContainer; + classList.add("wait"); + await (this.pdfDocument?.annotationStorage.size > 0 ? this.save() : this.download()); + classList.remove("wait"); + }, + async _documentError(key, moreInfo = null) { + this._unblockDocumentLoadEvent(); + const message = await this._otherError(key || "pdfjs-loading-error", moreInfo); + this.eventBus.dispatch("documenterror", { + source: this, + message, + reason: moreInfo?.message ?? null + }); + }, + async _otherError(key, moreInfo = null) { + const message = await this.l10n.get(key); + const moreInfoText = [`PDF.js v${version || "?"} (build: ${build || "?"})`]; + if (moreInfo) { + moreInfoText.push(`Message: ${moreInfo.message}`); + if (moreInfo.stack) { + moreInfoText.push(`Stack: ${moreInfo.stack}`); + } else { + if (moreInfo.filename) { + moreInfoText.push(`File: ${moreInfo.filename}`); + } + if (moreInfo.lineNumber) { + moreInfoText.push(`Line: ${moreInfo.lineNumber}`); + } + } + } + console.error(`${message}\n\n${moreInfoText.join("\n")}`); + return message; + }, + progress(level) { + const percent = Math.round(level * 100); + if (!this.loadingBar || percent <= this.loadingBar.percent) { + return; + } + this.loadingBar.percent = percent; + if (this.pdfDocument?.loadingParams.disableAutoFetch ?? AppOptions.get("disableAutoFetch")) { + this.loadingBar.setDisableAutoFetch(); + } + }, + load(pdfDocument) { + this.pdfDocument = pdfDocument; + pdfDocument.getDownloadInfo().then(({ + length + }) => { + this._contentLength = length; + this.loadingBar?.hide(); + firstPagePromise.then(() => { + this.eventBus.dispatch("documentloaded", { + source: this + }); + }); + }); + const pageLayoutPromise = pdfDocument.getPageLayout().catch(() => {}); + const pageModePromise = pdfDocument.getPageMode().catch(() => {}); + const openActionPromise = pdfDocument.getOpenAction().catch(() => {}); + this.toolbar?.setPagesCount(pdfDocument.numPages, false); + this.secondaryToolbar?.setPagesCount(pdfDocument.numPages); + this.pdfLinkService.setDocument(pdfDocument); + this.pdfDocumentProperties?.setDocument(pdfDocument); + const pdfViewer = this.pdfViewer; + pdfViewer.setDocument(pdfDocument); + const { + firstPagePromise, + onePageRendered, + pagesPromise + } = pdfViewer; + this.pdfThumbnailViewer?.setDocument(pdfDocument); + const storedPromise = (this.store = new ViewHistory(pdfDocument.fingerprints[0])).getMultiple({ + page: null, + zoom: DEFAULT_SCALE_VALUE, + scrollLeft: "0", + scrollTop: "0", + rotation: null, + sidebarView: SidebarView.UNKNOWN, + scrollMode: ScrollMode.UNKNOWN, + spreadMode: SpreadMode.UNKNOWN + }).catch(() => {}); + firstPagePromise.then(pdfPage => { + this.loadingBar?.setWidth(this.appConfig.viewerContainer); + this._initializeAnnotationStorageCallbacks(pdfDocument); + Promise.all([animationStarted, storedPromise, pageLayoutPromise, pageModePromise, openActionPromise]).then(async ([timeStamp, stored, pageLayout, pageMode, openAction]) => { + const viewOnLoad = AppOptions.get("viewOnLoad"); + this._initializePdfHistory({ + fingerprint: pdfDocument.fingerprints[0], + viewOnLoad, + initialDest: openAction?.dest + }); + const initialBookmark = this.initialBookmark; + const zoom = AppOptions.get("defaultZoomValue"); + let hash = zoom ? `zoom=${zoom}` : null; + let rotation = null; + let sidebarView = AppOptions.get("sidebarViewOnLoad"); + let scrollMode = AppOptions.get("scrollModeOnLoad"); + let spreadMode = AppOptions.get("spreadModeOnLoad"); + if (stored?.page && viewOnLoad !== ViewOnLoad.INITIAL) { + hash = `page=${stored.page}&zoom=${zoom || stored.zoom},` + `${stored.scrollLeft},${stored.scrollTop}`; + rotation = parseInt(stored.rotation, 10); + if (sidebarView === SidebarView.UNKNOWN) { + sidebarView = stored.sidebarView | 0; + } + if (scrollMode === ScrollMode.UNKNOWN) { + scrollMode = stored.scrollMode | 0; + } + if (spreadMode === SpreadMode.UNKNOWN) { + spreadMode = stored.spreadMode | 0; + } + } + if (pageMode && sidebarView === SidebarView.UNKNOWN) { + sidebarView = apiPageModeToSidebarView(pageMode); + } + if (pageLayout && scrollMode === ScrollMode.UNKNOWN && spreadMode === SpreadMode.UNKNOWN) { + const modes = apiPageLayoutToViewerModes(pageLayout); + spreadMode = modes.spreadMode; + } + this.setInitialView(hash, { + rotation, + sidebarView, + scrollMode, + spreadMode + }); + this.eventBus.dispatch("documentinit", { + source: this + }); + if (!this.isViewerEmbedded) { + pdfViewer.focus(); + } + await Promise.race([pagesPromise, new Promise(resolve => { + setTimeout(resolve, FORCE_PAGES_LOADED_TIMEOUT); + })]); + if (!initialBookmark && !hash) { + return; + } + if (pdfViewer.hasEqualPageSizes) { + return; + } + this.initialBookmark = initialBookmark; + pdfViewer.currentScaleValue = pdfViewer.currentScaleValue; + this.setInitialView(hash); + }).catch(() => { + this.setInitialView(); + }).then(function () { + pdfViewer.update(); + }); + }); + pagesPromise.then(() => { + this._unblockDocumentLoadEvent(); + this._initializeAutoPrint(pdfDocument, openActionPromise); + }, reason => { + this._documentError("pdfjs-loading-error", { + message: reason.message + }); + }); + onePageRendered.then(data => { + this.externalServices.reportTelemetry({ + type: "pageInfo", + timestamp: data.timestamp + }); + if (this.pdfOutlineViewer) { + pdfDocument.getOutline().then(outline => { + if (pdfDocument !== this.pdfDocument) { + return; + } + this.pdfOutlineViewer.render({ + outline, + pdfDocument + }); + }); + } + if (this.pdfAttachmentViewer) { + pdfDocument.getAttachments().then(attachments => { + if (pdfDocument !== this.pdfDocument) { + return; + } + this.pdfAttachmentViewer.render({ + attachments + }); + }); + } + if (this.pdfLayerViewer) { + pdfViewer.optionalContentConfigPromise.then(optionalContentConfig => { + if (pdfDocument !== this.pdfDocument) { + return; + } + this.pdfLayerViewer.render({ + optionalContentConfig, + pdfDocument + }); + }); + } + }); + this._initializePageLabels(pdfDocument); + this._initializeMetadata(pdfDocument); + }, + async _scriptingDocProperties(pdfDocument) { + if (!this.documentInfo) { + await new Promise(resolve => { + this.eventBus._on("metadataloaded", resolve, { + once: true + }); + }); + if (pdfDocument !== this.pdfDocument) { + return null; + } + } + if (!this._contentLength) { + await new Promise(resolve => { + this.eventBus._on("documentloaded", resolve, { + once: true + }); + }); + if (pdfDocument !== this.pdfDocument) { + return null; + } + } + return { + ...this.documentInfo, + baseURL: this.baseUrl, + filesize: this._contentLength, + filename: this._docFilename, + metadata: this.metadata?.getRaw(), + authors: this.metadata?.get("dc:creator"), + numPages: this.pagesCount, + URL: this.url + }; + }, + async _initializeAutoPrint(pdfDocument, openActionPromise) { + const [openAction, jsActions] = await Promise.all([openActionPromise, this.pdfViewer.enableScripting ? null : pdfDocument.getJSActions()]); + if (pdfDocument !== this.pdfDocument) { + return; + } + let triggerAutoPrint = openAction?.action === "Print"; + if (jsActions) { + console.warn("Warning: JavaScript support is not enabled"); + for (const name in jsActions) { + if (triggerAutoPrint) { + break; + } + switch (name) { + case "WillClose": + case "WillSave": + case "DidSave": + case "WillPrint": + case "DidPrint": + continue; + } + triggerAutoPrint = jsActions[name].some(js => AutoPrintRegExp.test(js)); + } + } + if (triggerAutoPrint) { + this.triggerPrinting(); + } + }, + async _initializeMetadata(pdfDocument) { + const { + info, + metadata, + contentDispositionFilename, + contentLength + } = await pdfDocument.getMetadata(); + if (pdfDocument !== this.pdfDocument) { + return; + } + this.documentInfo = info; + this.metadata = metadata; + this._contentDispositionFilename ??= contentDispositionFilename; + this._contentLength ??= contentLength; + console.log(`PDF ${pdfDocument.fingerprints[0]} [${info.PDFFormatVersion} ` + `${(info.Producer || "-").trim()} / ${(info.Creator || "-").trim()}] ` + `(PDF.js: ${version || "?"} [${build || "?"}])`); + let pdfTitle = info.Title; + const metadataTitle = metadata?.get("dc:title"); + if (metadataTitle) { + if (metadataTitle !== "Untitled" && !/[\uFFF0-\uFFFF]/g.test(metadataTitle)) { + pdfTitle = metadataTitle; + } + } + if (pdfTitle) { + this.setTitle(`${pdfTitle} - ${this._contentDispositionFilename || this._title}`); + } else if (this._contentDispositionFilename) { + this.setTitle(this._contentDispositionFilename); + } + if (info.IsXFAPresent && !info.IsAcroFormPresent && !pdfDocument.isPureXfa) { + if (pdfDocument.loadingParams.enableXfa) { + console.warn("Warning: XFA Foreground documents are not supported"); + } else { + console.warn("Warning: XFA support is not enabled"); + } + } else if ((info.IsAcroFormPresent || info.IsXFAPresent) && !this.pdfViewer.renderForms) { + console.warn("Warning: Interactive form support is not enabled"); + } + if (info.IsSignaturesPresent) { + console.warn("Warning: Digital signatures validation is not supported"); + } + this.eventBus.dispatch("metadataloaded", { + source: this + }); + }, + async _initializePageLabels(pdfDocument) { + const labels = await pdfDocument.getPageLabels(); + if (pdfDocument !== this.pdfDocument) { + return; + } + if (!labels || AppOptions.get("disablePageLabels")) { + return; + } + const numLabels = labels.length; + let standardLabels = 0, + emptyLabels = 0; + for (let i = 0; i < numLabels; i++) { + const label = labels[i]; + if (label === (i + 1).toString()) { + standardLabels++; + } else if (label === "") { + emptyLabels++; + } else { + break; + } + } + if (standardLabels >= numLabels || emptyLabels >= numLabels) { + return; + } + const { + pdfViewer, + pdfThumbnailViewer, + toolbar + } = this; + pdfViewer.setPageLabels(labels); + pdfThumbnailViewer?.setPageLabels(labels); + toolbar?.setPagesCount(numLabels, true); + toolbar?.setPageNumber(pdfViewer.currentPageNumber, pdfViewer.currentPageLabel); + }, + _initializePdfHistory({ + fingerprint, + viewOnLoad, + initialDest = null + }) { + if (!this.pdfHistory) { + return; + } + this.pdfHistory.initialize({ + fingerprint, + resetHistory: viewOnLoad === ViewOnLoad.INITIAL, + updateUrl: AppOptions.get("historyUpdateUrl") + }); + if (this.pdfHistory.initialBookmark) { + this.initialBookmark = this.pdfHistory.initialBookmark; + this.initialRotation = this.pdfHistory.initialRotation; + } + if (initialDest && !this.initialBookmark && viewOnLoad === ViewOnLoad.UNKNOWN) { + this.initialBookmark = JSON.stringify(initialDest); + this.pdfHistory.push({ + explicitDest: initialDest, + pageNumber: null + }); + } + }, + _initializeAnnotationStorageCallbacks(pdfDocument) { + if (pdfDocument !== this.pdfDocument) { + return; + } + const { + annotationStorage + } = pdfDocument; + annotationStorage.onSetModified = () => { + window.addEventListener("beforeunload", beforeUnload); + this._annotationStorageModified = true; + }; + annotationStorage.onResetModified = () => { + window.removeEventListener("beforeunload", beforeUnload); + delete this._annotationStorageModified; + }; + annotationStorage.onAnnotationEditor = typeStr => { + this._hasAnnotationEditors = !!typeStr; + this.setTitle(); + }; + }, + setInitialView(storedHash, { + rotation, + sidebarView, + scrollMode, + spreadMode + } = {}) { + const setRotation = angle => { + if (isValidRotation(angle)) { + this.pdfViewer.pagesRotation = angle; + } + }; + const setViewerModes = (scroll, spread) => { + if (isValidScrollMode(scroll)) { + this.pdfViewer.scrollMode = scroll; + } + if (isValidSpreadMode(spread)) { + this.pdfViewer.spreadMode = spread; + } + }; + this.isInitialViewSet = true; + this.pdfSidebar?.setInitialView(sidebarView); + setViewerModes(scrollMode, spreadMode); + if (this.initialBookmark) { + setRotation(this.initialRotation); + delete this.initialRotation; + this.pdfLinkService.setHash(this.initialBookmark); + this.initialBookmark = null; + } else if (storedHash) { + setRotation(rotation); + this.pdfLinkService.setHash(storedHash); + } + this.toolbar?.setPageNumber(this.pdfViewer.currentPageNumber, this.pdfViewer.currentPageLabel); + this.secondaryToolbar?.setPageNumber(this.pdfViewer.currentPageNumber); + if (!this.pdfViewer.currentScaleValue) { + this.pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE; + } + }, + _cleanup() { + if (!this.pdfDocument) { + return; + } + this.pdfViewer.cleanup(); + this.pdfThumbnailViewer?.cleanup(); + this.pdfDocument.cleanup(AppOptions.get("fontExtraProperties")); + }, + forceRendering() { + this.pdfRenderingQueue.printing = !!this.printService; + this.pdfRenderingQueue.isThumbnailViewEnabled = this.pdfSidebar?.visibleView === SidebarView.THUMBS; + this.pdfRenderingQueue.renderHighestPriority(); + }, + beforePrint() { + this._printAnnotationStoragePromise = this.pdfScriptingManager.dispatchWillPrint().catch(() => {}).then(() => this.pdfDocument?.annotationStorage.print); + if (this.printService) { + return; + } + if (!this.supportsPrinting) { + this._otherError("pdfjs-printing-not-supported"); + return; + } + if (!this.pdfViewer.pageViewsReady) { + this.l10n.get("pdfjs-printing-not-ready").then(msg => { + window.alert(msg); + }); + return; + } + this.printService = PDFPrintServiceFactory.createPrintService({ + pdfDocument: this.pdfDocument, + pagesOverview: this.pdfViewer.getPagesOverview(), + printContainer: this.appConfig.printContainer, + printResolution: AppOptions.get("printResolution"), + printAnnotationStoragePromise: this._printAnnotationStoragePromise + }); + this.forceRendering(); + this.setTitle(); + this.printService.layout(); + if (this._hasAnnotationEditors) { + this.externalServices.reportTelemetry({ + type: "editing", + data: { + type: "print", + stats: this.pdfDocument?.annotationStorage.editorStats + } + }); + } + }, + afterPrint() { + if (this._printAnnotationStoragePromise) { + this._printAnnotationStoragePromise.then(() => { + this.pdfScriptingManager.dispatchDidPrint(); + }); + this._printAnnotationStoragePromise = null; + } + if (this.printService) { + this.printService.destroy(); + this.printService = null; + this.pdfDocument?.annotationStorage.resetModified(); + } + this.forceRendering(); + this.setTitle(); + }, + rotatePages(delta) { + this.pdfViewer.pagesRotation += delta; + }, + requestPresentationMode() { + this.pdfPresentationMode?.request(); + }, + triggerPrinting() { + if (this.supportsPrinting) { + window.print(); + } + }, + bindEvents() { + if (this._eventBusAbortController) { + return; + } + const ac = this._eventBusAbortController = new AbortController(); + const opts = { + signal: ac.signal + }; + const { + eventBus, + externalServices, + pdfDocumentProperties, + pdfViewer, + preferences + } = this; + eventBus._on("resize", onResize.bind(this), opts); + eventBus._on("hashchange", onHashchange.bind(this), opts); + eventBus._on("beforeprint", this.beforePrint.bind(this), opts); + eventBus._on("afterprint", this.afterPrint.bind(this), opts); + eventBus._on("pagerender", onPageRender.bind(this), opts); + eventBus._on("pagerendered", onPageRendered.bind(this), opts); + eventBus._on("updateviewarea", onUpdateViewarea.bind(this), opts); + eventBus._on("pagechanging", onPageChanging.bind(this), opts); + eventBus._on("scalechanging", onScaleChanging.bind(this), opts); + eventBus._on("rotationchanging", onRotationChanging.bind(this), opts); + eventBus._on("sidebarviewchanged", onSidebarViewChanged.bind(this), opts); + eventBus._on("pagemode", onPageMode.bind(this), opts); + eventBus._on("namedaction", onNamedAction.bind(this), opts); + eventBus._on("presentationmodechanged", evt => pdfViewer.presentationModeState = evt.state, opts); + eventBus._on("presentationmode", this.requestPresentationMode.bind(this), opts); + eventBus._on("switchannotationeditormode", evt => pdfViewer.annotationEditorMode = evt, opts); + eventBus._on("print", this.triggerPrinting.bind(this), opts); + eventBus._on("download", this.downloadOrSave.bind(this), opts); + eventBus._on("firstpage", () => this.page = 1, opts); + eventBus._on("lastpage", () => this.page = this.pagesCount, opts); + eventBus._on("nextpage", () => pdfViewer.nextPage(), opts); + eventBus._on("previouspage", () => pdfViewer.previousPage(), opts); + eventBus._on("zoomin", this.zoomIn.bind(this), opts); + eventBus._on("zoomout", this.zoomOut.bind(this), opts); + eventBus._on("zoomreset", this.zoomReset.bind(this), opts); + eventBus._on("pagenumberchanged", onPageNumberChanged.bind(this), opts); + eventBus._on("scalechanged", evt => pdfViewer.currentScaleValue = evt.value, opts); + eventBus._on("rotatecw", this.rotatePages.bind(this, 90), opts); + eventBus._on("rotateccw", this.rotatePages.bind(this, -90), opts); + eventBus._on("optionalcontentconfig", evt => pdfViewer.optionalContentConfigPromise = evt.promise, opts); + eventBus._on("switchscrollmode", evt => pdfViewer.scrollMode = evt.mode, opts); + eventBus._on("scrollmodechanged", onViewerModesChanged.bind(this, "scrollMode"), opts); + eventBus._on("switchspreadmode", evt => pdfViewer.spreadMode = evt.mode, opts); + eventBus._on("spreadmodechanged", onViewerModesChanged.bind(this, "spreadMode"), opts); + eventBus._on("imagealttextsettings", onImageAltTextSettings.bind(this), opts); + eventBus._on("documentproperties", () => pdfDocumentProperties?.open(), opts); + eventBus._on("findfromurlhash", onFindFromUrlHash.bind(this), opts); + eventBus._on("updatefindmatchescount", onUpdateFindMatchesCount.bind(this), opts); + eventBus._on("updatefindcontrolstate", onUpdateFindControlState.bind(this), opts); + eventBus._on("fileinputchange", onFileInputChange.bind(this), opts); + eventBus._on("openfile", onOpenFile.bind(this), opts); + }, + bindWindowEvents() { + if (this._windowAbortController) { + return; + } + this._windowAbortController = new AbortController(); + const { + eventBus, + appConfig: { + mainContainer + }, + pdfViewer, + _windowAbortController: { + signal + } + } = this; + if (typeof AbortSignal.any === "function") { + this._touchManager = new TouchManager({ + container: window, + isPinchingDisabled: () => pdfViewer.isInPresentationMode, + isPinchingStopped: () => this.overlayManager?.active, + onPinching: this.touchPinchCallback.bind(this), + onPinchEnd: this.touchPinchEndCallback.bind(this), + signal + }); + } + function addWindowResolutionChange(evt = null) { + if (evt) { + pdfViewer.refresh(); + } + const mediaQueryList = window.matchMedia(`(resolution: ${window.devicePixelRatio || 1}dppx)`); + mediaQueryList.addEventListener("change", addWindowResolutionChange, { + once: true, + signal + }); + } + addWindowResolutionChange(); + window.addEventListener("wheel", onWheel.bind(this), { + passive: false, + signal + }); + window.addEventListener("click", onClick.bind(this), { + signal + }); + window.addEventListener("keydown", onKeyDown.bind(this), { + signal + }); + window.addEventListener("keyup", onKeyUp.bind(this), { + signal + }); + window.addEventListener("resize", () => eventBus.dispatch("resize", { + source: window + }), { + signal + }); + window.addEventListener("hashchange", () => { + eventBus.dispatch("hashchange", { + source: window, + hash: document.location.hash.substring(1) + }); + }, { + signal + }); + window.addEventListener("beforeprint", () => eventBus.dispatch("beforeprint", { + source: window + }), { + signal + }); + window.addEventListener("afterprint", () => eventBus.dispatch("afterprint", { + source: window + }), { + signal + }); + window.addEventListener("updatefromsandbox", evt => { + eventBus.dispatch("updatefromsandbox", { + source: window, + detail: evt.detail + }); + }, { + signal + }); + if (!("onscrollend" in document.documentElement)) { + return; + } + ({ + scrollTop: this._lastScrollTop, + scrollLeft: this._lastScrollLeft + } = mainContainer); + const scrollend = () => { + ({ + scrollTop: this._lastScrollTop, + scrollLeft: this._lastScrollLeft + } = mainContainer); + this._isScrolling = false; + mainContainer.addEventListener("scroll", scroll, { + passive: true, + signal + }); + mainContainer.removeEventListener("scrollend", scrollend); + mainContainer.removeEventListener("blur", scrollend); + }; + const scroll = () => { + if (this._isCtrlKeyDown) { + return; + } + if (this._lastScrollTop === mainContainer.scrollTop && this._lastScrollLeft === mainContainer.scrollLeft) { + return; + } + mainContainer.removeEventListener("scroll", scroll); + this._isScrolling = true; + mainContainer.addEventListener("scrollend", scrollend, { + signal + }); + mainContainer.addEventListener("blur", scrollend, { + signal + }); + }; + mainContainer.addEventListener("scroll", scroll, { + passive: true, + signal + }); + }, + unbindEvents() { + this._eventBusAbortController?.abort(); + this._eventBusAbortController = null; + }, + unbindWindowEvents() { + this._windowAbortController?.abort(); + this._windowAbortController = null; + this._touchManager = null; + }, + async testingClose() { + this.unbindEvents(); + this.unbindWindowEvents(); + this._globalAbortController?.abort(); + this._globalAbortController = null; + this.findBar?.close(); + await Promise.all([this.l10n?.destroy(), this.close()]); + }, + _accumulateTicks(ticks, prop) { + if (this[prop] > 0 && ticks < 0 || this[prop] < 0 && ticks > 0) { + this[prop] = 0; + } + this[prop] += ticks; + const wholeTicks = Math.trunc(this[prop]); + this[prop] -= wholeTicks; + return wholeTicks; + }, + _accumulateFactor(previousScale, factor, prop) { + if (factor === 1) { + return 1; + } + if (this[prop] > 1 && factor < 1 || this[prop] < 1 && factor > 1) { + this[prop] = 1; + } + const newFactor = Math.floor(previousScale * factor * this[prop] * 100) / (100 * previousScale); + this[prop] = factor / newFactor; + return newFactor; + }, + _unblockDocumentLoadEvent() { + document.blockUnblockOnload?.(false); + this._unblockDocumentLoadEvent = () => {}; + }, + get scriptingReady() { + return this.pdfScriptingManager.ready; + } +}; +initCom(PDFViewerApplication); +{ + PDFPrintServiceFactory.initGlobals(PDFViewerApplication); +} +{ + const HOSTED_VIEWER_ORIGINS = ["null", "http://mozilla.github.io", "https://mozilla.github.io"]; + var validateFileURL = function (file) { + if (!file) { + return; + } + try { + const viewerOrigin = new URL(window.location.href).origin || "null"; + if (HOSTED_VIEWER_ORIGINS.includes(viewerOrigin)) { + return; + } + const fileOrigin = new URL(file, window.location.href).origin; + if (fileOrigin !== viewerOrigin) { + throw new Error("file origin does not match viewer's"); + } + } catch (ex) { + PDFViewerApplication._documentError("pdfjs-loading-error", { + message: ex.message + }); + throw ex; + } + }; + var onFileInputChange = function (evt) { + if (this.pdfViewer?.isInPresentationMode) { + return; + } + const file = evt.fileInput.files[0]; + this.open({ + url: URL.createObjectURL(file), + originalUrl: file.name + }); + }; + var onOpenFile = function (evt) { + this._openFileInput?.click(); + }; +} +function onPageRender({ + pageNumber +}) { + if (pageNumber === this.page) { + this.toolbar?.updateLoadingIndicatorState(true); + } +} +function onPageRendered({ + pageNumber, + error +}) { + if (pageNumber === this.page) { + this.toolbar?.updateLoadingIndicatorState(false); + } + if (this.pdfSidebar?.visibleView === SidebarView.THUMBS) { + const pageView = this.pdfViewer.getPageView(pageNumber - 1); + const thumbnailView = this.pdfThumbnailViewer?.getThumbnail(pageNumber - 1); + if (pageView) { + thumbnailView?.setImage(pageView); + } + } + if (error) { + this._otherError("pdfjs-rendering-error", error); + } +} +function onPageMode({ + mode +}) { + let view; + switch (mode) { + case "thumbs": + view = SidebarView.THUMBS; + break; + case "bookmarks": + case "outline": + view = SidebarView.OUTLINE; + break; + case "attachments": + view = SidebarView.ATTACHMENTS; + break; + case "layers": + view = SidebarView.LAYERS; + break; + case "none": + view = SidebarView.NONE; + break; + default: + console.error('Invalid "pagemode" hash parameter: ' + mode); + return; + } + this.pdfSidebar?.switchView(view, true); +} +function onNamedAction(evt) { + switch (evt.action) { + case "GoToPage": + this.appConfig.toolbar?.pageNumber.select(); + break; + case "Find": + if (!this.supportsIntegratedFind) { + this.findBar?.toggle(); + } + break; + case "Print": + this.triggerPrinting(); + break; + case "SaveAs": + this.downloadOrSave(); + break; + } +} +function onSidebarViewChanged({ + view +}) { + this.pdfRenderingQueue.isThumbnailViewEnabled = view === SidebarView.THUMBS; + if (this.isInitialViewSet) { + this.store?.set("sidebarView", view).catch(() => {}); + } +} +function onUpdateViewarea({ + location +}) { + if (this.isInitialViewSet) { + this.store?.setMultiple({ + page: location.pageNumber, + zoom: location.scale, + scrollLeft: location.left, + scrollTop: location.top, + rotation: location.rotation + }).catch(() => {}); + } + if (this.appConfig.secondaryToolbar) { + this.appConfig.secondaryToolbar.viewBookmarkButton.href = this.pdfLinkService.getAnchorUrl(location.pdfOpenParams); + } +} +function onViewerModesChanged(name, evt) { + if (this.isInitialViewSet && !this.pdfViewer.isInPresentationMode) { + this.store?.set(name, evt.mode).catch(() => {}); + } +} +function onResize() { + const { + pdfDocument, + pdfViewer, + pdfRenderingQueue + } = this; + if (pdfRenderingQueue.printing && window.matchMedia("print").matches) { + return; + } + if (!pdfDocument) { + return; + } + const currentScaleValue = pdfViewer.currentScaleValue; + if (currentScaleValue === "auto" || currentScaleValue === "page-fit" || currentScaleValue === "page-width") { + pdfViewer.currentScaleValue = currentScaleValue; + } + pdfViewer.update(); +} +function onHashchange(evt) { + const hash = evt.hash; + if (!hash) { + return; + } + if (!this.isInitialViewSet) { + this.initialBookmark = hash; + } else if (!this.pdfHistory?.popStateInProgress) { + this.pdfLinkService.setHash(hash); + } +} +function onPageNumberChanged(evt) { + const { + pdfViewer + } = this; + if (evt.value !== "") { + this.pdfLinkService.goToPage(evt.value); + } + if (evt.value !== pdfViewer.currentPageNumber.toString() && evt.value !== pdfViewer.currentPageLabel) { + this.toolbar?.setPageNumber(pdfViewer.currentPageNumber, pdfViewer.currentPageLabel); + } +} +function onImageAltTextSettings() { + this.imageAltTextSettings?.open({ + enableGuessAltText: AppOptions.get("enableGuessAltText"), + enableNewAltTextWhenAddingImage: AppOptions.get("enableNewAltTextWhenAddingImage") + }); +} +function onFindFromUrlHash(evt) { + this.eventBus.dispatch("find", { + source: evt.source, + type: "", + query: evt.query, + caseSensitive: false, + entireWord: false, + highlightAll: true, + findPrevious: false, + matchDiacritics: true + }); +} +function onUpdateFindMatchesCount({ + matchesCount +}) { + if (this.supportsIntegratedFind) { + this.externalServices.updateFindMatchesCount(matchesCount); + } else { + this.findBar?.updateResultsCount(matchesCount); + } +} +function onUpdateFindControlState({ + state, + previous, + entireWord, + matchesCount, + rawQuery +}) { + if (this.supportsIntegratedFind) { + this.externalServices.updateFindControlState({ + result: state, + findPrevious: previous, + entireWord, + matchesCount, + rawQuery + }); + } else { + this.findBar?.updateUIState(state, previous, matchesCount); + } +} +function onScaleChanging(evt) { + this.toolbar?.setPageScale(evt.presetValue, evt.scale); + this.pdfViewer.update(); +} +function onRotationChanging(evt) { + if (this.pdfThumbnailViewer) { + this.pdfThumbnailViewer.pagesRotation = evt.pagesRotation; + } + this.forceRendering(); + this.pdfViewer.currentPageNumber = evt.pageNumber; +} +function onPageChanging({ + pageNumber, + pageLabel +}) { + this.toolbar?.setPageNumber(pageNumber, pageLabel); + this.secondaryToolbar?.setPageNumber(pageNumber); + if (this.pdfSidebar?.visibleView === SidebarView.THUMBS) { + this.pdfThumbnailViewer?.scrollThumbnailIntoView(pageNumber); + } + const currentPage = this.pdfViewer.getPageView(pageNumber - 1); + this.toolbar?.updateLoadingIndicatorState(currentPage?.renderingState === RenderingStates.RUNNING); +} +function onWheel(evt) { + const { + pdfViewer, + supportsMouseWheelZoomCtrlKey, + supportsMouseWheelZoomMetaKey, + supportsPinchToZoom + } = this; + if (pdfViewer.isInPresentationMode) { + return; + } + const deltaMode = evt.deltaMode; + let scaleFactor = Math.exp(-evt.deltaY / 100); + const isBuiltInMac = false; + const isPinchToZoom = evt.ctrlKey && !this._isCtrlKeyDown && deltaMode === WheelEvent.DOM_DELTA_PIXEL && evt.deltaX === 0 && (Math.abs(scaleFactor - 1) < 0.05 || isBuiltInMac) && evt.deltaZ === 0; + const origin = [evt.clientX, evt.clientY]; + if (isPinchToZoom || evt.ctrlKey && supportsMouseWheelZoomCtrlKey || evt.metaKey && supportsMouseWheelZoomMetaKey) { + evt.preventDefault(); + if (this._isScrolling || document.visibilityState === "hidden" || this.overlayManager.active) { + return; + } + if (isPinchToZoom && supportsPinchToZoom) { + scaleFactor = this._accumulateFactor(pdfViewer.currentScale, scaleFactor, "_wheelUnusedFactor"); + this.updateZoom(null, scaleFactor, origin); + } else { + const delta = normalizeWheelEventDirection(evt); + let ticks = 0; + if (deltaMode === WheelEvent.DOM_DELTA_LINE || deltaMode === WheelEvent.DOM_DELTA_PAGE) { + ticks = Math.abs(delta) >= 1 ? Math.sign(delta) : this._accumulateTicks(delta, "_wheelUnusedTicks"); + } else { + const PIXELS_PER_LINE_SCALE = 30; + ticks = this._accumulateTicks(delta / PIXELS_PER_LINE_SCALE, "_wheelUnusedTicks"); + } + this.updateZoom(ticks, null, origin); + } + } +} +function closeSecondaryToolbar(evt) { + if (!this.secondaryToolbar?.isOpen) { + return; + } + const appConfig = this.appConfig; + if (this.pdfViewer.containsElement(evt.target) || appConfig.toolbar?.container.contains(evt.target) && !appConfig.secondaryToolbar?.toggleButton.contains(evt.target)) { + this.secondaryToolbar.close(); + } +} +function closeEditorUndoBar(evt) { + if (!this.editorUndoBar?.isOpen) { + return; + } + if (this.appConfig.secondaryToolbar?.toolbar.contains(evt.target)) { + this.editorUndoBar.hide(); + } +} +function onClick(evt) { + closeSecondaryToolbar.call(this, evt); + closeEditorUndoBar.call(this, evt); +} +function onKeyUp(evt) { + if (evt.key === "Control") { + this._isCtrlKeyDown = false; + } +} +function onKeyDown(evt) { + this._isCtrlKeyDown = evt.key === "Control"; + if (this.editorUndoBar?.isOpen && evt.keyCode !== 9 && evt.keyCode !== 16 && !((evt.keyCode === 13 || evt.keyCode === 32) && getActiveOrFocusedElement() === this.appConfig.editorUndoBar.undoButton)) { + this.editorUndoBar.hide(); + } + if (this.overlayManager.active) { + return; + } + const { + eventBus, + pdfViewer + } = this; + const isViewerInPresentationMode = pdfViewer.isInPresentationMode; + let handled = false, + ensureViewerFocused = false; + const cmd = (evt.ctrlKey ? 1 : 0) | (evt.altKey ? 2 : 0) | (evt.shiftKey ? 4 : 0) | (evt.metaKey ? 8 : 0); + if (cmd === 1 || cmd === 8 || cmd === 5 || cmd === 12) { + switch (evt.keyCode) { + case 70: + if (!this.supportsIntegratedFind && !evt.shiftKey) { + this.findBar?.open(); + handled = true; + } + break; + case 71: + if (!this.supportsIntegratedFind) { + const { + state + } = this.findController; + if (state) { + const newState = { + source: window, + type: "again", + findPrevious: cmd === 5 || cmd === 12 + }; + eventBus.dispatch("find", { + ...state, + ...newState + }); + } + handled = true; + } + break; + case 61: + case 107: + case 187: + case 171: + this.zoomIn(); + handled = true; + break; + case 173: + case 109: + case 189: + this.zoomOut(); + handled = true; + break; + case 48: + case 96: + if (!isViewerInPresentationMode) { + setTimeout(() => { + this.zoomReset(); + }); + handled = false; + } + break; + case 38: + if (isViewerInPresentationMode || this.page > 1) { + this.page = 1; + handled = true; + ensureViewerFocused = true; + } + break; + case 40: + if (isViewerInPresentationMode || this.page < this.pagesCount) { + this.page = this.pagesCount; + handled = true; + ensureViewerFocused = true; + } + break; + } + } + if (cmd === 1 || cmd === 8) { + switch (evt.keyCode) { + case 83: + eventBus.dispatch("download", { + source: window + }); + handled = true; + break; + case 79: + { + eventBus.dispatch("openfile", { + source: window + }); + handled = true; + } + break; + } + } + if (cmd === 3 || cmd === 10) { + switch (evt.keyCode) { + case 80: + this.requestPresentationMode(); + handled = true; + this.externalServices.reportTelemetry({ + type: "buttons", + data: { + id: "presentationModeKeyboard" + } + }); + break; + case 71: + if (this.appConfig.toolbar) { + this.appConfig.toolbar.pageNumber.select(); + handled = true; + } + break; + } + } + if (handled) { + if (ensureViewerFocused && !isViewerInPresentationMode) { + pdfViewer.focus(); + } + evt.preventDefault(); + return; + } + const curElement = getActiveOrFocusedElement(); + const curElementTagName = curElement?.tagName.toUpperCase(); + if (curElementTagName === "INPUT" || curElementTagName === "TEXTAREA" || curElementTagName === "SELECT" || curElementTagName === "BUTTON" && (evt.keyCode === 13 || evt.keyCode === 32) || curElement?.isContentEditable) { + if (evt.keyCode !== 27) { + return; + } + } + if (cmd === 0) { + let turnPage = 0, + turnOnlyIfPageFit = false; + switch (evt.keyCode) { + case 38: + if (this.supportsCaretBrowsingMode) { + this.moveCaret(true, false); + handled = true; + break; + } + case 33: + if (pdfViewer.isVerticalScrollbarEnabled) { + turnOnlyIfPageFit = true; + } + turnPage = -1; + break; + case 8: + if (!isViewerInPresentationMode) { + turnOnlyIfPageFit = true; + } + turnPage = -1; + break; + case 37: + if (this.supportsCaretBrowsingMode) { + return; + } + if (pdfViewer.isHorizontalScrollbarEnabled) { + turnOnlyIfPageFit = true; + } + case 75: + case 80: + turnPage = -1; + break; + case 27: + if (this.secondaryToolbar?.isOpen) { + this.secondaryToolbar.close(); + handled = true; + } + if (!this.supportsIntegratedFind && this.findBar?.opened) { + this.findBar.close(); + handled = true; + } + break; + case 40: + if (this.supportsCaretBrowsingMode) { + this.moveCaret(false, false); + handled = true; + break; + } + case 34: + if (pdfViewer.isVerticalScrollbarEnabled) { + turnOnlyIfPageFit = true; + } + turnPage = 1; + break; + case 13: + case 32: + if (!isViewerInPresentationMode) { + turnOnlyIfPageFit = true; + } + turnPage = 1; + break; + case 39: + if (this.supportsCaretBrowsingMode) { + return; + } + if (pdfViewer.isHorizontalScrollbarEnabled) { + turnOnlyIfPageFit = true; + } + case 74: + case 78: + turnPage = 1; + break; + case 36: + if (isViewerInPresentationMode || this.page > 1) { + this.page = 1; + handled = true; + ensureViewerFocused = true; + } + break; + case 35: + if (isViewerInPresentationMode || this.page < this.pagesCount) { + this.page = this.pagesCount; + handled = true; + ensureViewerFocused = true; + } + break; + case 83: + this.pdfCursorTools?.switchTool(CursorTool.SELECT); + break; + case 72: + this.pdfCursorTools?.switchTool(CursorTool.HAND); + break; + case 82: + this.rotatePages(90); + break; + case 115: + this.pdfSidebar?.toggle(); + break; + } + if (turnPage !== 0 && (!turnOnlyIfPageFit || pdfViewer.currentScaleValue === "page-fit")) { + if (turnPage > 0) { + pdfViewer.nextPage(); + } else { + pdfViewer.previousPage(); + } + handled = true; + } + } + if (cmd === 4) { + switch (evt.keyCode) { + case 13: + case 32: + if (!isViewerInPresentationMode && pdfViewer.currentScaleValue !== "page-fit") { + break; + } + pdfViewer.previousPage(); + handled = true; + break; + case 38: + this.moveCaret(true, true); + handled = true; + break; + case 40: + this.moveCaret(false, true); + handled = true; + break; + case 82: + this.rotatePages(-90); + break; + } + } + if (!handled && !isViewerInPresentationMode) { + if (evt.keyCode >= 33 && evt.keyCode <= 40 || evt.keyCode === 32 && curElementTagName !== "BUTTON") { + ensureViewerFocused = true; + } + } + if (ensureViewerFocused && !pdfViewer.containsElement(curElement)) { + pdfViewer.focus(); + } + if (handled) { + evt.preventDefault(); + } +} +function beforeUnload(evt) { + evt.preventDefault(); + evt.returnValue = ""; + return false; +} + +;// ./web/viewer.js + + + + +const pdfjsVersion = "4.10.38"; +const pdfjsBuild = "f9bea397f"; +const AppConstants = { + LinkTarget: LinkTarget, + RenderingStates: RenderingStates, + ScrollMode: ScrollMode, + SpreadMode: SpreadMode +}; +window.PDFViewerApplication = PDFViewerApplication; +window.PDFViewerApplicationConstants = AppConstants; +window.PDFViewerApplicationOptions = AppOptions; +function getViewerConfiguration() { + return { + appContainer: document.body, + principalContainer: document.getElementById("mainContainer"), + mainContainer: document.getElementById("viewerContainer"), + viewerContainer: document.getElementById("viewer"), + toolbar: { + container: document.getElementById("toolbarContainer"), + numPages: document.getElementById("numPages"), + pageNumber: document.getElementById("pageNumber"), + scaleSelect: document.getElementById("scaleSelect"), + customScaleOption: document.getElementById("customScaleOption"), + previous: document.getElementById("previous"), + next: document.getElementById("next"), + zoomIn: document.getElementById("zoomInButton"), + zoomOut: document.getElementById("zoomOutButton"), + print: document.getElementById("printButton"), + editorFreeTextButton: document.getElementById("editorFreeTextButton"), + editorFreeTextParamsToolbar: document.getElementById("editorFreeTextParamsToolbar"), + editorHighlightButton: document.getElementById("editorHighlightButton"), + editorHighlightParamsToolbar: document.getElementById("editorHighlightParamsToolbar"), + editorHighlightColorPicker: document.getElementById("editorHighlightColorPicker"), + editorInkButton: document.getElementById("editorInkButton"), + editorInkParamsToolbar: document.getElementById("editorInkParamsToolbar"), + editorStampButton: document.getElementById("editorStampButton"), + editorStampParamsToolbar: document.getElementById("editorStampParamsToolbar"), + download: document.getElementById("downloadButton") + }, + secondaryToolbar: { + toolbar: document.getElementById("secondaryToolbar"), + toggleButton: document.getElementById("secondaryToolbarToggleButton"), + presentationModeButton: document.getElementById("presentationMode"), + openFileButton: document.getElementById("secondaryOpenFile"), + printButton: document.getElementById("secondaryPrint"), + downloadButton: document.getElementById("secondaryDownload"), + viewBookmarkButton: document.getElementById("viewBookmark"), + firstPageButton: document.getElementById("firstPage"), + lastPageButton: document.getElementById("lastPage"), + pageRotateCwButton: document.getElementById("pageRotateCw"), + pageRotateCcwButton: document.getElementById("pageRotateCcw"), + cursorSelectToolButton: document.getElementById("cursorSelectTool"), + cursorHandToolButton: document.getElementById("cursorHandTool"), + scrollPageButton: document.getElementById("scrollPage"), + scrollVerticalButton: document.getElementById("scrollVertical"), + scrollHorizontalButton: document.getElementById("scrollHorizontal"), + scrollWrappedButton: document.getElementById("scrollWrapped"), + spreadNoneButton: document.getElementById("spreadNone"), + spreadOddButton: document.getElementById("spreadOdd"), + spreadEvenButton: document.getElementById("spreadEven"), + imageAltTextSettingsButton: document.getElementById("imageAltTextSettings"), + imageAltTextSettingsSeparator: document.getElementById("imageAltTextSettingsSeparator"), + documentPropertiesButton: document.getElementById("documentProperties") + }, + sidebar: { + outerContainer: document.getElementById("outerContainer"), + sidebarContainer: document.getElementById("sidebarContainer"), + toggleButton: document.getElementById("sidebarToggleButton"), + resizer: document.getElementById("sidebarResizer"), + thumbnailButton: document.getElementById("viewThumbnail"), + outlineButton: document.getElementById("viewOutline"), + attachmentsButton: document.getElementById("viewAttachments"), + layersButton: document.getElementById("viewLayers"), + thumbnailView: document.getElementById("thumbnailView"), + outlineView: document.getElementById("outlineView"), + attachmentsView: document.getElementById("attachmentsView"), + layersView: document.getElementById("layersView"), + currentOutlineItemButton: document.getElementById("currentOutlineItem") + }, + findBar: { + bar: document.getElementById("findbar"), + toggleButton: document.getElementById("viewFindButton"), + findField: document.getElementById("findInput"), + highlightAllCheckbox: document.getElementById("findHighlightAll"), + caseSensitiveCheckbox: document.getElementById("findMatchCase"), + matchDiacriticsCheckbox: document.getElementById("findMatchDiacritics"), + entireWordCheckbox: document.getElementById("findEntireWord"), + findMsg: document.getElementById("findMsg"), + findResultsCount: document.getElementById("findResultsCount"), + findPreviousButton: document.getElementById("findPreviousButton"), + findNextButton: document.getElementById("findNextButton") + }, + passwordOverlay: { + dialog: document.getElementById("passwordDialog"), + label: document.getElementById("passwordText"), + input: document.getElementById("password"), + submitButton: document.getElementById("passwordSubmit"), + cancelButton: document.getElementById("passwordCancel") + }, + documentProperties: { + dialog: document.getElementById("documentPropertiesDialog"), + closeButton: document.getElementById("documentPropertiesClose"), + fields: { + fileName: document.getElementById("fileNameField"), + fileSize: document.getElementById("fileSizeField"), + title: document.getElementById("titleField"), + author: document.getElementById("authorField"), + subject: document.getElementById("subjectField"), + keywords: document.getElementById("keywordsField"), + creationDate: document.getElementById("creationDateField"), + modificationDate: document.getElementById("modificationDateField"), + creator: document.getElementById("creatorField"), + producer: document.getElementById("producerField"), + version: document.getElementById("versionField"), + pageCount: document.getElementById("pageCountField"), + pageSize: document.getElementById("pageSizeField"), + linearized: document.getElementById("linearizedField") + } + }, + altTextDialog: { + dialog: document.getElementById("altTextDialog"), + optionDescription: document.getElementById("descriptionButton"), + optionDecorative: document.getElementById("decorativeButton"), + textarea: document.getElementById("descriptionTextarea"), + cancelButton: document.getElementById("altTextCancel"), + saveButton: document.getElementById("altTextSave") + }, + newAltTextDialog: { + dialog: document.getElementById("newAltTextDialog"), + title: document.getElementById("newAltTextTitle"), + descriptionContainer: document.getElementById("newAltTextDescriptionContainer"), + textarea: document.getElementById("newAltTextDescriptionTextarea"), + disclaimer: document.getElementById("newAltTextDisclaimer"), + learnMore: document.getElementById("newAltTextLearnMore"), + imagePreview: document.getElementById("newAltTextImagePreview"), + createAutomatically: document.getElementById("newAltTextCreateAutomatically"), + createAutomaticallyButton: document.getElementById("newAltTextCreateAutomaticallyButton"), + downloadModel: document.getElementById("newAltTextDownloadModel"), + downloadModelDescription: document.getElementById("newAltTextDownloadModelDescription"), + error: document.getElementById("newAltTextError"), + errorCloseButton: document.getElementById("newAltTextCloseButton"), + cancelButton: document.getElementById("newAltTextCancel"), + notNowButton: document.getElementById("newAltTextNotNow"), + saveButton: document.getElementById("newAltTextSave") + }, + altTextSettingsDialog: { + dialog: document.getElementById("altTextSettingsDialog"), + createModelButton: document.getElementById("createModelButton"), + aiModelSettings: document.getElementById("aiModelSettings"), + learnMore: document.getElementById("altTextSettingsLearnMore"), + deleteModelButton: document.getElementById("deleteModelButton"), + downloadModelButton: document.getElementById("downloadModelButton"), + showAltTextDialogButton: document.getElementById("showAltTextDialogButton"), + altTextSettingsCloseButton: document.getElementById("altTextSettingsCloseButton"), + closeButton: document.getElementById("altTextSettingsCloseButton") + }, + annotationEditorParams: { + editorFreeTextFontSize: document.getElementById("editorFreeTextFontSize"), + editorFreeTextColor: document.getElementById("editorFreeTextColor"), + editorInkColor: document.getElementById("editorInkColor"), + editorInkThickness: document.getElementById("editorInkThickness"), + editorInkOpacity: document.getElementById("editorInkOpacity"), + editorStampAddImage: document.getElementById("editorStampAddImage"), + editorFreeHighlightThickness: document.getElementById("editorFreeHighlightThickness"), + editorHighlightShowAll: document.getElementById("editorHighlightShowAll") + }, + printContainer: document.getElementById("printContainer"), + editorUndoBar: { + container: document.getElementById("editorUndoBar"), + message: document.getElementById("editorUndoBarMessage"), + undoButton: document.getElementById("editorUndoBarUndoButton"), + closeButton: document.getElementById("editorUndoBarCloseButton") + } + }; +} +function webViewerLoad() { + const config = getViewerConfiguration(); + const event = new CustomEvent("webviewerloaded", { + bubbles: true, + cancelable: true, + detail: { + source: window + } + }); + try { + parent.document.dispatchEvent(event); + } catch (ex) { + console.error("webviewerloaded:", ex); + document.dispatchEvent(event); + } + PDFViewerApplication.run(config); +} +document.blockUnblockOnload?.(true); +if (document.readyState === "interactive" || document.readyState === "complete") { + webViewerLoad(); +} else { + document.addEventListener("DOMContentLoaded", webViewerLoad, true); +} + +var __webpack_exports__PDFViewerApplication = __webpack_exports__.PDFViewerApplication; +var __webpack_exports__PDFViewerApplicationConstants = __webpack_exports__.PDFViewerApplicationConstants; +var __webpack_exports__PDFViewerApplicationOptions = __webpack_exports__.PDFViewerApplicationOptions; +export { __webpack_exports__PDFViewerApplication as PDFViewerApplication, __webpack_exports__PDFViewerApplicationConstants as PDFViewerApplicationConstants, __webpack_exports__PDFViewerApplicationOptions as PDFViewerApplicationOptions }; + +//# sourceMappingURL=viewer.mjs.map \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 6e432c5..82f495b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -157,6 +157,7 @@ import { RegisterName } from "./components/RegisterName"; import { BuyQortInformation } from "./components/BuyQortInformation"; import { QortPayment } from "./components/QortPayment"; import { GeneralNotifications } from "./components/GeneralNotifications"; +import { PdfViewer } from "./common/PdfViewer"; type extStates = | "not-authenticated" @@ -1939,6 +1940,7 @@ function App() { // backgroundRepeat: desktopViewMode === "apps" && "no-repeat", }} > + { + const [pdfUrl, setPdfUrl] = useState(""); + + const openPdf = (e) => { + try { + const blob = e.detail?.blob; + if (blob) { + // Create Object URL + const url = URL.createObjectURL(blob); + setPdfUrl(url); + } + } catch (error) { + console.error(error); + } + }; + + useEffect(() => { + return () => { + if (pdfUrl) { + URL.revokeObjectURL(pdfUrl); + } + }; + }, [pdfUrl]); + + useEffect(() => { + subscribeToEvent("openPdf", openPdf); + + return () => { + unsubscribeFromEvent("openPdf", openPdf); + }; + }, []); + + if (!pdfUrl) return null; + + return ( + + + + + + + ); } -}; - - - return ( - - - - - - ); -}); +); diff --git a/src/components/Apps/AppViewerContainer.tsx b/src/components/Apps/AppViewerContainer.tsx index 194ae68..aa60a6c 100644 --- a/src/components/Apps/AppViewerContainer.tsx +++ b/src/components/Apps/AppViewerContainer.tsx @@ -1,20 +1,19 @@ -import React, { useContext, } from 'react'; +import React, { useContext } from 'react'; import { AppViewer } from './AppViewer'; import Frame from 'react-frame-component'; -import { MyContext, isMobile } from '../../App'; +import { MyContext } from '../../App'; -const AppViewerContainer = React.forwardRef(({ app, isSelected, hide, isDevMode, customHeight, skipAuth }, ref) => { - const { rootHeight } = useContext(MyContext); +const AppViewerContainer = React.forwardRef( + ({ app, isSelected, hide, isDevMode, customHeight, skipAuth }, ref) => { + const { rootHeight } = useContext(MyContext); - - return ( - - - - } - style={{ - position: (!isSelected || hide) && 'fixed', - left: (!isSelected || hide) && '-200vw', - height: customHeight ? customHeight : !isMobile ? '100vh' : `calc(${rootHeight} - 60px - 45px)`, - border: 'none', - width: '100%', - overflow: 'hidden', - }} - > - - - ); -}); + + + } + style={{ + border: 'none', + height: '100vh', + left: (!isSelected || hide) && '-200vw', + overflow: 'hidden', + position: (!isSelected || hide) && 'fixed', + width: '100%', + }} + > + + + ); + } +); export default AppViewerContainer; diff --git a/src/components/Apps/Apps-styles.tsx b/src/components/Apps/Apps-styles.tsx index 18e3af1..152c09a 100644 --- a/src/components/Apps/Apps-styles.tsx +++ b/src/components/Apps/Apps-styles.tsx @@ -137,9 +137,9 @@ export const AppCircle = styled(Box)(({ theme }) => ({ theme.palette.mode === 'dark' ? 'rgb(209, 209, 209)' : 'rgba(41, 41, 43, 1)', - borderWidth: '1px', borderRadius: '50%', borderStyle: 'solid', + borderWidth: '1px', color: theme.palette.text.primary, display: 'flex', flexDirection: 'column', diff --git a/src/components/Apps/AppsDevModeHome.tsx b/src/components/Apps/AppsDevModeHome.tsx index 8c346c2..0fc8242 100644 --- a/src/components/Apps/AppsDevModeHome.tsx +++ b/src/components/Apps/AppsDevModeHome.tsx @@ -22,7 +22,7 @@ import { Input, } from '@mui/material'; import { Add } from '@mui/icons-material'; -import { MyContext, getBaseApiReact, isMobile } from '../../App'; +import { MyContext, getBaseApiReact } from '../../App'; import LogoSelected from '../../assets/svgs/LogoSelected.svg'; import { executeEvent } from '../../utils/events'; import { Spacer } from '../../common/Spacer'; @@ -279,7 +279,9 @@ export const AppsDevModeHome = ({ Dev Mode Apps + + @@ -302,6 +304,7 @@ export const AppsDevModeHome = ({ Server + { addPreviewApp(); @@ -309,15 +312,17 @@ export const AppsDevModeHome = ({ > + + Zip + { addPreviewAppWithDirectory(); @@ -325,7 +330,7 @@ export const AppsDevModeHome = ({ > @@ -334,6 +339,7 @@ export const AppsDevModeHome = ({ Directory + { executeEvent('appsDevModeAddTab', { @@ -347,7 +353,7 @@ export const AppsDevModeHome = ({ > @@ -371,9 +377,11 @@ export const AppsDevModeHome = ({ /> + Q-Sandbox + { executeEvent('appsDevModeAddTab', { @@ -387,7 +395,7 @@ export const AppsDevModeHome = ({ > @@ -411,10 +419,12 @@ export const AppsDevModeHome = ({ /> + API + {isShow && ( {'Add custom framework'} + + - + + + {combinedListTempAndReal.map((message, index, list) => { let fullMessage = message; @@ -780,17 +759,17 @@ export const Thread = ({ > @@ -812,6 +791,7 @@ export const Thread = ({ {message?.name?.charAt(0)} + + {formatTimestampForum(message?.created)} + @@ -908,6 +890,7 @@ export const Thread = ({ {message?.name?.charAt(0)} + + {formatTimestampForum(message?.created)} + @@ -952,9 +937,9 @@ export const Thread = ({ + {loading && promotions.length === 0 && ( @@ -589,6 +583,7 @@ export const ListOfGroupPromotions = () => { > Group name: {` ${promotion?.groupName}`} + { Number of members:{' '} {` ${promotion?.memberCount}`} + {promotion?.description && ( { {promotion?.description} )} + {promotion?.isOpen === false && ( { your request )} + + { > Close + { > {promotion?.name?.charAt(0)} + { {promotion?.name} + { {promotion?.groupName} + + { : 'Private group'} + + { > {promotion?.data} + + { + @@ -779,6 +788,7 @@ export const ListOfGroupPromotions = () => { + {isShowModal && ( diff --git a/src/components/Group/ListOfThreadPostsWatched.tsx b/src/components/Group/ListOfThreadPostsWatched.tsx index 6e24ba3..e0a9d00 100644 --- a/src/components/Group/ListOfThreadPostsWatched.tsx +++ b/src/components/Group/ListOfThreadPostsWatched.tsx @@ -1,21 +1,14 @@ -import * as React from "react"; -import List from "@mui/material/List"; -import ListItem from "@mui/material/ListItem"; -import ListItemButton from "@mui/material/ListItemButton"; -import ListItemIcon from "@mui/material/ListItemIcon"; -import ListItemText from "@mui/material/ListItemText"; -import Checkbox from "@mui/material/Checkbox"; -import IconButton from "@mui/material/IconButton"; -import CommentIcon from "@mui/icons-material/Comment"; -import InfoIcon from "@mui/icons-material/Info"; -import GroupAddIcon from "@mui/icons-material/GroupAdd"; -import { executeEvent } from "../../utils/events"; -import { Box, Typography } from "@mui/material"; -import { Spacer } from "../../common/Spacer"; -import { getGroupNames } from "./UserListOfInvites"; -import { CustomLoader } from "../../common/CustomLoader"; -import VisibilityIcon from "@mui/icons-material/Visibility"; -import { isMobile } from "../../App"; +import * as React from 'react'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemText from '@mui/material/ListItemText'; +import IconButton from '@mui/material/IconButton'; +import { executeEvent } from '../../utils/events'; +import { Box, Typography } from '@mui/material'; +import { Spacer } from '../../common/Spacer'; +import { CustomLoader } from '../../common/CustomLoader'; +import VisibilityIcon from '@mui/icons-material/Visibility'; export const ListOfThreadPostsWatched = () => { const [posts, setPosts] = React.useState([]); @@ -24,33 +17,33 @@ export const ListOfThreadPostsWatched = () => { const getPosts = async () => { try { await new Promise((res, rej) => { - window.sendMessage("getThreadActivity", {}) - .then((response) => { - if (!response?.error) { - if (!response) { - res(null); + window + .sendMessage('getThreadActivity', {}) + .then((response) => { + if (!response?.error) { + if (!response) { + res(null); + return; + } + const uniquePosts = response.reduce((acc, current) => { + const x = acc.find( + (item) => item?.thread?.threadId === current?.thread?.threadId + ); + if (!x) { + return acc.concat([current]); + } else { + return acc; + } + }, []); + setPosts(uniquePosts); + res(uniquePosts); return; } - const uniquePosts = response.reduce((acc, current) => { - const x = acc.find( - (item) => item?.thread?.threadId === current?.thread?.threadId - ); - if (!x) { - return acc.concat([current]); - } else { - return acc; - } - }, []); - setPosts(uniquePosts); - res(uniquePosts); - return; - } - rej(response.error); - }) - .catch((error) => { - rej(error.message || "An error occurred"); - }); - + rej(response.error); + }) + .catch((error) => { + rej(error.message || 'An error occurred'); + }); }); } catch (error) { } finally { @@ -63,49 +56,50 @@ export const ListOfThreadPostsWatched = () => { }, []); return ( - + New Thread Posts: - + {loading && posts.length === 0 && ( @@ -114,19 +108,18 @@ export const ListOfThreadPostsWatched = () => { {!loading && posts.length === 0 && ( Nothing to display @@ -134,47 +127,46 @@ export const ListOfThreadPostsWatched = () => { )} {posts?.length > 0 && ( - - {posts?.map((post) => { - return ( - { - executeEvent("openThreadNewPost", { - data: post, - }); - }} - disablePadding - secondaryAction={ - - - - } - > - - - - - ); - })} - + + {posts?.map((post) => { + return ( + { + executeEvent('openThreadNewPost', { + data: post, + }); + }} + disablePadding + secondaryAction={ + + + + } + > + + + + + ); + })} + )} - ); diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index de361fa..a69dde4 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -1,10 +1,6 @@ import * as React from 'react'; import Button from '@mui/material/Button'; import Dialog from '@mui/material/Dialog'; -import ListItemText from '@mui/material/ListItemText'; -import ListItemButton from '@mui/material/ListItemButton'; -import List from '@mui/material/List'; -import Divider from '@mui/material/Divider'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; import IconButton from '@mui/material/IconButton'; @@ -19,7 +15,7 @@ import { ListOfBans } from './ListOfBans'; import { ListOfJoinRequests } from './ListOfJoinRequests'; import { Box, ButtonBase, Card, Tab, Tabs } from '@mui/material'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; -import { MyContext, getBaseApiReact, isMobile } from '../../App'; +import { MyContext, getBaseApiReact } from '../../App'; import { getGroupMembers, getNames } from './Group'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { getFee } from '../../background'; @@ -27,6 +23,7 @@ import { LoadingButton } from '@mui/lab'; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { Spacer } from '../../common/Spacer'; import InsertLinkIcon from '@mui/icons-material/InsertLink'; + function a11yProps(index: number) { return { id: `simple-tab-${index}`, @@ -193,9 +190,9 @@ export const ManageMembers = ({ @@ -221,9 +218,10 @@ export const ManageMembers = ({ '&.Mui-selected': { color: 'white', }, - fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile + fontSize: '1rem', }} /> + + + + + GroupId: {groupInfo?.groupId} + GroupName: {groupInfo?.groupName} + Number of members: {groupInfo?.memberCount} + Join Group Link + + {selectedGroup?.groupId && !isOwner && ( Load members with names + + )} + + { borderRadius: '19px', display: 'flex', flexDirection: 'column', - height: isMobile ? '165px' : '250px', + height: '250px', overflow: 'auto', padding: '20px', width: '322px', diff --git a/src/components/Group/ThingsToDoInitial.tsx b/src/components/Group/ThingsToDoInitial.tsx index 367b3b1..023cfde 100644 --- a/src/components/Group/ThingsToDoInitial.tsx +++ b/src/components/Group/ThingsToDoInitial.tsx @@ -1,28 +1,23 @@ -import * as React from "react"; -import List from "@mui/material/List"; -import ListItem from "@mui/material/ListItem"; -import ListItemButton from "@mui/material/ListItemButton"; -import ListItemIcon from "@mui/material/ListItemIcon"; -import ListItemText from "@mui/material/ListItemText"; -import Checkbox from "@mui/material/Checkbox"; -import IconButton from "@mui/material/IconButton"; -import CommentIcon from "@mui/icons-material/Comment"; -import InfoIcon from "@mui/icons-material/Info"; -import { Box, Typography } from "@mui/material"; -import { Spacer } from "../../common/Spacer"; -import { isMobile } from "../../App"; -import { QMailMessages } from "./QMailMessages"; -import { executeEvent } from "../../utils/events"; +import * as React from 'react'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import { Box, Typography } from '@mui/material'; +import { Spacer } from '../../common/Spacer'; +import { QMailMessages } from './QMailMessages'; +import { executeEvent } from '../../utils/events'; -export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance, userInfo }) => { +export const ThingsToDoInitial = ({ + myAddress, + name, + hasGroups, + balance, + userInfo, +}) => { const [checked1, setChecked1] = React.useState(false); const [checked2, setChecked2] = React.useState(false); - // const [checked3, setChecked3] = React.useState(false); - - // React.useEffect(() => { - // if (hasGroups) setChecked3(true); - // }, [hasGroups]); - React.useEffect(() => { if (balance && +balance >= 6) { @@ -30,111 +25,114 @@ export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance, userInf } }, [balance]); - React.useEffect(() => { if (name) setChecked2(true); }, [name]); + const isLoaded = React.useMemo(() => { + if (userInfo !== null) return true; + return false; + }, [userInfo]); - const isLoaded = React.useMemo(()=> { - if(userInfo !== null) return true - return false - }, [ userInfo]) + const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(() => { + if (isLoaded && checked1 && checked2) return true; + return false; + }, [checked1, isLoaded, checked2]); - const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(()=> { - if(isLoaded && checked1 && checked2) return true - return false -}, [checked1, isLoaded, checked2]) - -if(hasDoneNameAndBalanceAndIsLoaded){ - return ( - - ); -} -if(!isLoaded) return null + if (hasDoneNameAndBalanceAndIsLoaded) { + return ( + + ); + } + if (!isLoaded) return null; return ( - {!isLoaded ? 'Loading...' : 'Getting Started' } + {!isLoaded ? 'Loading...' : 'Getting Started'} + {isLoaded && ( - - - { - executeEvent("openBuyQortInfo", {}) - }} - > - - - - {/* + + { + executeEvent('openBuyQortInfo', {}); + }} + > + + + + {/* */} - - - - - // - // - // } - disablePadding - > - - - { - executeEvent('openRegisterName', {}) - }} sx={{ - "& .MuiTypography-root": { - fontSize: "1rem", - fontWeight: 400, - }, - }} primary={`Register a name`} /> - - - - - - {/* + + + + // + // + // } + disablePadding + > + + { + executeEvent('openRegisterName', {}); + }} + sx={{ + '& .MuiTypography-root': { + fontSize: '1rem', + fontWeight: 400, + }, + }} + primary={`Register a name`} + /> + + + + + + {/* */} - + )} - ); diff --git a/src/components/ReactionPicker.tsx b/src/components/ReactionPicker.tsx index 2f5f08e..c95adcf 100644 --- a/src/components/ReactionPicker.tsx +++ b/src/components/ReactionPicker.tsx @@ -1,9 +1,8 @@ -import React, { useState, useRef, useEffect } from 'react'; +import { useState, useRef, useEffect } from 'react'; import ReactDOM from 'react-dom'; import Picker, { EmojiStyle, Theme } from 'emoji-picker-react'; import './ReactionPicker.css'; import { ButtonBase } from '@mui/material'; -import { isMobile } from '../App'; export const ReactionPicker = ({ onReaction }) => { const [showPicker, setShowPicker] = useState(false); @@ -30,7 +29,7 @@ export const ReactionPicker = ({ onReaction }) => { } else { // Get the button's position const buttonRect = buttonRef.current.getBoundingClientRect(); - const pickerWidth = isMobile ? 300 : 350; // Adjust based on picker width + const pickerWidth = 350; // Calculate position to align the right edge of the picker with the button's right edge setPickerPosition({ @@ -90,15 +89,15 @@ export const ReactionPicker = ({ onReaction }) => { }} > , document.body diff --git a/src/components/TaskManager/TaskManager.tsx b/src/components/TaskManager/TaskManager.tsx index f1f075b..673c385 100644 --- a/src/components/TaskManager/TaskManager.tsx +++ b/src/components/TaskManager/TaskManager.tsx @@ -12,7 +12,7 @@ import PendingIcon from '@mui/icons-material/Pending'; import TaskAltIcon from '@mui/icons-material/TaskAlt'; import ExpandLess from '@mui/icons-material/ExpandLess'; import ExpandMore from '@mui/icons-material/ExpandMore'; -import { MyContext, getBaseApiReact, isMobile } from '../../App'; +import { MyContext, getBaseApiReact } from '../../App'; import { executeEvent } from '../../utils/events'; export const TaskManager = ({ getUserInfo }) => { @@ -141,8 +141,7 @@ export const TaskManager = ({ getUserInfo }) => { }); }, [txList]); - if (isMobile || txList?.length === 0 || txList.every((item) => item?.done)) - return null; + if (txList?.length === 0 || txList.every((item) => item?.done)) return null; return ( <> diff --git a/src/useAppFullscreen.tsx b/src/useAppFullscreen.tsx index 3dbe4a1..1ce6842 100644 --- a/src/useAppFullscreen.tsx +++ b/src/useAppFullscreen.tsx @@ -1,67 +1,85 @@ import { useCallback, useEffect } from 'react'; -import { isMobile } from './App'; export const useAppFullScreen = (setFullScreen) => { - const enterFullScreen = useCallback(() => { - const element = document.documentElement; // Target the entire HTML document - if (element.requestFullscreen) { - element.requestFullscreen(); - } else if (element.mozRequestFullScreen) { // Firefox - element.mozRequestFullScreen(); - } else if (element.webkitRequestFullscreen) { // Chrome, Safari and Opera - element.webkitRequestFullscreen(); - } else if (element.msRequestFullscreen) { // IE/Edge - element.msRequestFullscreen(); - } - }, []); + const enterFullScreen = useCallback(() => { + const element = document.documentElement; // Target the entire HTML document + if (element.requestFullscreen) { + element.requestFullscreen(); + } else if (element.mozRequestFullScreen) { + // Firefox + element.mozRequestFullScreen(); + } else if (element.webkitRequestFullscreen) { + // Chrome, Safari and Opera + element.webkitRequestFullscreen(); + } else if (element.msRequestFullscreen) { + // IE/Edge + element.msRequestFullscreen(); + } + }, []); - const exitFullScreen = useCallback(() => { - if (document.fullscreenElement) { - document.exitFullscreen(); - } else if (document.mozFullScreenElement) { - document.mozCancelFullScreen(); - } else if (document.webkitFullscreenElement) { - document.webkitExitFullscreen(); - } else if (document.msFullscreenElement) { - document.msExitFullscreen(); - } - }, []); + const exitFullScreen = useCallback(() => { + if (document.fullscreenElement) { + document.exitFullscreen(); + } else if (document.mozFullScreenElement) { + document.mozCancelFullScreen(); + } else if (document.webkitFullscreenElement) { + document.webkitExitFullscreen(); + } else if (document.msFullscreenElement) { + document.msExitFullscreen(); + } + }, []); - const toggleFullScreen = useCallback(() => { - if(!isMobile || isMobile) return - if (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement) { - exitFullScreen(); - setFullScreen(false) - } else { - enterFullScreen(); - setFullScreen(true) - } - }, [enterFullScreen, exitFullScreen]); + const toggleFullScreen = useCallback(() => { + if ( + document.fullscreenElement || + document.mozFullScreenElement || + document.webkitFullscreenElement || + document.msFullscreenElement + ) { + exitFullScreen(); + setFullScreen(false); + } else { + enterFullScreen(); + setFullScreen(true); + } + }, [enterFullScreen, exitFullScreen]); - // Listen for changes to fullscreen state - useEffect(() => { - const handleFullScreenChange = () => { - if (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement) { - - } else { - setFullScreen(false); - } - }; + // Listen for changes to fullscreen state + useEffect(() => { + const handleFullScreenChange = () => { + if ( + document.fullscreenElement || + document.mozFullScreenElement || + document.webkitFullscreenElement || + document.msFullscreenElement + ) { + // TODO check empty block + } else { + setFullScreen(false); + } + }; - document.addEventListener('fullscreenchange', handleFullScreenChange); - document.addEventListener('webkitfullscreenchange', handleFullScreenChange); // Safari - document.addEventListener('mozfullscreenchange', handleFullScreenChange); // Firefox - document.addEventListener('MSFullscreenChange', handleFullScreenChange); // IE/Edge + document.addEventListener('fullscreenchange', handleFullScreenChange); + document.addEventListener('webkitfullscreenchange', handleFullScreenChange); // Safari + document.addEventListener('mozfullscreenchange', handleFullScreenChange); // Firefox + document.addEventListener('MSFullscreenChange', handleFullScreenChange); // IE/Edge - return () => { - document.removeEventListener('fullscreenchange', handleFullScreenChange); - document.removeEventListener('webkitfullscreenchange', handleFullScreenChange); - document.removeEventListener('mozfullscreenchange', handleFullScreenChange); - document.removeEventListener('MSFullscreenChange', handleFullScreenChange); - }; - }, []); + return () => { + document.removeEventListener('fullscreenchange', handleFullScreenChange); + document.removeEventListener( + 'webkitfullscreenchange', + handleFullScreenChange + ); + document.removeEventListener( + 'mozfullscreenchange', + handleFullScreenChange + ); + document.removeEventListener( + 'MSFullscreenChange', + handleFullScreenChange + ); + }; + }, []); - return { enterFullScreen, exitFullScreen, toggleFullScreen }; + return { enterFullScreen, exitFullScreen, toggleFullScreen }; }; - - From e1bb064d1af310fee2490b770f84476c3e0e31a5 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 13:49:23 +0200 Subject: [PATCH 180/717] Format code --- src/backgroundFunctions/encryption.ts | 611 ++++++++++++++------------ 1 file changed, 326 insertions(+), 285 deletions(-) diff --git a/src/backgroundFunctions/encryption.ts b/src/backgroundFunctions/encryption.ts index 8c7ec8b..6cc0150 100644 --- a/src/backgroundFunctions/encryption.ts +++ b/src/backgroundFunctions/encryption.ts @@ -1,327 +1,368 @@ -import { getBaseApi } from "../background"; -import { createSymmetricKeyAndNonce, decryptGroupData, encryptDataGroup, objectToBase64 } from "../qdn/encryption/group-encryption"; -import { publishData } from "../qdn/publish/pubish"; -import { getData } from "../utils/chromeStorage"; -import { RequestQueueWithPromise } from "../utils/queue/queue"; - +import { getBaseApi } from '../background'; +import { + createSymmetricKeyAndNonce, + decryptGroupData, + encryptDataGroup, + objectToBase64, +} from '../qdn/encryption/group-encryption'; +import { publishData } from '../qdn/publish/pubish'; +import { getData } from '../utils/chromeStorage'; +import { RequestQueueWithPromise } from '../utils/queue/queue'; export const requestQueueGetPublicKeys = new RequestQueueWithPromise(10); const apiEndpoints = [ - "https://api.qortal.org", - "https://api2.qortal.org", - "https://appnode.qortal.org", - "https://apinode.qortalnodes.live", - "https://apinode1.qortalnodes.live", - "https://apinode2.qortalnodes.live", - "https://apinode3.qortalnodes.live", - "https://apinode4.qortalnodes.live", + 'https://api.qortal.org', + 'https://api2.qortal.org', + 'https://appnode.qortal.org', + 'https://apinode.qortalnodes.live', + 'https://apinode1.qortalnodes.live', + 'https://apinode2.qortalnodes.live', + 'https://apinode3.qortalnodes.live', + 'https://apinode4.qortalnodes.live', ]; async function findUsableApi() { - for (const endpoint of apiEndpoints) { - try { - const response = await fetch(`${endpoint}/admin/status`); - if (!response.ok) throw new Error("Failed to fetch"); - - const data = await response.json(); - if (data.isSynchronizing === false && data.syncPercent === 100) { - console.log(`Usable API found: ${endpoint}`); - return endpoint; - } else { - console.log(`API not ready: ${endpoint}`); - } - } catch (error) { - console.error(`Error checking API ${endpoint}:`, error); + for (const endpoint of apiEndpoints) { + try { + const response = await fetch(`${endpoint}/admin/status`); + if (!response.ok) throw new Error('Failed to fetch'); + + const data = await response.json(); + if (data.isSynchronizing === false && data.syncPercent === 100) { + console.log(`Usable API found: ${endpoint}`); + return endpoint; + } else { + console.log(`API not ready: ${endpoint}`); } + } catch (error) { + console.error(`Error checking API ${endpoint}:`, error); } - - throw new Error("No usable API found"); } + throw new Error('No usable API found'); +} async function getSaveWallet() { - const res = await getData("walletInfo").catch(() => null); + const res = await getData('walletInfo').catch(() => null); - if (res) { - return res - } else { - throw new Error("No wallet saved"); - } - } -export async function getNameInfo() { - const wallet = await getSaveWallet(); - const address = wallet.address0; - const validApi = await getBaseApi() - const response = await fetch(validApi + "/names/address/" + address); - const nameData = await response.json(); - if (nameData?.length > 0) { - return nameData[0].name; - } else { - return ""; - } - } -async function getKeyPair() { - const res = await getData("keyPair").catch(() => null); if (res) { - return res - } else { - throw new Error("Wallet not authenticated"); - } + return res; + } else { + throw new Error('No wallet saved'); } - const getPublicKeys = async (groupNumber: number) => { - const validApi = await getBaseApi(); - const response = await fetch(`${validApi}/groups/members/${groupNumber}?limit=0`); - const groupData = await response.json(); - - if (groupData && Array.isArray(groupData.members)) { - // Use the request queue for fetching public keys - const memberPromises = groupData.members - .filter((member) => member.member) - .map((member) => - requestQueueGetPublicKeys.enqueue(async () => { - const resAddress = await fetch(`${validApi}/addresses/${member.member}`); - const resData = await resAddress.json(); - return resData.publicKey; - }) - ); - - const members = await Promise.all(memberPromises); - return members; - } - - return []; - }; +} - export const getPublicKeysByAddress = async (admins: string[]) => { - const validApi = await getBaseApi(); - - if (Array.isArray(admins)) { - // Use the request queue to limit concurrent fetches - const memberPromises = admins - .filter((address) => address) // Ensure the address is valid - .map((address) => - requestQueueGetPublicKeys.enqueue(async () => { - const resAddress = await fetch(`${validApi}/addresses/${address}`); - const resData = await resAddress.json(); - return resData.publicKey; - }) - ); - - const members = await Promise.all(memberPromises); - return members; - } - - return []; // Return empty array if admins is not an array - }; +export async function getNameInfo() { + const wallet = await getSaveWallet(); + const address = wallet.address0; + const validApi = await getBaseApi(); + const response = await fetch(validApi + '/names/address/' + address); + const nameData = await response.json(); + if (nameData?.length > 0) { + return nameData[0].name; + } else { + return ''; + } +} +async function getKeyPair() { + const res = await getData('keyPair').catch(() => null); + if (res) { + return res; + } else { + throw new Error('Wallet not authenticated'); + } +} +const getPublicKeys = async (groupNumber: number) => { + const validApi = await getBaseApi(); + const response = await fetch( + `${validApi}/groups/members/${groupNumber}?limit=0` + ); + const groupData = await response.json(); - -export const encryptAndPublishSymmetricKeyGroupChat = async ({groupId, previousData}: { - groupId: number, - previousData: Object, -}) => { - try { - - let highestKey = 0 - if(previousData){ - highestKey = Math.max(...Object.keys((previousData || {})).filter(item=> !isNaN(+item)).map(Number)); - - } - - const resKeyPair = await getKeyPair() - const parsedData = resKeyPair - const privateKey = parsedData.privateKey - const userPublicKey = parsedData.publicKey - const groupmemberPublicKeys = await getPublicKeys(groupId) - const symmetricKey = createSymmetricKeyAndNonce() - const nextNumber = highestKey + 1 - const objectToSave = { - ...previousData, - [nextNumber]: symmetricKey - } - - const symmetricKeyAndNonceBase64 = await objectToBase64(objectToSave) - - const encryptedData = encryptDataGroup({ - data64: symmetricKeyAndNonceBase64, - publicKeys: groupmemberPublicKeys, - privateKey, - userPublicKey + if (groupData && Array.isArray(groupData.members)) { + // Use the request queue for fetching public keys + const memberPromises = groupData.members + .filter((member) => member.member) + .map((member) => + requestQueueGetPublicKeys.enqueue(async () => { + const resAddress = await fetch( + `${validApi}/addresses/${member.member}` + ); + const resData = await resAddress.json(); + return resData.publicKey; }) - if(encryptedData){ - const registeredName = await getNameInfo() - const data = await publishData({ - registeredName, file: encryptedData, service: 'DOCUMENT_PRIVATE', identifier: `symmetric-qchat-group-${groupId}`, uploadType: 'file', isBase64: true, withFee: true - }) - return { - data, - numberOfMembers: groupmemberPublicKeys.length - } - - } else { - throw new Error('Cannot encrypt content') - } - } catch (error: any) { - throw new Error(error.message); + ); + + const members = await Promise.all(memberPromises); + return members; + } + + return []; +}; + +export const getPublicKeysByAddress = async (admins: string[]) => { + const validApi = await getBaseApi(); + + if (Array.isArray(admins)) { + // Use the request queue to limit concurrent fetches + const memberPromises = admins + .filter((address) => address) // Ensure the address is valid + .map((address) => + requestQueueGetPublicKeys.enqueue(async () => { + const resAddress = await fetch(`${validApi}/addresses/${address}`); + const resData = await resAddress.json(); + return resData.publicKey; + }) + ); + + const members = await Promise.all(memberPromises); + return members; + } + + return []; // Return empty array if admins is not an array +}; + +export const encryptAndPublishSymmetricKeyGroupChat = async ({ + groupId, + previousData, +}: { + groupId: number; + previousData: Object; +}) => { + try { + let highestKey = 0; + if (previousData) { + highestKey = Math.max( + ...Object.keys(previousData || {}) + .filter((item) => !isNaN(+item)) + .map(Number) + ); } -} -export const encryptAndPublishSymmetricKeyGroupChatForAdmins = async ({groupId, previousData, admins}: { - groupId: number, - previousData: Object, + + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const privateKey = parsedData.privateKey; + const userPublicKey = parsedData.publicKey; + const groupmemberPublicKeys = await getPublicKeys(groupId); + const symmetricKey = createSymmetricKeyAndNonce(); + const nextNumber = highestKey + 1; + const objectToSave = { + ...previousData, + [nextNumber]: symmetricKey, + }; + + const symmetricKeyAndNonceBase64 = await objectToBase64(objectToSave); + + const encryptedData = encryptDataGroup({ + data64: symmetricKeyAndNonceBase64, + publicKeys: groupmemberPublicKeys, + privateKey, + userPublicKey, + }); + if (encryptedData) { + const registeredName = await getNameInfo(); + const data = await publishData({ + registeredName, + file: encryptedData, + service: 'DOCUMENT_PRIVATE', + identifier: `symmetric-qchat-group-${groupId}`, + uploadType: 'file', + isBase64: true, + withFee: true, + }); + return { + data, + numberOfMembers: groupmemberPublicKeys.length, + }; + } else { + throw new Error('Cannot encrypt content'); + } + } catch (error: any) { + throw new Error(error.message); + } +}; + +export const encryptAndPublishSymmetricKeyGroupChatForAdmins = async ({ + groupId, + previousData, + admins, +}: { + groupId: number; + previousData: Object; }) => { try { - - let highestKey = 0 - if(previousData){ - highestKey = Math.max(...Object.keys((previousData || {})).filter(item=> !isNaN(+item)).map(Number)); - - } - - const resKeyPair = await getKeyPair() - const parsedData = resKeyPair - const privateKey = parsedData.privateKey - const userPublicKey = parsedData.publicKey - const groupmemberPublicKeys = await getPublicKeysByAddress(admins.map((admin)=> admin.address)) + let highestKey = 0; + if (previousData) { + highestKey = Math.max( + ...Object.keys(previousData || {}) + .filter((item) => !isNaN(+item)) + .map(Number) + ); + } - - const symmetricKey = createSymmetricKeyAndNonce() - const nextNumber = highestKey + 1 - const objectToSave = { - ...previousData, - [nextNumber]: symmetricKey - } - - const symmetricKeyAndNonceBase64 = await objectToBase64(objectToSave) - - const encryptedData = encryptDataGroup({ - data64: symmetricKeyAndNonceBase64, - publicKeys: groupmemberPublicKeys, - privateKey, - userPublicKey - }) - if(encryptedData){ - const registeredName = await getNameInfo() - const data = await publishData({ - registeredName, file: encryptedData, service: 'DOCUMENT_PRIVATE', identifier: `admins-symmetric-qchat-group-${groupId}`, uploadType: 'file', isBase64: true, withFee: true - }) - return { - data, - numberOfMembers: groupmemberPublicKeys.length - } - - } else { - throw new Error('Cannot encrypt content') - } + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const privateKey = parsedData.privateKey; + const userPublicKey = parsedData.publicKey; + const groupmemberPublicKeys = await getPublicKeysByAddress( + admins.map((admin) => admin.address) + ); + + const symmetricKey = createSymmetricKeyAndNonce(); + const nextNumber = highestKey + 1; + const objectToSave = { + ...previousData, + [nextNumber]: symmetricKey, + }; + + const symmetricKeyAndNonceBase64 = await objectToBase64(objectToSave); + + const encryptedData = encryptDataGroup({ + data64: symmetricKeyAndNonceBase64, + publicKeys: groupmemberPublicKeys, + privateKey, + userPublicKey, + }); + if (encryptedData) { + const registeredName = await getNameInfo(); + const data = await publishData({ + registeredName, + file: encryptedData, + service: 'DOCUMENT_PRIVATE', + identifier: `admins-symmetric-qchat-group-${groupId}`, + uploadType: 'file', + isBase64: true, + withFee: true, + }); + return { + data, + numberOfMembers: groupmemberPublicKeys.length, + }; + } else { + throw new Error('Cannot encrypt content'); + } } catch (error: any) { - throw new Error(error.message); + throw new Error(error.message); } -} -export const publishGroupEncryptedResource = async ({encryptedData, identifier}) => { - try { - - if(encryptedData && identifier){ - const registeredName = await getNameInfo() - if(!registeredName) throw new Error('You need a name to publish') - const data = await publishData({ - registeredName, file: encryptedData, service: 'DOCUMENT', identifier, uploadType: 'file', isBase64: true, withFee: true - }) - return data - - } else { - throw new Error('Cannot encrypt content') - } - } catch (error: any) { - throw new Error(error.message); - } -} -export const publishOnQDN = async ({data, identifier, service, title, - description, - category, - tag1, - tag2, - tag3, - tag4, - tag5, - uploadType = 'file' +}; + +export const publishGroupEncryptedResource = async ({ + encryptedData, + identifier, }) => { + try { + if (encryptedData && identifier) { + const registeredName = await getNameInfo(); + if (!registeredName) throw new Error('You need a name to publish'); + const data = await publishData({ + registeredName, + file: encryptedData, + service: 'DOCUMENT', + identifier, + uploadType: 'file', + isBase64: true, + withFee: true, + }); + return data; + } else { + throw new Error('Cannot encrypt content'); + } + } catch (error: any) { + throw new Error(error.message); + } +}; - if(data && service){ - const registeredName = await getNameInfo() - if(!registeredName) throw new Error('You need a name to publish') - - const res = await publishData({ - registeredName, file: data, service, identifier, uploadType, isBase64: true, withFee: true, title, - description, - category, - tag1, - tag2, - tag3, - tag4, - tag5 - - }) - return res +export const publishOnQDN = async ({ + data, + identifier, + service, + title, + description, + category, + tag1, + tag2, + tag3, + tag4, + tag5, + uploadType = 'file', +}) => { + if (data && service) { + const registeredName = await getNameInfo(); + if (!registeredName) throw new Error('You need a name to publish'); - - - } else { - throw new Error('Cannot publish content') - } - -} + const res = await publishData({ + registeredName, + file: data, + service, + identifier, + uploadType, + isBase64: true, + withFee: true, + title, + description, + category, + tag1, + tag2, + tag3, + tag4, + tag5, + }); + return res; + } else { + throw new Error('Cannot publish content'); + } +}; export function uint8ArrayToBase64(uint8Array: any) { - const length = uint8Array.length - let binaryString = '' - const chunkSize = 1024 * 1024; // Process 1MB at a time - for (let i = 0; i < length; i += chunkSize) { - const chunkEnd = Math.min(i + chunkSize, length) - const chunk = uint8Array.subarray(i, chunkEnd) + const length = uint8Array.length; + let binaryString = ''; + const chunkSize = 1024 * 1024; // Process 1MB at a time + for (let i = 0; i < length; i += chunkSize) { + const chunkEnd = Math.min(i + chunkSize, length); + const chunk = uint8Array.subarray(i, chunkEnd); - // @ts-ignore - binaryString += Array.from(chunk, byte => String.fromCharCode(byte)).join('') - } - return btoa(binaryString) + // @ts-ignore + binaryString += Array.from(chunk, (byte) => String.fromCharCode(byte)).join( + '' + ); + } + return btoa(binaryString); } export function base64ToUint8Array(base64: string) { - const binaryString = atob(base64) - const len = binaryString.length - const bytes = new Uint8Array(len) + const binaryString = atob(base64); + const len = binaryString.length; + const bytes = new Uint8Array(len); - for (let i = 0; i < len; i++) { - bytes[i] = binaryString.charCodeAt(i) - } - - return bytes + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); } -export const decryptGroupEncryption = async ({data}: { - data: string -}) => { - try { - const resKeyPair = await getKeyPair() - const parsedData = resKeyPair - const privateKey = parsedData.privateKey - const encryptedData = decryptGroupData( - data, - privateKey, - ) - return { - data: uint8ArrayToBase64(encryptedData.decryptedData), - count: encryptedData.count - } - } catch (error: any) { - throw new Error(error.message); - } + return bytes; } +export const decryptGroupEncryption = async ({ data }: { data: string }) => { + try { + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const privateKey = parsedData.privateKey; + const encryptedData = decryptGroupData(data, privateKey); + return { + data: uint8ArrayToBase64(encryptedData.decryptedData), + count: encryptedData.count, + }; + } catch (error: any) { + throw new Error(error.message); + } +}; + export function uint8ArrayToObject(uint8Array: any) { - // Decode the byte array using TextDecoder - const decoder = new TextDecoder() - const jsonString = decoder.decode(uint8Array) - // Convert the JSON string back into an object - return JSON.parse(jsonString) -} \ No newline at end of file + // Decode the byte array using TextDecoder + const decoder = new TextDecoder(); + const jsonString = decoder.decode(uint8Array); + // Convert the JSON string back into an object + return JSON.parse(jsonString); +} From de0f52311e684ab13e595ce706ea0334d6168a91 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 13:49:42 +0200 Subject: [PATCH 181/717] Remove files for mobile --- src/components/Apps/Apps.tsx | 357 ----------------- src/components/Apps/AppsCategory.tsx | 204 ---------- src/components/Apps/AppsHome.tsx | 51 --- src/components/Apps/AppsLibrary.tsx | 384 ------------------ src/components/Apps/AppsNavBar.tsx | 367 ----------------- src/components/Mobile/MobileFooter.tsx | 203 ---------- src/components/Mobile/MobileHeader.tsx | 528 ------------------------- 7 files changed, 2094 deletions(-) delete mode 100644 src/components/Apps/Apps.tsx delete mode 100644 src/components/Apps/AppsCategory.tsx delete mode 100644 src/components/Apps/AppsHome.tsx delete mode 100644 src/components/Apps/AppsLibrary.tsx delete mode 100644 src/components/Apps/AppsNavBar.tsx delete mode 100644 src/components/Mobile/MobileFooter.tsx delete mode 100644 src/components/Mobile/MobileHeader.tsx diff --git a/src/components/Apps/Apps.tsx b/src/components/Apps/Apps.tsx deleted file mode 100644 index b4e6f67..0000000 --- a/src/components/Apps/Apps.tsx +++ /dev/null @@ -1,357 +0,0 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { AppsHome } from './AppsHome'; -import { Spacer } from '../../common/Spacer'; -import { getBaseApiReact } from '../../App'; -import { AppInfo } from './AppInfo'; -import { - executeEvent, - subscribeToEvent, - unsubscribeFromEvent, -} from '../../utils/events'; -import { AppsParent } from './Apps-styles'; -import AppViewerContainer from './AppViewerContainer'; -import ShortUniqueId from 'short-unique-id'; -import { AppPublish } from './AppPublish'; -import { AppsCategory } from './AppsCategory'; -import { AppsLibrary } from './AppsLibrary'; - -const uid = new ShortUniqueId({ length: 8 }); - -export const Apps = ({ mode, setMode, show, myName }) => { - const [availableQapps, setAvailableQapps] = useState([]); - const [selectedAppInfo, setSelectedAppInfo] = useState(null); - const [selectedCategory, setSelectedCategory] = useState(null); - const [tabs, setTabs] = useState([]); - const [selectedTab, setSelectedTab] = useState(null); - const [isNewTabWindow, setIsNewTabWindow] = useState(false); - const [categories, setCategories] = useState([]); - const iframeRefs = useRef({}); - - const myApp = useMemo(() => { - return availableQapps.find( - (app) => app.name === myName && app.service === 'APP' - ); - }, [myName, availableQapps]); - - const myWebsite = useMemo(() => { - return availableQapps.find( - (app) => app.name === myName && app.service === 'WEBSITE' - ); - }, [myName, availableQapps]); - - useEffect(() => { - setTimeout(() => { - executeEvent('setTabsToNav', { - data: { - tabs: tabs, - selectedTab: selectedTab, - isNewTabWindow: isNewTabWindow, - }, - }); - }, 100); - }, [show, tabs, selectedTab, isNewTabWindow]); - - const getCategories = React.useCallback(async () => { - try { - const url = `${getBaseApiReact()}/arbitrary/categories`; - - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - if (!response?.ok) return; - const responseData = await response.json(); - - setCategories(responseData); - } catch (error) { - console.log(error); - } finally { - // dispatch(setIsLoadingGlobal(false)) - } - }, []); - - const getQapps = React.useCallback(async () => { - try { - let apps = []; - let websites = []; - // dispatch(setIsLoadingGlobal(true)) - const url = `${getBaseApiReact()}/arbitrary/resources/search?service=APP&mode=ALL&limit=0&includestatus=true&includemetadata=true`; - - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - if (!response?.ok) return; - const responseData = await response.json(); - const urlWebsites = `${getBaseApiReact()}/arbitrary/resources/search?service=WEBSITE&mode=ALL&limit=0&includestatus=true&includemetadata=true`; - - const responseWebsites = await fetch(urlWebsites, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - if (!responseWebsites?.ok) return; - const responseDataWebsites = await responseWebsites.json(); - - apps = responseData; - websites = responseDataWebsites; - const combine = [...apps, ...websites]; - setAvailableQapps(combine); - } catch (error) { - console.log(error); - } finally { - // dispatch(setIsLoadingGlobal(false)) - } - }, []); - useEffect(() => { - getQapps(); - getCategories(); - }, [getQapps, getCategories]); - - const selectedAppInfoFunc = (e) => { - const data = e.detail?.data; - setSelectedAppInfo(data); - setMode('appInfo'); - }; - - useEffect(() => { - subscribeToEvent('selectedAppInfo', selectedAppInfoFunc); - - return () => { - unsubscribeFromEvent('selectedAppInfo', selectedAppInfoFunc); - }; - }, []); - - const selectedAppInfoCategoryFunc = (e) => { - const data = e.detail?.data; - setSelectedAppInfo(data); - setMode('appInfo-from-category'); - }; - - useEffect(() => { - subscribeToEvent('selectedAppInfoCategory', selectedAppInfoCategoryFunc); - - return () => { - unsubscribeFromEvent( - 'selectedAppInfoCategory', - selectedAppInfoCategoryFunc - ); - }; - }, []); - - const selectedCategoryFunc = (e) => { - const data = e.detail?.data; - setSelectedCategory(data); - setMode('category'); - }; - - useEffect(() => { - subscribeToEvent('selectedCategory', selectedCategoryFunc); - - return () => { - unsubscribeFromEvent('selectedCategory', selectedCategoryFunc); - }; - }, []); - - const navigateBackFunc = (e) => { - if ( - [ - 'category', - 'appInfo-from-category', - 'appInfo', - 'library', - 'publish', - ].includes(mode) - ) { - // Handle the various modes as needed - if (mode === 'category') { - setMode('library'); - setSelectedCategory(null); - } else if (mode === 'appInfo-from-category') { - setMode('category'); - } else if (mode === 'appInfo') { - setMode('library'); - } else if (mode === 'library') { - if (isNewTabWindow) { - setMode('viewer'); - } else { - setMode('home'); - } - } else if (mode === 'publish') { - setMode('library'); - } - } else if (selectedTab?.tabId) { - executeEvent(`navigateBackApp-${selectedTab?.tabId}`, {}); - } - }; - - useEffect(() => { - subscribeToEvent('navigateBack', navigateBackFunc); - - return () => { - unsubscribeFromEvent('navigateBack', navigateBackFunc); - }; - }, [mode, selectedTab]); - - const addTabFunc = (e) => { - const data = e.detail?.data; - const newTab = { - ...data, - tabId: uid.rnd(), - }; - setTabs((prev) => [...prev, newTab]); - setSelectedTab(newTab); - setMode('viewer'); - - setIsNewTabWindow(false); - }; - - useEffect(() => { - subscribeToEvent('addTab', addTabFunc); - - return () => { - unsubscribeFromEvent('addTab', addTabFunc); - }; - }, [tabs]); - - const setSelectedTabFunc = (e) => { - const data = e.detail?.data; - - setSelectedTab(data); - setTimeout(() => { - executeEvent('setTabsToNav', { - data: { - tabs: tabs, - selectedTab: data, - isNewTabWindow: isNewTabWindow, - }, - }); - }, 100); - setIsNewTabWindow(false); - }; - - useEffect(() => { - subscribeToEvent('setSelectedTab', setSelectedTabFunc); - - return () => { - unsubscribeFromEvent('setSelectedTab', setSelectedTabFunc); - }; - }, [tabs, isNewTabWindow]); - - const removeTabFunc = (e) => { - const data = e.detail?.data; - const copyTabs = [...tabs].filter((tab) => tab?.tabId !== data?.tabId); - if (copyTabs?.length === 0) { - setMode('home'); - } else { - setSelectedTab(copyTabs[0]); - } - setTabs(copyTabs); - setSelectedTab(copyTabs[0]); - setTimeout(() => { - executeEvent('setTabsToNav', { - data: { - tabs: copyTabs, - selectedTab: copyTabs[0], - }, - }); - }, 400); - }; - - useEffect(() => { - subscribeToEvent('removeTab', removeTabFunc); - - return () => { - unsubscribeFromEvent('removeTab', removeTabFunc); - }; - }, [tabs]); - - const setNewTabWindowFunc = (e) => { - setIsNewTabWindow(true); - setSelectedTab(null); - }; - - useEffect(() => { - subscribeToEvent('newTabWindow', setNewTabWindowFunc); - - return () => { - unsubscribeFromEvent('newTabWindow', setNewTabWindowFunc); - }; - }, [tabs]); - - return ( - - {mode !== 'viewer' && !selectedTab && } - {mode === 'home' && ( - - )} - - - - {mode === 'appInfo' && !selectedTab && ( - - )} - {mode === 'appInfo-from-category' && !selectedTab && ( - - )} - - {mode === 'publish' && !selectedTab && ( - - )} - - {tabs.map((tab) => { - if (!iframeRefs.current[tab.tabId]) { - iframeRefs.current[tab.tabId] = React.createRef(); - } - return ( - - ); - })} - - {isNewTabWindow && mode === 'viewer' && ( - <> - - - - )} - {mode !== 'viewer' && !selectedTab && } - - ); -}; diff --git a/src/components/Apps/AppsCategory.tsx b/src/components/Apps/AppsCategory.tsx deleted file mode 100644 index be10853..0000000 --- a/src/components/Apps/AppsCategory.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import React, { - useCallback, - useContext, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; -import { - AppCircle, - AppCircleContainer, - AppCircleLabel, - AppLibrarySubTitle, - AppsContainer, - AppsLibraryContainer, - AppsParent, - AppsSearchContainer, - AppsSearchLeft, - AppsSearchRight, - AppsWidthLimiter, - PublishQAppCTAButton, - PublishQAppCTALeft, - PublishQAppCTAParent, - PublishQAppCTARight, - PublishQAppDotsBG, -} from './Apps-styles'; -import { Avatar, Box, ButtonBase, InputBase, styled } from '@mui/material'; -import { Add } from '@mui/icons-material'; -import { MyContext, getBaseApiReact } from '../../App'; -import LogoSelected from '../../assets/svgs/LogoSelected.svg'; -import IconSearch from '../../assets/svgs/Search.svg'; -import IconClearInput from '../../assets/svgs/ClearInput.svg'; -import qappDevelopText from '../../assets/svgs/qappDevelopText.svg'; -import qappDots from '../../assets/svgs/qappDots.svg'; - -import { Spacer } from '../../common/Spacer'; -import { AppInfoSnippet } from './AppInfoSnippet'; -import { Virtuoso } from 'react-virtuoso'; -import { executeEvent } from '../../utils/events'; -const officialAppList = [ - 'q-tube', - 'q-blog', - 'q-share', - 'q-support', - 'q-mail', - 'q-fund', - 'q-shop', - 'q-trade', - 'q-support', - 'q-manager', - 'q-wallets', - 'q-search', - 'q-nodecontrol', -]; - -const ScrollerStyled = styled('div')({ - // Hide scrollbar for WebKit browsers (Chrome, Safari) - '::-webkit-scrollbar': { - width: '0px', - height: '0px', - }, - - // Hide scrollbar for Firefox - scrollbarWidth: 'none', - - // Hide scrollbar for IE and older Edge - '-msOverflowStyle': 'none', -}); - -const StyledVirtuosoContainer = styled('div')({ - position: 'relative', - width: '100%', - display: 'flex', - flexDirection: 'column', - - // Hide scrollbar for WebKit browsers (Chrome, Safari) - '::-webkit-scrollbar': { - width: '0px', - height: '0px', - }, - - // Hide scrollbar for Firefox - scrollbarWidth: 'none', - - // Hide scrollbar for IE and older Edge - '-msOverflowStyle': 'none', -}); - -export const AppsCategory = ({ availableQapps, myName, category, isShow }) => { - const [searchValue, setSearchValue] = useState(''); - const virtuosoRef = useRef(); - const { rootHeight } = useContext(MyContext); - - const categoryList = useMemo(() => { - return availableQapps.filter( - (app) => app?.metadata?.category === category?.id - ); - }, [availableQapps, category]); - - const [debouncedValue, setDebouncedValue] = useState(''); // Debounced value - - // Debounce logic - useEffect(() => { - const handler = setTimeout(() => { - setDebouncedValue(searchValue); - }, 350); - - // Cleanup timeout if searchValue changes before the timeout completes - return () => { - clearTimeout(handler); - }; - }, [searchValue]); // Runs effect when searchValue changes - - // Example: Perform search or other actions based on debouncedValue - - const searchedList = useMemo(() => { - if (!debouncedValue) return categoryList; - return categoryList.filter((app) => - app.name.toLowerCase().includes(debouncedValue.toLowerCase()) - ); - }, [debouncedValue, categoryList]); - - const rowRenderer = (index) => { - let app = searchedList[index]; - return ( - - ); - }; - - return ( - - - - - - - setSearchValue(e.target.value)} - sx={{ ml: 1, flex: 1 }} - placeholder="Search for apps" - inputProps={{ - 'aria-label': 'Search for apps', - fontSize: '16px', - fontWeight: 400, - }} - /> - - - {searchValue && ( - { - setSearchValue(''); - }} - > - - - )} - - - - - - - {`Category: ${category?.name}`} - - - - - - - - - - ); -}; diff --git a/src/components/Apps/AppsHome.tsx b/src/components/Apps/AppsHome.tsx deleted file mode 100644 index fda90ea..0000000 --- a/src/components/Apps/AppsHome.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { - AppCircle, - AppCircleContainer, - AppCircleLabel, - AppLibrarySubTitle, - AppsContainer, -} from './Apps-styles'; -import { ButtonBase } from '@mui/material'; -import { Add } from '@mui/icons-material'; -import { SortablePinnedApps } from './SortablePinnedApps'; -import { Spacer } from '../../common/Spacer'; - -export const AppsHome = ({ setMode, myApp, myWebsite, availableQapps }) => { - return ( - <> - - Apps Dashboard - - - - - { - setMode('library'); - }} - > - - - + - - Library - - - - - - - ); -}; diff --git a/src/components/Apps/AppsLibrary.tsx b/src/components/Apps/AppsLibrary.tsx deleted file mode 100644 index e5cac4a..0000000 --- a/src/components/Apps/AppsLibrary.tsx +++ /dev/null @@ -1,384 +0,0 @@ -import { useContext, useEffect, useMemo, useRef, useState } from 'react'; -import { - AppCircle, - AppCircleContainer, - AppCircleLabel, - AppLibrarySubTitle, - AppsContainer, - AppsLibraryContainer, - AppsSearchContainer, - AppsSearchLeft, - AppsSearchRight, - AppsWidthLimiter, - PublishQAppCTAButton, - PublishQAppCTALeft, - PublishQAppCTAParent, - PublishQAppCTARight, - PublishQAppDotsBG, -} from './Apps-styles'; -import { - Avatar, - Box, - ButtonBase, - InputBase, - styled, - useTheme, -} from '@mui/material'; -import { MyContext, getBaseApiReact } from '../../App'; -import LogoSelected from '../../assets/svgs/LogoSelected.svg'; -import IconSearch from '../../assets/svgs/Search.svg'; -import IconClearInput from '../../assets/svgs/ClearInput.svg'; -import qappDevelopText from '../../assets/svgs/qappDevelopText.svg'; -import qappDots from '../../assets/svgs/qappDots.svg'; -// import { Return } from './assets/svgs/Return.tsx'; -import ReturnSVG from '../../assets/svgs/Return.svg'; -import { Spacer } from '../../common/Spacer'; -import { AppInfoSnippet } from './AppInfoSnippet'; -import { Virtuoso } from 'react-virtuoso'; -import { executeEvent } from '../../utils/events'; -import { - ComposeP, - MailIconImg, - ShowMessageReturnButton, -} from '../Group/Forum/Mail-styles'; - -const officialAppList = [ - 'q-tube', - 'q-blog', - 'q-share', - 'q-support', - 'q-mail', - 'q-fund', - 'q-shop', - 'q-trade', - 'q-support', - 'q-manager', - 'q-wallets', - 'q-search', - 'q-nodecontrol', -]; - -const ScrollerStyled = styled('div')({ - // Hide scrollbar for WebKit browsers (Chrome, Safari) - '::-webkit-scrollbar': { - width: '0px', - height: '0px', - }, - - // Hide scrollbar for Firefox - scrollbarWidth: 'none', - - // Hide scrollbar for IE and older Edge - '-msOverflowStyle': 'none', -}); - -const StyledVirtuosoContainer = styled('div')({ - display: 'flex', - flexDirection: 'column', - position: 'relative', - width: '100%', - - // Hide scrollbar for WebKit browsers (Chrome, Safari) - '::-webkit-scrollbar': { - width: '0px', - height: '0px', - }, - - // Hide scrollbar for Firefox - scrollbarWidth: 'none', - - // Hide scrollbar for IE and older Edge - '-msOverflowStyle': 'none', -}); - -export const AppsLibrary = ({ - availableQapps, - setMode, - myName, - hasPublishApp, - isShow, - categories = { categories }, -}) => { - const [searchValue, setSearchValue] = useState(''); - const virtuosoRef = useRef(); - const { rootHeight } = useContext(MyContext); - - const officialApps = useMemo(() => { - return availableQapps.filter( - (app) => - app.service === 'APP' && - officialAppList.includes(app?.name?.toLowerCase()) - ); - }, [availableQapps]); - - const [debouncedValue, setDebouncedValue] = useState(''); // Debounced value - - // Debounce logic - useEffect(() => { - const handler = setTimeout(() => { - setDebouncedValue(searchValue); - }, 350); - - // Cleanup timeout if searchValue changes before the timeout completes - return () => { - clearTimeout(handler); - }; - }, [searchValue]); // Runs effect when searchValue changes - - // Example: Perform search or other actions based on debouncedValue - - const searchedList = useMemo(() => { - if (!debouncedValue) return []; - return availableQapps.filter((app) => - app.name.toLowerCase().includes(debouncedValue.toLowerCase()) - ); - }, [debouncedValue]); - - const rowRenderer = (index) => { - let app = searchedList[index]; - return ( - - ); - }; - - const theme = useTheme(); - - return ( - - - - - - - - setSearchValue(e.target.value)} - sx={{ ml: 1, flex: 1 }} - placeholder="Search for apps" - inputProps={{ - 'aria-label': 'Search for apps', - fontSize: '16px', - fontWeight: 400, - }} - /> - - - {searchValue && ( - { - setSearchValue(''); - }} - > - - - )} - - - - - - - - { - executeEvent('navigateBack', {}); - }} - > - // TODO return icon - Return to Apps Dashboard - - - - - {searchedList?.length > 0 ? ( - - - - - - ) : ( - <> - - Official Apps - - - - - {officialApps?.map((qapp) => { - return ( - { - // executeEvent("addTab", { - // data: qapp - // }) - executeEvent('selectedAppInfo', { - data: qapp, - }); - }} - > - - - - center-icon - - - - - {qapp?.metadata?.title || qapp?.name} - - - - ); - })} - - - - - - {hasPublishApp ? 'Update Apps!' : 'Create Apps!'} - - - - - - - - - - - - - - - - - { - setMode('publish'); - }} - > - - {hasPublishApp ? 'Update' : 'Publish'} - - - - - - - - - - Categories - - - - - {categories?.map((category) => { - return ( - { - executeEvent('selectedCategory', { - data: category, - }); - }} - > - - {category?.name} - - - ); - })} - - - - )} - - ); -}; diff --git a/src/components/Apps/AppsNavBar.tsx b/src/components/Apps/AppsNavBar.tsx deleted file mode 100644 index 9731bd5..0000000 --- a/src/components/Apps/AppsNavBar.tsx +++ /dev/null @@ -1,367 +0,0 @@ -import { useEffect, useMemo, useRef, useState } from 'react'; -import { - AppsNavBarLeft, - AppsNavBarParent, - AppsNavBarRight, -} from './Apps-styles'; -import { NavBack } from '../../assets/Icons/NavBack.tsx'; -import { NavAdd } from '../../assets/Icons/NavAdd.tsx'; -import { NavMoreMenu } from '../../assets/Icons/NavMoreMenu.tsx'; -import { - ButtonBase, - ListItemIcon, - ListItemText, - Menu, - MenuItem, - Tab, - Tabs, -} from '@mui/material'; -import { - executeEvent, - subscribeToEvent, - unsubscribeFromEvent, -} from '../../utils/events'; -import TabComponent from './TabComponent'; -import PushPinIcon from '@mui/icons-material/PushPin'; -import RefreshIcon from '@mui/icons-material/Refresh'; -import { useRecoilState, useSetRecoilState } from 'recoil'; -import { - navigationControllerAtom, - settingsLocalLastUpdatedAtom, - sortablePinnedAppsAtom, -} from '../../atoms/global'; - -export function saveToLocalStorage( - key, - subKey, - newValue, - otherRootData = {}, - deleteWholeKey -) { - try { - if (deleteWholeKey) { - localStorage.setItem(key, null); - return; - } - // Fetch existing data - const existingData = localStorage.getItem(key); - let combinedData = {}; - - if (existingData) { - // Parse the existing data - const parsedData = JSON.parse(existingData); - // Merge with the new data under the subKey - combinedData = { - ...parsedData, - ...otherRootData, - timestamp: Date.now(), // Update the root timestamp - [subKey]: newValue, // Assuming the data is an array - }; - } else { - // If no existing data, just use the new data under the subKey - combinedData = { - ...otherRootData, - timestamp: Date.now(), // Set the initial root timestamp - [subKey]: newValue, - }; - } - - // Save combined data back to localStorage - const serializedValue = JSON.stringify(combinedData); - localStorage.setItem(key, serializedValue); - } catch (error) { - console.error('Error saving to localStorage:', error); - } -} - -export const AppsNavBar = () => { - const [tabs, setTabs] = useState([]); - const [selectedTab, setSelectedTab] = useState(null); - const [isNewTabWindow, setIsNewTabWindow] = useState(false); - const tabsRef = useRef(null); - const [anchorEl, setAnchorEl] = useState(null); - const open = Boolean(anchorEl); - const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState( - sortablePinnedAppsAtom - ); - const [navigationController, setNavigationController] = useRecoilState( - navigationControllerAtom - ); - - const isDisableBackButton = useMemo(() => { - if (selectedTab && navigationController[selectedTab?.tabId]?.hasBack) - return false; - if (selectedTab && !navigationController[selectedTab?.tabId]?.hasBack) - return true; - return false; - }, [navigationController, selectedTab]); - - const setSettingsLocalLastUpdated = useSetRecoilState( - settingsLocalLastUpdatedAtom - ); - - const handleClick = (event) => { - setAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - useEffect(() => { - // Scroll to the last tab whenever the tabs array changes (e.g., when a new tab is added) - if (tabsRef.current) { - const tabElements = tabsRef.current.querySelectorAll('.MuiTab-root'); - if (tabElements.length > 0) { - const lastTab = tabElements[tabElements.length - 1]; - lastTab.scrollIntoView({ - behavior: 'smooth', - block: 'nearest', - inline: 'end', - }); - } - } - }, [tabs.length]); // Dependency on the number of tabs - - const setTabsToNav = (e) => { - const { tabs, selectedTab, isNewTabWindow } = e.detail?.data; - - setTabs([...tabs]); - setSelectedTab(!selectedTab ? null : { ...selectedTab }); - setIsNewTabWindow(isNewTabWindow); - }; - - useEffect(() => { - subscribeToEvent('setTabsToNav', setTabsToNav); - - return () => { - unsubscribeFromEvent('setTabsToNav', setTabsToNav); - }; - }, []); - - const isSelectedAppPinned = !!sortablePinnedApps?.find( - (item) => - item?.name === selectedTab?.name && item?.service === selectedTab?.service - ); - return ( - - - { - executeEvent('navigateBack', selectedTab?.tabId); - }} - disabled={isDisableBackButton} - sx={{ - opacity: !isDisableBackButton ? 1 : 0.3, - cursor: !isDisableBackButton ? 'pointer' : 'default', - }} - > - - - - - {tabs?.map((tab) => ( - - } // Pass custom component - sx={{ - '&.Mui-selected': { - color: 'white', - }, - padding: '0px', - margin: '0px', - minWidth: '0px', - width: '50px', - }} - /> - ))} - - - - {selectedTab && ( - - { - setSelectedTab(null); - executeEvent('newTabWindow', {}); - }} - > - - - - { - if (!selectedTab) return; - handleClick(e); - }} - > - - - - )} - - - { - if (!selectedTab) return; - - setSortablePinnedApps((prev) => { - let updatedApps; - - if (isSelectedAppPinned) { - // Remove the selected app if it is pinned - updatedApps = prev.filter( - (item) => - !( - item?.name === selectedTab?.name && - item?.service === selectedTab?.service - ) - ); - } else { - // Add the selected app if it is not pinned - updatedApps = [ - ...prev, - { - name: selectedTab?.name, - service: selectedTab?.service, - }, - ]; - } - - saveToLocalStorage( - 'ext_saved_settings', - 'sortablePinnedApps', - updatedApps - ); - return updatedApps; - }); - setSettingsLocalLastUpdated(Date.now()); - - handleClose(); - }} - > - - - - - - - - { - executeEvent('refreshApp', { - tabId: selectedTab?.tabId, - }); - handleClose(); - }} - > - - - - - - - - - ); -}; diff --git a/src/components/Mobile/MobileFooter.tsx b/src/components/Mobile/MobileFooter.tsx deleted file mode 100644 index 52e5c7c..0000000 --- a/src/components/Mobile/MobileFooter.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import * as React from "react"; -import { - BottomNavigation, - BottomNavigationAction, - ButtonBase, - Typography, -} from "@mui/material"; -import { Home, Groups, Message, ShowChart } from "@mui/icons-material"; -import Box from "@mui/material/Box"; -import BottomLogo from "../../assets/svgs/BottomLogo5.svg"; -import LogoSelected from "../../assets/svgs/LogoSelected.svg"; -import { Browser } from '@capacitor/browser'; - -import { CustomSvg } from "../../common/CustomSvg"; -import { WalletIcon } from "../../assets/Icons/WalletIcon"; -import { HubsIcon } from "../../assets/Icons/HubsIcon"; -import { TradingIcon } from "../../assets/Icons/TradingIcon"; -import { MessagingIcon } from "../../assets/Icons/MessagingIcon"; -import { executeEvent } from "../../utils/events"; - -const IconWrapper = ({ children, label, color }) => { - return ( - - {children} - - {label} - - - ); -}; - -export const MobileFooter = ({ - selectedGroup, - groupSection, - isUnread, - goToAnnouncements, - isUnreadChat, - goToChat, - goToThreads, - setOpenManageMembers, - groupChatHasUnread, - groupsAnnHasUnread, - directChatHasUnread, - chatMode, - openDrawerGroups, - goToHome, - setIsOpenDrawerProfile, - mobileViewMode, - setMobileViewMode, - setMobileViewModeKeepOpen, - hasUnreadGroups, - hasUnreadDirects -}) => { - const [value, setValue] = React.useState(0); - return ( - - setValue(newValue)} - sx={{ backgroundColor: "transparent", flexGrow: 1 }} - > - { - // setMobileViewMode('wallet') - setIsOpenDrawerProfile(true); - }} - icon={ - - - - } - sx={{ color: value === 0 ? "white" : "gray", padding: "0px 10px" }} - /> - { - setMobileViewMode("groups"); - }} - icon={ - - - - } - sx={{ - color: value === 0 ? "white" : "gray", - paddingLeft: "10px", - paddingRight: "42px", - }} - /> - - - {/* Floating Center Button */} - - { - if(mobileViewMode === 'home'){ - setMobileViewMode('apps') - - } else { - setMobileViewMode('home') - - } - }}> - - {/* Custom Center Icon */} - center-icon - - - - - setValue(newValue)} - sx={{ backgroundColor: "transparent", flexGrow: 1 }} - > - { - setMobileViewModeKeepOpen("messaging"); - }} - icon={ - - - - } - sx={{ - color: value === 2 ? "white" : "gray", - paddingLeft: "55px", - paddingRight: "10px", - }} - /> - { - executeEvent("addTab", { data: { service: 'APP', name: 'q-trade' } }); - executeEvent("open-apps-mode", { }); - }} - - icon={ - - - - } - sx={{ color: value === 3 ? "white" : "gray", padding: "0px 10px" }} - /> - - - ); -}; diff --git a/src/components/Mobile/MobileHeader.tsx b/src/components/Mobile/MobileHeader.tsx deleted file mode 100644 index 1ab74dc..0000000 --- a/src/components/Mobile/MobileHeader.tsx +++ /dev/null @@ -1,528 +0,0 @@ -import React, { useState } from "react"; -import { - AppBar, - Toolbar, - IconButton, - Typography, - Box, - MenuItem, - Select, - ButtonBase, - Menu, - ListItemIcon, - ListItemText, -} from "@mui/material"; -import { HomeIcon } from "../../assets/Icons/HomeIcon"; -import { LogoutIcon } from "../../assets/Icons/LogoutIcon"; -import { NotificationIcon } from "../../assets/Icons/NotificationIcon"; -import { ArrowDownIcon } from "../../assets/Icons/ArrowDownIcon"; -import { MessagingIcon } from "../../assets/Icons/MessagingIcon"; -import { MessagingIcon2 } from "../../assets/Icons/MessagingIcon2"; -import { HubsIcon } from "../../assets/Icons/HubsIcon"; -import { Save } from "../Save/Save"; -import CloseFullscreenIcon from "@mui/icons-material/CloseFullscreen"; -import { useRecoilState } from "recoil"; -import { fullScreenAtom, hasSettingsChangedAtom } from "../../atoms/global"; -import { useAppFullScreen } from "../../useAppFullscreen"; - -const Header = ({ - logoutFunc, - goToHome, - setIsOpenDrawerProfile, - isThin, - setMobileViewModeKeepOpen, - hasUnreadGroups, - hasUnreadDirects, - setMobileViewMode, - myName, - setSelectedDirect, - setNewChat, -}) => { - const [anchorEl, setAnchorEl] = useState(null); - const open = Boolean(anchorEl); - const [fullScreen, setFullScreen] = useRecoilState(fullScreenAtom); - const { exitFullScreen } = useAppFullScreen(setFullScreen); - const handleClick = (event) => { - setAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - if (isThin) { - return ( - - - {/* Left Home Icon */} - - { - setMobileViewModeKeepOpen(""); - goToHome(); - }} - // onClick={onHomeClick} - > - - - - - - {fullScreen && ( - { - exitFullScreen(); - setFullScreen(false); - }} - > - - - )} - - - {/* Center Title */} - - QORTAL - - - {/* Right Logout Icon */} - - { - setMobileViewModeKeepOpen("messaging"); - }} - > - - - - - - - - - - { - setSelectedDirect(null); - setNewChat(false); - setMobileViewMode("groups"); - setMobileViewModeKeepOpen(""); - handleClose(); - }} - > - - - - - - { - setMobileViewModeKeepOpen("messaging"); - - handleClose(); - }} - > - - - - - - - - ); - } - return ( - <> - {/* Main Header */} - - - {/* Left Home Icon */} - - - - - {fullScreen && ( - { - exitFullScreen(); - setFullScreen(false); - }} - > - - - )} - - {/* Center Title */} - - QORTAL - - - {/* Right Logout Icon */} - - - - - - - - - {/* Secondary Section */} - - - - {myName} - - {/* - */} - - - - - - - - - {/* Right Dropdown */} - {/* { - setIsOpenDrawerProfile(true); - }} - > - - - View Wallet - - - - - */} - - - - { - setMobileViewMode("groups"); - setMobileViewModeKeepOpen(""); - handleClose(); - }} - > - - - - - - - { - setMobileViewModeKeepOpen("messaging"); - - handleClose(); - }} - > - - - - - - - - ); -}; - -export default Header; From f9cfb12a2662bc84d996bac35b32ca58374da150 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 13:53:57 +0200 Subject: [PATCH 182/717] Use desktop file --- src/components/Apps/SortablePinnedApps.tsx | 2 +- src/components/ContextMenuPinnedApps.tsx | 300 +++++++++------- src/components/Save/Save.tsx | 2 +- .../Tutorials/useHandleTutorials.tsx | 334 +++++++++--------- 4 files changed, 335 insertions(+), 303 deletions(-) diff --git a/src/components/Apps/SortablePinnedApps.tsx b/src/components/Apps/SortablePinnedApps.tsx index 30dcec7..f9a24a7 100644 --- a/src/components/Apps/SortablePinnedApps.tsx +++ b/src/components/Apps/SortablePinnedApps.tsx @@ -23,7 +23,7 @@ import { sortablePinnedAppsAtom, } from '../../atoms/global'; import { useRecoilState, useSetRecoilState } from 'recoil'; -import { saveToLocalStorage } from './AppsNavBar'; +import { saveToLocalStorage } from './AppsNavBarDesktop'; import { ContextMenuPinnedApps } from '../ContextMenuPinnedApps'; import LockIcon from '@mui/icons-material/Lock'; import { useHandlePrivateApps } from './useHandlePrivateApps'; diff --git a/src/components/ContextMenuPinnedApps.tsx b/src/components/ContextMenuPinnedApps.tsx index bb64a4c..7dad268 100644 --- a/src/components/ContextMenuPinnedApps.tsx +++ b/src/components/ContextMenuPinnedApps.tsx @@ -1,152 +1,182 @@ import React, { useState, useRef } from 'react'; -import { ListItemIcon, Menu, MenuItem, Typography, styled } from '@mui/material'; +import { + ListItemIcon, + Menu, + MenuItem, + Typography, + styled, +} from '@mui/material'; import PushPinIcon from '@mui/icons-material/PushPin'; -import { saveToLocalStorage } from './Apps/AppsNavBar'; +import { saveToLocalStorage } from './Apps/AppsNavBarDesktop'; import { useRecoilState } from 'recoil'; import { sortablePinnedAppsAtom } from '../atoms/global'; const CustomStyledMenu = styled(Menu)(({ theme }) => ({ - '& .MuiPaper-root': { - backgroundColor: '#f9f9f9', - borderRadius: '12px', - padding: theme.spacing(1), - boxShadow: '0 5px 15px rgba(0, 0, 0, 0.2)', - }, - '& .MuiMenuItem-root': { - fontSize: '14px', - color: '#444', - transition: '0.3s background-color', - '&:hover': { - backgroundColor: '#f0f0f0', - }, + '& .MuiPaper-root': { + backgroundColor: '#f9f9f9', + borderRadius: '12px', + padding: theme.spacing(1), + boxShadow: '0 5px 15px rgba(0, 0, 0, 0.2)', + }, + '& .MuiMenuItem-root': { + fontSize: '14px', + color: '#444', + transition: '0.3s background-color', + '&:hover': { + backgroundColor: '#f0f0f0', }, + }, })); export const ContextMenuPinnedApps = ({ children, app, isMine }) => { - const [menuPosition, setMenuPosition] = useState(null); - const longPressTimeout = useRef(null); - const maxHoldTimeout = useRef(null); - const preventClick = useRef(false); - const startTouchPosition = useRef({ x: 0, y: 0 }); // Track initial touch position - const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(sortablePinnedAppsAtom); + const [menuPosition, setMenuPosition] = useState(null); + const longPressTimeout = useRef(null); + const maxHoldTimeout = useRef(null); + const preventClick = useRef(false); + const startTouchPosition = useRef({ x: 0, y: 0 }); // Track initial touch position + const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState( + sortablePinnedAppsAtom + ); - const handleContextMenu = (event) => { - if(isMine) return - event.preventDefault(); - event.stopPropagation(); - preventClick.current = true; - setMenuPosition({ - mouseX: event.clientX, - mouseY: event.clientY, - }); - }; + const handleContextMenu = (event) => { + if (isMine) return; + event.preventDefault(); + event.stopPropagation(); + preventClick.current = true; + setMenuPosition({ + mouseX: event.clientX, + mouseY: event.clientY, + }); + }; - const handleTouchStart = (event) => { - if(isMine) return + const handleTouchStart = (event) => { + if (isMine) return; - const { clientX, clientY } = event.touches[0]; - startTouchPosition.current = { x: clientX, y: clientY }; + const { clientX, clientY } = event.touches[0]; + startTouchPosition.current = { x: clientX, y: clientY }; - longPressTimeout.current = setTimeout(() => { - preventClick.current = true; - - event.stopPropagation(); - setMenuPosition({ - mouseX: clientX, - mouseY: clientY, + longPressTimeout.current = setTimeout(() => { + preventClick.current = true; + + event.stopPropagation(); + setMenuPosition({ + mouseX: clientX, + mouseY: clientY, + }); + }, 500); + + // Set a maximum hold duration (e.g., 1.5 seconds) + maxHoldTimeout.current = setTimeout(() => { + clearTimeout(longPressTimeout.current); + }, 1500); + }; + + const handleTouchMove = (event) => { + if (isMine) return; + + const { clientX, clientY } = event.touches[0]; + const { x, y } = startTouchPosition.current; + + // Determine if the touch has moved beyond a small threshold (e.g., 10px) + const movedEnough = + Math.abs(clientX - x) > 10 || Math.abs(clientY - y) > 10; + + if (movedEnough) { + clearTimeout(longPressTimeout.current); + clearTimeout(maxHoldTimeout.current); + } + }; + + const handleTouchEnd = (event) => { + if (isMine) return; + + clearTimeout(longPressTimeout.current); + clearTimeout(maxHoldTimeout.current); + if (preventClick.current) { + event.preventDefault(); + event.stopPropagation(); + preventClick.current = false; + } + }; + + const handleClose = (e) => { + if (isMine) return; + + e.preventDefault(); + e.stopPropagation(); + setMenuPosition(null); + }; + + return ( +
    + {children} + { + e.stopPropagation(); + }} + > + { + handleClose(e); + setSortablePinnedApps((prev) => { + if (app?.isPrivate) { + const updatedApps = prev.filter( + (item) => + !( + item?.privateAppProperties?.name === + app?.privateAppProperties?.name && + item?.privateAppProperties?.service === + app?.privateAppProperties?.service && + item?.privateAppProperties?.identifier === + app?.privateAppProperties?.identifier + ) + ); + saveToLocalStorage( + 'ext_saved_settings', + 'sortablePinnedApps', + updatedApps + ); + return updatedApps; + } else { + const updatedApps = prev.filter( + (item) => + !( + item?.name === app?.name && item?.service === app?.service + ) + ); + saveToLocalStorage( + 'ext_saved_settings', + 'sortablePinnedApps', + updatedApps + ); + return updatedApps; + } }); - }, 500); - - // Set a maximum hold duration (e.g., 1.5 seconds) - maxHoldTimeout.current = setTimeout(() => { - clearTimeout(longPressTimeout.current); - }, 1500); - }; - - const handleTouchMove = (event) => { - if(isMine) return - - const { clientX, clientY } = event.touches[0]; - const { x, y } = startTouchPosition.current; - - // Determine if the touch has moved beyond a small threshold (e.g., 10px) - const movedEnough = Math.abs(clientX - x) > 10 || Math.abs(clientY - y) > 10; - - if (movedEnough) { - clearTimeout(longPressTimeout.current); - clearTimeout(maxHoldTimeout.current); - } - }; - - const handleTouchEnd = (event) => { - if(isMine) return - - clearTimeout(longPressTimeout.current); - clearTimeout(maxHoldTimeout.current); - if (preventClick.current) { - event.preventDefault(); - event.stopPropagation(); - preventClick.current = false; - } - }; - - const handleClose = (e) => { - if(isMine) return - - e.preventDefault(); - e.stopPropagation(); - setMenuPosition(null); - }; - - return ( -
    - {children} - { - e.stopPropagation(); - }} - > - { - handleClose(e); - setSortablePinnedApps((prev) => { - if(app?.isPrivate){ - const updatedApps = prev.filter( - (item) => !(item?.privateAppProperties?.name === app?.privateAppProperties?.name && item?.privateAppProperties?.service === app?.privateAppProperties?.service && item?.privateAppProperties?.identifier === app?.privateAppProperties?.identifier) - ); - saveToLocalStorage('ext_saved_settings', 'sortablePinnedApps', updatedApps); - return updatedApps; - } else { - const updatedApps = prev.filter( - (item) => !(item?.name === app?.name && item?.service === app?.service) - ); - saveToLocalStorage('ext_saved_settings', 'sortablePinnedApps', updatedApps); - return updatedApps; - } - }); - }}> - - - - - Unpin app - - - -
    - ); + + + + + Unpin app + +
    +
    +
    + ); }; diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index cf8dba6..c8196d8 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -19,7 +19,7 @@ import { SaveIcon } from '../../assets/Icons/SaveIcon'; import { IconWrapper } from '../Desktop/DesktopFooter'; import { Spacer } from '../../common/Spacer'; import { LoadingButton } from '@mui/lab'; -import { saveToLocalStorage } from '../Apps/AppsNavBar'; +import { saveToLocalStorage } from '../Apps/AppsNavBarDesktop'; import { decryptData, encryptData } from '../../qortalRequests/get'; import { saveFileToDiskGeneric } from '../../utils/generateWallet/generateWallet'; import { diff --git a/src/components/Tutorials/useHandleTutorials.tsx b/src/components/Tutorials/useHandleTutorials.tsx index 2fabfa0..1df8626 100644 --- a/src/components/Tutorials/useHandleTutorials.tsx +++ b/src/components/Tutorials/useHandleTutorials.tsx @@ -1,192 +1,194 @@ -import React, { useCallback, useEffect, useState } from "react"; -import { saveToLocalStorage } from "../Apps/AppsNavBar"; -import creationImg from './img/creation.webp' -import dashboardImg from './img/dashboard.webp' -import groupsImg from './img/groups.webp' -import importantImg from './img/important.webp' -import navigationImg from './img/navigation.webp' -import overviewImg from './img/overview.webp' -import startedImg from './img/started.webp' -import obtainingImg from './img/obtaining-qort.jpg' +import { useCallback, useEffect, useState } from 'react'; +import { saveToLocalStorage } from '../Apps/AppsNavBarDesktop'; +import creationImg from './img/creation.webp'; +import dashboardImg from './img/dashboard.webp'; +import groupsImg from './img/groups.webp'; +import importantImg from './img/important.webp'; +import navigationImg from './img/navigation.webp'; +import overviewImg from './img/overview.webp'; +import startedImg from './img/started.webp'; +import obtainingImg from './img/obtaining-qort.jpg'; const checkIfGatewayIsOnline = async () => { - try { - const url = `https://ext-node.qortal.link/admin/status`; - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - const data = await response.json(); - if (data?.height) { - return true - } - return false - - } catch (error) { - return false - - } + try { + const url = `https://ext-node.qortal.link/admin/status`; + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + const data = await response.json(); + if (data?.height) { + return true; + } + return false; + } catch (error) { + return false; } +}; export const useHandleTutorials = () => { const [openTutorialModal, setOpenTutorialModal] = useState(null); -const [shownTutorials, setShowTutorials] = useState(null) + const [shownTutorials, setShowTutorials] = useState(null); -useEffect(()=> { + useEffect(() => { try { - const storedData = localStorage.getItem('shown-tutorials'); + const storedData = localStorage.getItem('shown-tutorials'); - - if (storedData) { - setShowTutorials(JSON.parse(storedData)); - } else { - setShowTutorials({}) - } + if (storedData) { + setShowTutorials(JSON.parse(storedData)); + } else { + setShowTutorials({}); + } } catch (error) { - //error + //error } -}, []) + }, []); - const saveShowTutorial = useCallback((type)=> { + const saveShowTutorial = useCallback((type) => { try { - - setShowTutorials((prev)=> { - return { - ...(prev || {}), - [type]: true - } - }) - saveToLocalStorage('shown-tutorials', type, true) + setShowTutorials((prev) => { + return { + ...(prev || {}), + [type]: true, + }; + }); + saveToLocalStorage('shown-tutorials', type, true); } catch (error) { - //error + //error } - }, []) - const showTutorial = useCallback(async (type, isForce) => { - try { - const isOnline = await checkIfGatewayIsOnline() - if(!isOnline) return + }, []); + const showTutorial = useCallback( + async (type, isForce) => { + try { + const isOnline = await checkIfGatewayIsOnline(); + if (!isOnline) return; switch (type) { - case "create-account": - { - if((shownTutorials || {})['create-account'] && !isForce) return - saveShowTutorial('create-account') - setOpenTutorialModal({ - title: "Account Creation", - resource: { - name: "a-test", - service: "VIDEO", - identifier: "account-creation-hub", - poster: creationImg - }, - }); - } - break; - case "important-information": - { - if((shownTutorials || {})['important-information'] && !isForce) return - saveShowTutorial('important-information') + case 'create-account': + { + if ((shownTutorials || {})['create-account'] && !isForce) return; + saveShowTutorial('create-account'); + setOpenTutorialModal({ + title: 'Account Creation', + resource: { + name: 'a-test', + service: 'VIDEO', + identifier: 'account-creation-hub', + poster: creationImg, + }, + }); + } + break; + case 'important-information': + { + if ((shownTutorials || {})['important-information'] && !isForce) + return; + saveShowTutorial('important-information'); - setOpenTutorialModal({ - title: "Important Information!", - resource: { - name: "a-test", - service: "VIDEO", - identifier: "important-information-hub", - poster: importantImg - }, - }); - } - break; - case "getting-started": - { - if((shownTutorials || {})['getting-started'] && !isForce) return - saveShowTutorial('getting-started') + setOpenTutorialModal({ + title: 'Important Information!', + resource: { + name: 'a-test', + service: 'VIDEO', + identifier: 'important-information-hub', + poster: importantImg, + }, + }); + } + break; + case 'getting-started': + { + if ((shownTutorials || {})['getting-started'] && !isForce) return; + saveShowTutorial('getting-started'); - setOpenTutorialModal({ - multi: [ - - { - title: "1. Getting Started", - resource: { - name: "a-test", - service: "VIDEO", - identifier: "getting-started-hub", - poster: startedImg - }, - }, - { - title: "2. Overview", - resource: { - name: "a-test", - service: "VIDEO", - identifier: "overview-hub", - poster: overviewImg - }, - }, - { - title: "3. Qortal Groups", - resource: { - name: "a-test", - service: "VIDEO", - identifier: "groups-hub", - poster: groupsImg - }, - }, - { - title: "4. Obtaining Qort", - resource: { - name: "a-test", - service: "VIDEO", - identifier: "obtaining-qort", - poster: obtainingImg - }, - }, - ], - }); - } - break; - case "qapps": + setOpenTutorialModal({ + multi: [ { - if((shownTutorials || {})['qapps'] && !isForce) return - saveShowTutorial('qapps') + title: '1. Getting Started', + resource: { + name: 'a-test', + service: 'VIDEO', + identifier: 'getting-started-hub', + poster: startedImg, + }, + }, + { + title: '2. Overview', + resource: { + name: 'a-test', + service: 'VIDEO', + identifier: 'overview-hub', + poster: overviewImg, + }, + }, + { + title: '3. Qortal Groups', + resource: { + name: 'a-test', + service: 'VIDEO', + identifier: 'groups-hub', + poster: groupsImg, + }, + }, + { + title: '4. Obtaining Qort', + resource: { + name: 'a-test', + service: 'VIDEO', + identifier: 'obtaining-qort', + poster: obtainingImg, + }, + }, + ], + }); + } + break; + case 'qapps': + { + if ((shownTutorials || {})['qapps'] && !isForce) return; + saveShowTutorial('qapps'); - setOpenTutorialModal({ - multi: [ - { - title: "1. Apps Dashboard", - resource: { - name: "a-test", - service: "VIDEO", - identifier: "apps-dashboard-hub", - poster: dashboardImg - }, - }, - { - title: "2. Apps Navigation", - resource: { - name: "a-test", - service: "VIDEO", - identifier: "apps-navigation-hub", - poster: navigationImg - }, - } - ], - }); - } - break; - default: - break; - } - } catch (error) { + setOpenTutorialModal({ + multi: [ + { + title: '1. Apps Dashboard', + resource: { + name: 'a-test', + service: 'VIDEO', + identifier: 'apps-dashboard-hub', + poster: dashboardImg, + }, + }, + { + title: '2. Apps Navigation', + resource: { + name: 'a-test', + service: 'VIDEO', + identifier: 'apps-navigation-hub', + poster: navigationImg, + }, + }, + ], + }); + } + break; + default: + break; + } + } catch (error) { //error - } - }, [shownTutorials]); + } + }, + [shownTutorials] + ); return { showTutorial, - hasSeenGettingStarted: shownTutorials === null ? null : !!(shownTutorials || {})['getting-started'], + hasSeenGettingStarted: + shownTutorials === null + ? null + : !!(shownTutorials || {})['getting-started'], openTutorialModal, setOpenTutorialModal, - shownTutorialsInitiated: !!shownTutorials + shownTutorialsInitiated: !!shownTutorials, }; }; From 2f7a04c6b1db9cd07730fdc9e35846b88ce56061 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 14:26:01 +0200 Subject: [PATCH 183/717] Remove android folder --- android/.gitignore | 101 ------- android/app/.gitignore | 2 - android/app/build.gradle | 54 ---- android/app/capacitor.build.gradle | 23 -- android/app/proguard-rules.pro | 21 -- .../myapp/ExampleInstrumentedTest.java | 26 -- android/app/src/main/AndroidManifest.xml | 45 ---- .../app/BackgroundFetchHeadlessTask.java | 31 --- .../java/com/example/app/MainActivity.java | 5 - .../main/res/drawable-land-hdpi/splash.png | Bin 7705 -> 0 bytes .../main/res/drawable-land-mdpi/splash.png | Bin 4040 -> 0 bytes .../main/res/drawable-land-xhdpi/splash.png | Bin 9251 -> 0 bytes .../main/res/drawable-land-xxhdpi/splash.png | Bin 13984 -> 0 bytes .../main/res/drawable-land-xxxhdpi/splash.png | Bin 17683 -> 0 bytes .../main/res/drawable-port-hdpi/splash.png | Bin 7934 -> 0 bytes .../main/res/drawable-port-mdpi/splash.png | Bin 4096 -> 0 bytes .../main/res/drawable-port-xhdpi/splash.png | Bin 9875 -> 0 bytes .../main/res/drawable-port-xxhdpi/splash.png | Bin 13346 -> 0 bytes .../main/res/drawable-port-xxxhdpi/splash.png | Bin 17489 -> 0 bytes .../drawable-v24/ic_launcher_foreground.xml | 34 --- .../res/drawable/ic_launcher_background.xml | 170 ------------ android/app/src/main/res/drawable/qort.png | Bin 1907 -> 0 bytes android/app/src/main/res/drawable/splash.png | Bin 163932 -> 0 bytes .../app/src/main/res/layout/activity_main.xml | 12 - .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 6229 -> 0 bytes .../mipmap-hdpi/ic_launcher_adaptive_back.png | Bin 3838 -> 0 bytes .../mipmap-hdpi/ic_launcher_adaptive_fore.png | Bin 7458 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 3925 -> 0 bytes .../mipmap-mdpi/ic_launcher_adaptive_back.png | Bin 2041 -> 0 bytes .../mipmap-mdpi/ic_launcher_adaptive_fore.png | Bin 4426 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 9001 -> 0 bytes .../ic_launcher_adaptive_back.png | Bin 6519 -> 0 bytes .../ic_launcher_adaptive_fore.png | Bin 11597 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 15567 -> 0 bytes .../ic_launcher_adaptive_back.png | Bin 10836 -> 0 bytes .../ic_launcher_adaptive_fore.png | Bin 21943 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 23027 -> 0 bytes .../ic_launcher_adaptive_back.png | Bin 16050 -> 0 bytes .../ic_launcher_adaptive_fore.png | Bin 34387 -> 0 bytes .../res/values/ic_launcher_background.xml | 4 - android/app/src/main/res/values/strings.xml | 7 - android/app/src/main/res/values/styles.xml | 22 -- android/app/src/main/res/xml/file_paths.xml | 5 - .../getcapacitor/myapp/ExampleUnitTest.java | 18 -- android/build.gradle | 33 --- android/capacitor.settings.gradle | 18 -- android/gradle.properties | 22 -- android/gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 - android/gradlew | 248 ------------------ android/gradlew.bat | 92 ------- android/settings.gradle | 5 - android/variables.gradle | 16 -- 54 files changed, 1026 deletions(-) delete mode 100644 android/.gitignore delete mode 100644 android/app/.gitignore delete mode 100644 android/app/build.gradle delete mode 100644 android/app/capacitor.build.gradle delete mode 100644 android/app/proguard-rules.pro delete mode 100644 android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java delete mode 100644 android/app/src/main/AndroidManifest.xml delete mode 100644 android/app/src/main/java/com/example/app/BackgroundFetchHeadlessTask.java delete mode 100644 android/app/src/main/java/com/example/app/MainActivity.java delete mode 100644 android/app/src/main/res/drawable-land-hdpi/splash.png delete mode 100644 android/app/src/main/res/drawable-land-mdpi/splash.png delete mode 100644 android/app/src/main/res/drawable-land-xhdpi/splash.png delete mode 100644 android/app/src/main/res/drawable-land-xxhdpi/splash.png delete mode 100644 android/app/src/main/res/drawable-land-xxxhdpi/splash.png delete mode 100644 android/app/src/main/res/drawable-port-hdpi/splash.png delete mode 100644 android/app/src/main/res/drawable-port-mdpi/splash.png delete mode 100644 android/app/src/main/res/drawable-port-xhdpi/splash.png delete mode 100644 android/app/src/main/res/drawable-port-xxhdpi/splash.png delete mode 100644 android/app/src/main/res/drawable-port-xxxhdpi/splash.png delete mode 100644 android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml delete mode 100644 android/app/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 android/app/src/main/res/drawable/qort.png delete mode 100644 android/app/src/main/res/drawable/splash.png delete mode 100644 android/app/src/main/res/layout/activity_main.xml delete mode 100644 android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png delete mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png delete mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png delete mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png delete mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png delete mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png delete mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png delete mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png delete mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png delete mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png delete mode 100644 android/app/src/main/res/values/ic_launcher_background.xml delete mode 100644 android/app/src/main/res/values/strings.xml delete mode 100644 android/app/src/main/res/values/styles.xml delete mode 100644 android/app/src/main/res/xml/file_paths.xml delete mode 100644 android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java delete mode 100644 android/build.gradle delete mode 100644 android/capacitor.settings.gradle delete mode 100644 android/gradle.properties delete mode 100644 android/gradle/wrapper/gradle-wrapper.jar delete mode 100644 android/gradle/wrapper/gradle-wrapper.properties delete mode 100755 android/gradlew delete mode 100644 android/gradlew.bat delete mode 100644 android/settings.gradle delete mode 100644 android/variables.gradle diff --git a/android/.gitignore b/android/.gitignore deleted file mode 100644 index 48354a3..0000000 --- a/android/.gitignore +++ /dev/null @@ -1,101 +0,0 @@ -# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore - -# Built application files -*.apk -*.aar -*.ap_ -*.aab - -# Files for the ART/Dalvik VM -*.dex - -# Java class files -*.class - -# Generated files -bin/ -gen/ -out/ -# Uncomment the following line in case you need and you don't have the release build type files in your app -# release/ - -# Gradle files -.gradle/ -build/ - -# Local configuration file (sdk path, etc) -local.properties - -# Proguard folder generated by Eclipse -proguard/ - -# Log Files -*.log - -# Android Studio Navigation editor temp files -.navigation/ - -# Android Studio captures folder -captures/ - -# IntelliJ -*.iml -.idea/workspace.xml -.idea/tasks.xml -.idea/gradle.xml -.idea/assetWizardSettings.xml -.idea/dictionaries -.idea/libraries -# Android Studio 3 in .gitignore file. -.idea/caches -.idea/modules.xml -# Comment next line if keeping position of elements in Navigation Editor is relevant for you -.idea/navEditor.xml - -# Keystore files -# Uncomment the following lines if you do not want to check your keystore files in. -#*.jks -#*.keystore - -# External native build folder generated in Android Studio 2.2 and later -.externalNativeBuild -.cxx/ - -# Google Services (e.g. APIs or Firebase) -# google-services.json - -# Freeline -freeline.py -freeline/ -freeline_project_description.json - -# fastlane -fastlane/report.xml -fastlane/Preview.html -fastlane/screenshots -fastlane/test_output -fastlane/readme.md - -# Version control -vcs.xml - -# lint -lint/intermediates/ -lint/generated/ -lint/outputs/ -lint/tmp/ -# lint/reports/ - -# Android Profiling -*.hprof - -# Cordova plugins for Capacitor -capacitor-cordova-android-plugins - -# Copied web assets -app/src/main/assets/public - -# Generated Config files -app/src/main/assets/capacitor.config.json -app/src/main/assets/capacitor.plugins.json -app/src/main/res/xml/config.xml diff --git a/android/app/.gitignore b/android/app/.gitignore deleted file mode 100644 index 043df80..0000000 --- a/android/app/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/build/* -!/build/.npmkeep diff --git a/android/app/build.gradle b/android/app/build.gradle deleted file mode 100644 index 3f5e684..0000000 --- a/android/app/build.gradle +++ /dev/null @@ -1,54 +0,0 @@ -apply plugin: 'com.android.application' - -android { - namespace "com.example.app" - compileSdk rootProject.ext.compileSdkVersion - defaultConfig { - applicationId "com.example.app" - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 1 - versionName "1.0" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - aaptOptions { - // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. - // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61 - ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~' - } - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -repositories { - flatDir{ - dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' - } -} - -dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" - implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion" - implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion" - implementation project(':capacitor-android') - testImplementation "junit:junit:$junitVersion" - androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" - androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" - implementation project(':capacitor-cordova-android-plugins') -} - -apply from: 'capacitor.build.gradle' - -try { - def servicesJSON = file('google-services.json') - if (servicesJSON.text) { - apply plugin: 'com.google.gms.google-services' - } -} catch(Exception e) { - logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work") -} diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle deleted file mode 100644 index d4f22b6..0000000 --- a/android/app/capacitor.build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN - -android { - compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } -} - -apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" -dependencies { - implementation project(':capacitor-browser') - implementation project(':capacitor-filesystem') - implementation project(':capacitor-local-notifications') - implementation project(':evva-capacitor-secure-storage-plugin') - implementation project(':transistorsoft-capacitor-background-fetch') - implementation "androidx.webkit:webkit:1.4.0" -} - - -if (hasProperty('postBuildExtras')) { - postBuildExtras() -} diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro deleted file mode 100644 index f1b4245..0000000 --- a/android/app/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java b/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java deleted file mode 100644 index f2c2217..0000000 --- a/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.getcapacitor.myapp; - -import static org.junit.Assert.*; - -import android.content.Context; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - - assertEquals("com.getcapacitor.app", appContext.getPackageName()); - } -} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 2b2013c..0000000 --- a/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/app/src/main/java/com/example/app/BackgroundFetchHeadlessTask.java b/android/app/src/main/java/com/example/app/BackgroundFetchHeadlessTask.java deleted file mode 100644 index fac2dc1..0000000 --- a/android/app/src/main/java/com/example/app/BackgroundFetchHeadlessTask.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.example.app; - - -import android.content.Context; -import android.util.Log; - -import com.transistorsoft.tsbackgroundfetch.BackgroundFetch; -import com.transistorsoft.tsbackgroundfetch.BGTask; - -public class BackgroundFetchHeadlessTask{ - public void onFetch(Context context, BGTask task) { - // Get a reference to the BackgroundFetch Android API. - BackgroundFetch backgroundFetch = BackgroundFetch.getInstance(context); - // Get the taskId. - String taskId = task.getTaskId(); - // Log a message to adb logcat. - Log.d("MyHeadlessTask", "BackgroundFetchHeadlessTask onFetch -- CUSTOM IMPLEMENTATION: " + taskId); - - boolean isTimeout = task.getTimedOut(); - // Is this a timeout? - if (isTimeout) { - backgroundFetch.finish(taskId); - return; - } - // Do your work here... - // - // - // Signal finish just like the Javascript API. - backgroundFetch.finish(taskId); - } -} \ No newline at end of file diff --git a/android/app/src/main/java/com/example/app/MainActivity.java b/android/app/src/main/java/com/example/app/MainActivity.java deleted file mode 100644 index 966f32d..0000000 --- a/android/app/src/main/java/com/example/app/MainActivity.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.example.app; - -import com.getcapacitor.BridgeActivity; - -public class MainActivity extends BridgeActivity {} diff --git a/android/app/src/main/res/drawable-land-hdpi/splash.png b/android/app/src/main/res/drawable-land-hdpi/splash.png deleted file mode 100644 index e31573b4fc93e60d171f4046c0220e1463075d9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7705 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 wKmFJ0_JHhk1&0B>;%YXATM diff --git a/android/app/src/main/res/drawable-land-mdpi/splash.png b/android/app/src/main/res/drawable-land-mdpi/splash.png deleted file mode 100644 index f7a64923ea1a0565d25fa139c176d6bf42184e48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4040 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 XyOn+%7f`N3b2T^T3uj5+eShz7v)7qy diff --git a/android/app/src/main/res/drawable-land-xhdpi/splash.png b/android/app/src/main/res/drawable-land-xhdpi/splash.png deleted file mode 100644 index 807725501bdd92e94e51e7b2b0006f69e0083a0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9251 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 diff --git a/android/app/src/main/res/drawable-land-xxhdpi/splash.png b/android/app/src/main/res/drawable-land-xxhdpi/splash.png deleted file mode 100644 index 14c6c8fe39fcd51a0414866ad28cbe8ff3acb060..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13984 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®q@N z14_H8ofW)l{|J8q+a)eH)I0r)>WXdzV%7J>PA~6_J)KLT90iYa^K=Wz7D!OybzqSru=f4?|KFl;Y)gP_H6V4x`~kZ6fE(xM1&;?72-TZNk+0 zr+Crr5yl%Iy@vfmt3eYFl!jIvPGFz^8Ek+2`48O1_pCX3xNWh-zBa{rIcc%+=|XVj zANYTg&s}TKb#OztQrCW(Xk?V^i{`q~%HtcveTxq(_HKeC9GzrtguMT4Nvs@KakPTA z9>*8bBZmLz`lK5=l)=b|=dT3a5ag^a1^znZyx5QKfUb1b9yacArRp%3@QWo(hrsCU z-K!-=jDmv!zb7XT>)r|-Z0Ry}lk2;dk-ECqMwr_nKN#x*X6~B5hVIN>6$1HwBz3Of z=Pk){AL5*=d90f17_qZEJLm;Q%WMdX=*N&!ki@E&cy7?>{1ssAH(tACtp*r@d^til z)x(1#6(kPD+joSF&J3sxJU@{-sWCS+pZq{Gsx=?z4wP;>?)1yHv0?X?VP{}cX4~aH zxeBPKw_rgW8rvewS1W2#^y+c>-183iMbJCqc38RN_o~__9-n|jcd&oA`m7*&Fqqpc z;Tev*0LS-ZK47Sq1unfvP1S43uA12P?PJmI8BeTYPr~R*tYUm^0;U%Hmu?bSZHEK6 zPjsW=E67Kq-&trmf;)UkmRABH2U)V)-eRT$j(%G12lLMsThSsU10iP#{)ZnvjzN$d z*K%P3`}oqyvpWP~venr>3viH8^`)Ma*=B31hw*Q+tqE>i2y7w!(o^lI^Yss^=tHW( z;cnCT(%B1gLz+TRGW9roFjI1EQTu-u`(f#RmZ8;FSN(bsC1J;+(i_R6mrW=yYx$cy z#%QKVrEx~kVMg~yo?^N28Wnk6x%L;J8i|*|ANEiNjq(Vhzuzl3ikpA*G!Z}kLAzAI z9qnySo%D|AuJj12%h;Otqjs(>LPj?rNdeU8so>P(C>XMzlho94ZD#w=cCOOU;=3&^ zsqAG!i{~lY271D|m>ztPV`)X@FO_;`wPjppYNQpM+ncvtz1lZjN>!Q^*I}T%uP78Z7tbV2$q3W_)14=kLFyJ z1GqL6T>ClgeZorL!}xP4f%OB_EsmJ`uw7dGWNV9OLlhb|UMpVhc{4@Bhh`tO!ZqzD zhusd<=K^ah!L@gQ?6dOpI-ge^e>S5W9eII57Zu16eU?GRbgKTeVk9yS{iK|O(zLR> zheb?;jwGCHS80NCn=jKxgJ>}qu4l%5NPihjzazGv#J?Jcyl;<#IW&x4mm>nrW8>}C z3U@aeD~)*F(0o^2{GnKVm$Jr#aZE ztl~TOkM^SdzJapQ((!-i8b!RkVQBKkL`2ZCBuy!qI1L{3Er526plVols~68U-^9Px zR(3{j;Z9RHX^muc0dUywJ|`yyZFf=k&-Gb#m4u73Lm5Ks%BfHj%2|gjn#i> zLC5pO$2Em9H;qoKQmMtl<@wgtPF1%2HariD5O~u>8=^*J&au~JH%Ih@&2Uging3U_ z0bzfKucW$ZHSx}!#buB?+-J)%RQbbXM-!BJTS&#dU_@lxU6>te2O+9 z@F{F{Nb!;{Cd`Gx+$G?11aB~S#wIH%D=*=7f7H@D@%B1)&bF$@t3JDq4l*%(wJTlh zo`?uMq{YilKUewPNaC)GuOr<8j9&ofqRU__BRUX^x8Cj3a;a$rXzgXqW>LR#CUn%~m)t zYC&ol(gAkbc^fd`xWU&bk5vT6KbFmsR=O78Bn%t7 znbw&=c+|T&#r+bls5rU6D#HMvqA<|;)BV%jOMonkm^p$7Vcel-Wwn$=uAJv&(8W>% z9))Fxpl*(%E#wFm_m!U~2HqgZs^2vaGeY(UfYKrSHV}w^D0N6!se5Ewy)Yy-!(2

    aKj2hWG7>znxs|SE zN4rHtiSPqLskWp(?(_YYwgq+1@8v+~8As|(bC>$D(atG3ZE8-ZM3SVcg|vHQz$I=!(A`k`5= zOqR>&%G)$)k*QLz7MTB9wleWpv&N9Sta64wy}3Ytd?x!Ja8z>(z~(3UNFu^eFmn#6 zw!!gUxOuZi$PQIs*ixfZR3iLyADJ z5&s%tPfk>V!x|A-;oq%1!yk9H$UBP0ToA*EDtz(^!_AnF1bBQ7joj|? z5b)gSI8c8O$PYFE!vXJ<4gebg*9G9P2wcB{#kv0FItc5T@PDNo)}Rh4Us}L{e}xzW zhwt`)j`M)mP=G6H0;^&q=I0{jU%bIRkF#uLF;{vVC&H|_uc diff --git a/android/app/src/main/res/drawable-land-xxxhdpi/splash.png b/android/app/src/main/res/drawable-land-xxxhdpi/splash.png deleted file mode 100644 index 244ca2506dbe0fd8f6a05520ac7d1a629ea81438..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17683 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 diff --git a/android/app/src/main/res/drawable-port-xxhdpi/splash.png b/android/app/src/main/res/drawable-port-xxhdpi/splash.png deleted file mode 100644 index bfabe6871a17a5e95b78fb30d49b7d2b4d2fe4c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13346 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

    VwO4Hu8X}%XHUAz_J@50rzbkIsat>4oWtQt< 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 diff --git a/android/app/src/main/res/drawable-port-xxxhdpi/splash.png b/android/app/src/main/res/drawable-port-xxxhdpi/splash.png deleted file mode 100644 index 6929071268eb03ee0f088142b6523566b78550e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17489 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 KR&><$`~L^lv%S~= diff --git a/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index c7bd21d..0000000 --- a/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index d5fccc5..0000000 --- a/android/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/app/src/main/res/drawable/qort.png b/android/app/src/main/res/drawable/qort.png deleted file mode 100644 index 39d090f724b9c2708a771b0208dd41266d433cdf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1907 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

    rWulOx-H*Y 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 - - - - diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 90f9580..0000000 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 12a409b4fdc47c082a1a1491b7f9e0a4cb147028..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6229 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 diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png deleted file mode 100644 index a05f3fc64b1194902313c4271059ac18b189eb6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3838 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 diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png deleted file mode 100644 index 3814b231dd6a9e7eac001759a8f173505e9e6564..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2041 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 diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index ff8e3910ea67e809b094dd5fa0e654fc3a95a8c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9001 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 L=h*EsIb{=75Eu#FqaMIa=CEM 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 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png deleted file mode 100644 index fe154c845177f01f8e8367b6bdb1b147ca90be83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10836 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 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png deleted file mode 100644 index c4fdc572b720e3046d7395055071ad3f77f5033c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21943 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%0A6If|AXs|(Lu{7tY9Zo8PeK)d+O+{Vt#P+ zD%Tc@g7Uf;=|2HE#Y;m3(n?(f&ZjMd@hn9Gg@s+I@9i66MpZ3gA~+6YtXAD&f6Sl? zE8NSo65HTi8QmbmUp?id4c;BzDx|HEA@SK8)A{~IP)|I9ts@j0u22Y7wf%H^zy7FH znJ$&<6c}04hEicFYZCXsPX`06a+2=SS(6Uq&eRdfR@e@|8#~F}G&?Ep7*T`q@CaW( zDVs#4=E>|k+primgDeDsgDGmVuyAEM3PDHt2p-`tpZA+{)Za6c1)P@Sv}stM|9cuD z0wQk1mv6U&wP8E~P?aaO;=`k2zs*+dWhAS9TF;eu_onWEeJsD1^nW6j76cN0oU`Zgn#iv z)edSs<}h4$yzt_#ncfK zi+B!;RCQTX;HA2lVlhH=wj{N_swHcVEDt6sDynGN7MnT(E0ia=I3C(>sxEJhpp2 zLe|(~mhe+R>m1>$nTHx187T&qt*vddUnH@XhpxCuZ#Xuq>&p7K-Rf8LNOKJvs`$cR zjr_0!s)UsR*EP&fc;V~Q27eJDDNCi26OEd9IQX=5ei*cU;i)e>mrGxGE@)o0r4|1t z0ziF`+VX5Wyb(wjn-(Gr0bU5o`LgRuZ+Eighrtw@pC+axrP8`J3&I{g2>s-3uopi2 zcyu5FPTY{q;(cS1WLU4I9BT-(s#)QHRBc9W*3{QuZwteS0dsJ%}=UEHZ~^K0x-_3^h;H3ZVtaIzg>FmGkf`X|xzO5h9MUG90r z1~OS6WjA<7jB~to1$*snMo6~yupoi$+f*vOeaNX0!)LV7@%?;QM{Ve~uh>%QlW?I* z&`FchX?VSLID#FVZi4G1;=JKT@M5QosP?Z#yooKwT{CZF6BM(o9jINiZqr(B6s^O2-$62BOUx$|Big4_TTwO5~MvQjMyE`w{_2R!g3CS;0h_>PtdMl>d1fhKgS(ZI?sQj5r6`Q67XF=O$|Q)HK`j z_&Fl{(HKZ$DmcilkSk7!VAGocc1u(DUSN;yzRk$8DK4M1b(Bi{bV6O?J`zEu)q~{M z%ePvR8%mZtS3%Z@ZiOL}tqQXXq2tV1y&iAm2eQ}Fr*pwOgs36L1r-f3TPxQS*}C?T zqyZLfgn~0t-DP}2(Zj{pef8|&19G2^DYB0*!%!o0mV0faF&U>su(0Gb$6cJbg$PeB ze4VCnG~_wHtXIxY{f4Et(cnkv)6= z@;Ta(6<8Bv#dk+-P3D7<{o&L-->`>orR^*{uhb4nv*}V8Sj#w|@auNMXX{!s*p1DLcQL`e0t9$E^&zlssPQQ9^`~W>p%u%oD)u7tly=$JcY|wpI zS{UV?<3g@h%h^t!15qx~dmeOWsDU3zNRGuV&K5fCQ-mu)^g|q1%r&LY4e-|zCp?C= z-j~j&MANw5lS?N8i}m9U+;n+fi?nukMUjv;lDQ3vCe-wq6g&;MzrGx1X>INwITw^A z)wyT2)$dayU}h}^pKbI}20|8|=&`v}lj1TIPrdK36qhc(PS5F?bIAyNcjx+x1r@pa zQyRpZ9==9i@l^IG&YOGy9HF8;u(8|b0f z(<{Q6dK3*MB{KifR&JY#7`|FnS}JwVZQF10wsiS2j`ksAo#mT;qIF&VjNZuq4e~oJ z>8#@PZ!+&_c=RA55U-vDZ+Gb8zf;~Ehq6TcoKtXl*_VwIKK*6=D+NUBRCSOw?y?{@ zbbA#zp})$RYYY41i42e!Hj?QZD0&AH^P}X_a0FS4%q4bG8Z+|&_il54w^bH2)h364MlFPC=dVI$6os@zE9c7|MBtRG1Ic^ zi`5$9IWC@cMBW2Was1CZYU0mJmaQdgmlV>B5q`6X=%7h*Nu=wX$a&j_baapEt+=+z zsw#B`HoSx*bq7Bu5U1@z^XPAPG}++F5)TLNxq4TkzFq2ZR@bQLzN;^0mpjb7Q~MWmzmk6C|h#8%m6K+U+) zvcqB3aSTAhw}y^Ap&TWDo!3rdiR=4Q9DV|?rDDi$r`>N_-mhh#N@p(S{VL!@A=7Ms z_Nd?qt1DeCra>sHw#sf;*imdo#G(cW6{+K5sD0p;_fQ(o4%JQ4n8c@aq_0RX!cApz zpnbu-K6#OkC#@H%`WB!!B1Sjx8%|;h2dG>K|7!J_LGy|rmMa!(wJ=HLz8(2)DyB=1 z#JnIMDd2*5|?kVpI{ClouQIF)^> zpgAmvE_9)MAxX@&Wvk<;2E4TXfJ})|H-t{Lm4GsR?g~C z2W-Y8c-1G?a*qqtEYqi<)}p3M&Az~#aldupoC^F1QuvFDXR5*XbE@tsfLy5X&hsL% zWAZn1&wZ8U)}I(6FQlU8F&$6 zX-osZqgC&;^-B~JYXr&o1eiGckS0sYxRcfKQk4o;Fv|~|2^|q##{50evFC5d?F>cgl zX1JBT=fF?N7WPJF{QdEr*Dfzf0XePt-q=lE!kmYn3d)LTCTGoW4Iw&wh9pO~;)7V> zXV+@?SCh@+=CReE)g8x7cQUG^HiuI%1IBJI(}ahPN+^|df8!~D$DTLIb1(MzWQQ=} zls^ec!v6mC=6yLx*h>$%Lb}sG3RaQHA1C|X!BP~88oq{Yn>5R84th;(QGxShVYeer zke^4%R(Is6(d%s=a9PsNH-wMj-auPGVMx94#1z_-vxfp|hoT&Lfh20Gvft_vK1<$0 z8=Vn+;lw1Dg>E7yE@#8|rzYw{sectE}>qFug_x&RZZ>gT0b5VwIO z5w|T{lq{`J>gi=ZeB+`+P^P>?$cr@(jcX6@26JY^U%d#Wbj72;Z79(BI7ODNKg9Ha z(IZa6|Dg6?Vk|@yh_Y-%?sk`Rz;#}-L8)#p2V8Db+zIa3bWVO3IB~s3g%QfsJmfn0 zL$vNa{$H-TZ#DV31N>I55yu`&yW1}D{OP-B2Jt6++RE7r^_c~@`M-WcVhW9?EUx*J za8A9lGVkB+G87H3RN`9DHJ27pglGgMhPC1LyVY9XRTxoLIkS^w23c{!S3xEnAfgp< z$q;C&;!arK5q?)1RtmW@=L-SxmrG7HFh)3ETrJH$Y<{piZ~x`v4f*$s7qM!!Xlx?} zZ|_yzV_sHPK;z2e)Z_qezV2b+aVuxY&%;_*A(B|_Bh@&1_}>UTik?Qp{~^hM%WGIY ztK@pH=K!A6iIVYln`KOfS3s_5uGBej8>X)jO+{!dm^I?gL9!8{T;vgjO4r&4^;=uzzUI3P2WlhF;W(=4eA$HS%^F-$8$&|)4w7MuRzHd> zH4ii+>xS=lTU4Nnz)K}wAmhkHr69FnkFzkU=eM_8i1HQqZjlFhNBq6oSky>H4E2#X z_@SRg_7Jrz_37QyV@F_5W~NS83APX&u+n*ERAxqM!BT3K_`r*GFaO-Mz)*iRgeNyv z7*>BWnLyk8eM#Oyd3y3=!oxU&UL1RpVzFq~9A${H_D?EB%*m(`Dsx$ORKHEMA*T*s za#zaYWFnG8qjO=06h25O8d$8=?ucR?iM$Gjxp z@O3t!Kccm_*ft_Z%SV-hRRmhUK#5PL>JW0XNYida<5^lM?@Ntxe=lGqdha^@uNrk{ zXd^xo=mrw1&^aIscM}El4aIU>I#2B{?HLNaz_{g}1j|U=G7>VfN2h7Mqk znO)%$4Oc7YXw{`Qg}&=BO>y*zWOQpaz4ekp+lpThYiwXnAtJVg2pZ9wC^jwh3GQ5E^J1F z*X&oOpKat|W#CH>w6pjA5)-$CW|;YjFb`LP-8vptlI@U+e`pt!MQ3~O8r2R5`ulw$ z=rop2En-ppyHqYgS$dWAJDsPo5L#JK1`Y1F3`M<@%jF8UybbE}z~|I~U`^#-6*TX( ziL@g;lvJ?pLnenr+?3Qf7}&V7TZ$*Bs3XQtrtu67tQVuDyeD6%>aEtPtqi;by!_px z$1t60D~Bu_*a?*7&-{3MmZSSE8nEb$2e>jorRWihoQE>9R88+`xwSm7>fe)Lb&SEW zO5RtY$liqm-%8vC3P;wIU9w0?_^q9p!{Q>xt~B1~Nn_q-f*SGuJA>7)g&S2lbV(t9 zH`&Uvw=2JrK+m*WG}};Bekl@SW^4BuWbzH!zc^W49f%8TgS4pYfUd+(6Z76zIIQ&9 z&#eBV^2#mx3jopz?l*^`fOX%DT_hi5ne{8u*1+q06hDoj3vd#sW%NEiV?8^MFCW3J zCiV^?Q)gs%QN_CuWX|2Vu6}#_{0G&)?hnMHl$NvMK2yVOaJK@l7uqRsNAzn*U6yu~ zrdJMPw2ofkHGw1BwN- z=;tPcQY?;tc%^a-b8=x!TW({Tys?9?06(sHsci*=fW_+6(rjEfCFn)(>eYl`RAI}1 zqY^o%e@XLsZgCy&UFb*g1O{N8W_4&rABc=#SrnoDGPaCPg~MSS`!7qa&%kDICX9cX zH`2jHHD4Uk{B!Q1Ig|KSCIG7TLuy-4$*KM~K?lS?7OT>=`otV9z3tW(wdyku zCNJ`Bgn+=d`0u>{DL%0c=UR$#@}hGo_N62teeycev4=tGYngAD3Ll* zn!MWd3=H?iyqsk+D%DP1myJvG9BKNEsjI;LpUj|8(NE+bAD1_)|WrG`~d#L>HqtxSfy>{ z{+H{>sr`!~&knoDs#Uj?dq>->#aXF3CFj>tW7d*AALv`mWXU$1U>Ul0tH3W+(gDVi zzl+~fDT;7X@@_lz9pB%0qINHH2G;MGX_)#INaxF*W^?HApOzc#nzxzKTJJ=(zk1T) z;QYcsImASd%xcwc{IcFqM!uD`xJt^>W7^DVxqshQcFU9jQK;l5G{Y}c&86R=Lh8~n z%?;ONN{o_eP19vNE$4?;im&_QHn!1OoUSP3_izL&By`E;X3XL?-|7@z#CVe{3DspQ zzE6rJZsj%?uH!N?5sg6@hi}X1tHCnLd07V;yTqa~KaJc8>x!K^l5WIeM}-QwD3hp= zhFsE!!BwRfVtmqLGu;w!l+%R4CiJUREUW9cq_)=_eTDl1b$PX6WZt2+Y3F^(6i2Q+ zt_~#T1ko)2j`mTULCoR~I}C+)iRCsJ@oHE+v0J>#OTehA7ev3chlkX>U}D6KPLnZK z<>5FBfUceFA`b_jzY5?%@^kWHs-{FXP}w zn((jCQQ1(H67iP}(Tp~2cu!VZl$h`PX$73V8m<2Rmy|D5wC~>zvyE$2Enm1n+mQXB z1bQBnw4&OG@n!iAg~;z=9MD&$jIzzjhjfS@+q4v|5c?~j0_snqM6bDbfkaBouwlwW z&)tvH@ej=F7X5Tme5x^(|EHb%@MeR19{}E}nh9PrwM%W)RLs06Vx}5<#1<8+RU;^B z6Pp@Ulp3)GRkKKI&xjGNQls{06>U+g{iWZ3;P(%_f51KGo_n7AoO|zcpU)`DIs2#i z4I(zutt^E-wGvHq_w;h3qVbaLx<}4rsq{PNTDj0k zXk|eQ??^O+5^QMBa!1mx6K#x+e2Ei=ukLX%;kkQ-s;{Y0hQ-`yDHmZ-PKC7LJtE6K z%~q8H1^^38gKzOZmEjK>Y(6_FI9O~f@IIjmdRF^5E2uL6Ua#wk9Stg*D(}g{$b@pi zOzsA2GuJwzls9pGYUK;Mt7+ZO>F{hXvfaIgqqUWrFs>xw>heO=Q*nRAxx{7%@eDYcqH86;kIHhZsonQcdIdFvyL54LD?({)2C&R{mzxM*H#Bka|;n z!It7KpkQ!0C*P2lc~BocsBzcF<5@@DRlb|8wcYEXHIDEvP;o|sekE=0aOhlJ8I306 zs&OJjYv$OE7Sl?&!j<;bXE)5pvUBf=A_n;yKn7+i6Lo${_*LfPm{Vc??Q;lc|1D{h zM78HjA4G#~USpUj(T-r18@_O8B=2&Yy4u=@9FlK|L2EPig6{>IWp;`Rv2Z^yrGcjD zi7hVfK;N8spZD2p`j95vW;&`(Xe40`Xk0DkCE)1booIIlsI9=|;7-E8RE{l3Fs z8R|z+aq&uO`hN7~*@tI=pA)lbyhEfK-!!8x7ki8;TOpU1LxdEl(~T7KKb>_pZN}D` zYz`ki=K5BlGUeLD&`*agN#Fs+&tmQ;Ex8CW0$9!$0F zTlCu6I%Jd8^2?6=1lYOC1gXf}`J3B2$lE;W*Rr5ZU0WsiL0`>_a8UJ&k+gujFoJ8= zTSnbd^1p#10V*LNEG?}d)F9ib`ih8Mh9xct_OOWrb72ha8ZP-?7xWVui1-eHq;rt4 z`cy7L2$|Fr;vhn~@nqfk{%%of%sL2XGW+V%$T={NcJ8Z15>KiwP`--0gqs=QQ``||WErLLFbFxeQp@)*(2E}rpTkKZi{ zrEVR*_rwjUB2Z4Wi9z~uY940AD#b9Be@ZRNQQ!K>f~3PzYtFO7xjUZ#dOcFR%2wk_ zf9N7a7lTd#VGcGg@XYNBjSOQbplQmsEokOs_e9%Qo(A=s2aZuQ7Qp4~xEY5V#tIR1 z9nIJ@&FvaSiyDeU?Qg>5{h#wjTD2K6 zFx#2TF~1WoSR~wPI9p$xSVHiOcwU}CGqK?ghfiaGxgW$KfQLWXIvkt?=gdjJk7bbw zm=743>veGtCuN2)DTI4ff5CY<9O!40rCej7LGF}q^LZ~v?6RX|yp0|D09LG~f3h}> zc!Lxsf9`5U;9hz=qD2f!!l-YaEUrCq+TkD3>2U-%T{+-s8Y0t z^QoP5M$LO-)qJUX-BE4#w)Em;=a*!F{w>|Pp|rN23f0(|fj@hXIgcM{~ighRYzGK1dVO5nbv9pu~>e$h5cN|N|~z$B&n5o%6a%~MDn_o z&CQz~K{7=94tBK65_RMbCt!_9v~~(#O1esCZmslSByT#Csy6PQ;9c*4KWL@Kv!`G7 zYbsVQ2p232xYZkQL4!jf9*s%y(g6m5pZw}mbDrlt&ncao4_$PN^&D#|D?!Yo`ca4% zmeQtR+P?U-&8H8mhr;P+yXod{H;XCJSIZ%cplJh!mGLCox#Z zqRKCM9YUg3hKAzV>8Q<=SV8Vy>1wdJB<66Y&rj1y&yScm0@a9eWMj836x`n$SE`eI9J*&k27BF~v(r&#EQ zC8wwSx4T`BAiZGMG=pylqB^ml#e$R%WKY3PtnP^vD=|&3f#+(lTXJKy{_-SuBkpoV z{pK;#X9Kf-Mdei-wFAg%-8@bIDc*i6SZ}Z>s+UE3{J?t;Syqn&wv%3B-!k%WKv|yq>-_y?4n>D>;sPcX$(UqGRCj@GKqkX|E964EUo!o3%Q|9Sa$Vh%pGTyP-(2J}V-M{*U{SmTkO%vSi)6WQX z+}9_$$&TVg%u=IEpbs6-^bs%_=KScLzY#$9i# zL~ZQ)kWsxHbp8r1bYUWJy95)kr(A`r6$?91LdH;Q zl`toL5}lGblg4AOZ^l_4OYb2PEHYdB*>LC2KG3ccC&H3ZR#7794u?09vyM7W`FCAd zO-qXH;{kyZxQplBxeFp&9`D5YmI8F3uh&^4nP>RO-d1(#2=fY!7q@of9!jCPd9ZwtJ^BfZuZS(7H`ll07fa>hh zH4D{+Y)3u6Aa?HP>_RAQ9}>P$6ATWOsGr#cSLPLCOyKvbTETqc{%Fh$?NW= zu5{QA+R~#{)q_;itcXZ#FSQ0RCM#a!(LZ_J;Dybq&50}NPD;v!^-ax`SufJC#iF2s z@e#E90ul(pChwBwJFG#BTotA9LI26Z+N)!1z|n5Uhz=;y%lfeF7IggwET{%+JGn7wPrnA2h~bEeoIh=M(g2IV7{%^{v>os4EN zh{gX7oRHQnI8;*W8#t!Rp4)$X?n+e&d?W8&)jg@CT7~WH8|bP4Mjr;z9Tt&$;S?Uj zVYISyGK5lPTf9rKdkJZpy!`!yg9fGcp7hN+06QV%E%%zA@{;C!CO+iX-z=pVvW zPg-mqs)~t0H76O{fEF~M{Wtx3Ll{UJs)e8(`Wj3f{>g{JMWo)%BeS8SN@&DYsZAr@ zX!IaT+@W_C{7si;7=@?ylJOEa^R*81Ia+|>iq-fF<$tb5*Gd;VVVFb*Q)cVhE z#N-gYGg>}T$txLeat|pi_SF16Sy>{vD2Eb|&LlPdz6uMI88N@li zJesR1;O22z>umay(|s&gO@>iPYV;bt6C_kOlf=kvzBhbPsL#lUdt{S5CoZSp*GqgfmgrH z_KQfu)r&=gKUqkX>_J$PDjRs~s!hEq09AT_jm+n>%j&ghNbyvKQ zW{AfShfOT&biLSLQ4JWpVk@X89?9SV?zL7?)&!z@{=MMFmEQUo z`*P@wwwLAfaz?NWWGFXO#zaxZk3;;knJ$5$^s~)`gy7sQt&xPR2BEifo%xc^W%jm5 zl15$Iq+kxco>s@Ah-YD_vIi}wqzJ=sV9k1Ctw8v@A@(8H&6~3BZSC&t;^k_+m)5)< z=trV<=Ci$$88^)%hbPiNVL=bS*Ze?b77IHRjC{A-=I|~lyuIIMxNdt{4C+CW8s_?+ zjVLJaw`IOq)*+gr6DDd>V!~LC&GIi*_?_Yw-WHJjA^+DZ6sK5)oZ1i{5sEPQ-;n=S i?7x8d|MttTKipUP@Q?nL^r3*Pm-Mv}T6LO^QU3>@uFr!2 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 7191d0c4d0b308e4e7225fc1eead6e69dbd895b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23027 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

    F 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 EKLs;)zyJUM diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png deleted file mode 100644 index 69a0eb8415ca32dc54ffb4b1aad19f45a0ae07d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34387 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

    3<@7gem61=*2I)2g~$iK%)C&jJmM7Va)ZCB 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%0DnM+0SD320Iz{o#1-t~ul;8GpIk)Zu2T4C4M+Rq_ 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 UCbr36eg}Uj$f^QrrOkr>KXZD`{r~^~ diff --git a/android/app/src/main/res/values/ic_launcher_background.xml b/android/app/src/main/res/values/ic_launcher_background.xml deleted file mode 100644 index c5d5899..0000000 --- a/android/app/src/main/res/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #FFFFFF - \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml deleted file mode 100644 index 624265c..0000000 --- a/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - Qortal - Qortal - com.example.app - com.example.app - diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml deleted file mode 100644 index be874e5..0000000 --- a/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/android/app/src/main/res/xml/file_paths.xml b/android/app/src/main/res/xml/file_paths.xml deleted file mode 100644 index bd0c4d8..0000000 --- a/android/app/src/main/res/xml/file_paths.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java b/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java deleted file mode 100644 index 0297327..0000000 --- a/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.getcapacitor.myapp; - -import static org.junit.Assert.*; - -import org.junit.Test; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - - @Test - public void addition_isCorrect() throws Exception { - assertEquals(4, 2 + 2); - } -} diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index 7e1c3f3..0000000 --- a/android/build.gradle +++ /dev/null @@ -1,33 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - - repositories { - google() - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:8.2.1' - classpath 'com.google.gms:google-services:4.4.0' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -apply from: "variables.gradle" - -allprojects { - repositories { - google() - mavenCentral() - maven { - // capacitor-background-fetch - url("${project(':transistorsoft-capacitor-background-fetch').projectDir}/libs") - } - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/android/capacitor.settings.gradle b/android/capacitor.settings.gradle deleted file mode 100644 index c72458d..0000000 --- a/android/capacitor.settings.gradle +++ /dev/null @@ -1,18 +0,0 @@ -// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN -include ':capacitor-android' -project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') - -include ':capacitor-browser' -project(':capacitor-browser').projectDir = new File('../node_modules/@capacitor/browser/android') - -include ':capacitor-filesystem' -project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android') - -include ':capacitor-local-notifications' -project(':capacitor-local-notifications').projectDir = new File('../node_modules/@capacitor/local-notifications/android') - -include ':evva-capacitor-secure-storage-plugin' -project(':evva-capacitor-secure-storage-plugin').projectDir = new File('../node_modules/@evva/capacitor-secure-storage-plugin/android') - -include ':transistorsoft-capacitor-background-fetch' -project(':transistorsoft-capacitor-background-fetch').projectDir = new File('../node_modules/@transistorsoft/capacitor-background-fetch/android') diff --git a/android/gradle.properties b/android/gradle.properties deleted file mode 100644 index 2e87c52..0000000 --- a/android/gradle.properties +++ /dev/null @@ -1,22 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true - -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app's APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 033e24c4cdf41af1ab109bc7f253b2b887023340..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63375 zcmb5VV{~QRw)Y#`wrv{~+qP{x72B%VwzFc}c2cp;N~)5ZbDrJayPv(!dGEd-##*zr z)#n-$y^sH|_dchh3@8{H5D*j;5D<{i*8l5IFJ|DjL!e)upfGNX(kojugZ3I`oH1PvW`wFW_ske0j@lB9bX zO;2)`y+|!@X(fZ1<2n!Qx*)_^Ai@Cv-dF&(vnudG?0CsddG_&Wtae(n|K59ew)6St z#dj7_(Cfwzh$H$5M!$UDd8=4>IQsD3xV=lXUq($;(h*$0^yd+b{qq63f0r_de#!o_ zXDngc>zy`uor)4A^2M#U*DC~i+dc<)Tb1Tv&~Ev@oM)5iJ4Sn#8iRw16XXuV50BS7 zdBL5Mefch(&^{luE{*5qtCZk$oFr3RH=H!c3wGR=HJ(yKc_re_X9pD` zJ;uxPzUfVpgU>DSq?J;I@a+10l0ONXPcDkiYcihREt5~T5Gb}sT0+6Q;AWHl`S5dV>lv%-p9l#xNNy7ZCr%cyqHY%TZ8Q4 zbp&#ov1*$#grNG#1vgfFOLJCaNG@K|2!W&HSh@3@Y%T?3YI75bJp!VP*$*!< z;(ffNS_;@RJ`=c7yX04!u3JP*<8jeqLHVJu#WV&v6wA!OYJS4h<_}^QI&97-;=ojW zQ-1t)7wnxG*5I%U4)9$wlv5Fr;cIizft@&N+32O%B{R1POm$oap@&f| zh+5J{>U6ftv|vAeKGc|zC=kO(+l7_cLpV}-D#oUltScw})N>~JOZLU_0{Ka2e1evz z{^a*ZrLr+JUj;)K&u2CoCAXLC2=fVScI(m_p~0FmF>>&3DHziouln?;sxW`NB}cSX z8?IsJB)Z=aYRz!X=yJn$kyOWK%rCYf-YarNqKzmWu$ZvkP12b4qH zhS9Q>j<}(*frr?z<%9hl*i^#@*O2q(Z^CN)c2c z>1B~D;@YpG?G!Yk+*yn4vM4sO-_!&m6+`k|3zd;8DJnxsBYtI;W3We+FN@|tQ5EW= z!VU>jtim0Mw#iaT8t_<+qKIEB-WwE04lBd%Letbml9N!?SLrEG$nmn7&W(W`VB@5S zaY=sEw2}i@F_1P4OtEw?xj4@D6>_e=m=797#hg}f*l^`AB|Y0# z9=)o|%TZFCY$SzgSjS|8AI-%J4x}J)!IMxY3_KYze`_I=c1nmrk@E8c9?MVRu)7+Ue79|)rBX7tVB7U|w4*h(;Gi3D9le49B38`wuv zp7{4X^p+K4*$@gU(Tq3K1a#3SmYhvI42)GzG4f|u zwQFT1n_=n|jpi=70-yE9LA+d*T8u z`=VmmXJ_f6WmZveZPct$Cgu^~gFiyL>Lnpj*6ee>*0pz=t$IJ}+rE zsf@>jlcG%Wx;Cp5x)YSVvB1$yyY1l&o zvwX=D7k)Dn;ciX?Z)Pn8$flC8#m`nB&(8?RSdBvr?>T9?E$U3uIX7T?$v4dWCa46 z+&`ot8ZTEgp7G+c52oHJ8nw5}a^dwb_l%MOh(ebVj9>_koQP^$2B~eUfSbw9RY$_< z&DDWf2LW;b0ZDOaZ&2^i^g+5uTd;GwO(-bbo|P^;CNL-%?9mRmxEw~5&z=X^Rvbo^WJW=n_%*7974RY}JhFv46> zd}`2|qkd;89l}R;i~9T)V-Q%K)O=yfVKNM4Gbacc7AOd>#^&W&)Xx!Uy5!BHnp9kh z`a(7MO6+Ren#>R^D0K)1sE{Bv>}s6Rb9MT14u!(NpZOe-?4V=>qZ>}uS)!y~;jEUK z&!U7Fj&{WdgU#L0%bM}SYXRtM5z!6M+kgaMKt%3FkjWYh=#QUpt$XX1!*XkpSq-pl zhMe{muh#knk{9_V3%qdDcWDv}v)m4t9 zQhv{;} zc{}#V^N3H>9mFM8`i`0p+fN@GqX+kl|M94$BK3J-X`Hyj8r!#x6Vt(PXjn?N)qedP z=o1T^#?1^a{;bZ&x`U{f?}TMo8ToN zkHj5v|}r}wDEi7I@)Gj+S1aE-GdnLN+$hw!=DzglMaj#{qjXi_dwpr|HL(gcCXwGLEmi|{4&4#OZ4ChceA zKVd4K!D>_N=_X;{poT~4Q+!Le+ZV>=H7v1*l%w`|`Dx8{)McN@NDlQyln&N3@bFpV z_1w~O4EH3fF@IzJ9kDk@7@QctFq8FbkbaH7K$iX=bV~o#gfh?2JD6lZf(XP>~DACF)fGFt)X%-h1yY~MJU{nA5 ze2zxWMs{YdX3q5XU*9hOH0!_S24DOBA5usB+Ws$6{|AMe*joJ?RxfV}*7AKN9V*~J zK+OMcE@bTD>TG1*yc?*qGqjBN8mgg@h1cJLDv)0!WRPIkC` zZrWXrceVw;fB%3`6kq=a!pq|hFIsQ%ZSlo~)D z|64!aCnw-?>}AG|*iOl44KVf8@|joXi&|)1rB;EQWgm+iHfVbgllP$f!$Wf42%NO5b(j9Bw6L z;0dpUUK$5GX4QbMlTmLM_jJt!ur`_0~$b#BB7FL*%XFf<b__1o)Ao3rlobbN8-(T!1d-bR8D3S0@d zLI!*GMb5s~Q<&sjd}lBb8Nr0>PqE6_!3!2d(KAWFxa{hm`@u|a(%#i(#f8{BP2wbs zt+N_slWF4IF_O|{w`c~)Xvh&R{Au~CFmW#0+}MBd2~X}t9lz6*E7uAD`@EBDe$>7W zzPUkJx<`f$0VA$=>R57^(K^h86>09?>_@M(R4q($!Ck6GG@pnu-x*exAx1jOv|>KH zjNfG5pwm`E-=ydcb+3BJwuU;V&OS=6yM^4Jq{%AVqnTTLwV`AorIDD}T&jWr8pB&j28fVtk_y*JRP^t@l*($UZ z6(B^-PBNZ+z!p?+e8@$&jCv^EWLb$WO=}Scr$6SM*&~B95El~;W_0(Bvoha|uQ1T< zO$%_oLAwf1bW*rKWmlD+@CP&$ObiDy=nh1b2ejz%LO9937N{LDe7gle4i!{}I$;&Y zkexJ9Ybr+lrCmKWg&}p=`2&Gf10orS?4$VrzWidT=*6{KzOGMo?KI0>GL0{iFWc;C z+LPq%VH5g}6V@-tg2m{C!-$fapJ9y}c$U}aUmS{9#0CM*8pC|sfer!)nG7Ji>mfRh z+~6CxNb>6eWKMHBz-w2{mLLwdA7dA-qfTu^A2yG1+9s5k zcF=le_UPYG&q!t5Zd_*E_P3Cf5T6821bO`daa`;DODm8Ih8k89=RN;-asHIigj`n=ux>*f!OC5#;X5i;Q z+V!GUy0|&Y_*8k_QRUA8$lHP;GJ3UUD08P|ALknng|YY13)}!!HW@0z$q+kCH%xet zlWf@BXQ=b=4}QO5eNnN~CzWBbHGUivG=`&eWK}beuV*;?zt=P#pM*eTuy3 zP}c#}AXJ0OIaqXji78l;YrP4sQe#^pOqwZUiiN6^0RCd#D271XCbEKpk`HI0IsN^s zES7YtU#7=8gTn#lkrc~6)R9u&SX6*Jk4GFX7){E)WE?pT8a-%6P+zS6o&A#ml{$WX zABFz#i7`DDlo{34)oo?bOa4Z_lNH>n;f0nbt$JfAl~;4QY@}NH!X|A$KgMmEsd^&Y zt;pi=>AID7ROQfr;MsMtClr5b0)xo|fwhc=qk33wQ|}$@?{}qXcmECh>#kUQ-If0$ zseb{Wf4VFGLNc*Rax#P8ko*=`MwaR-DQ8L8V8r=2N{Gaips2_^cS|oC$+yScRo*uF zUO|5=?Q?{p$inDpx*t#Xyo6=s?bbN}y>NNVxj9NZCdtwRI70jxvm3!5R7yiWjREEd zDUjrsZhS|P&|Ng5r+f^kA6BNN#|Se}_GF>P6sy^e8kBrgMv3#vk%m}9PCwUWJg-AD zFnZ=}lbi*mN-AOm zCs)r=*YQAA!`e#1N>aHF=bb*z*hXH#Wl$z^o}x##ZrUc=kh%OHWhp=7;?8%Xj||@V?1c ziWoaC$^&04;A|T)!Zd9sUzE&$ODyJaBpvqsw19Uiuq{i#VK1!htkdRWBnb z`{rat=nHArT%^R>u#CjjCkw-7%g53|&7z-;X+ewb?OLWiV|#nuc8mp*LuGSi3IP<<*Wyo9GKV7l0Noa4Jr0g3p_$ z*R9{qn=?IXC#WU>48-k5V2Oc_>P;4_)J@bo1|pf=%Rcbgk=5m)CJZ`caHBTm3%!Z9 z_?7LHr_BXbKKr=JD!%?KhwdYSdu8XxPoA{n8^%_lh5cjRHuCY9Zlpz8g+$f@bw@0V z+6DRMT9c|>1^3D|$Vzc(C?M~iZurGH2pXPT%F!JSaAMdO%!5o0uc&iqHx?ImcX6fI zCApkzc~OOnfzAd_+-DcMp&AOQxE_EsMqKM{%dRMI5`5CT&%mQO?-@F6tE*xL?aEGZ z8^wH@wRl`Izx4sDmU>}Ym{ybUm@F83qqZPD6nFm?t?(7>h*?`fw)L3t*l%*iw0Qu#?$5eq!Qc zpQvqgSxrd83NsdO@lL6#{%lsYXWen~d3p4fGBb7&5xqNYJ)yn84!e1PmPo7ChVd%4 zHUsV0Mh?VpzZD=A6%)Qrd~i7 z96*RPbid;BN{Wh?adeD_p8YU``kOrGkNox3D9~!K?w>#kFz!4lzOWR}puS(DmfjJD z`x0z|qB33*^0mZdM&6$|+T>fq>M%yoy(BEjuh9L0>{P&XJ3enGpoQRx`v6$txXt#c z0#N?b5%srj(4xmPvJxrlF3H%OMB!jvfy z;wx8RzU~lb?h_}@V=bh6p8PSb-dG|-T#A?`c&H2`_!u+uenIZe`6f~A7r)`9m8atC zt(b|6Eg#!Q*DfRU=Ix`#B_dK)nnJ_+>Q<1d7W)eynaVn`FNuN~%B;uO2}vXr5^zi2 z!ifIF5@Zlo0^h~8+ixFBGqtweFc`C~JkSq}&*a3C}L?b5Mh-bW=e)({F_g4O3 zb@SFTK3VD9QuFgFnK4Ve_pXc3{S$=+Z;;4+;*{H}Rc;845rP?DLK6G5Y-xdUKkA6E3Dz&5f{F^FjJQ(NSpZ8q-_!L3LL@H* zxbDF{gd^U3uD;)a)sJwAVi}7@%pRM&?5IaUH%+m{E)DlA_$IA1=&jr{KrhD5q&lTC zAa3c)A(K!{#nOvenH6XrR-y>*4M#DpTTOGQEO5Jr6kni9pDW`rvY*fs|ItV;CVITh z=`rxcH2nEJpkQ^(;1c^hfb8vGN;{{oR=qNyKtR1;J>CByul*+=`NydWnSWJR#I2lN zTvgnR|MBx*XFsfdA&;tr^dYaqRZp*2NwkAZE6kV@1f{76e56eUmGrZ>MDId)oqSWw z7d&r3qfazg+W2?bT}F)4jD6sWaw`_fXZGY&wnGm$FRPFL$HzVTH^MYBHWGCOk-89y zA+n+Q6EVSSCpgC~%uHfvyg@ufE^#u?JH?<73A}jj5iILz4Qqk5$+^U(SX(-qv5agK znUkfpke(KDn~dU0>gdKqjTkVk`0`9^0n_wzXO7R!0Thd@S;U`y)VVP&mOd-2 z(hT(|$=>4FY;CBY9#_lB$;|Wd$aOMT5O_3}DYXEHn&Jrc3`2JiB`b6X@EUOD zVl0S{ijm65@n^19T3l%>*;F(?3r3s?zY{thc4%AD30CeL_4{8x6&cN}zN3fE+x<9; zt2j1RRVy5j22-8U8a6$pyT+<`f+x2l$fd_{qEp_bfxfzu>ORJsXaJn4>U6oNJ#|~p z`*ZC&NPXl&=vq2{Ne79AkQncuxvbOG+28*2wU$R=GOmns3W@HE%^r)Fu%Utj=r9t` zd;SVOnA(=MXgnOzI2@3SGKHz8HN~Vpx&!Ea+Df~`*n@8O=0!b4m?7cE^K*~@fqv9q zF*uk#1@6Re_<^9eElgJD!nTA@K9C732tV~;B`hzZ321Ph=^BH?zXddiu{Du5*IPg} zqDM=QxjT!Rp|#Bkp$(mL)aar)f(dOAXUiw81pX0DC|Y4;>Vz>>DMshoips^8Frdv} zlTD=cKa48M>dR<>(YlLPOW%rokJZNF2gp8fwc8b2sN+i6&-pHr?$rj|uFgktK@jg~ zIFS(%=r|QJ=$kvm_~@n=ai1lA{7Z}i+zj&yzY+!t$iGUy|9jH#&oTNJ;JW-3n>DF+ z3aCOzqn|$X-Olu_p7brzn`uk1F*N4@=b=m;S_C?#hy{&NE#3HkATrg?enaVGT^$qIjvgc61y!T$9<1B@?_ibtDZ{G zeXInVr5?OD_nS_O|CK3|RzzMmu+8!#Zb8Ik;rkIAR%6?$pN@d<0dKD2c@k2quB%s( zQL^<_EM6ow8F6^wJN1QcPOm|ehA+dP(!>IX=Euz5qqIq}Y3;ibQtJnkDmZ8c8=Cf3 zu`mJ!Q6wI7EblC5RvP*@)j?}W=WxwCvF3*5Up_`3*a~z$`wHwCy)2risye=1mSp%p zu+tD6NAK3o@)4VBsM!@);qgsjgB$kkCZhaimHg&+k69~drbvRTacWKH;YCK(!rC?8 zP#cK5JPHSw;V;{Yji=55X~S+)%(8fuz}O>*F3)hR;STU`z6T1aM#Wd+FP(M5*@T1P z^06O;I20Sk!bxW<-O;E081KRdHZrtsGJflFRRFS zdi5w9OVDGSL3 zNrC7GVsGN=b;YH9jp8Z2$^!K@h=r-xV(aEH@#JicPy;A0k1>g1g^XeR`YV2HfmqXY zYbRwaxHvf}OlCAwHoVI&QBLr5R|THf?nAevV-=~V8;gCsX>jndvNOcFA+DI+zbh~# zZ7`qNk&w+_+Yp!}j;OYxIfx_{f0-ONc?mHCiCUak=>j>~>YR4#w# zuKz~UhT!L~GfW^CPqG8Lg)&Rc6y^{%3H7iLa%^l}cw_8UuG;8nn9)kbPGXS}p3!L_ zd#9~5CrH8xtUd?{d2y^PJg+z(xIfRU;`}^=OlehGN2=?}9yH$4Rag}*+AWotyxfCJ zHx=r7ZH>j2kV?%7WTtp+-HMa0)_*DBBmC{sd$)np&GEJ__kEd`xB5a2A z*J+yx>4o#ZxwA{;NjhU*1KT~=ZK~GAA;KZHDyBNTaWQ1+;tOFFthnD)DrCn`DjBZ% zk$N5B4^$`n^jNSOr=t(zi8TN4fpaccsb`zOPD~iY=UEK$0Y70bG{idLx@IL)7^(pL z{??Bnu=lDeguDrd%qW1)H)H`9otsOL-f4bSu};o9OXybo6J!Lek`a4ff>*O)BDT_g z<6@SrI|C9klY(>_PfA^qai7A_)VNE4c^ZjFcE$Isp>`e5fLc)rg@8Q_d^Uk24$2bn z9#}6kZ2ZxS9sI(RqT7?El2@B+($>eBQrNi_k#CDJ8D9}8$mmm z4oSKO^F$i+NG)-HE$O6s1--6EzJa?C{x=QgK&c=)b(Q9OVoAXYEEH20G|q$}Hue%~ zO3B^bF=t7t48sN zWh_zA`w~|){-!^g?6Mqf6ieV zFx~aPUOJGR=4{KsW7I?<=J2|lY`NTU=lt=%JE9H1vBpkcn=uq(q~=?iBt_-r(PLBM zP-0dxljJO>4Wq-;stY)CLB4q`-r*T$!K2o}?E-w_i>3_aEbA^MB7P5piwt1dI-6o!qWCy0 ztYy!x9arGTS?kabkkyv*yxvsPQ7Vx)twkS6z2T@kZ|kb8yjm+^$|sEBmvACeqbz)RmxkkDQX-A*K!YFziuhwb|ym>C$}U|J)4y z$(z#)GH%uV6{ec%Zy~AhK|+GtG8u@c884Nq%w`O^wv2#A(&xH@c5M`Vjk*SR_tJnq z0trB#aY)!EKW_}{#L3lph5ow=@|D5LzJYUFD6 z7XnUeo_V0DVSIKMFD_T0AqAO|#VFDc7c?c-Q%#u00F%!_TW1@JVnsfvm@_9HKWflBOUD~)RL``-!P;(bCON_4eVdduMO>?IrQ__*zE@7(OX zUtfH@AX*53&xJW*Pu9zcqxGiM>xol0I~QL5B%Toog3Jlenc^WbVgeBvV8C8AX^Vj& z^I}H})B=VboO%q1;aU5ACMh{yK4J;xlMc`jCnZR^!~LDs_MP&8;dd@4LDWw~*>#OT zeZHwdQWS!tt5MJQI~cw|Ka^b4c|qyd_ly(+Ql2m&AAw^ zQeSXDOOH!!mAgzAp0z)DD>6Xo``b6QwzUV@w%h}Yo>)a|xRi$jGuHQhJVA%>)PUvK zBQ!l0hq<3VZ*RnrDODP)>&iS^wf64C;MGqDvx>|p;35%6(u+IHoNbK z;Gb;TneFo*`zUKS6kwF*&b!U8e5m4YAo03a_e^!5BP42+r)LFhEy?_7U1IR<; z^0v|DhCYMSj<-;MtY%R@Fg;9Kky^pz_t2nJfKWfh5Eu@_l{^ph%1z{jkg5jQrkvD< z#vdK!nku*RrH~TdN~`wDs;d>XY1PH?O<4^U4lmA|wUW{Crrv#r%N>7k#{Gc44Fr|t z@UZP}Y-TrAmnEZ39A*@6;ccsR>)$A)S>$-Cj!=x$rz7IvjHIPM(TB+JFf{ehuIvY$ zsDAwREg*%|=>Hw$`us~RP&3{QJg%}RjJKS^mC_!U;E5u>`X`jW$}P`Mf}?7G7FX#{ zE(9u1SO;3q@ZhDL9O({-RD+SqqPX)`0l5IQu4q)49TUTkxR(czeT}4`WV~pV*KY&i zAl3~X%D2cPVD^B43*~&f%+Op)wl<&|D{;=SZwImydWL6@_RJjxP2g)s=dH)u9Npki zs~z9A+3fj0l?yu4N0^4aC5x)Osnm0qrhz@?nwG_`h(71P znbIewljU%T*cC=~NJy|)#hT+lx#^5MuDDnkaMb*Efw9eThXo|*WOQzJ*#3dmRWm@! zfuSc@#kY{Um^gBc^_Xdxnl!n&y&}R4yAbK&RMc+P^Ti;YIUh|C+K1|=Z^{nZ}}rxH*v{xR!i%qO~o zTr`WDE@k$M9o0r4YUFFeQO7xCu_Zgy)==;fCJ94M_rLAv&~NhfvcLWCoaGg2ao~3e zBG?Ms9B+efMkp}7BhmISGWmJsKI@a8b}4lLI48oWKY|8?zuuNc$lt5Npr+p7a#sWu zh!@2nnLBVJK!$S~>r2-pN||^w|fY`CT{TFnJy`B|e5;=+_v4l8O-fkN&UQbA4NKTyntd zqK{xEKh}U{NHoQUf!M=2(&w+eef77VtYr;xs%^cPfKLObyOV_9q<(%76-J%vR>w9!us-0c-~Y?_EVS%v!* z15s2s3eTs$Osz$JayyH|5nPAIPEX=U;r&p;K14G<1)bvn@?bM5kC{am|C5%hyxv}a z(DeSKI5ZfZ1*%dl8frIX2?);R^^~LuDOpNpk-2R8U1w92HmG1m&|j&J{EK=|p$;f9 z7Rs5|jr4r8k5El&qcuM+YRlKny%t+1CgqEWO>3;BSRZi(LA3U%Jm{@{y+A+w(gzA< z7dBq6a1sEWa4cD0W7=Ld9z0H7RI^Z7vl(bfA;72j?SWCo`#5mVC$l1Q2--%V)-uN* z9ha*s-AdfbDZ8R8*fpwjzx=WvOtmSzGFjC#X)hD%Caeo^OWjS(3h|d9_*U)l%{Ab8 zfv$yoP{OuUl@$(-sEVNt{*=qi5P=lpxWVuz2?I7Dc%BRc+NGNw+323^ z5BXGfS71oP^%apUo(Y#xkxE)y?>BFzEBZ}UBbr~R4$%b7h3iZu3S(|A;&HqBR{nK& z$;GApNnz=kNO^FL&nYcfpB7Qg;hGJPsCW44CbkG1@l9pn0`~oKy5S777uH)l{irK!ru|X+;4&0D;VE*Ii|<3P zUx#xUqvZT5kVQxsF#~MwKnv7;1pR^0;PW@$@T7I?s`_rD1EGUdSA5Q(C<>5SzE!vw z;{L&kKFM-MO>hy#-8z`sdVx})^(Dc-dw;k-h*9O2_YZw}|9^y-|8RQ`BWJUJL(Cer zP5Z@fNc>pTXABbTRY-B5*MphpZv6#i802giwV&SkFCR zGMETyUm(KJbh+&$8X*RB#+{surjr;8^REEt`2&Dubw3$mx>|~B5IKZJ`s_6fw zKAZx9&PwBqW1Oz0r0A4GtnZd7XTKViX2%kPfv+^X3|_}RrQ2e3l=KG_VyY`H?I5&CS+lAX5HbA%TD9u6&s#v!G> zzW9n4J%d5ye7x0y`*{KZvqyXUfMEE^ZIffzI=Hh|3J}^yx7eL=s+TPH(Q2GT-sJ~3 zI463C{(ag7-hS1ETtU;_&+49ABt5!A7CwLwe z=SoA8mYZIQeU;9txI=zcQVbuO%q@E)JI+6Q!3lMc=Gbj(ASg-{V27u>z2e8n;Nc*pf}AqKz1D>p9G#QA+7mqqrEjGfw+85Uyh!=tTFTv3|O z+)-kFe_8FF_EkTw!YzwK^Hi^_dV5x-Ob*UWmD-})qKj9@aE8g240nUh=g|j28^?v7 zHRTBo{0KGaWBbyX2+lx$wgXW{3aUab6Bhm1G1{jTC7ota*JM6t+qy)c5<@ zpc&(jVdTJf(q3xB=JotgF$X>cxh7k*(T`-V~AR+`%e?YOeALQ2Qud( zz35YizXt(aW3qndR}fTw1p()Ol4t!D1pitGNL95{SX4ywzh0SF;=!wf=?Q?_h6!f* zh7<+GFi)q|XBsvXZ^qVCY$LUa{5?!CgwY?EG;*)0ceFe&=A;!~o`ae}Z+6me#^sv- z1F6=WNd6>M(~ z+092z>?Clrcp)lYNQl9jN-JF6n&Y0mp7|I0dpPx+4*RRK+VQI~>en0Dc;Zfl+x z_e_b7s`t1_A`RP3$H}y7F9_na%D7EM+**G_Z0l_nwE+&d_kc35n$Fxkd4r=ltRZhh zr9zER8>j(EdV&Jgh(+i}ltESBK62m0nGH6tCBr90!4)-`HeBmz54p~QP#dsu%nb~W z7sS|(Iydi>C@6ZM(Us!jyIiszMkd)^u<1D+R@~O>HqZIW&kearPWmT>63%_t2B{_G zX{&a(gOYJx!Hq=!T$RZ&<8LDnxsmx9+TBL0gTk$|vz9O5GkK_Yx+55^R=2g!K}NJ3 zW?C;XQCHZl7H`K5^BF!Q5X2^Mj93&0l_O3Ea3!Ave|ixx+~bS@Iv18v2ctpSt4zO{ zp#7pj!AtDmti$T`e9{s^jf(ku&E|83JIJO5Qo9weT6g?@vX!{7)cNwymo1+u(YQ94 zopuz-L@|5=h8A!(g-MXgLJC0MA|CgQF8qlonnu#j z;uCeq9ny9QSD|p)9sp3ebgY3rk#y0DA(SHdh$DUm^?GI<>%e1?&}w(b zdip1;P2Z=1wM+$q=TgLP$}svd!vk+BZ@h<^4R=GS2+sri7Z*2f`9 z5_?i)xj?m#pSVchk-SR!2&uNhzEi+#5t1Z$o0PoLGz*pT64%+|Wa+rd5Z}60(j?X= z{NLjtgRb|W?CUADqOS@(*MA-l|E342NxRaxLTDqsOyfWWe%N(jjBh}G zm7WPel6jXijaTiNita+z(5GCO0NM=Melxud57PP^d_U## zbA;9iVi<@wr0DGB8=T9Ab#2K_#zi=$igyK48@;V|W`fg~7;+!q8)aCOo{HA@vpSy-4`^!ze6-~8|QE||hC{ICKllG9fbg_Y7v z$jn{00!ob3!@~-Z%!rSZ0JO#@>|3k10mLK0JRKP-Cc8UYFu>z93=Ab-r^oL2 zl`-&VBh#=-?{l1TatC;VweM^=M7-DUE>m+xO7Xi6vTEsReyLs8KJ+2GZ&rxw$d4IT zPXy6pu^4#e;;ZTsgmG+ZPx>piodegkx2n0}SM77+Y*j^~ICvp#2wj^BuqRY*&cjmL zcKp78aZt>e{3YBb4!J_2|K~A`lN=u&5j!byw`1itV(+Q_?RvV7&Z5XS1HF)L2v6ji z&kOEPmv+k_lSXb{$)of~(BkO^py&7oOzpjdG>vI1kcm_oPFHy38%D4&A4h_CSo#lX z2#oqMCTEP7UvUR3mwkPxbl8AMW(e{ARi@HCYLPSHE^L<1I}OgZD{I#YH#GKnpRmW3 z2jkz~Sa(D)f?V?$gNi?6)Y;Sm{&?~2p=0&BUl_(@hYeX8YjaRO=IqO7neK0RsSNdYjD zaw$g2sG(>JR=8Iz1SK4`*kqd_3-?;_BIcaaMd^}<@MYbYisWZm2C2|Np_l|8r9yM|JkUngSo@?wci(7&O9a z%|V(4C1c9pps0xxzPbXH=}QTxc2rr7fXk$9`a6TbWKPCz&p=VsB8^W96W=BsB|7bc zf(QR8&Ktj*iz)wK&mW`#V%4XTM&jWNnDF56O+2bo<3|NyUhQ%#OZE8$Uv2a@J>D%t zMVMiHh?es!Ex19q&6eC&L=XDU_BA&uR^^w>fpz2_`U87q_?N2y;!Z!bjoeKrzfC)} z?m^PM=(z{%n9K`p|7Bz$LuC7!>tFOuN74MFELm}OD9?%jpT>38J;=1Y-VWtZAscaI z_8jUZ#GwWz{JqvGEUmL?G#l5E=*m>`cY?m*XOc*yOCNtpuIGD+Z|kn4Xww=BLrNYS zGO=wQh}Gtr|7DGXLF%|`G>J~l{k^*{;S-Zhq|&HO7rC_r;o`gTB7)uMZ|WWIn@e0( zX$MccUMv3ABg^$%_lNrgU{EVi8O^UyGHPNRt%R!1#MQJn41aD|_93NsBQhP80yP<9 zG4(&0u7AtJJXLPcqzjv`S~5;Q|5TVGccN=Uzm}K{v)?f7W!230C<``9(64}D2raRU zAW5bp%}VEo{4Rko`bD%Ehf=0voW?-4Mk#d3_pXTF!-TyIt6U+({6OXWVAa;s-`Ta5 zTqx&8msH3+DLrVmQOTBOAj=uoxKYT3DS1^zBXM?1W+7gI!aQNPYfUl{3;PzS9*F7g zWJN8x?KjBDx^V&6iCY8o_gslO16=kh(|Gp)kz8qlQ`dzxQv;)V&t+B}wwdi~uBs4? zu~G|}y!`3;8#vIMUdyC7YEx6bb^1o}G!Jky4cN?BV9ejBfN<&!4M)L&lRKiuMS#3} z_B}Nkv+zzxhy{dYCW$oGC&J(Ty&7%=5B$sD0bkuPmj7g>|962`(Q{ZZMDv%YMuT^KweiRDvYTEop3IgFv#)(w>1 zSzH>J`q!LK)c(AK>&Ib)A{g`Fdykxqd`Yq@yB}E{gnQV$K!}RsgMGWqC3DKE(=!{}ekB3+(1?g}xF>^icEJbc z5bdxAPkW90atZT+&*7qoLqL#p=>t-(-lsnl2XMpZcYeW|o|a322&)yO_8p(&Sw{|b zn(tY$xn5yS$DD)UYS%sP?c|z>1dp!QUD)l;aW#`%qMtQJjE!s2z`+bTSZmLK7SvCR z=@I4|U^sCwZLQSfd*ACw9B@`1c1|&i^W_OD(570SDLK`MD0wTiR8|$7+%{cF&){$G zU~|$^Ed?TIxyw{1$e|D$050n8AjJvvOWhLtLHbSB|HIfhMpqVf>AF&}ZQHhOJ14Bz zww+XL+qP}nww+W`F>b!by|=&a(cM4JIDhsTXY8@|ntQG}-}jm0&Bcj|LV(#sc=BNS zRjh;k9l>EdAFdd)=H!U`~$WP*}~^3HZ_?H>gKw>NBa;tA8M1{>St|)yDF_=~{KEPAGkg3VB`QCHol!AQ0|?e^W?81f{@()Wy!vQ$bY; z0ctx)l7VK83d6;dp!s{Nu=SwXZ8lHQHC*J2g@P0a={B8qHdv(+O3wV=4-t4HK1+smO#=S; z3cSI#Nh+N@AqM#6wPqjDmQM|x95JG|l1#sAU|>I6NdF*G@bD?1t|ytHlkKD+z9}#j zbU+x_cR-j9yX4s{_y>@zk*ElG1yS({BInGJcIT>l4N-DUs6fufF#GlF2lVUNOAhJT zGZThq54GhwCG(h4?yWR&Ax8hU<*U)?g+HY5-@{#ls5CVV(Wc>Bavs|l<}U|hZn z_%m+5i_gaakS*Pk7!v&w3&?R5Xb|AkCdytTY;r+Z7f#Id=q+W8cn)*9tEet=OG+Y} z58U&!%t9gYMx2N=8F?gZhIjtkH!`E*XrVJ?$2rRxLhV1z82QX~PZi8^N5z6~f-MUE zLKxnNoPc-SGl7{|Oh?ZM$jq67sSa)Wr&3)0YxlJt(vKf!-^L)a|HaPv*IYXb;QmWx zsqM>qY;tpK3RH-omtta+Xf2Qeu^$VKRq7`e$N-UCe1_2|1F{L3&}M0XbJ@^xRe&>P zRdKTgD6601x#fkDWkoYzRkxbn#*>${dX+UQ;FbGnTE-+kBJ9KPn)501#_L4O_k`P3 zm+$jI{|EC?8BXJY{P~^f-{**E53k%kVO$%p+=H5DiIdwMmUo>2euq0UzU90FWL!>; z{5@sd0ecqo5j!6AH@g6Mf3keTP$PFztq}@)^ZjK;H6Go$#SV2|2bAFI0%?aXgVH$t zb4Kl`$Xh8qLrMbZUS<2*7^F0^?lrOE=$DHW+O zvLdczsu0^TlA6RhDy3=@s!k^1D~Awulk!Iyo#}W$xq8{yTAK!CLl={H0@YGhg-g~+ z(u>pss4k#%8{J%~%8=H5!T`rqK6w^es-cNVE}=*lP^`i&K4R=peg1tdmT~UAbDKc& zg%Y*1E{hBf<)xO>HDWV7BaMWX6FW4ou1T2m^6{Jb!Su1UaCCYY8RR8hAV$7ho|FyEyP~ zEgK`@%a$-C2`p zV*~G>GOAs*3KN;~IY_UR$ISJxB(N~K>=2C2V6>xTmuX4klRXdrJd&UPAw7&|KEwF8Zcy2j-*({gSNR1^p02Oj88GN9a_Hq;Skdp}kO0;FLbje%2ZvPiltDZgv^ z#pb4&m^!79;O8F+Wr9X71laPY!CdNXG?J6C9KvdAE2xWW1>U~3;0v≫L+crb^Bz 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat deleted file mode 100644 index 6689b85..0000000 --- a/android/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index 3b4431d..0000000 --- a/android/settings.gradle +++ /dev/null @@ -1,5 +0,0 @@ -include ':app' -include ':capacitor-cordova-android-plugins' -project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/') - -apply from: 'capacitor.settings.gradle' \ No newline at end of file diff --git a/android/variables.gradle b/android/variables.gradle deleted file mode 100644 index 8ef305d..0000000 --- a/android/variables.gradle +++ /dev/null @@ -1,16 +0,0 @@ -ext { - minSdkVersion = 22 - compileSdkVersion = 34 - targetSdkVersion = 34 - androidxActivityVersion = '1.8.0' - androidxAppCompatVersion = '1.6.1' - androidxCoordinatorLayoutVersion = '1.2.0' - androidxCoreVersion = '1.12.0' - androidxFragmentVersion = '1.6.2' - coreSplashScreenVersion = '1.0.1' - androidxWebkitVersion = '1.9.0' - junitVersion = '4.13.2' - androidxJunitVersion = '1.1.5' - androidxEspressoCoreVersion = '3.5.1' - cordovaAndroidVersion = '10.1.1' -} \ No newline at end of file From 396e5297c2b2bf17b13b11a292ec9a17f26d4cfc Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 16:05:48 +0200 Subject: [PATCH 184/717] Fix missing bracket --- src/App.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 1c6913a..3ad6fe2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -289,7 +289,6 @@ export const isMainWindow = true; function App() { const [extState, setExtstate] = useState('not-authenticated'); const [desktopViewMode, setDesktopViewMode] = useState('home'); - const [backupjson, setBackupjson] = useState(null); const [rawWallet, setRawWallet] = useState(null); const [ltcBalanceLoading, setLtcBalanceLoading] = useState(false); @@ -2018,7 +2017,7 @@ function App() { setIsOpenDrawerProfile={setIsOpenDrawerProfile} userInfo={userInfo} /> - renderProfile() + {renderProfile()} )} From 58eb704dcd6c49abf367db2fd9aac6ddd735b8a7 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 16:06:04 +0200 Subject: [PATCH 185/717] Set correct property --- src/assets/Icons/Download.tsx | 2 +- src/assets/Icons/Logout.tsx | 2 +- src/assets/Icons/NavAdd.tsx | 2 +- src/assets/Icons/NavCloseTab.tsx | 4 ++-- src/assets/Icons/NavMoreMenu.tsx | 2 +- src/assets/Icons/Return.tsx | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/assets/Icons/Download.tsx b/src/assets/Icons/Download.tsx index 28ac605..a8498db 100644 --- a/src/assets/Icons/Download.tsx +++ b/src/assets/Icons/Download.tsx @@ -25,7 +25,7 @@ export const Download: React.FC = ({ clipRule="evenodd" d="M12.8047 0.393196V7.21185H16.3036L10.0003 13.5139L3.69697 7.21185H7.19584V0H12.8045L12.8047 0.393196ZM2.7047 16.8587V13.9861H0V18.6179C0 19.3774 0.622589 20 1.38213 20H18.6179C19.3774 20 20 19.3774 20 18.6179V13.9861H17.2962V17.2963L2.70461 17.2954L2.7047 16.8587Z" fill={setColor} - fill-opacity={setOpacity} + fillOpacity={setOpacity} /> ); diff --git a/src/assets/Icons/Logout.tsx b/src/assets/Icons/Logout.tsx index f7d9c57..b15d0f4 100644 --- a/src/assets/Icons/Logout.tsx +++ b/src/assets/Icons/Logout.tsx @@ -21,7 +21,7 @@ export const Logout: React.FC = ({ color, opacity, ...children }) => { clipRule="evenodd" d="M7.56485 0H16.3611C17.2662 0 18 0.727797 18 1.62558V18.3744C18 19.2722 17.2662 20 16.3611 20H7.56485C6.65969 20 5.92593 19.2722 5.92593 18.3744V12.6013H10.6168C11.4569 12.6013 12.1404 11.9039 12.1404 11.0467V8.87329C12.1404 8.01613 11.4569 7.31875 10.6168 7.31875H5.92593V1.62558C5.92593 0.727797 6.65969 0 7.56485 0ZM11.1667 11.0467C11.1667 11.3719 10.9205 11.6354 10.6168 11.6354H4.8144C4.74521 11.6354 4.68911 11.6955 4.68911 11.7696V12.8632C4.68911 13.3492 4.17007 13.6259 3.8078 13.3329L0.218431 10.4298C-0.0728102 10.1942 -0.0728102 9.72579 0.218431 9.49024L3.8078 6.58709C4.17005 6.29409 4.68911 6.57077 4.68911 7.05684V8.1504C4.68911 8.2245 4.74521 8.28454 4.8144 8.28454H10.6168C10.9205 8.28454 11.1667 8.54813 11.1667 8.87329V11.0467Z" fill={setColor} - fill-opacity={setOpacity} + fillOpacity={setOpacity} /> ); diff --git a/src/assets/Icons/NavAdd.tsx b/src/assets/Icons/NavAdd.tsx index dd45bad..c234be2 100644 --- a/src/assets/Icons/NavAdd.tsx +++ b/src/assets/Icons/NavAdd.tsx @@ -25,7 +25,7 @@ export const NavAdd: React.FC = ({ color, opacity, ...children }) => { ); diff --git a/src/assets/Icons/NavCloseTab.tsx b/src/assets/Icons/NavCloseTab.tsx index 8b2734e..6062175 100644 --- a/src/assets/Icons/NavCloseTab.tsx +++ b/src/assets/Icons/NavCloseTab.tsx @@ -32,14 +32,14 @@ export const NavCloseTab: React.FC = ({ stroke={theme.palette.text.primary} stroke-width="2" fill={setColor} - fill-opacity={setOpacity} + fillOpacity={setOpacity} /> ); diff --git a/src/assets/Icons/NavMoreMenu.tsx b/src/assets/Icons/NavMoreMenu.tsx index ca641ba..2916f49 100644 --- a/src/assets/Icons/NavMoreMenu.tsx +++ b/src/assets/Icons/NavMoreMenu.tsx @@ -23,7 +23,7 @@ export const NavMoreMenu: React.FC = ({ ); diff --git a/src/assets/Icons/Return.tsx b/src/assets/Icons/Return.tsx index df0abad..81a6690 100644 --- a/src/assets/Icons/Return.tsx +++ b/src/assets/Icons/Return.tsx @@ -20,7 +20,7 @@ export const Return: React.FC = ({ color, opacity, ...children }) => { clipRule="evenodd" d="M2.645 5.81803H15C15.9471 5.81803 16.8557 6.20131 17.5257 6.88278C18.195 7.56497 18.5714 8.49007 18.5714 9.45445V10.909C18.5714 11.8734 18.195 12.7985 17.5257 13.4807C16.8557 14.1622 15.9471 14.5454 15 14.5454C12.0164 14.5454 8.57143 14.5454 8.57143 14.5454C8.17714 14.5454 7.85714 14.8713 7.85714 15.2727C7.85714 15.6742 8.17714 16 8.57143 16H15C16.3264 16 17.5979 15.464 18.5357 14.5091C19.4736 13.5541 20 12.2596 20 10.909C20 10.4268 20 9.93664 20 9.45445C20 8.10461 19.4736 6.80932 18.5357 5.8544C17.5979 4.9002 16.3264 4.36347 15 4.36347H2.645L6.17929 1.27906C6.47857 1.01797 6.51286 0.55832 6.25643 0.253588C6 -0.0511433 5.54857 -0.0860541 5.24929 0.175041L0.249285 4.53874C0.0914279 4.67692 0 4.87838 0 5.09075C0 5.30312 0.0914279 5.50458 0.249285 5.64276L5.24929 10.0065C5.54857 10.2676 6 10.2326 6.25643 9.92791C6.51286 9.62318 6.47857 9.16353 6.17929 8.90244L2.645 5.81803Z" fill={setColor} - fill-opacity={opacity} + fillOpacity={opacity} /> ); From 27858d3df2fa4d86bd87da9b6ce40c4f4c85f396 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 16:13:45 +0200 Subject: [PATCH 186/717] Set correct format for msOverflowStyle property --- src/components/Apps/AppViewerContainer.tsx | 2 +- src/components/Apps/AppsCategoryDesktop.tsx | 4 ++-- src/components/Apps/AppsLibraryDesktop.tsx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Apps/AppViewerContainer.tsx b/src/components/Apps/AppViewerContainer.tsx index aa60a6c..daa3a7e 100644 --- a/src/components/Apps/AppViewerContainer.tsx +++ b/src/components/Apps/AppViewerContainer.tsx @@ -19,7 +19,7 @@ const AppViewerContainer = React.forwardRef( padding: 0; } * { - -msOverflowStyle: none; /* IE and Edge */ + msOverflowStyle: 'none', /* IE and Edge */ scrollbar-width: none; /* Firefox */ } *::-webkit-scrollbar { diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index fbad9a7..754630e 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -68,7 +68,7 @@ const ScrollerStyled = styled('div')({ scrollbarWidth: 'none', // Hide scrollbar for IE and older Edge - '-msOverflowStyle': 'none', + msOverflowStyle: 'none', }); const StyledVirtuosoContainer = styled('div')({ @@ -87,7 +87,7 @@ const StyledVirtuosoContainer = styled('div')({ scrollbarWidth: 'none', // Hide scrollbar for IE and older Edge - '-msOverflowStyle': 'none', + msOverflowStyle: 'none', }); export const AppsCategoryDesktop = ({ diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index 62c3bec..73ac307 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -76,7 +76,7 @@ const ScrollerStyled = styled('div')({ scrollbarWidth: 'none', // Hide scrollbar for IE and older Edge - '-msOverflowStyle': 'none', + msOverflowStyle: 'none', }); const StyledVirtuosoContainer = styled('div')({ @@ -95,7 +95,7 @@ const StyledVirtuosoContainer = styled('div')({ scrollbarWidth: 'none', // Hide scrollbar for IE and older Edge - '-msOverflowStyle': 'none', + msOverflowStyle: 'none', }); export const AppsLibraryDesktop = ({ From 510d8037b397cb825c7f64044106291b5ef56355 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 16:15:17 +0200 Subject: [PATCH 187/717] Remove unused method --- src/components/PasswordField/PasswordField.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/PasswordField/PasswordField.tsx b/src/components/PasswordField/PasswordField.tsx index 31f0a49..b59538e 100644 --- a/src/components/PasswordField/PasswordField.tsx +++ b/src/components/PasswordField/PasswordField.tsx @@ -4,7 +4,6 @@ import { TextField, TextFieldProps, styled, - useTheme, } from '@mui/material'; import { forwardRef, useState } from 'react'; import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; @@ -52,7 +51,6 @@ export const CustomInput = styled(TextField)(({ theme }) => ({ export const PasswordField = forwardRef( ({ ...props }, ref) => { const [canViewPassword, setCanViewPassword] = useState(false); - const theme = useTheme(); return ( Date: Sun, 20 Apr 2025 16:28:47 +0200 Subject: [PATCH 188/717] Refactor style with theme --- src/components/GeneralNotifications.tsx | 44 ++++++++++++++----------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/components/GeneralNotifications.tsx b/src/components/GeneralNotifications.tsx index 3235fbb..97c498b 100644 --- a/src/components/GeneralNotifications.tsx +++ b/src/components/GeneralNotifications.tsx @@ -43,7 +43,13 @@ export const GeneralNotifications = ({ address }) => { > + PAYMENT NOTIFICATION } @@ -53,13 +59,13 @@ export const GeneralNotifications = ({ address }) => { slotProps={{ tooltip: { sx: { - color: '#ffffff', - backgroundColor: '#444444', + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, }, }, arrow: { sx: { - color: '#444444', + color: theme.palette.text.primary, }, }, }} @@ -68,9 +74,7 @@ export const GeneralNotifications = ({ address }) => { sx={{ color: hasNewPayment ? 'var(--unread)' - : theme.palette.mode === 'dark' - ? 'rgb(209, 209, 209)' - : 'rgba(41, 41, 43, 1)', + : theme.palette.text.primary, }} /> @@ -88,14 +92,14 @@ export const GeneralNotifications = ({ address }) => { > {!hasNewPayment && ( @@ -110,12 +114,12 @@ export const GeneralNotifications = ({ address }) => { {hasNewPayment && ( { setAnchorEl(null); @@ -124,18 +128,18 @@ export const GeneralNotifications = ({ address }) => { > { />{' '} {formatDate(latestTx?.timestamp)} + { > {latestTx?.amount} + Date: Sun, 20 Apr 2025 16:54:13 +0200 Subject: [PATCH 189/717] Remove custom icons and use the MUI material icons --- src/App.tsx | 137 +++++++++++++++----------------- src/assets/Icons/Download.tsx | 32 -------- src/assets/Icons/Logout.tsx | 28 ------- src/assets/Icons/LogoutIcon.tsx | 18 ----- 4 files changed, 66 insertions(+), 149 deletions(-) delete mode 100644 src/assets/Icons/Download.tsx delete mode 100644 src/assets/Icons/Logout.tsx delete mode 100644 src/assets/Icons/LogoutIcon.tsx diff --git a/src/App.tsx b/src/App.tsx index 3ad6fe2..7d3a7c7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -29,13 +29,12 @@ import { decryptStoredWallet } from './utils/decryptWallet'; import { CountdownCircleTimer } from 'react-countdown-circle-timer'; import Logo1Dark from './assets/svgs/Logo1Dark.svg'; import RefreshIcon from '@mui/icons-material/Refresh'; +import DownloadIcon from '@mui/icons-material/Download'; import Copy from './assets/svgs/Copy.svg'; import ltcLogo from './assets/ltc.png'; import PersonSearchIcon from '@mui/icons-material/PersonSearch'; import qortLogo from './assets/qort.png'; import { CopyToClipboard } from 'react-copy-to-clipboard'; -import { Download } from './assets/Icons/Download.tsx'; -import { Logout } from './assets/Icons/Logout.tsx'; import { Return } from './assets/Icons/Return.tsx'; import WarningIcon from '@mui/icons-material/Warning'; import Success from './assets/svgs/Success.svg'; @@ -73,6 +72,7 @@ import { TaskManager } from './components/TaskManager/TaskManager.tsx'; import { useModal } from './common/useModal'; import { CustomizedSnackbars } from './components/Snackbar/Snackbar'; import SettingsIcon from '@mui/icons-material/Settings'; +import LogoutIcon from '@mui/icons-material/Logout'; import HelpIcon from '@mui/icons-material/Help'; import { cleanUrl, @@ -1538,46 +1538,43 @@ function App() { > - - LOG OUT - - } - placement="left" - arrow - sx={{ fontSize: '24' }} - slotProps={{ - tooltip: { - sx: { - color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, - }, - }, - arrow: { - sx: { - color: theme.palette.text.primary, - }, - }, + { + logoutFunc(); + setIsOpenDrawerProfile(false); }} > - + LOGOUT + + } + placement="left" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, + }, + }, + arrow: { + sx: { + color: theme.palette.text.primary, + }, + }, }} - onClick={() => { - logoutFunc(); - setIsOpenDrawerProfile(false); - }} - /> - + > + + + @@ -1888,41 +1885,39 @@ function App() { )} - - BACKUP WALLET - - } - placement="left" - arrow - sx={{ fontSize: '24' }} - slotProps={{ - tooltip: { - sx: { - color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, - }, - }, - arrow: { - sx: { - color: theme.palette.text.primary, - }, - }, + + { + setExtstate('download-wallet'); + setIsOpenDrawerProfile(false); }} > - + BACKUP WALLET + + } + placement="left" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, + }, + }, + arrow: { + sx: { + color: theme.palette.text.primary, + }, + }, }} - onClick={() => { - setExtstate('download-wallet'); - setIsOpenDrawerProfile(false); - }} - /> - + > + + + diff --git a/src/assets/Icons/Download.tsx b/src/assets/Icons/Download.tsx deleted file mode 100644 index a8498db..0000000 --- a/src/assets/Icons/Download.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useTheme } from '@mui/material'; -import { SVGProps } from './interfaces'; - -export const Download: React.FC = ({ - color, - opacity, - ...children -}) => { - const theme = useTheme(); - - const setColor = color ? color : theme.palette.text.primary; - const setOpacity = opacity ? opacity : 1; - - return ( - - - - ); -}; diff --git a/src/assets/Icons/Logout.tsx b/src/assets/Icons/Logout.tsx deleted file mode 100644 index b15d0f4..0000000 --- a/src/assets/Icons/Logout.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useTheme } from '@mui/material'; -import { SVGProps } from './interfaces'; - -export const Logout: React.FC = ({ color, opacity, ...children }) => { - const theme = useTheme(); - - const setColor = color ? color : theme.palette.text.primary; - const setOpacity = opacity ? opacity : 1; - - return ( - - - - ); -}; diff --git a/src/assets/Icons/LogoutIcon.tsx b/src/assets/Icons/LogoutIcon.tsx deleted file mode 100644 index d1a4408..0000000 --- a/src/assets/Icons/LogoutIcon.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export const LogoutIcon = ({ color, height = 20, width = 18 }) => { - return ( - - - - ); -}; From b5a9620dc4acee44795ef8f20dcdc9e86f5f4406 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 13:53:57 +0200 Subject: [PATCH 190/717] Use desktop file --- src/components/Apps/AppInfo.tsx | 29 +++++--- src/components/Apps/AppInfoSnippet.tsx | 91 ++++++++++++++------------ 2 files changed, 67 insertions(+), 53 deletions(-) diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index 48f0b61..60ed119 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -1,3 +1,4 @@ +import React, { useEffect, useMemo, useState } from 'react'; import { AppCircle, AppCircleContainer, @@ -17,9 +18,11 @@ import { AppsLibraryContainer, AppsWidthLimiter, } from './Apps-styles'; -import { Avatar, Box } from '@mui/material'; -import { getBaseApiReact } from '../../App'; +import { Avatar, Box, ButtonBase, InputBase } from '@mui/material'; +import { Add } from '@mui/icons-material'; +import { getBaseApiReact, isMobile } from '../../App'; import LogoSelected from '../../assets/svgs/LogoSelected.svg'; + import { Spacer } from '../../common/Spacer'; import { executeEvent } from '../../utils/events'; import { AppRating } from './AppRating'; @@ -27,7 +30,7 @@ import { settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom, } from '../../atoms/global'; -import { saveToLocalStorage } from './AppsNavBar'; +import { saveToLocalStorage } from './AppsNavBarDesktop'; import { useRecoilState, useSetRecoilState } from 'recoil'; export const AppInfo = ({ app, myName }) => { @@ -46,8 +49,9 @@ export const AppInfo = ({ app, myName }) => { return ( { width: '90%', }} > - - + {!isMobile && } { }} > - {isSelectedAppPinned - ? 'Unpin from dashboard' - : 'Pin to dashboard'} + {!isMobile ? ( + <> + {isSelectedAppPinned + ? 'Unpin from dashboard' + : 'Pin to dashboard'} + + ) : ( + <>{isSelectedAppPinned ? 'Unpin' : 'Pin'} + )} - { - setSortablePinnedApps((prev) => { - let updatedApps; + {!isMobile && ( + { + setSortablePinnedApps((prev) => { + let updatedApps; - if (isSelectedAppPinned) { - // Remove the selected app if it is pinned - updatedApps = prev.filter( - (item) => - !( - item?.name === app?.name && item?.service === app?.service - ) + if (isSelectedAppPinned) { + // Remove the selected app if it is pinned + updatedApps = prev.filter( + (item) => + !( + item?.name === app?.name && + item?.service === app?.service + ) + ); + } else { + // Add the selected app if it is not pinned + updatedApps = [ + ...prev, + { + name: app?.name, + service: app?.service, + }, + ]; + } + + saveToLocalStorage( + 'ext_saved_settings', + 'sortablePinnedApps', + updatedApps ); - } else { - // Add the selected app if it is not pinned - updatedApps = [ - ...prev, - { - name: app?.name, - service: app?.service, - }, - ]; - } - - saveToLocalStorage( - 'ext_saved_settings', - 'sortablePinnedApps', - updatedApps - ); - return updatedApps; - }); - setSettingsLocalLastUpdated(Date.now()); - }} - sx={{ - backgroundColor: '#359ff7ff', - opacity: isSelectedAppPinned ? 0.6 : 1, - }} - > - - {' '} - {isSelectedAppPinned ? 'Unpin' : 'Pin'} - - + return updatedApps; + }); + setSettingsLocalLastUpdated(Date.now()); + }} + sx={{ + backgroundColor: '#359ff7ff', + opacity: isSelectedAppPinned ? 0.6 : 1, + }} + > + + {' '} + {isSelectedAppPinned ? 'Unpin' : 'Pin'} + + + )} { From 7f207e5d9132d79058801df626ea0797e9f9014b Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 16:05:48 +0200 Subject: [PATCH 191/717] Fix missing bracket --- src/App.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 1c6913a..3ad6fe2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -289,7 +289,6 @@ export const isMainWindow = true; function App() { const [extState, setExtstate] = useState('not-authenticated'); const [desktopViewMode, setDesktopViewMode] = useState('home'); - const [backupjson, setBackupjson] = useState(null); const [rawWallet, setRawWallet] = useState(null); const [ltcBalanceLoading, setLtcBalanceLoading] = useState(false); @@ -2018,7 +2017,7 @@ function App() { setIsOpenDrawerProfile={setIsOpenDrawerProfile} userInfo={userInfo} /> - renderProfile() + {renderProfile()} )} From d83c20640ae73c068d4b3ed7e99ff29e41e9a2cf Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 16:06:04 +0200 Subject: [PATCH 192/717] Set correct property --- src/assets/Icons/Download.tsx | 2 +- src/assets/Icons/Logout.tsx | 2 +- src/assets/Icons/NavAdd.tsx | 2 +- src/assets/Icons/NavCloseTab.tsx | 4 ++-- src/assets/Icons/NavMoreMenu.tsx | 2 +- src/assets/Icons/Return.tsx | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/assets/Icons/Download.tsx b/src/assets/Icons/Download.tsx index 28ac605..a8498db 100644 --- a/src/assets/Icons/Download.tsx +++ b/src/assets/Icons/Download.tsx @@ -25,7 +25,7 @@ export const Download: React.FC = ({ clipRule="evenodd" d="M12.8047 0.393196V7.21185H16.3036L10.0003 13.5139L3.69697 7.21185H7.19584V0H12.8045L12.8047 0.393196ZM2.7047 16.8587V13.9861H0V18.6179C0 19.3774 0.622589 20 1.38213 20H18.6179C19.3774 20 20 19.3774 20 18.6179V13.9861H17.2962V17.2963L2.70461 17.2954L2.7047 16.8587Z" fill={setColor} - fill-opacity={setOpacity} + fillOpacity={setOpacity} /> ); diff --git a/src/assets/Icons/Logout.tsx b/src/assets/Icons/Logout.tsx index f7d9c57..b15d0f4 100644 --- a/src/assets/Icons/Logout.tsx +++ b/src/assets/Icons/Logout.tsx @@ -21,7 +21,7 @@ export const Logout: React.FC = ({ color, opacity, ...children }) => { clipRule="evenodd" d="M7.56485 0H16.3611C17.2662 0 18 0.727797 18 1.62558V18.3744C18 19.2722 17.2662 20 16.3611 20H7.56485C6.65969 20 5.92593 19.2722 5.92593 18.3744V12.6013H10.6168C11.4569 12.6013 12.1404 11.9039 12.1404 11.0467V8.87329C12.1404 8.01613 11.4569 7.31875 10.6168 7.31875H5.92593V1.62558C5.92593 0.727797 6.65969 0 7.56485 0ZM11.1667 11.0467C11.1667 11.3719 10.9205 11.6354 10.6168 11.6354H4.8144C4.74521 11.6354 4.68911 11.6955 4.68911 11.7696V12.8632C4.68911 13.3492 4.17007 13.6259 3.8078 13.3329L0.218431 10.4298C-0.0728102 10.1942 -0.0728102 9.72579 0.218431 9.49024L3.8078 6.58709C4.17005 6.29409 4.68911 6.57077 4.68911 7.05684V8.1504C4.68911 8.2245 4.74521 8.28454 4.8144 8.28454H10.6168C10.9205 8.28454 11.1667 8.54813 11.1667 8.87329V11.0467Z" fill={setColor} - fill-opacity={setOpacity} + fillOpacity={setOpacity} /> ); diff --git a/src/assets/Icons/NavAdd.tsx b/src/assets/Icons/NavAdd.tsx index dd45bad..c234be2 100644 --- a/src/assets/Icons/NavAdd.tsx +++ b/src/assets/Icons/NavAdd.tsx @@ -25,7 +25,7 @@ export const NavAdd: React.FC = ({ color, opacity, ...children }) => { ); diff --git a/src/assets/Icons/NavCloseTab.tsx b/src/assets/Icons/NavCloseTab.tsx index 8b2734e..6062175 100644 --- a/src/assets/Icons/NavCloseTab.tsx +++ b/src/assets/Icons/NavCloseTab.tsx @@ -32,14 +32,14 @@ export const NavCloseTab: React.FC = ({ stroke={theme.palette.text.primary} stroke-width="2" fill={setColor} - fill-opacity={setOpacity} + fillOpacity={setOpacity} /> ); diff --git a/src/assets/Icons/NavMoreMenu.tsx b/src/assets/Icons/NavMoreMenu.tsx index ca641ba..2916f49 100644 --- a/src/assets/Icons/NavMoreMenu.tsx +++ b/src/assets/Icons/NavMoreMenu.tsx @@ -23,7 +23,7 @@ export const NavMoreMenu: React.FC = ({ ); diff --git a/src/assets/Icons/Return.tsx b/src/assets/Icons/Return.tsx index df0abad..81a6690 100644 --- a/src/assets/Icons/Return.tsx +++ b/src/assets/Icons/Return.tsx @@ -20,7 +20,7 @@ export const Return: React.FC = ({ color, opacity, ...children }) => { clipRule="evenodd" d="M2.645 5.81803H15C15.9471 5.81803 16.8557 6.20131 17.5257 6.88278C18.195 7.56497 18.5714 8.49007 18.5714 9.45445V10.909C18.5714 11.8734 18.195 12.7985 17.5257 13.4807C16.8557 14.1622 15.9471 14.5454 15 14.5454C12.0164 14.5454 8.57143 14.5454 8.57143 14.5454C8.17714 14.5454 7.85714 14.8713 7.85714 15.2727C7.85714 15.6742 8.17714 16 8.57143 16H15C16.3264 16 17.5979 15.464 18.5357 14.5091C19.4736 13.5541 20 12.2596 20 10.909C20 10.4268 20 9.93664 20 9.45445C20 8.10461 19.4736 6.80932 18.5357 5.8544C17.5979 4.9002 16.3264 4.36347 15 4.36347H2.645L6.17929 1.27906C6.47857 1.01797 6.51286 0.55832 6.25643 0.253588C6 -0.0511433 5.54857 -0.0860541 5.24929 0.175041L0.249285 4.53874C0.0914279 4.67692 0 4.87838 0 5.09075C0 5.30312 0.0914279 5.50458 0.249285 5.64276L5.24929 10.0065C5.54857 10.2676 6 10.2326 6.25643 9.92791C6.51286 9.62318 6.47857 9.16353 6.17929 8.90244L2.645 5.81803Z" fill={setColor} - fill-opacity={opacity} + fillOpacity={opacity} /> ); From b65aaf645bf643474290d80a607a81ed37223962 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 16:13:45 +0200 Subject: [PATCH 193/717] Set correct format for msOverflowStyle property --- src/components/Apps/AppViewerContainer.tsx | 2 +- src/components/Apps/AppsCategoryDesktop.tsx | 4 ++-- src/components/Apps/AppsLibraryDesktop.tsx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Apps/AppViewerContainer.tsx b/src/components/Apps/AppViewerContainer.tsx index aa60a6c..daa3a7e 100644 --- a/src/components/Apps/AppViewerContainer.tsx +++ b/src/components/Apps/AppViewerContainer.tsx @@ -19,7 +19,7 @@ const AppViewerContainer = React.forwardRef( padding: 0; } * { - -msOverflowStyle: none; /* IE and Edge */ + msOverflowStyle: 'none', /* IE and Edge */ scrollbar-width: none; /* Firefox */ } *::-webkit-scrollbar { diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index fbad9a7..754630e 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -68,7 +68,7 @@ const ScrollerStyled = styled('div')({ scrollbarWidth: 'none', // Hide scrollbar for IE and older Edge - '-msOverflowStyle': 'none', + msOverflowStyle: 'none', }); const StyledVirtuosoContainer = styled('div')({ @@ -87,7 +87,7 @@ const StyledVirtuosoContainer = styled('div')({ scrollbarWidth: 'none', // Hide scrollbar for IE and older Edge - '-msOverflowStyle': 'none', + msOverflowStyle: 'none', }); export const AppsCategoryDesktop = ({ diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index 62c3bec..73ac307 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -76,7 +76,7 @@ const ScrollerStyled = styled('div')({ scrollbarWidth: 'none', // Hide scrollbar for IE and older Edge - '-msOverflowStyle': 'none', + msOverflowStyle: 'none', }); const StyledVirtuosoContainer = styled('div')({ @@ -95,7 +95,7 @@ const StyledVirtuosoContainer = styled('div')({ scrollbarWidth: 'none', // Hide scrollbar for IE and older Edge - '-msOverflowStyle': 'none', + msOverflowStyle: 'none', }); export const AppsLibraryDesktop = ({ From 92f255a175068259735ea54c4abb346c808fe388 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 16:15:17 +0200 Subject: [PATCH 194/717] Remove unused method --- src/components/PasswordField/PasswordField.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/PasswordField/PasswordField.tsx b/src/components/PasswordField/PasswordField.tsx index 31f0a49..b59538e 100644 --- a/src/components/PasswordField/PasswordField.tsx +++ b/src/components/PasswordField/PasswordField.tsx @@ -4,7 +4,6 @@ import { TextField, TextFieldProps, styled, - useTheme, } from '@mui/material'; import { forwardRef, useState } from 'react'; import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; @@ -52,7 +51,6 @@ export const CustomInput = styled(TextField)(({ theme }) => ({ export const PasswordField = forwardRef( ({ ...props }, ref) => { const [canViewPassword, setCanViewPassword] = useState(false); - const theme = useTheme(); return ( Date: Sun, 20 Apr 2025 16:28:47 +0200 Subject: [PATCH 195/717] Refactor style with theme --- src/components/GeneralNotifications.tsx | 44 ++++++++++++++----------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/components/GeneralNotifications.tsx b/src/components/GeneralNotifications.tsx index 3235fbb..97c498b 100644 --- a/src/components/GeneralNotifications.tsx +++ b/src/components/GeneralNotifications.tsx @@ -43,7 +43,13 @@ export const GeneralNotifications = ({ address }) => { > + PAYMENT NOTIFICATION } @@ -53,13 +59,13 @@ export const GeneralNotifications = ({ address }) => { slotProps={{ tooltip: { sx: { - color: '#ffffff', - backgroundColor: '#444444', + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, }, }, arrow: { sx: { - color: '#444444', + color: theme.palette.text.primary, }, }, }} @@ -68,9 +74,7 @@ export const GeneralNotifications = ({ address }) => { sx={{ color: hasNewPayment ? 'var(--unread)' - : theme.palette.mode === 'dark' - ? 'rgb(209, 209, 209)' - : 'rgba(41, 41, 43, 1)', + : theme.palette.text.primary, }} /> @@ -88,14 +92,14 @@ export const GeneralNotifications = ({ address }) => { > {!hasNewPayment && ( @@ -110,12 +114,12 @@ export const GeneralNotifications = ({ address }) => { {hasNewPayment && ( { setAnchorEl(null); @@ -124,18 +128,18 @@ export const GeneralNotifications = ({ address }) => { > { />{' '} {formatDate(latestTx?.timestamp)} + { > {latestTx?.amount} + Date: Sun, 20 Apr 2025 16:54:13 +0200 Subject: [PATCH 196/717] Remove custom icons and use the MUI material icons --- src/App.tsx | 137 +++++++++++++++----------------- src/assets/Icons/Download.tsx | 32 -------- src/assets/Icons/Logout.tsx | 28 ------- src/assets/Icons/LogoutIcon.tsx | 18 ----- 4 files changed, 66 insertions(+), 149 deletions(-) delete mode 100644 src/assets/Icons/Download.tsx delete mode 100644 src/assets/Icons/Logout.tsx delete mode 100644 src/assets/Icons/LogoutIcon.tsx diff --git a/src/App.tsx b/src/App.tsx index 3ad6fe2..7d3a7c7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -29,13 +29,12 @@ import { decryptStoredWallet } from './utils/decryptWallet'; import { CountdownCircleTimer } from 'react-countdown-circle-timer'; import Logo1Dark from './assets/svgs/Logo1Dark.svg'; import RefreshIcon from '@mui/icons-material/Refresh'; +import DownloadIcon from '@mui/icons-material/Download'; import Copy from './assets/svgs/Copy.svg'; import ltcLogo from './assets/ltc.png'; import PersonSearchIcon from '@mui/icons-material/PersonSearch'; import qortLogo from './assets/qort.png'; import { CopyToClipboard } from 'react-copy-to-clipboard'; -import { Download } from './assets/Icons/Download.tsx'; -import { Logout } from './assets/Icons/Logout.tsx'; import { Return } from './assets/Icons/Return.tsx'; import WarningIcon from '@mui/icons-material/Warning'; import Success from './assets/svgs/Success.svg'; @@ -73,6 +72,7 @@ import { TaskManager } from './components/TaskManager/TaskManager.tsx'; import { useModal } from './common/useModal'; import { CustomizedSnackbars } from './components/Snackbar/Snackbar'; import SettingsIcon from '@mui/icons-material/Settings'; +import LogoutIcon from '@mui/icons-material/Logout'; import HelpIcon from '@mui/icons-material/Help'; import { cleanUrl, @@ -1538,46 +1538,43 @@ function App() { > - - LOG OUT - - } - placement="left" - arrow - sx={{ fontSize: '24' }} - slotProps={{ - tooltip: { - sx: { - color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, - }, - }, - arrow: { - sx: { - color: theme.palette.text.primary, - }, - }, + { + logoutFunc(); + setIsOpenDrawerProfile(false); }} > - + LOGOUT + + } + placement="left" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, + }, + }, + arrow: { + sx: { + color: theme.palette.text.primary, + }, + }, }} - onClick={() => { - logoutFunc(); - setIsOpenDrawerProfile(false); - }} - /> - + > + + + @@ -1888,41 +1885,39 @@ function App() { )} - - BACKUP WALLET - - } - placement="left" - arrow - sx={{ fontSize: '24' }} - slotProps={{ - tooltip: { - sx: { - color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, - }, - }, - arrow: { - sx: { - color: theme.palette.text.primary, - }, - }, + + { + setExtstate('download-wallet'); + setIsOpenDrawerProfile(false); }} > - + BACKUP WALLET + + } + placement="left" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, + }, + }, + arrow: { + sx: { + color: theme.palette.text.primary, + }, + }, }} - onClick={() => { - setExtstate('download-wallet'); - setIsOpenDrawerProfile(false); - }} - /> - + > + + + diff --git a/src/assets/Icons/Download.tsx b/src/assets/Icons/Download.tsx deleted file mode 100644 index a8498db..0000000 --- a/src/assets/Icons/Download.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useTheme } from '@mui/material'; -import { SVGProps } from './interfaces'; - -export const Download: React.FC = ({ - color, - opacity, - ...children -}) => { - const theme = useTheme(); - - const setColor = color ? color : theme.palette.text.primary; - const setOpacity = opacity ? opacity : 1; - - return ( - - - - ); -}; diff --git a/src/assets/Icons/Logout.tsx b/src/assets/Icons/Logout.tsx deleted file mode 100644 index b15d0f4..0000000 --- a/src/assets/Icons/Logout.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useTheme } from '@mui/material'; -import { SVGProps } from './interfaces'; - -export const Logout: React.FC = ({ color, opacity, ...children }) => { - const theme = useTheme(); - - const setColor = color ? color : theme.palette.text.primary; - const setOpacity = opacity ? opacity : 1; - - return ( - - - - ); -}; diff --git a/src/assets/Icons/LogoutIcon.tsx b/src/assets/Icons/LogoutIcon.tsx deleted file mode 100644 index d1a4408..0000000 --- a/src/assets/Icons/LogoutIcon.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export const LogoutIcon = ({ color, height = 20, width = 18 }) => { - return ( - - - - ); -}; From 958acbe9436f6b6e0a867461588dadfee4fa8648 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 17:07:29 +0200 Subject: [PATCH 197/717] Remove empty file --- src/constants/forum.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/constants/forum.ts diff --git a/src/constants/forum.ts b/src/constants/forum.ts deleted file mode 100644 index e69de29..0000000 From 2f3252a2e316953d4bd758d9d713171059d3a977 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 17:08:03 +0200 Subject: [PATCH 198/717] Get correct files from other branch --- src/components/Apps/AppInfo.tsx | 26 +++----- src/components/Apps/AppInfoSnippet.tsx | 88 ++++++++++++-------------- 2 files changed, 51 insertions(+), 63 deletions(-) diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index 60ed119..3ac6282 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -18,11 +18,9 @@ import { AppsLibraryContainer, AppsWidthLimiter, } from './Apps-styles'; -import { Avatar, Box, ButtonBase, InputBase } from '@mui/material'; -import { Add } from '@mui/icons-material'; -import { getBaseApiReact, isMobile } from '../../App'; +import { Avatar, Box } from '@mui/material'; +import { getBaseApiReact } from '../../App'; import LogoSelected from '../../assets/svgs/LogoSelected.svg'; - import { Spacer } from '../../common/Spacer'; import { executeEvent } from '../../utils/events'; import { AppRating } from './AppRating'; @@ -49,9 +47,8 @@ export const AppInfo = ({ app, myName }) => { return ( { width: '90%', }} > - {!isMobile && } + + { }} > - {!isMobile ? ( - <> - {isSelectedAppPinned - ? 'Unpin from dashboard' - : 'Pin to dashboard'} - - ) : ( - <>{isSelectedAppPinned ? 'Unpin' : 'Pin'} - )} + {isSelectedAppPinned + ? 'Unpin from dashboard' + : 'Pin to dashboard'} - {!isMobile && ( - { - setSortablePinnedApps((prev) => { - let updatedApps; + { + setSortablePinnedApps((prev) => { + let updatedApps; - if (isSelectedAppPinned) { - // Remove the selected app if it is pinned - updatedApps = prev.filter( - (item) => - !( - item?.name === app?.name && - item?.service === app?.service - ) - ); - } else { - // Add the selected app if it is not pinned - updatedApps = [ - ...prev, - { - name: app?.name, - service: app?.service, - }, - ]; - } - - saveToLocalStorage( - 'ext_saved_settings', - 'sortablePinnedApps', - updatedApps + if (isSelectedAppPinned) { + // Remove the selected app if it is pinned + updatedApps = prev.filter( + (item) => + !( + item?.name === app?.name && item?.service === app?.service + ) ); - return updatedApps; - }); - setSettingsLocalLastUpdated(Date.now()); - }} - sx={{ - backgroundColor: '#359ff7ff', - opacity: isSelectedAppPinned ? 0.6 : 1, - }} - > - - {' '} - {isSelectedAppPinned ? 'Unpin' : 'Pin'} - - - )} + } else { + // Add the selected app if it is not pinned + updatedApps = [ + ...prev, + { + name: app?.name, + service: app?.service, + }, + ]; + } + + saveToLocalStorage( + 'ext_saved_settings', + 'sortablePinnedApps', + updatedApps + ); + return updatedApps; + }); + setSettingsLocalLastUpdated(Date.now()); + }} + sx={{ + backgroundColor: '#359ff7ff', + opacity: isSelectedAppPinned ? 0.6 : 1, + }} + > + + {' '} + {isSelectedAppPinned ? 'Unpin' : 'Pin'} + + { From fda3208072031c84347abe5daaae69d6c54a3ab5 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 17:57:19 +0200 Subject: [PATCH 199/717] Remove Add and use AddIcon from MUI --- src/common/BoundedNumericTextField.tsx | 10 +++---- src/components/Apps/AppInfo.tsx | 1 - src/components/Apps/Apps-styles.tsx | 39 ++++++++++--------------- src/components/Apps/AppsHomeDesktop.tsx | 4 +-- src/components/Apps/AppsPrivate.tsx | 4 +-- 5 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/common/BoundedNumericTextField.tsx b/src/common/BoundedNumericTextField.tsx index 9951d7e..fa0d4b1 100644 --- a/src/common/BoundedNumericTextField.tsx +++ b/src/common/BoundedNumericTextField.tsx @@ -1,9 +1,4 @@ -import { - IconButton, - InputAdornment, - TextField, - TextFieldProps, -} from '@mui/material'; +import { IconButton, InputAdornment, TextFieldProps } from '@mui/material'; import React, { useRef, useState } from 'react'; import AddIcon from '@mui/icons-material/Add'; import RemoveIcon from '@mui/icons-material/Remove'; @@ -44,6 +39,7 @@ export const BoundedNumericTextField = ({ const stringIsEmpty = (value: string) => { return value === ''; }; + const isAllZerosNum = /^0*\.?0*$/; const isFloatNum = /^-?[0-9]*\.?[0-9]*$/; const isIntegerNum = /^-?[0-9]+$/; @@ -85,6 +81,7 @@ export const BoundedNumericTextField = ({ } return value; }; + const filterValue = (value: string) => { if (stringIsEmpty(value)) return ''; value = filterTypes(value); @@ -120,6 +117,7 @@ export const BoundedNumericTextField = ({ setTextFieldValue(value); }; + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { onChange, ...noChangeProps } = { ...props }; return ( diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index 3ac6282..aab351d 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -1,4 +1,3 @@ -import React, { useEffect, useMemo, useState } from 'react'; import { AppCircle, AppCircleContainer, diff --git a/src/components/Apps/Apps-styles.tsx b/src/components/Apps/Apps-styles.tsx index 152c09a..cc0f299 100644 --- a/src/components/Apps/Apps-styles.tsx +++ b/src/components/Apps/Apps-styles.tsx @@ -2,12 +2,12 @@ import { Typography, Box, ButtonBase } from '@mui/material'; import { styled } from '@mui/system'; export const AppsParent = styled(Box)(({ theme }) => ({ + alignItems: 'center', display: 'flex', - width: '100%', flexDirection: 'column', height: '100%', - alignItems: 'center', overflow: 'auto', + width: '100%', // For WebKit-based browsers (Chrome, Safari, etc.) '::-webkit-scrollbar': { width: '0px', // Set the width to 0 to hide the scrollbar @@ -25,34 +25,34 @@ export const AppsParent = styled(Box)(({ theme }) => ({ })); export const AppsContainer = styled(Box)(({ theme }) => ({ - display: 'flex', - width: '90%', - justifyContent: 'space-evenly', - gap: '24px', - flexWrap: 'wrap', alignItems: 'flex-start', alignSelf: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, + display: 'flex', + flexWrap: 'wrap', + gap: '24px', + justifyContent: 'space-evenly', + width: '90%', })); export const AppsLibraryContainer = styled(Box)(({ theme }) => ({ - display: 'flex', - width: '100%', - flexDirection: 'column', - justifyContent: 'flex-start', alignItems: 'center', backgroundColor: theme.palette.background.paper, + display: 'flex', + flexDirection: 'column', + justifyContent: 'flex-start', + width: '100%', })); export const AppsWidthLimiter = styled(Box)(({ theme }) => ({ - display: 'flex', - width: '90%', - flexDirection: 'column', - justifyContent: 'flex-start', alignItems: 'flex-start', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, + display: 'flex', + flexDirection: 'column', + justifyContent: 'flex-start', + width: '90%', })); export const AppsSearchContainer = styled(Box)(({ theme }) => ({ @@ -99,15 +99,6 @@ export const AppCircleContainer = styled(Box)(({ theme }) => ({ width: '100%', })); -export const Add = styled(Typography)(({ theme }) => ({ - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, - fontSize: '36px', - fontWeight: 500, - lineHeight: '43.57px', - textAlign: 'left', -})); - export const AppCircleLabel = styled(Typography)(({ theme }) => ({ backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, diff --git a/src/components/Apps/AppsHomeDesktop.tsx b/src/components/Apps/AppsHomeDesktop.tsx index f02941f..d9ad291 100644 --- a/src/components/Apps/AppsHomeDesktop.tsx +++ b/src/components/Apps/AppsHomeDesktop.tsx @@ -7,7 +7,7 @@ import { AppsContainer, } from './Apps-styles'; import { Box, ButtonBase, Input, useTheme } from '@mui/material'; -import { Add } from '@mui/icons-material'; +import AddIcon from '@mui/icons-material/Add'; import { executeEvent } from '../../utils/events'; import { Spacer } from '../../common/Spacer'; import { SortablePinnedApps } from './SortablePinnedApps'; @@ -140,7 +140,7 @@ export const AppsHomeDesktop = ({ }} > - + + Library diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index fc032ff..4d3e852 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -23,13 +23,13 @@ import { import { Label } from '../Group/AddGroup'; import { Spacer } from '../../common/Spacer'; import { - Add, AppCircle, AppCircleContainer, AppCircleLabel, PublishQAppChoseFile, PublishQAppInfo, } from './Apps-styles'; +import AddIcon from '@mui/icons-material/Add'; import ImageUploader from '../../common/ImageUploader'; import { MyContext } from '../../App'; import { fileToBase64 } from '../../utils/fileReading'; @@ -234,7 +234,7 @@ export const AppsPrivate = ({ myName }) => { }} > - + + Private From 8cd67ddba75e39411e5d356a4cd085ceff65361c Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 18:03:35 +0200 Subject: [PATCH 200/717] Use material searchIcon --- src/components/Apps/AppsLibraryDesktop.tsx | 52 ++++++++++++---------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index 73ac307..b2d170b 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -6,7 +6,6 @@ import { AppLibrarySubTitle, AppsContainer, AppsLibraryContainer, - AppsParent, AppsSearchContainer, AppsSearchLeft, AppsSearchRight, @@ -27,7 +26,7 @@ import { } from '@mui/material'; import { getBaseApiReact } from '../../App'; import LogoSelected from '../../assets/svgs/LogoSelected.svg'; -import IconSearch from '../../assets/svgs/Search.svg'; +import SearchIcon from '@mui/icons-material/Search'; import IconClearInput from '../../assets/svgs/ClearInput.svg'; import qappDevelopText from '../../assets/svgs/qappDevelopText.svg'; import qappLibraryText from '../../assets/svgs/qappLibraryText.svg'; @@ -41,7 +40,6 @@ import { AppsDesktopLibraryBody, AppsDesktopLibraryHeader, } from './AppsDesktop-styles'; -import ReturnSVG from '../../assets/svgs/Return.svg'; import { ComposeP, MailIconImg, @@ -202,7 +200,8 @@ export const AppsLibraryDesktop = ({ }} > - + + setSearchValue(e.target.value)} @@ -215,6 +214,7 @@ export const AppsLibraryDesktop = ({ }} /> + {searchValue && ( + { getQapps(); @@ -234,7 +235,6 @@ export const AppsLibraryDesktop = ({ > + + { executeEvent('navigateBack', {}); }} - > - - Return to Apps Dashboard - + > + + {searchedList?.length > 0 ? ( { - // executeEvent("addTab", { - // data: qapp - // }) executeEvent('selectedAppInfo', { data: qapp, }); @@ -358,6 +355,7 @@ export const AppsLibraryDesktop = ({ /> + {qapp?.metadata?.title || qapp?.name} @@ -366,7 +364,9 @@ export const AppsLibraryDesktop = ({ ); })} + + {hasPublishApp ? 'Update Apps!' : 'Create Apps!'} + + + + { setMode('publish'); @@ -410,10 +414,12 @@ export const AppsLibraryDesktop = ({ {hasPublishApp ? 'Update' : 'Publish'} + + All @@ -473,14 +479,14 @@ export const AppsLibraryDesktop = ({ > {category?.name} From a5c4a2ae75edf1821074e87e864e6430f4a6ed46 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 18:48:00 +0200 Subject: [PATCH 201/717] Refactor q-app library page --- src/assets/Icons/QappDevelopText.tsx | 30 ++++++ src/assets/Icons/QappLibraryText.tsx | 34 ++++++ src/assets/svgs/qappDevelopText.svg | 3 - src/assets/svgs/qappLibraryText.svg | 4 - src/components/Apps/Apps-styles.tsx | 108 ++++++++++---------- src/components/Apps/AppsCategoryDesktop.tsx | 30 +----- src/components/Apps/AppsLibraryDesktop.tsx | 56 +++++----- 7 files changed, 156 insertions(+), 109 deletions(-) create mode 100644 src/assets/Icons/QappDevelopText.tsx create mode 100644 src/assets/Icons/QappLibraryText.tsx delete mode 100644 src/assets/svgs/qappDevelopText.svg delete mode 100644 src/assets/svgs/qappLibraryText.svg diff --git a/src/assets/Icons/QappDevelopText.tsx b/src/assets/Icons/QappDevelopText.tsx new file mode 100644 index 0000000..e0856cf --- /dev/null +++ b/src/assets/Icons/QappDevelopText.tsx @@ -0,0 +1,30 @@ +import { useTheme } from '@mui/material'; +import { SVGProps } from './interfaces'; + +export const QappDevelopText: React.FC = ({ + color, + opacity, + ...children +}) => { + const theme = useTheme(); + + const setColor = color ? color : theme.palette.text.primary; + const setOpacity = opacity ? opacity : 1; + + return ( + + + + ); +}; diff --git a/src/assets/Icons/QappLibraryText.tsx b/src/assets/Icons/QappLibraryText.tsx new file mode 100644 index 0000000..146717e --- /dev/null +++ b/src/assets/Icons/QappLibraryText.tsx @@ -0,0 +1,34 @@ +import { useTheme } from '@mui/material'; +import { SVGProps } from './interfaces'; + +export const QappLibraryText: React.FC = ({ + color, + opacity, + ...children +}) => { + const theme = useTheme(); + + const setColor = color ? color : theme.palette.text.primary; + const setOpacity = opacity ? opacity : 1; + + return ( + + + + + ); +}; diff --git a/src/assets/svgs/qappDevelopText.svg b/src/assets/svgs/qappDevelopText.svg deleted file mode 100644 index 3aa786a..0000000 --- a/src/assets/svgs/qappDevelopText.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/assets/svgs/qappLibraryText.svg b/src/assets/svgs/qappLibraryText.svg deleted file mode 100644 index 297c466..0000000 --- a/src/assets/svgs/qappLibraryText.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/components/Apps/Apps-styles.tsx b/src/components/Apps/Apps-styles.tsx index cc0f299..056308d 100644 --- a/src/components/Apps/Apps-styles.tsx +++ b/src/components/Apps/Apps-styles.tsx @@ -114,7 +114,7 @@ export const AppCircleLabel = styled(Typography)(({ theme }) => ({ })); export const AppLibrarySubTitle = styled(Typography)(({ theme }) => ({ - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, color: theme.palette.text.primary, fontSize: '16px', fontWeight: 500, @@ -158,148 +158,150 @@ export const AppInfoSnippetLeft = styled(Box)(({ theme }) => ({ })); export const AppInfoSnippetRight = styled(Box)(({ theme }) => ({ - display: 'flex', - justifyContent: 'flex-end', alignItems: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, + display: 'flex', + justifyContent: 'flex-end', })); export const AppDownloadButton = styled(ButtonBase)(({ theme }) => ({ - backgroundColor: '#247C0E', - color: theme.palette.text.primary, - width: '101px', - height: '29px', - display: 'flex', - justifyContent: 'center', alignItems: 'center', - borderRadius: '25px', alignSelf: 'center', + backgroundColor: theme.palette.background.default, + borderRadius: '25px', + color: theme.palette.text.primary, + display: 'flex', + height: '29px', + justifyContent: 'center', + width: '101px', })); export const AppDownloadButtonText = styled(Typography)(({ theme }) => ({ + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, fontSize: '14px', fontWeight: 500, lineHeight: 1.2, - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, })); export const AppPublishTagsContainer = styled(Box)(({ theme }) => ({ - gap: '10px', - flexWrap: 'wrap', - justifyContent: 'flex-start', - width: '100%', - display: 'flex', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, + display: 'flex', + flexWrap: 'wrap', + gap: '10px', + justifyContent: 'flex-start', + width: '100%', })); export const AppInfoSnippetMiddle = styled(Box)(({ theme }) => ({ + alignItems: 'flex-start', + backgroundColor: theme.palette.background.default, display: 'flex', flexDirection: 'column', justifyContent: 'center', - alignItems: 'flex-start', - backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppInfoAppName = styled(Typography)(({ theme }) => ({ + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, fontSize: '16px', fontWeight: 500, lineHeight: 1.2, textAlign: 'start', - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, })); export const AppInfoUserName = styled(Typography)(({ theme }) => ({ + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, fontSize: '13px', fontWeight: 400, lineHeight: 1.2, textAlign: 'start', - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, })); export const AppsNavBarParent = styled(Box)(({ theme }) => ({ - display: 'flex', - justifyContent: 'space-between', alignItems: 'center', - width: '100%', + backgroundColor: theme.palette.background.default, + bottom: 0, + color: theme.palette.text.primary, + display: 'flex', height: '60px', + justifyContent: 'space-between', padding: '0px 10px', position: 'fixed', - bottom: 0, + width: '100%', zIndex: 1, - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, })); export const AppsNavBarLeft = styled(Box)(({ theme }) => ({ - display: 'flex', - justifyContent: 'flex-start', alignItems: 'center', - flexGrow: 1, backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, + display: 'flex', + flexGrow: 1, + justifyContent: 'flex-start', })); export const AppsNavBarRight = styled(Box)(({ theme }) => ({ - display: 'flex', - justifyContent: 'flex-end', alignItems: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, + display: 'flex', + justifyContent: 'flex-end', })); export const TabParent = styled(Box)(({ theme }) => ({ - height: '36px', - width: '36px', - position: 'relative', - borderRadius: '50%', - display: 'flex', alignItems: 'center', - justifyContent: 'center', backgroundColor: theme.palette.background.default, + borderRadius: '50%', color: theme.palette.text.primary, + display: 'flex', + height: '36px', + justifyContent: 'center', + position: 'relative', + width: '36px', })); export const PublishQAppCTAParent = styled(Box)(({ theme }) => ({ - display: 'flex', - justifyContent: 'space-between', alignItems: 'center', - width: '100%', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, + display: 'flex', + justifyContent: 'space-between', + width: '100%', })); export const PublishQAppCTALeft = styled(Box)(({ theme }) => ({ - display: 'flex', - justifyContent: 'flex-start', alignItems: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, + display: 'flex', + justifyContent: 'flex-start', })); export const PublishQAppCTARight = styled(Box)(({ theme }) => ({ - display: 'flex', - justifyContent: 'flex-end', alignItems: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, + display: 'flex', + justifyContent: 'flex-end', })); export const PublishQAppCTAButton = styled(ButtonBase)(({ theme }) => ({ - width: '101px', - height: '29px', - display: 'flex', - justifyContent: 'center', alignItems: 'center', + backgroundColor: theme.palette.background.paper, + borderColor: theme.palette.background.default, borderRadius: '25px', - border: '1px solid #FFFFFF', - backgroundColor: theme.palette.background.default, + borderStyle: 'solid', + borderWidth: '1px', color: theme.palette.text.primary, + display: 'flex', + height: '29px', + justifyContent: 'center', + width: '101px', })); export const PublishQAppDotsBG = styled(Box)(({ theme }) => ({ diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index 754630e..738b561 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -1,46 +1,24 @@ -import React, { - useCallback, - useContext, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { useContext, useEffect, useMemo, useRef, useState } from 'react'; import { - AppCircle, - AppCircleContainer, - AppCircleLabel, AppLibrarySubTitle, - AppsContainer, AppsLibraryContainer, - AppsParent, AppsSearchContainer, AppsSearchLeft, AppsSearchRight, AppsWidthLimiter, - PublishQAppCTAButton, - PublishQAppCTALeft, - PublishQAppCTAParent, - PublishQAppCTARight, - PublishQAppDotsBG, } from './Apps-styles'; -import { Avatar, Box, ButtonBase, InputBase, styled } from '@mui/material'; -import { Add } from '@mui/icons-material'; -import { MyContext, getBaseApiReact } from '../../App'; -import LogoSelected from '../../assets/svgs/LogoSelected.svg'; +import { ButtonBase, InputBase, styled } from '@mui/material'; +import { MyContext } from '../../App'; import IconSearch from '../../assets/svgs/Search.svg'; import IconClearInput from '../../assets/svgs/ClearInput.svg'; -import qappDevelopText from '../../assets/svgs/qappDevelopText.svg'; -import qappDots from '../../assets/svgs/qappDots.svg'; - import { Spacer } from '../../common/Spacer'; import { AppInfoSnippet } from './AppInfoSnippet'; import { Virtuoso } from 'react-virtuoso'; -import { executeEvent } from '../../utils/events'; import { AppsDesktopLibraryBody, AppsDesktopLibraryHeader, } from './AppsDesktop-styles'; + const officialAppList = [ 'q-tube', 'q-blog', diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index b2d170b..3960f25 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -23,15 +23,16 @@ import { InputBase, Typography, styled, + useTheme, } from '@mui/material'; import { getBaseApiReact } from '../../App'; import LogoSelected from '../../assets/svgs/LogoSelected.svg'; import SearchIcon from '@mui/icons-material/Search'; import IconClearInput from '../../assets/svgs/ClearInput.svg'; -import qappDevelopText from '../../assets/svgs/qappDevelopText.svg'; -import qappLibraryText from '../../assets/svgs/qappLibraryText.svg'; +import { QappDevelopText } from '../../assets/Icons/QappDevelopText.tsx'; +import { QappLibraryText } from '../../assets/Icons/QappLibraryText.tsx'; import RefreshIcon from '@mui/icons-material/Refresh'; -import qappDots from '../../assets/svgs/qappDots.svg'; +import AppsIcon from '@mui/icons-material/Apps'; import { Spacer } from '../../common/Spacer'; import { AppInfoSnippet } from './AppInfoSnippet'; import { Virtuoso } from 'react-virtuoso'; @@ -40,11 +41,8 @@ import { AppsDesktopLibraryBody, AppsDesktopLibraryHeader, } from './AppsDesktop-styles'; -import { - ComposeP, - MailIconImg, - ShowMessageReturnButton, -} from '../Group/Forum/Mail-styles'; +import { ShowMessageReturnButton } from '../Group/Forum/Mail-styles'; +import { Theme } from 'emoji-picker-react'; const officialAppList = [ 'q-tube', @@ -107,6 +105,7 @@ export const AppsLibraryDesktop = ({ }) => { const [searchValue, setSearchValue] = useState(''); const virtuosoRef = useRef(); + const theme = useTheme(); const officialApps = useMemo(() => { return availableQapps.filter( @@ -181,17 +180,18 @@ export const AppsLibraryDesktop = ({ - + Official Apps + + {officialApps?.map((qapp) => { @@ -387,7 +389,7 @@ export const AppsLibraryDesktop = ({ textAlign: 'start', }} > - {hasPublishApp ? 'Update Apps!' : 'Create Apps!'} + {hasPublishApp ? 'Update your app' : 'Publish your app'} @@ -399,11 +401,12 @@ export const AppsLibraryDesktop = ({ > - + - + + Categories + + All + {categories?.map((category) => { return ( {category?.name} From e8ccfb3d46390331d0d6ce529f492b62a86da0f4 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 18:58:55 +0200 Subject: [PATCH 202/717] Refactor q-app library component style --- src/components/Apps/Apps-styles.tsx | 4 +-- src/components/Apps/AppsCategoryDesktop.tsx | 32 +++++++-------------- src/components/Apps/AppsDesktop.tsx | 8 +++--- src/components/Apps/AppsLibraryDesktop.tsx | 1 - 4 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/components/Apps/Apps-styles.tsx b/src/components/Apps/Apps-styles.tsx index 056308d..45fd02c 100644 --- a/src/components/Apps/Apps-styles.tsx +++ b/src/components/Apps/Apps-styles.tsx @@ -38,7 +38,7 @@ export const AppsContainer = styled(Box)(({ theme }) => ({ export const AppsLibraryContainer = styled(Box)(({ theme }) => ({ alignItems: 'center', - backgroundColor: theme.palette.background.paper, + backgroundColor: theme.palette.background.default, display: 'flex', flexDirection: 'column', justifyContent: 'flex-start', @@ -114,7 +114,7 @@ export const AppCircleLabel = styled(Typography)(({ theme }) => ({ })); export const AppLibrarySubTitle = styled(Typography)(({ theme }) => ({ - backgroundColor: theme.palette.background.paper, + backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, fontSize: '16px', fontWeight: 500, diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index 738b561..25d7ca3 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -9,7 +9,7 @@ import { } from './Apps-styles'; import { ButtonBase, InputBase, styled } from '@mui/material'; import { MyContext } from '../../App'; -import IconSearch from '../../assets/svgs/Search.svg'; +import SearchIcon from '@mui/icons-material/Search'; import IconClearInput from '../../assets/svgs/ClearInput.svg'; import { Spacer } from '../../common/Spacer'; import { AppInfoSnippet } from './AppInfoSnippet'; @@ -19,22 +19,6 @@ import { AppsDesktopLibraryHeader, } from './AppsDesktop-styles'; -const officialAppList = [ - 'q-tube', - 'q-blog', - 'q-share', - 'q-support', - 'q-mail', - 'q-fund', - 'q-shop', - 'q-trade', - 'q-support', - 'q-manager', - 'q-wallets', - 'q-search', - 'q-nodecontrol', -]; - const ScrollerStyled = styled('div')({ // Hide scrollbar for WebKit browsers (Chrome, Safari) '::-webkit-scrollbar': { @@ -136,15 +120,15 @@ export const AppsCategoryDesktop = ({ @@ -159,7 +143,8 @@ export const AppsCategoryDesktop = ({ }} > - + + setSearchValue(e.target.value)} @@ -172,6 +157,7 @@ export const AppsCategoryDesktop = ({ }} /> + {searchValue && ( + + {`Category: ${category?.name}`} + {mode === 'appInfo' && !selectedTab && ( diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index 3960f25..6d5c3ec 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -42,7 +42,6 @@ import { AppsDesktopLibraryHeader, } from './AppsDesktop-styles'; import { ShowMessageReturnButton } from '../Group/Forum/Mail-styles'; -import { Theme } from 'emoji-picker-react'; const officialAppList = [ 'q-tube', From 8d20f1a6dba0cf9301737fc9c5ba378bef94f9b1 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 20 Apr 2025 19:33:38 +0200 Subject: [PATCH 203/717] Apply theme to app pages --- src/components/Apps/AppInfo.tsx | 43 ++++++++++++++++----- src/components/Apps/AppInfoSnippet.tsx | 17 ++++++-- src/components/Apps/AppRating.tsx | 2 + src/components/Apps/Apps-styles.tsx | 24 ++++++++++-- src/components/Apps/AppsCategoryDesktop.tsx | 17 +++++--- src/components/Apps/AppsDesktop-styles.tsx | 24 ------------ src/components/Apps/AppsLibraryDesktop.tsx | 14 ++++--- 7 files changed, 88 insertions(+), 53 deletions(-) delete mode 100644 src/components/Apps/AppsDesktop-styles.tsx diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index aab351d..53a155a 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -7,7 +7,6 @@ import { AppInfoSnippetContainer, AppInfoSnippetLeft, AppInfoSnippetMiddle, - AppInfoSnippetRight, AppInfoUserName, AppsCategoryInfo, AppsCategoryInfoLabel, @@ -17,7 +16,7 @@ import { AppsLibraryContainer, AppsWidthLimiter, } from './Apps-styles'; -import { Avatar, Box } from '@mui/material'; +import { Avatar, Box, useTheme } from '@mui/material'; import { getBaseApiReact } from '../../App'; import LogoSelected from '../../assets/svgs/LogoSelected.svg'; import { Spacer } from '../../common/Spacer'; @@ -35,6 +34,7 @@ export const AppInfo = ({ app, myName }) => { const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState( sortablePinnedAppsAtom ); + const theme = useTheme(); const isSelectedAppPinned = !!sortablePinnedApps?.find( (item) => item?.name === app?.name && item?.service === app?.service @@ -104,17 +104,21 @@ export const AppInfo = ({ app, myName }) => { + {app?.metadata?.title || app?.name} + + {app?.name} + - + { setSettingsLocalLastUpdated(Date.now()); }} sx={{ - backgroundColor: '#359ff7ff', - width: '100%', - maxWidth: '320px', + backgroundColor: theme.palette.background.paper, height: '29px', + maxWidth: '320px', opacity: isSelectedAppPinned ? 0.6 : 1, + width: '100%', }} > @@ -172,6 +176,7 @@ export const AppInfo = ({ app, myName }) => { : 'Pin to dashboard'} + { executeEvent('addTab', { @@ -179,10 +184,12 @@ export const AppInfo = ({ app, myName }) => { }); }} sx={{ - backgroundColor: isInstalled ? '#0091E1' : '#247C0E', - width: '100%', - maxWidth: '320px', + backgroundColor: isInstalled + ? '#0091E1' + : theme.palette.background.paper, height: '29px', + maxWidth: '320px', + width: '100%', }} > @@ -191,25 +198,41 @@ export const AppInfo = ({ app, myName }) => { + + + - + + + + Category: + + {app?.metadata?.categoryName || 'none'} + + About this Q-App + + {app?.metadata?.description || 'No description'} diff --git a/src/components/Apps/AppInfoSnippet.tsx b/src/components/Apps/AppInfoSnippet.tsx index 2b3b17c..c877bba 100644 --- a/src/components/Apps/AppInfoSnippet.tsx +++ b/src/components/Apps/AppInfoSnippet.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { AppCircle, AppCircleContainer, @@ -11,7 +10,7 @@ import { AppInfoSnippetRight, AppInfoUserName, } from './Apps-styles'; -import { Avatar, ButtonBase } from '@mui/material'; +import { Avatar, ButtonBase, useTheme } from '@mui/material'; import { getBaseApiReact } from '../../App'; import LogoSelected from '../../assets/svgs/LogoSelected.svg'; import { Spacer } from '../../common/Spacer'; @@ -41,6 +40,8 @@ export const AppInfoSnippet = ({ const setSettingsLocalLastUpdated = useSetRecoilState( settingsLocalLastUpdatedAtom ); + const theme = useTheme(); + return ( + { @@ -112,12 +114,17 @@ export const AppInfoSnippet = ({ > {app?.metadata?.title || app?.name} + + {app?.name} + + + @@ -174,7 +181,9 @@ export const AppInfoSnippet = ({ }); }} sx={{ - backgroundColor: isInstalled ? '#0091E1' : '#247C0E', + backgroundColor: isInstalled + ? '#0091E1' + : theme.palette.background.paper, }} > diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index abf1bba..191650d 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -91,6 +91,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { } } }, []); + useEffect(() => { if (hasCalledRef.current) return; if (!app) return; @@ -108,6 +109,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { message: `Would you like to rate this app a rating of ${newValue}?. It will create a POLL tx.`, publishFee: fee.fee + ' QORT', }); + if (hasPublishedRating === false) { const pollName = `app-library-${app.service}-rating-${app.name}`; const pollOptions = [`1, 2, 3, 4, 5, initialValue-${newValue}`]; diff --git a/src/components/Apps/Apps-styles.tsx b/src/components/Apps/Apps-styles.tsx index 45fd02c..a335810 100644 --- a/src/components/Apps/Apps-styles.tsx +++ b/src/components/Apps/Apps-styles.tsx @@ -36,6 +36,24 @@ export const AppsContainer = styled(Box)(({ theme }) => ({ width: '90%', })); +export const AppsDesktopLibraryHeader = styled(Box)(({ theme }) => ({ + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + display: 'flex', + flexDirection: 'column', + flexShrink: 0, + width: '100%', +})); + +export const AppsDesktopLibraryBody = styled(Box)(({ theme }) => ({ + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + display: 'flex', + flexDirection: 'column', + flexGrow: 1, + width: '100%', +})); + export const AppsLibraryContainer = styled(Box)(({ theme }) => ({ alignItems: 'center', backgroundColor: theme.palette.background.default, @@ -177,13 +195,11 @@ export const AppDownloadButton = styled(ButtonBase)(({ theme }) => ({ width: '101px', })); -export const AppDownloadButtonText = styled(Typography)(({ theme }) => ({ - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, +export const AppDownloadButtonText = styled(Typography)({ fontSize: '14px', fontWeight: 500, lineHeight: 1.2, -})); +}); export const AppPublishTagsContainer = styled(Box)(({ theme }) => ({ backgroundColor: theme.palette.background.default, diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index 25d7ca3..5103488 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -1,23 +1,21 @@ import { useContext, useEffect, useMemo, useRef, useState } from 'react'; import { AppLibrarySubTitle, + AppsDesktopLibraryBody, + AppsDesktopLibraryHeader, AppsLibraryContainer, AppsSearchContainer, AppsSearchLeft, AppsSearchRight, AppsWidthLimiter, } from './Apps-styles'; -import { ButtonBase, InputBase, styled } from '@mui/material'; +import { ButtonBase, InputBase, styled, useTheme } from '@mui/material'; import { MyContext } from '../../App'; import SearchIcon from '@mui/icons-material/Search'; import IconClearInput from '../../assets/svgs/ClearInput.svg'; import { Spacer } from '../../common/Spacer'; import { AppInfoSnippet } from './AppInfoSnippet'; import { Virtuoso } from 'react-virtuoso'; -import { - AppsDesktopLibraryBody, - AppsDesktopLibraryHeader, -} from './AppsDesktop-styles'; const ScrollerStyled = styled('div')({ // Hide scrollbar for WebKit browsers (Chrome, Safari) @@ -60,6 +58,7 @@ export const AppsCategoryDesktop = ({ }) => { const [searchValue, setSearchValue] = useState(''); const virtuosoRef = useRef(); + const theme = useTheme(); const { rootHeight } = useContext(MyContext); const categoryList = useMemo(() => { @@ -148,7 +147,13 @@ export const AppsCategoryDesktop = ({ setSearchValue(e.target.value)} - sx={{ ml: 1, flex: 1 }} + sx={{ + background: theme.palette.background.paper, + borderRadius: '6px', + flex: 1, + ml: 1, + paddingLeft: '12px', + }} placeholder="Search for apps" inputProps={{ 'aria-label': 'Search for apps', diff --git a/src/components/Apps/AppsDesktop-styles.tsx b/src/components/Apps/AppsDesktop-styles.tsx deleted file mode 100644 index 26cb567..0000000 --- a/src/components/Apps/AppsDesktop-styles.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { - AppBar, - Button, - Toolbar, - Typography, - Box, - TextField, - InputLabel, - ButtonBase, - } from "@mui/material"; - import { styled } from "@mui/system"; - - export const AppsDesktopLibraryHeader = styled(Box)(({ theme }) => ({ - display: "flex", - flexDirection: 'column', - flexShrink: 0, - width: '100%' - })); - export const AppsDesktopLibraryBody = styled(Box)(({ theme }) => ({ - display: "flex", - flexDirection: 'column', - flexGrow: 1, - width: '100%' - })); \ No newline at end of file diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index 6d5c3ec..1c6206e 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -5,6 +5,8 @@ import { AppCircleLabel, AppLibrarySubTitle, AppsContainer, + AppsDesktopLibraryBody, + AppsDesktopLibraryHeader, AppsLibraryContainer, AppsSearchContainer, AppsSearchLeft, @@ -37,10 +39,6 @@ import { Spacer } from '../../common/Spacer'; import { AppInfoSnippet } from './AppInfoSnippet'; import { Virtuoso } from 'react-virtuoso'; import { executeEvent } from '../../utils/events'; -import { - AppsDesktopLibraryBody, - AppsDesktopLibraryHeader, -} from './AppsDesktop-styles'; import { ShowMessageReturnButton } from '../Group/Forum/Mail-styles'; const officialAppList = [ @@ -204,7 +202,13 @@ export const AppsLibraryDesktop = ({ setSearchValue(e.target.value)} - sx={{ ml: 1, flex: 1 }} + sx={{ + background: theme.palette.background.paper, + borderRadius: '6px', + flex: 1, + ml: 1, + paddingLeft: '12px', + }} placeholder="Search for apps" inputProps={{ 'aria-label': 'Search for apps', From f065c2b07acc5a2941d6ad9a2c290b7bc5eb8d72 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 21 Apr 2025 09:51:46 +0200 Subject: [PATCH 204/717] Add react-i18next dependencies --- package-lock.json | 151 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 4 ++ 2 files changed, 152 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5e2803b..b98590c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,6 +56,9 @@ "emoji-picker-react": "^4.12.0", "file-saver": "^2.0.5", "html-to-text": "^9.0.5", + "i18next": "^25.0.1", + "i18next-browser-languagedetector": "^8.0.5", + "i18next-http-backend": "^3.0.2", "jssha": "3.3.1", "lit": "^3.2.1", "lodash": "^4.17.21", @@ -69,6 +72,7 @@ "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", "react-frame-component": "^5.2.7", + "react-i18next": "^15.4.1", "react-infinite-scroller": "^1.2.6", "react-intersection-observer": "^9.13.0", "react-json-view-lite": "^2.0.1", @@ -1479,9 +1483,10 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", - "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -8044,6 +8049,15 @@ "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==" }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -10680,6 +10694,15 @@ "node": ">=18" } }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, "node_modules/html-to-text": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", @@ -10774,6 +10797,55 @@ "ms": "^2.0.0" } }, + "node_modules/i18next": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.0.1.tgz", + "integrity": "sha512-8S8PyZbrymJZn3DaN70/34JYWNhsqrU6yA4MuzcygJBv+41dgNMocEA8h+kV1P7MCc1ll03lOTOIXE7mpNCicw==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.10" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.5.tgz", + "integrity": "sha512-OstebRKqKiQw8xEvQF5aRyUujsCatanj7Q9eo5iiH2gJpoXGZ7483ol3sVBwfqbobTQPNH1J+NAyJ1aCQoEC+w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/i18next-http-backend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.2.tgz", + "integrity": "sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==", + "license": "MIT", + "dependencies": { + "cross-fetch": "4.0.0" + } + }, "node_modules/iconv-corefoundation": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", @@ -12891,6 +12963,48 @@ "semver": "^7.3.5" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-gyp": { "version": "9.4.1", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", @@ -16533,6 +16647,28 @@ "react-dom": ">= 16.3" } }, + "node_modules/react-i18next": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.4.1.tgz", + "integrity": "sha512-ahGab+IaSgZmNPYXdV1n+OYky95TGpFwnKRflX/16dY04DsYYKHtVLjeny7sBSCREEcoMbAgSkFiGLF5g5Oofw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.0", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-infinite-scroller": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/react-infinite-scroller/-/react-infinite-scroller-1.2.6.tgz", @@ -19462,6 +19598,15 @@ } } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", diff --git a/package.json b/package.json index 7e08596..3e37c8f 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,9 @@ "emoji-picker-react": "^4.12.0", "file-saver": "^2.0.5", "html-to-text": "^9.0.5", + "i18next": "^25.0.1", + "i18next-browser-languagedetector": "^8.0.5", + "i18next-http-backend": "^3.0.2", "jssha": "3.3.1", "lit": "^3.2.1", "lodash": "^4.17.21", @@ -74,6 +77,7 @@ "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", "react-frame-component": "^5.2.7", + "react-i18next": "^15.4.1", "react-infinite-scroller": "^1.2.6", "react-intersection-observer": "^9.13.0", "react-json-view-lite": "^2.0.1", From 655c99091712b6f098fb6bc97d890c3fb45b7875 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 21 Apr 2025 10:53:42 +0200 Subject: [PATCH 205/717] Add i18n section --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 25f4e90..8e12f3e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Qortal Hub - Desktop Interface for Qortal -Qortal Hub is the newest interface for Qortal, part of the 'Qortal Trifecta' series of new User Interfaces for the platform/network. +Qortal Hub is the newest interface for Qortal, part of the 'Qortal Trifecta' series of new User Interfaces for the platform/network. It is likely that Qortal Hub will become the new 'primary interface' for Qortal, and that the primary development focus surrounding Qortal Interface development, will be focused here instead of the previous 'qortal-ui' repo. @@ -8,14 +8,17 @@ It is likely that Qortal Hub will become the new 'primary interface' for Qortal, Qortal Hub came along with the new Group Encryption methodologies applied, which provide **encrypted chat in Q-Chat for private groups.** Qortal Hub was the first to implement the new method of group encryption, which allows new users to see previously published data, unlike the previous group encryption methodology of things like 'threads' in Q-Mail. -Allowing new users to view older messages also comes along with a massive boost to the usability of the group encryption, and as such has been leveraged in multiple places inside Qortal Hub, Qortal Extension, and Qortal Go. +Allowing new users to view older messages also comes along with a massive boost to the usability of the group encryption, and as such has been leveraged in multiple places inside Qortal Hub, Qortal Extension, and Qortal Go. ## Ease of Use Expanded -Qortal Hub has a focus on ease of use for new users. Providing both the ability to utlilize Qortal without needing to run a local node (though running a local node is still the recommended method to access Qortal), and multiple built-in (QDN-published) walk-thru videos (by Qortal Justin) that explain the various basics of any given section of the application. This allows new users to 'jump right in' to utilizing Qortal Hub, and Qortal overall, in a much more streamlined fashion than that which was previously required by the 'legacy UI' (qortal-ui). +Qortal Hub has a focus on ease of use for new users. Providing both the ability to utlilize Qortal without needing to run a local node (though running a local node is still the recommended method to access Qortal), and multiple built-in (QDN-published) walk-thru videos (by Qortal Justin) that explain the various basics of any given section of the application. This allows new users to 'jump right in' to utilizing Qortal Hub, and Qortal overall, in a much more streamlined fashion than that which was previously required by the 'legacy UI' (qortal-ui). -Leveraging a redundant set of publicly accessible nodes provided by crowetic, Qortal Hub, Qortal Go, and Qortal Extension, all allow the use of Qortal without running a node, making it very simple to 'install and go' and start making use of the extensive functionality provided within the Qortal Ecosystem. +Leveraging a redundant set of publicly accessible nodes provided by crowetic, Qortal Hub, Qortal Go, and Qortal Extension, all allow the use of Qortal without running a node, making it very simple to 'install and go' and start making use of the extensive functionality provided within the Qortal Ecosystem. Many additional details and a fully featured wiki will be created over time. Reach out on the chat on https://qortal.dev or in any of the community locations for Qortal, if you have any issues. Thank you! +## Internationalization (i18n) +Qortal-Hub supports internationalization (i18n) using [i18next](https://www.i18next.com/), allowing seamless translation of UI text into multiple languages. +The setup includes modularized translation files, language detection, and runtime language switching. From 57202da103b23f79dac5bfe2b86e3f07b47df744 Mon Sep 17 00:00:00 2001 From: "nico.benaz" <52411515+nbenaglia@users.noreply.github.com> Date: Mon, 21 Apr 2025 11:28:15 +0200 Subject: [PATCH 206/717] Remove curly braces --- src/components/Group/Group.tsx | 111 ++++++++++++++++----------------- 1 file changed, 53 insertions(+), 58 deletions(-) diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index bbd0455..419fa0d 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -2521,64 +2521,59 @@ export const Group = ({ )} - { - - } - { - - } - - { - - } + + + + + Date: Mon, 21 Apr 2025 11:32:13 +0200 Subject: [PATCH 207/717] Remove curly brackets --- src/components/Group/Group.tsx | 70 +++++++++++++++++----------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 419fa0d..4573022 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -2235,42 +2235,40 @@ export const Group = ({ : '0px', }} > - { - - } + Date: Mon, 21 Apr 2025 12:14:51 +0200 Subject: [PATCH 208/717] Add i18n and some languages --- README.md | 3 +- i18n.js | 48 ++++++++++++++++++++++++++++++ package-lock.json | 10 +++++++ package.json | 1 + public/locales/en/auth.json | 10 +++++++ public/locales/en/core.json | 9 ++++++ public/locales/it/auth.json | 10 +++++++ public/locales/it/core.json | 9 ++++++ src/ExtStates/NotAuthenticated.tsx | 21 ++++++++----- src/main.tsx | 1 + 10 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 i18n.js create mode 100644 public/locales/en/auth.json create mode 100644 public/locales/en/core.json create mode 100644 public/locales/it/auth.json create mode 100644 public/locales/it/core.json diff --git a/README.md b/README.md index 8e12f3e..4a62e6d 100644 --- a/README.md +++ b/README.md @@ -21,4 +21,5 @@ Many additional details and a fully featured wiki will be created over time. Rea ## Internationalization (i18n) Qortal-Hub supports internationalization (i18n) using [i18next](https://www.i18next.com/), allowing seamless translation of UI text into multiple languages. -The setup includes modularized translation files, language detection, and runtime language switching. +The setup includes modularized translation files, language detection, context and runtime language switching. +Files with translation are in `public/locales/` folder. diff --git a/i18n.js b/i18n.js new file mode 100644 index 0000000..09886c5 --- /dev/null +++ b/i18n.js @@ -0,0 +1,48 @@ +import { initReactI18next } from 'react-i18next'; +import HttpBackend from 'i18next-http-backend'; +import LocalStorageBackend from 'i18next-localstorage-backend'; +import HttpApi from 'i18next-http-backend'; +import i18n from 'i18next'; +import LanguageDetector from 'i18next-browser-languagedetector'; + +// Detect environment +const isDev = process.env.NODE_ENV === 'development'; + +// Register custom postProcessor: it capitalizes the first letter of a translation- +// Usage: +// t('greeting', { postProcess: 'capitalize' }) +const capitalize = { + type: 'postProcessor', + name: 'capitalize', + process: (value, key, options, translator) => { + return value.charAt(0).toUpperCase() + value.slice(1); + }, +}; + +i18n + .use(HttpApi) + .use(LanguageDetector) + .use(initReactI18next) + .use(capitalize) + .init({ + debug: isDev, + fallbackLng: 'en', + ns: ['auth', 'core'], + supportedLngs: ['en', 'it', 'fr', 'es'], + backend: { + backends: [LocalStorageBackend, HttpBackend], + backendOptions: [ + { + expirationTime: 7 * 24 * 60 * 60 * 1000, // 7 days + }, + { + loadPath: '/locales/{{lng}}/{{ns}}.json', + }, + ], + }, + interpolation: { + escapeValue: false, + }, + }); + +export default i18n; diff --git a/package-lock.json b/package-lock.json index b98590c..071586f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,6 +59,7 @@ "i18next": "^25.0.1", "i18next-browser-languagedetector": "^8.0.5", "i18next-http-backend": "^3.0.2", + "i18next-localstorage-backend": "^4.2.0", "jssha": "3.3.1", "lit": "^3.2.1", "lodash": "^4.17.21", @@ -10846,6 +10847,15 @@ "cross-fetch": "4.0.0" } }, + "node_modules/i18next-localstorage-backend": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/i18next-localstorage-backend/-/i18next-localstorage-backend-4.2.0.tgz", + "integrity": "sha512-vglEQF0AnLriX7dLA2drHnqAYzHxnLwWQzBDw8YxcIDjOvYZz5rvpal59Dq4In+IHNmGNM32YgF0TDjBT0fHmA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.22.15" + } + }, "node_modules/iconv-corefoundation": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", diff --git a/package.json b/package.json index 3e37c8f..0060144 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "i18next": "^25.0.1", "i18next-browser-languagedetector": "^8.0.5", "i18next-http-backend": "^3.0.2", + "i18next-localstorage-backend": "^4.2.0", "jssha": "3.3.1", "lit": "^3.2.1", "lodash": "^4.17.21", diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json new file mode 100644 index 0000000..2c0bd67 --- /dev/null +++ b/public/locales/en/auth.json @@ -0,0 +1,10 @@ +{ + "account_many": "accounts", + "account_one": "account", + "advanced_users": "for advanced users", + "create_account": "create account", + "welcome": "Welcome to", + "use_local_node": "use local node", + "change_apikey": "change API key", + "choose_custom_node": "choose custom node" +} diff --git a/public/locales/en/core.json b/public/locales/en/core.json new file mode 100644 index 0000000..d8f5ca1 --- /dev/null +++ b/public/locales/en/core.json @@ -0,0 +1,9 @@ +{ + "add": { + "task": "Add task" + }, + "cancel": "Cancel", + "description": "Description" , + "save": "Save", + "title": "Title" +} diff --git a/public/locales/it/auth.json b/public/locales/it/auth.json new file mode 100644 index 0000000..c72fbc7 --- /dev/null +++ b/public/locales/it/auth.json @@ -0,0 +1,10 @@ +{ + "account_many": "account", + "account_one": "account", + "advanced_users": "Per utenti avanzati", + "change_apikey": "Cambia la chiave API", + "choose_custom_node": "Scegli un nodo custom", + "create_account": "crea un account", + "use_local_node": "Usa nodo locale", + "welcome": "Benvenuto in" +} diff --git a/public/locales/it/core.json b/public/locales/it/core.json new file mode 100644 index 0000000..d23032e --- /dev/null +++ b/public/locales/it/core.json @@ -0,0 +1,9 @@ +{ + "add": { + "task": "Aggiungi compito" + }, + "cancel": "Cancella", + "description": "Descrizione" , + "save": "Salva", + "title": "Titolo" +} diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index a892414..f1b8ece 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -30,6 +30,7 @@ import { cleanUrl, gateways } from '../background'; import { GlobalContext } from '../App'; import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip'; import ThemeSelector from '../components/Theme/ThemeSelector'; +import { useTranslation } from 'react-i18next'; const manifestData = { version: '0.5.3', @@ -84,6 +85,7 @@ export const NotAuthenticated = ({ React.useState(null); const { showTutorial, hasSeenGettingStarted } = useContext(GlobalContext); const theme = useTheme(); + const { t } = useTranslation('auth'); const importedApiKeyRef = useRef(null); const currentNodeRef = useRef(null); @@ -183,6 +185,7 @@ export const NotAuthenticated = ({ useEffect(() => { importedApiKeyRef.current = importedApiKey; }, [importedApiKey]); + useEffect(() => { currentNodeRef.current = currentNode; }, [currentNode]); @@ -309,6 +312,7 @@ export const NotAuthenticated = ({ } else if (currentNodeRef.current) { payload = currentNodeRef.current; } + let isValid = false; const url = `${payload?.url}/admin/settings/localAuthBypassEnabled`; @@ -402,6 +406,7 @@ export const NotAuthenticated = ({ const addCustomNode = () => { setMode('add-node'); }; + const saveCustomNodes = (myNodes, isFullListOfNodes) => { let nodes = [...(myNodes || [])]; if (!isFullListOfNodes && customNodeToSaveIndex !== null) { @@ -455,7 +460,9 @@ export const NotAuthenticated = ({ > + + - WELCOME TO + {t('auth:welcome', { postProcess: 'capitalize' })} setExtstate('wallets')}> - {/* */} - Accounts + {t('auth:account_many', { postProcess: 'capitalize' })} - {/* - - */} @@ -565,10 +568,11 @@ export const NotAuthenticated = ({ }, }} > - Create account + {t('auth:create_account', { postProcess: 'capitalize' })} + {'Using node: '} {currentNode?.url} + <> - For advanced users + {t('auth:advanced_users', { postProcess: 'capitalize' })} From 3917aef45262fc4b88836be1663e823c3c528042 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 21 Apr 2025 13:38:31 +0200 Subject: [PATCH 209/717] Add i18n files and set first translations --- README.md | 2 ++ docs/i18n_languages.md | 10 ++++++++++ i18n.js | 2 +- public/locales/en/auth.json | 9 ++++++--- public/locales/en/core.json | 12 +++++------- public/locales/it/auth.json | 13 ++++++++----- public/locales/it/core.json | 12 +++++------- src/ExtStates/NotAuthenticated.tsx | 13 ++++++++----- 8 files changed, 45 insertions(+), 28 deletions(-) create mode 100644 docs/i18n_languages.md diff --git a/README.md b/README.md index 4a62e6d..34cfbbb 100644 --- a/README.md +++ b/README.md @@ -23,3 +23,5 @@ Many additional details and a fully featured wiki will be created over time. Rea Qortal-Hub supports internationalization (i18n) using [i18next](https://www.i18next.com/), allowing seamless translation of UI text into multiple languages. The setup includes modularized translation files, language detection, context and runtime language switching. Files with translation are in `public/locales/` folder. + +See [guidelines](./docs/i18n_languages.md). diff --git a/docs/i18n_languages.md b/docs/i18n_languages.md new file mode 100644 index 0000000..7447193 --- /dev/null +++ b/docs/i18n_languages.md @@ -0,0 +1,10 @@ +# I18N Guidelines + +In JSON file: + +- Keep the file sorted +- Always write in lowercase + +In GUI: + +- If the first letter of the translation must be uppercase, use the postProcess, for example: `{t_auth('advanced_users', { postProcess: 'capitalize' })}` diff --git a/i18n.js b/i18n.js index 09886c5..6f21518 100644 --- a/i18n.js +++ b/i18n.js @@ -28,7 +28,7 @@ i18n debug: isDev, fallbackLng: 'en', ns: ['auth', 'core'], - supportedLngs: ['en', 'it', 'fr', 'es'], + supportedLngs: ['en', 'it'], backend: { backends: [LocalStorageBackend, HttpBackend], backendOptions: [ diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json index 2c0bd67..d68032f 100644 --- a/public/locales/en/auth.json +++ b/public/locales/en/auth.json @@ -2,9 +2,12 @@ "account_many": "accounts", "account_one": "account", "advanced_users": "for advanced users", + "build_version": "build version", + "change_apikey": "change APIkey", + "choose_custom_node": "choose custom node", "create_account": "create account", - "welcome": "Welcome to", + "import_key": "import APIkey", "use_local_node": "use local node", - "change_apikey": "change API key", - "choose_custom_node": "choose custom node" + "using_node": "using node", + "welcome": "welcome to" } diff --git a/public/locales/en/core.json b/public/locales/en/core.json index d8f5ca1..042d80f 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -1,9 +1,7 @@ { - "add": { - "task": "Add task" - }, - "cancel": "Cancel", - "description": "Description" , - "save": "Save", - "title": "Title" + "cancel": "cancel", + "choose": "choose", + "description": "description", + "save": "save", + "title": "title" } diff --git a/public/locales/it/auth.json b/public/locales/it/auth.json index c72fbc7..fa99817 100644 --- a/public/locales/it/auth.json +++ b/public/locales/it/auth.json @@ -1,10 +1,13 @@ { "account_many": "account", "account_one": "account", - "advanced_users": "Per utenti avanzati", - "change_apikey": "Cambia la chiave API", - "choose_custom_node": "Scegli un nodo custom", + "advanced_users": "per utenti avanzati", + "build_version": "versione build", + "change_apikey": "cambia la chiave API", + "choose_custom_node": "scegli un nodo custom", "create_account": "crea un account", - "use_local_node": "Usa nodo locale", - "welcome": "Benvenuto in" + "import_key": "importa chiave API", + "use_local_node": "usa nodo locale", + "using_node": "nodo in uso", + "welcome": "benvenuto in" } diff --git a/public/locales/it/core.json b/public/locales/it/core.json index d23032e..2ee28d1 100644 --- a/public/locales/it/core.json +++ b/public/locales/it/core.json @@ -1,9 +1,7 @@ { - "add": { - "task": "Aggiungi compito" - }, - "cancel": "Cancella", - "description": "Descrizione" , - "save": "Salva", - "title": "Titolo" + "cancel": "cancella", + "choose": "scegli", + "description": "descrizione", + "save": "salva", + "title": "titolo" } diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index f1b8ece..b395a50 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -47,6 +47,7 @@ export const HtmlTooltip = styled(({ className, ...props }: TooltipProps) => ( fontSize: theme.typography.pxToRem(12), }, })); + function removeTrailingSlash(url) { return url.replace(/\/+$/, ''); } @@ -85,7 +86,7 @@ export const NotAuthenticated = ({ React.useState(null); const { showTutorial, hasSeenGettingStarted } = useContext(GlobalContext); const theme = useTheme(); - const { t } = useTranslation('auth'); + const { t } = useTranslation(['auth', 'core']); const importedApiKeyRef = useRef(null); const currentNodeRef = useRef(null); @@ -581,7 +582,8 @@ export const NotAuthenticated = ({ visibility: !useLocalNode && 'hidden', }} > - {'Using node: '} {currentNode?.url} + {t('auth:using_node', { postProcess: 'capitalize' })}:{' '} + {currentNode?.url} <> @@ -693,7 +695,7 @@ export const NotAuthenticated = ({ variant="contained" component="label" > - Choose custom node + {t('auth:choose_custom_node', { postProcess: 'capitalize' })} - Build version: {manifestData?.version} + {t('auth:build_version', { postProcess: 'capitalize' })}: + {manifestData?.version} @@ -788,7 +791,7 @@ export const NotAuthenticated = ({ }} variant="contained" > - Choose + {t('core:choose', { postProcess: 'capitalize' })} From 22845e8d410235f8d16e326b646e6477b6110a6a Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 21 Apr 2025 14:39:23 +0300 Subject: [PATCH 210/717] update readme --- README.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/README.md b/README.md index 25f4e90..001c6e9 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,86 @@ Leveraging a redundant set of publicly accessible nodes provided by crowetic, Qo Many additional details and a fully featured wiki will be created over time. Reach out on the chat on https://qortal.dev or in any of the community locations for Qortal, if you have any issues. Thank you! +# 🤝 Contributing Guide + +Thank you for your interest in contributing! We follow a structured Git workflow to keep the project clean, stable, and production-ready at all times. + +--- + +## 📦 Branch Overview + +| Branch | Purpose | +|------------------|----------------------------------------------------------| +| `master` | Stable, production-ready code. All releases are tagged here. | +| `develop` | Active development branch. All new features go here first. | +| `release/x.y.z` | Pre-release branch for staging, QA, and final polish. | + +--- + +## 🌿 Creating a Feature or Fix + +1. **Start from `develop`:** + + ```bash + git checkout develop + git checkout -b feature/your-feature-name + ``` + +2. **Make your changes and commit them.** + +3. **Push your branch:** + + ```bash + git push origin feature/your-feature-name + ``` + +4. **Open a Pull Request into `develop`.** + +--- + +## 🚀 Releasing Code (Maintainers Only) + +A new `release/x.y.z` branch must be created for **every release**. + +1. **Create a `release/` branch from `master`:** + + ```bash + git checkout master + git checkout -b release/1.2.0 + ``` + +2. **Merge in `develop` or selected branches if `develop` is not ready:** + + ```bash + git merge develop + # or + git merge feature/finished-feature + git merge feature/another-complete-feature + ``` + +3. **Polish, test, and fix issues as needed.** + +4. **Merge back into `develop`:** + + ```bash + git checkout develop + git merge release/1.2.0 + git push origin develop + ``` + +5. **Finalize the release:** + + ```bash + git checkout master + git merge release/1.2.0 + git tag v1.2.0 + git push origin master --tags + ``` + +6. **Delete the release branch:** + + ```bash + git branch -d release/1.2.0 + git push origin --delete release/1.2.0 + ``` + From 4c01456565c5b7370ee03206f95dd0c0ad910208 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 21 Apr 2025 14:56:59 +0300 Subject: [PATCH 211/717] moved readme --- README.md | 84 -------------------------------------------- docs/contribution.md | 84 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 84 deletions(-) create mode 100644 docs/contribution.md diff --git a/README.md b/README.md index 001c6e9..5ffbaf6 100644 --- a/README.md +++ b/README.md @@ -18,87 +18,3 @@ Leveraging a redundant set of publicly accessible nodes provided by crowetic, Qo Many additional details and a fully featured wiki will be created over time. Reach out on the chat on https://qortal.dev or in any of the community locations for Qortal, if you have any issues. Thank you! - -# 🤝 Contributing Guide - -Thank you for your interest in contributing! We follow a structured Git workflow to keep the project clean, stable, and production-ready at all times. - ---- - -## 📦 Branch Overview - -| Branch | Purpose | -|------------------|----------------------------------------------------------| -| `master` | Stable, production-ready code. All releases are tagged here. | -| `develop` | Active development branch. All new features go here first. | -| `release/x.y.z` | Pre-release branch for staging, QA, and final polish. | - ---- - -## 🌿 Creating a Feature or Fix - -1. **Start from `develop`:** - - ```bash - git checkout develop - git checkout -b feature/your-feature-name - ``` - -2. **Make your changes and commit them.** - -3. **Push your branch:** - - ```bash - git push origin feature/your-feature-name - ``` - -4. **Open a Pull Request into `develop`.** - ---- - -## 🚀 Releasing Code (Maintainers Only) - -A new `release/x.y.z` branch must be created for **every release**. - -1. **Create a `release/` branch from `master`:** - - ```bash - git checkout master - git checkout -b release/1.2.0 - ``` - -2. **Merge in `develop` or selected branches if `develop` is not ready:** - - ```bash - git merge develop - # or - git merge feature/finished-feature - git merge feature/another-complete-feature - ``` - -3. **Polish, test, and fix issues as needed.** - -4. **Merge back into `develop`:** - - ```bash - git checkout develop - git merge release/1.2.0 - git push origin develop - ``` - -5. **Finalize the release:** - - ```bash - git checkout master - git merge release/1.2.0 - git tag v1.2.0 - git push origin master --tags - ``` - -6. **Delete the release branch:** - - ```bash - git branch -d release/1.2.0 - git push origin --delete release/1.2.0 - ``` - diff --git a/docs/contribution.md b/docs/contribution.md new file mode 100644 index 0000000..b86ad3b --- /dev/null +++ b/docs/contribution.md @@ -0,0 +1,84 @@ + +# 🤝 Contributing Guide + +Thank you for your interest in contributing! We follow a structured Git workflow to keep the project clean, stable, and production-ready at all times. + +--- + +## 📦 Branch Overview + +| Branch | Purpose | +|------------------|----------------------------------------------------------| +| `master` | Stable, production-ready code. All releases are tagged here. | +| `develop` | Active development branch. All new features go here first. | +| `release/x.y.z` | Pre-release branch for staging, QA, and final polish. | + +--- + +## 🌿 Creating a Feature or Fix + +1. **Start from `develop`:** + + ```bash + git checkout develop + git checkout -b feature/your-feature-name + ``` + +2. **Make your changes and commit them.** + +3. **Push your branch:** + + ```bash + git push origin feature/your-feature-name + ``` + +4. **Open a Pull Request into `develop`.** + +--- + +## 🚀 Releasing Code (Maintainers Only) + +A new `release/x.y.z` branch must be created for **every release**. + +1. **Create a `release/` branch from `master`:** + + ```bash + git checkout master + git checkout -b release/1.2.0 + ``` + +2. **Merge in `develop` or selected branches if `develop` is not ready:** + + ```bash + git merge develop + # or + git merge feature/finished-feature + git merge feature/another-complete-feature + ``` + +3. **Polish, test, and fix issues as needed.** + +4. **Merge back into `develop`:** + + ```bash + git checkout develop + git merge release/1.2.0 + git push origin develop + ``` + +5. **Finalize the release:** + + ```bash + git checkout master + git merge release/1.2.0 + git tag v1.2.0 + git push origin master --tags + ``` + +6. **Delete the release branch:** + + ```bash + git branch -d release/1.2.0 + git push origin --delete release/1.2.0 + ``` + From 8140eb7058081ee173ec2ebdcbc1aa9faddd966e Mon Sep 17 00:00:00 2001 From: Phillip <48392931+Philreact@users.noreply.github.com> Date: Mon, 21 Apr 2025 14:59:06 +0300 Subject: [PATCH 212/717] Update docs/contribution.md Co-authored-by: nico.benaz <52411515+nbenaglia@users.noreply.github.com> --- docs/contribution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contribution.md b/docs/contribution.md index b86ad3b..07d06fe 100644 --- a/docs/contribution.md +++ b/docs/contribution.md @@ -9,7 +9,7 @@ Thank you for your interest in contributing! We follow a structured Git workflow | Branch | Purpose | |------------------|----------------------------------------------------------| -| `master` | Stable, production-ready code. All releases are tagged here. | +| `master` | Stable, production-ready code. All releases are tagged from here. | | `develop` | Active development branch. All new features go here first. | | `release/x.y.z` | Pre-release branch for staging, QA, and final polish. | From 01b76c981732d5f0929bb298700b18b8ac25052a Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 21 Apr 2025 14:47:23 +0200 Subject: [PATCH 213/717] Add translations --- public/locales/en/auth.json | 21 +++++-- public/locales/en/core.json | 4 ++ public/locales/it/auth.json | 21 +++++-- public/locales/it/core.json | 4 ++ src/ExtStates/NotAuthenticated.tsx | 90 ++++++++++++++++++++---------- 5 files changed, 99 insertions(+), 41 deletions(-) diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json index d68032f..02c348d 100644 --- a/public/locales/en/auth.json +++ b/public/locales/en/auth.json @@ -2,12 +2,23 @@ "account_many": "accounts", "account_one": "account", "advanced_users": "for advanced users", + "apikey": { + "alternative": "alternative: File select", + "change": "change APIkey", + "enter": "enter APIkey", + "import": "import APIkey", + "key": "API key", + "select_valid": "select a valid apikey" + }, "build_version": "build version", - "change_apikey": "change APIkey", - "choose_custom_node": "choose custom node", "create_account": "create account", - "import_key": "import APIkey", - "use_local_node": "use local node", - "using_node": "using node", + "return_to_list": "return to list", + "node": { + "choose": "choose custom node", + "custom_many": "custom nodes", + "use_custom": "use custom node", + "use_local": "use local node", + "using": "using node" + }, "welcome": "welcome to" } diff --git a/public/locales/en/core.json b/public/locales/en/core.json index 042d80f..bfc8b94 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -1,7 +1,11 @@ { + "add": "add", "cancel": "cancel", "choose": "choose", + "close": "close", "description": "description", + "edit": "edit", + "error": "an error occurred", "save": "save", "title": "title" } diff --git a/public/locales/it/auth.json b/public/locales/it/auth.json index fa99817..306bd0f 100644 --- a/public/locales/it/auth.json +++ b/public/locales/it/auth.json @@ -2,12 +2,23 @@ "account_many": "account", "account_one": "account", "advanced_users": "per utenti avanzati", + "apikey": { + "alternative": "alternativa: seleziona un file", + "change": "cambia la chiave API", + "enter": "inserisci la chiave API", + "import": "importa chiave API", + "key": "chiave API", + "select_valid": "selezione una chiave API valida" + }, + "node": { + "choose": "scegli un nodo custom", + "custom_many": "nodi custom", + "use_custom": "use nodo custom", + "use_local": "usa nodo locale", + "using": "nodo in uso" + }, "build_version": "versione build", - "change_apikey": "cambia la chiave API", - "choose_custom_node": "scegli un nodo custom", "create_account": "crea un account", - "import_key": "importa chiave API", - "use_local_node": "usa nodo locale", - "using_node": "nodo in uso", + "return_to_list": "ritorna alla lista", "welcome": "benvenuto in" } diff --git a/public/locales/it/core.json b/public/locales/it/core.json index 2ee28d1..3bbac5b 100644 --- a/public/locales/it/core.json +++ b/public/locales/it/core.json @@ -1,7 +1,11 @@ { + "add": "aggiungi", "cancel": "cancella", "choose": "scegli", + "close": "chiudi", "description": "descrizione", + "edit": "modifica", + "error": "si è verificato un errore", "save": "salva", "title": "titolo" } diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index b395a50..44886e7 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -40,8 +40,8 @@ export const HtmlTooltip = styled(({ className, ...props }: TooltipProps) => ( ))(({ theme }) => ({ [`& .${tooltipClasses.tooltip}`]: { - backgroundColor: '#232428', - color: 'white', + backgroundColor: theme.palette.background.paper, + color: theme.palette.text.primary, maxWidth: 320, padding: '20px', fontSize: theme.typography.pxToRem(12), @@ -350,7 +350,7 @@ export const NotAuthenticated = ({ .catch((error) => { console.error( 'Failed to set API key:', - error.message || 'An error occurred' + error.message || t('core:error', { postProcess: 'capitalize' }) ); }); } else { @@ -359,7 +359,9 @@ export const NotAuthenticated = ({ if (!fromStartUp) { setInfoSnack({ type: 'error', - message: 'Select a valid apikey', + message: t('auth:apikey.select_valid', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); } @@ -382,7 +384,10 @@ export const NotAuthenticated = ({ .catch((error) => { console.error( 'Failed to set API key:', - error.message || 'An error occurred' + error.message || + t('core:error', { + postProcess: 'capitalize', + }) ); }); return; @@ -390,7 +395,11 @@ export const NotAuthenticated = ({ if (!fromStartUp) { setInfoSnack({ type: 'error', - message: error?.message || 'Select a valid apikey', + message: + error?.message || + t('auth:apikey.select_valid', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); } @@ -507,7 +516,8 @@ export const NotAuthenticated = ({ transaction you make is linked to your ID, and this is where you manage all your QORT and other tradeable cryptocurrencies on Qortal. - + {' '} + // TODO translate } > @@ -538,7 +548,8 @@ export const NotAuthenticated = ({ }} > New users start here! - + {' '} + // TODO translate + {' '} + // TODO translate } > @@ -582,7 +594,7 @@ export const NotAuthenticated = ({ visibility: !useLocalNode && 'hidden', }} > - {t('auth:using_node', { postProcess: 'capitalize' })}:{' '} + {t('auth:node.using', { postProcess: 'capitalize' })}:{' '} {currentNode?.url} @@ -635,7 +647,7 @@ export const NotAuthenticated = ({ }, '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { - backgroundColor: 'white', // Change track color when checked + backgroundColor: theme.palette.background.default, }, }} checked={useLocalNode} @@ -666,7 +678,11 @@ export const NotAuthenticated = ({ disabled={false} /> } - label={`Use ${isLocal ? 'Local' : 'Custom'} Node`} + label={ + isLocal + ? t('auth:node.use_local', { postProcess: 'capitalize' }) + : t('auth:node.use_custom', { postProcess: 'capitalize' }) + } /> {currentNode?.url === 'http://127.0.0.1:12391' && ( @@ -677,14 +693,19 @@ export const NotAuthenticated = ({ variant="contained" component="label" > - {apiKey ? 'Change ' : 'Import '} apikey + {apiKey + ? t('auth:node.use_local', { postProcess: 'capitalize' }) + : t('auth:apikey.import', { postProcess: 'capitalize' })} {`api key : ${importedApiKey}`} + > + {t('auth:apikey.key', { postProcess: 'capitalize' })}: $ + {importedApiKey} + )} @@ -709,6 +730,7 @@ export const NotAuthenticated = ({ + - {'Custom nodes'} + + {' '} + {t('auth:node.custom_many', { postProcess: 'capitalize' })}: + @@ -807,7 +832,7 @@ export const NotAuthenticated = ({ > @@ -850,8 +875,9 @@ export const NotAuthenticated = ({ }} variant="contained" > - Choose + {t('core:choose', { postProcess: 'capitalize' })} + + @@ -920,13 +946,13 @@ export const NotAuthenticated = ({ }} autoFocus > - Close + {t('core:close', { postProcess: 'capitalize' })} )} {mode === 'list' && ( )} @@ -939,7 +965,7 @@ export const NotAuthenticated = ({ setCustomNodeToSaveIndex(null); }} > - Return to list + {t('auth:return_to_list', { postProcess: 'capitalize' })} )} @@ -962,7 +988,9 @@ export const NotAuthenticated = ({ aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" > - {'Enter apikey'} + + {t('auth:enter_apikey', { postProcess: 'capitalize' })} + - Alternative: File select + {t('auth:apikey_alternative', { postProcess: 'capitalize' })} - Close + {t('core:close', { postProcess: 'capitalize' })}

    From 8b1ad92606df40432278ab28f29a18c901ddbfa0 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 21 Apr 2025 17:19:49 +0300 Subject: [PATCH 214/717] added asset qrs --- src/App.tsx | 2 + src/background.ts | 55 +++ .../Apps/useQortalMessageListener.tsx | 8 +- src/qortalRequests.ts | 41 +- src/qortalRequests/get.ts | 374 +++++++++++++++++- src/transactions/TransferAssetTransaction.ts | 35 ++ src/transactions/transactions.ts | 2 + src/utils/decode.ts | 17 +- 8 files changed, 529 insertions(+), 5 deletions(-) create mode 100644 src/transactions/TransferAssetTransaction.ts diff --git a/src/App.tsx b/src/App.tsx index 2a3d1b6..d779131 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3253,6 +3253,8 @@ function App() { lineHeight: 1.2, maxWidth: "90%", textAlign: "center", + fontSize: '16px', + marginBottom: '10px' }} > {messageQortalRequestExtension?.text1} diff --git a/src/background.ts b/src/background.ts index 5bd922b..570486f 100644 --- a/src/background.ts +++ b/src/background.ts @@ -952,6 +952,28 @@ export async function getBalanceInfo() { const data = await response.json(); return data; } + +export async function getAssetBalanceInfo(assetId: number) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + const validApi = await getBaseApi(); + const response = await fetch(validApi + `/assets/balances?address=${address}&assetid=${assetId}&ordering=ASSET_BALANCE_ACCOUNT&limit=1`); + + if (!response?.ok) throw new Error("Cannot fetch asset balance"); + const data = await response.json(); + return +data?.[0]?.balance +} + +export async function getAssetInfo(assetId: number) { + const validApi = await getBaseApi(); + const response = await fetch(validApi + `/assets/info?assetId=${assetId}`); + + if (!response?.ok) throw new Error("Cannot fetch asset info"); + const data = await response.json(); + return data +} + + export async function getLTCBalance() { const wallet = await getSaveWallet(); let _url = `${buyTradeNodeBaseUrl}/crosschain/ltc/walletbalance`; @@ -2268,6 +2290,39 @@ export async function kickFromGroup({ return res; } +export async function transferAsset({ + amount, + recipient, + assetId, +}) { + const lastReference = await getLastRef(); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + const feeres = await getFee("TRANSFER_ASSET"); + + const tx = await createTransaction(12, keyPair, { + fee: feeres.fee, + recipient: recipient, + amount: amount, + assetId: assetId, + lastReference: lastReference, + }); + + + const signedBytes = Base58.encode(tx.signedBytes); + + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error(res?.message || "Transaction was not able to be processed"); + return res; +} + export async function createGroup({ groupName, groupDescription, diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index bef9a99..d3c92ca 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -259,7 +259,9 @@ export const listOfAllQortalRequests = [ 'UPDATE_GROUP', 'SELL_NAME', 'CANCEL_SELL_NAME', - 'BUY_NAME' + 'BUY_NAME', + 'MULTI_ASSET_PAYMENT_WITH_PRIVATE_DATA', + 'TRANSFER_ASSET' ] export const UIQortalRequests = [ @@ -319,7 +321,9 @@ export const UIQortalRequests = [ 'UPDATE_GROUP', 'SELL_NAME', 'CANCEL_SELL_NAME', - 'BUY_NAME' + 'BUY_NAME', + 'MULTI_ASSET_PAYMENT_WITH_PRIVATE_DATA', + 'TRANSFER_ASSET' ]; diff --git a/src/qortalRequests.ts b/src/qortalRequests.ts index f034dbd..37e0126 100644 --- a/src/qortalRequests.ts +++ b/src/qortalRequests.ts @@ -1,6 +1,6 @@ import { gateways, getApiKeyFromStorage } from "./background"; import { listOfAllQortalRequests } from "./components/Apps/useQortalMessageListener"; -import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banFromGroupRequest, cancelGroupBanRequest, cancelGroupInviteRequest, cancelSellOrder, createAndCopyEmbedLink, createBuyOrder, createGroupRequest, createPoll, createSellOrder, decryptAESGCMRequest, decryptData, decryptDataWithSharingKey, decryptQortalGroupData, deleteHostedData, deleteListItems, deployAt, encryptData, encryptDataWithSharingKey, encryptQortalGroupData, getCrossChainServerInfo, getDaySummary, getNodeInfo, getNodeStatus, getForeignFee, getHostedData, getListItems, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getUserWalletTransactions, getWalletBalance, inviteToGroupRequest, joinGroup, kickFromGroupRequest, leaveGroupRequest, openNewTab, publishMultipleQDNResources, publishQDNResource, registerNameRequest, removeForeignServer, removeGroupAdminRequest, saveFile, sendChatMessage, sendCoin, setCurrentForeignServer, signTransaction, updateForeignFee, updateNameRequest, voteOnPoll, getArrrSyncStatus, updateGroupRequest, buyNameRequest, sellNameRequest, cancelSellNameRequest } from "./qortalRequests/get"; +import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banFromGroupRequest, cancelGroupBanRequest, cancelGroupInviteRequest, cancelSellOrder, createAndCopyEmbedLink, createBuyOrder, createGroupRequest, createPoll, createSellOrder, decryptAESGCMRequest, decryptData, decryptDataWithSharingKey, decryptQortalGroupData, deleteHostedData, deleteListItems, deployAt, encryptData, encryptDataWithSharingKey, encryptQortalGroupData, getCrossChainServerInfo, getDaySummary, getNodeInfo, getNodeStatus, getForeignFee, getHostedData, getListItems, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getUserWalletTransactions, getWalletBalance, inviteToGroupRequest, joinGroup, kickFromGroupRequest, leaveGroupRequest, openNewTab, publishMultipleQDNResources, publishQDNResource, registerNameRequest, removeForeignServer, removeGroupAdminRequest, saveFile, sendChatMessage, sendCoin, setCurrentForeignServer, signTransaction, updateForeignFee, updateNameRequest, voteOnPoll, getArrrSyncStatus, updateGroupRequest, buyNameRequest, sellNameRequest, cancelSellNameRequest, multiPaymentWithPrivateData, transferAssetRequest } from "./qortalRequests/get"; import { getData, storeData } from "./utils/chromeStorage"; import { executeEvent } from "./utils/events"; @@ -1314,6 +1314,45 @@ export const isRunningGateway = async ()=> { } break; } + + case "MULTI_ASSET_PAYMENT_WITH_PRIVATE_DATA" : { + try { + const res = await multiPaymentWithPrivateData(request.payload, isFromExtension) + event.source.postMessage({ + requestId: request.requestId, + action: request.action, + payload: res, + type: "backgroundMessageResponse", + }, event.origin); + } catch (error) { + event.source.postMessage({ + requestId: request.requestId, + action: request.action, + error: error?.message, + type: "backgroundMessageResponse", + }, event.origin); + } + break; + } + case "TRANSFER_ASSET" : { + try { + const res = await transferAssetRequest(request.payload, isFromExtension) + event.source.postMessage({ + requestId: request.requestId, + action: request.action, + payload: res, + type: "backgroundMessageResponse", + }, event.origin); + } catch (error) { + event.source.postMessage({ + requestId: request.requestId, + action: request.action, + error: error?.message, + type: "backgroundMessageResponse", + }, event.origin); + } + break; + } default: break; } diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index 4ae357f..dcc391e 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -35,6 +35,11 @@ import { cancelSellName, buyName, getBaseApi, + getAssetBalanceInfo, + getNameOrAddress, + getAssetInfo, + getPublicKey, + transferAsset, } from "../background"; import { getNameInfo, uint8ArrayToObject } from "../backgroundFunctions/encryption"; import { showSaveFilePicker } from "../components/Apps/useQortalMessageListener"; @@ -75,6 +80,10 @@ import { fileToBase64 } from "../utils/fileReading"; import { mimeToExtensionMap } from "../utils/memeTypes"; import { RequestQueueWithPromise } from "../utils/queue/queue"; import utils from "../utils/utils"; +import ShortUniqueId from "short-unique-id"; +import { isValidBase64WithDecode } from "../utils/decode"; + +const uid = new ShortUniqueId({ length: 6 }); export const requestQueueGetAtAddresses = new RequestQueueWithPromise(10); @@ -4886,4 +4895,367 @@ export const buyNameRequest = async (data, isFromExtension) => { } else { throw new Error("User declined request"); } -}; \ No newline at end of file +}; + +export const multiPaymentWithPrivateData = async (data, isFromExtension) => { + const requiredFields = ["payments", "assetId"]; + requiredFields.forEach((field) => { + if (data[field] === undefined || data[field] === null) { + throw new Error(`Missing required field: ${field}`); + } + }); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const privateKey = parsedData.privateKey; + const userPublicKey = parsedData.publicKey + const {fee: paymentFee} = await getFee("TRANSFER_ASSET"); + const {fee: arbitraryFee} = await getFee("ARBITRARY"); + + let name = null + const payments = data.payments; + const assetId = data.assetId + const pendingTransactions = [] + const pendingAdditionalArbitraryTxs = [] + const additionalArbitraryTxsWithoutPayment = data?.additionalArbitraryTxsWithoutPayment || [] + let totalAmount = 0 + let fee = 0 + for (const payment of payments) { + const paymentRefId = uid.rnd(); + const requiredFieldsPayment = ["recipient", "amount"]; + + for (const field of requiredFieldsPayment) { + if (!payment[field]) { + throw new Error(`Missing required field: ${field}`); + } + } + + const confirmReceiver = await getNameOrAddress(payment.recipient); + if (confirmReceiver.error) { + throw new Error("Invalid receiver address or name"); + } + const receiverPublicKey = await getPublicKey(confirmReceiver) + + const amount = +payment.amount.toFixed(8) + + pendingTransactions.push({ + type: "PAYMENT", + recipientAddress: confirmReceiver, + amount: amount, + paymentRefId, + }); + + fee = fee + +paymentFee; + totalAmount = totalAmount + amount; + + if (payment.arbitraryTxs && payment.arbitraryTxs.length > 0) { + for (const arbitraryTx of payment.arbitraryTxs) { + const requiredFieldsArbitraryTx = ["service", "identifier", "base64"]; + + for (const field of requiredFieldsArbitraryTx) { + if (!arbitraryTx[field]) { + throw new Error(`Missing required field: ${field}`); + } + } + + if (!name) { + const getName = await getNameInfo(); + if (!getName) throw new Error("Name needed to publish"); + name = getName; + } + + const isValid = isValidBase64WithDecode(arbitraryTx.base64); + if (!isValid) throw new Error("Invalid base64 data"); + if(!arbitraryTx?.service?.includes('_PRIVATE')) throw new Error('Please use a PRIVATE service') + const additionalPublicKeys = arbitraryTx?.additionalPublicKeys || [] + pendingTransactions.push({ + type: "ARBITRARY", + identifier: arbitraryTx.identifier, + service: arbitraryTx.service, + base64: arbitraryTx.base64, + description: arbitraryTx?.description || "", + paymentRefId, + publicKeys: [receiverPublicKey, ...additionalPublicKeys] + }); + + fee = fee + +arbitraryFee; + } + } + } + + if (additionalArbitraryTxsWithoutPayment && additionalArbitraryTxsWithoutPayment.length > 0) { + for (const arbitraryTx of additionalArbitraryTxsWithoutPayment) { + const requiredFieldsArbitraryTx = ["service", "identifier", "base64"]; + + for (const field of requiredFieldsArbitraryTx) { + if (!arbitraryTx[field]) { + throw new Error(`Missing required field: ${field}`); + } + } + + if (!name) { + const getName = await getNameInfo(); + if (!getName) throw new Error("Name needed to publish"); + name = getName; + } + + const isValid = isValidBase64WithDecode(arbitraryTx.base64); + if (!isValid) throw new Error("Invalid base64 data"); + if(!arbitraryTx?.service?.includes('_PRIVATE')) throw new Error('Please use a PRIVATE service') + const additionalPublicKeys = arbitraryTx?.additionalPublicKeys || [] + pendingAdditionalArbitraryTxs.push({ + type: "ARBITRARY", + identifier: arbitraryTx.identifier, + service: arbitraryTx.service, + base64: arbitraryTx.base64, + description: arbitraryTx?.description || "", + publicKeys: additionalPublicKeys + }); + + fee = fee + +arbitraryFee; + } + } + + + + if(!name) throw new Error('A name is needed to publish') + const balance = await getBalanceInfo(); + + if(+balance < fee) throw new Error('Your QORT balance is insufficient') +const assetBalance = await getAssetBalanceInfo(assetId) + const assetInfo = await getAssetInfo(assetId) + if(assetBalance < totalAmount) throw new Error('Your asset balance is insufficient') + + const resPermission = await getUserPermission( + { + text1: "Do you give this application permission to make the following payments and publishes?", + text2: `Asset used in payments: ${assetInfo.name}`, + html: ` +
    + + + ${pendingTransactions. + filter((item)=> item.type === 'PAYMENT').map( + (payment) => ` +
    +
    Recipient: ${ + payment.recipientAddress + }
    +
    Amount: ${payment.amount}
    +
    ` + ) + .join("")} + ${[...pendingTransactions, ...pendingAdditionalArbitraryTxs]. + filter((item)=> item.type === 'ARBITRARY').map( + (arbitraryTx) => ` +
    +
    Service: ${ + arbitraryTx.service + }
    +
    Name: ${name}
    +
    Identifier: ${ + arbitraryTx.identifier + }
    +
    ` + ) + .join("")} +
    + + `, + highlightedText: `Total Amount: ${totalAmount}`, + fee: fee + }, + isFromExtension + ); + const { accepted, checkbox1 = false } = resPermission; + if (!accepted) { + throw new Error("User declined request"); + } + + + + + // const failedTxs = [] + const paymentsDone = { + + } + + const transactionsDone = [] + + + for (const transaction of pendingTransactions) { + const type = transaction.type; + + if (type === "PAYMENT") { + const makePayment = await retryTransaction( + transferAsset, + [{ amount: transaction.amount, assetId, recipient: transaction.recipientAddress }], true + ); + if (makePayment) { + transactionsDone.push(makePayment?.signature); + if (transaction.paymentRefId) { + paymentsDone[transaction.paymentRefId] = makePayment + } + } + } + else if (type === "ARBITRARY" && paymentsDone[transaction.paymentRefId]) { + const objectToEncrypt = { + data: transaction.base64, + payment: paymentsDone[transaction.paymentRefId], + }; + + const toBase64 = await retryTransaction(objectToBase64, [objectToEncrypt], true); + + if (!toBase64) continue; // Skip if encryption fails + + const encryptDataResponse = await retryTransaction(encryptDataGroup, [ + { + data64: toBase64, + publicKeys: transaction.publicKeys, + privateKey, + userPublicKey, + }, + ], true); + + if (!encryptDataResponse) continue; // Skip if encryption fails + + const resPublish = await retryTransaction(publishData, [ + { + registeredName: encodeURIComponent(name), + file: encryptDataResponse, + service: transaction.service, + identifier: encodeURIComponent(transaction.identifier), + uploadType: "file", + description: transaction?.description, + isBase64: true, + apiVersion: 2, + withFee: true, + }, + ], true); + + if (resPublish?.signature) { + transactionsDone.push(resPublish?.signature); + } + } + } + + for (const transaction of pendingAdditionalArbitraryTxs) { + + const objectToEncrypt = { + data: transaction.base64, + }; + + const toBase64 = await retryTransaction(objectToBase64, [objectToEncrypt], true); + + if (!toBase64) continue; // Skip if encryption fails + + const encryptDataResponse = await retryTransaction(encryptDataGroup, [ + { + data64: toBase64, + publicKeys: transaction.publicKeys, + privateKey, + userPublicKey, + }, + ], true); + + if (!encryptDataResponse) continue; // Skip if encryption fails + + const resPublish = await retryTransaction(publishData, [ + { + registeredName: encodeURIComponent(name), + file: encryptDataResponse, + service: transaction.service, + identifier: encodeURIComponent(transaction.identifier), + uploadType: "file", + description: transaction?.description, + isBase64: true, + apiVersion: 2, + withFee: true, + }, + ], true); + + if (resPublish?.signature) { + transactionsDone.push(resPublish?.signature); + } + + } + + return transactionsDone +}; + + +export const transferAssetRequest = async (data, isFromExtension) => { + const requiredFields = ["amount", "assetId", "recipient"]; + requiredFields.forEach((field) => { + if (data[field] === undefined || data[field] === null) { + throw new Error(`Missing required field: ${field}`); + } + }); + const amount = data.amount + const assetId = data.assetId + const recipient = data.recipient + + + const {fee} = await getFee("TRANSFER_ASSET"); + const balance = await getBalanceInfo(); + + if(+balance < +fee) throw new Error('Your QORT balance is insufficient') + const assetBalance = await getAssetBalanceInfo(assetId) + if(assetBalance < amount) throw new Error('Your asset balance is insufficient') + const confirmReceiver = await getNameOrAddress(recipient); + if (confirmReceiver.error) { + throw new Error("Invalid receiver address or name"); + } + const assetInfo = await getAssetInfo(assetId) + const resPermission = await getUserPermission( + { + text1: `Do you give this application permission to transfer the following asset?`, + text2: `Asset: ${assetInfo?.name}`, + highlightedText: `Amount: ${amount}`, + fee: fee + }, + isFromExtension + ); + + const { accepted } = resPermission; + if (!accepted) { + throw new Error("User declined request"); + } + const res = await transferAsset({amount, recipient: confirmReceiver, assetId}) + return res +} \ No newline at end of file diff --git a/src/transactions/TransferAssetTransaction.ts b/src/transactions/TransferAssetTransaction.ts new file mode 100644 index 0000000..9d0bedb --- /dev/null +++ b/src/transactions/TransferAssetTransaction.ts @@ -0,0 +1,35 @@ +// @ts-nocheck + +import { QORT_DECIMALS } from '../constants/constants' +import TransactionBase from './TransactionBase' + +export default class TransferAssetTransaction extends TransactionBase { + constructor() { + super() + this.type = 12 + } + + set recipient(recipient) { + this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) + } + + set amount(amount) { + this._amount = Math.round(amount * QORT_DECIMALS) + this._amountBytes = this.constructor.utils.int64ToBytes(this._amount) + } + + set assetId(assetId) { + this._assetId = this.constructor.utils.int64ToBytes(assetId) + } + + get params() { + const params = super.params + params.push( + this._recipient, + this._assetId, + this._amountBytes, + this._feeBytes + ) + return params + } +} diff --git a/src/transactions/transactions.ts b/src/transactions/transactions.ts index f989f3c..a117560 100644 --- a/src/transactions/transactions.ts +++ b/src/transactions/transactions.ts @@ -24,6 +24,7 @@ import UpdateGroupTransaction from './UpdateGroupTransaction.js' import SellNameTransacion from './SellNameTransacion.js' import CancelSellNameTransacion from './CancelSellNameTransacion.js' import BuyNameTransacion from './BuyNameTransacion.js' +import TransferAssetTransaction from './TransferAssetTransaction.js' export const transactionTypes = { @@ -35,6 +36,7 @@ export const transactionTypes = { 7: BuyNameTransacion, 8: CreatePollTransaction, 9: VoteOnPollTransaction, + 12: TransferAssetTransaction, 16: DeployAtTransaction, 18: ChatTransaction, 181: GroupChatTransaction, diff --git a/src/utils/decode.ts b/src/utils/decode.ts index 3123810..06fdb2b 100644 --- a/src/utils/decode.ts +++ b/src/utils/decode.ts @@ -13,4 +13,19 @@ export function decodeIfEncoded(input) { // Return input as-is if not URI-encoded return input; - } \ No newline at end of file + } + + export const isValidBase64 = (str: string): boolean => { + if (typeof str !== "string" || str.length % 4 !== 0) return false; + + const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/; + return base64Regex.test(str); + }; + + export const isValidBase64WithDecode = (str: string): boolean => { + try { + return isValidBase64(str) && Boolean(atob(str)); + } catch { + return false; + } + }; \ No newline at end of file From 653532e78ed6060b3bba4d698e13f47fa86ac29f Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 21 Apr 2025 20:02:06 +0200 Subject: [PATCH 215/717] i18n for tutorial --- i18n.js | 2 +- public/locales/en/tutorial.json | 12 +++++++++ src/components/Tutorials/Tutorials.tsx | 10 +++---- .../Tutorials/useHandleTutorials.tsx | 27 ++++++++++++++----- 4 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 public/locales/en/tutorial.json diff --git a/i18n.js b/i18n.js index 6f21518..e785beb 100644 --- a/i18n.js +++ b/i18n.js @@ -27,7 +27,7 @@ i18n .init({ debug: isDev, fallbackLng: 'en', - ns: ['auth', 'core'], + ns: ['auth', 'core', 'tutorial'], supportedLngs: ['en', 'it'], backend: { backends: [LocalStorageBackend, HttpBackend], diff --git a/public/locales/en/tutorial.json b/public/locales/en/tutorial.json new file mode 100644 index 0000000..8aeab89 --- /dev/null +++ b/public/locales/en/tutorial.json @@ -0,0 +1,12 @@ +{ + "1_getting_started": "1. Getting Started", + "2_overview": "2. Overview", + "3_groups": "3. Qortal Groups", + "4_obtain_qort": "4. Obtaining Qort", + "account_creation": "Account Creation", + "important_info": "Important Information!", + "apps": { + "dashboard": "1. Apps Dashboard", + "navigation": "2. Apps Navigation" + } +} diff --git a/src/components/Tutorials/Tutorials.tsx b/src/components/Tutorials/Tutorials.tsx index 382774c..06274e7 100644 --- a/src/components/Tutorials/Tutorials.tsx +++ b/src/components/Tutorials/Tutorials.tsx @@ -13,11 +13,13 @@ import { } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; import { VideoPlayer } from '../Embeds/VideoPlayer'; +import { useTranslation } from 'react-i18next'; export const Tutorials = () => { const { openTutorialModal, setOpenTutorialModal } = useContext(GlobalContext); const [multiNumber, setMultiNumber] = useState(0); const theme = useTheme(); + const { t } = useTranslation(['core', 'tutorial']); const handleClose = () => { setOpenTutorialModal(null); @@ -61,9 +63,7 @@ export const Tutorials = () => { })} - - {selectedTutorial?.title} {` Tutorial`} - + {selectedTutorial?.title} { @@ -138,7 +138,7 @@ export const Tutorials = () => { diff --git a/src/components/Tutorials/useHandleTutorials.tsx b/src/components/Tutorials/useHandleTutorials.tsx index 1df8626..3569beb 100644 --- a/src/components/Tutorials/useHandleTutorials.tsx +++ b/src/components/Tutorials/useHandleTutorials.tsx @@ -8,6 +8,7 @@ import navigationImg from './img/navigation.webp'; import overviewImg from './img/overview.webp'; import startedImg from './img/started.webp'; import obtainingImg from './img/obtaining-qort.jpg'; +import { useTranslation } from 'react-i18next'; const checkIfGatewayIsOnline = async () => { try { @@ -27,9 +28,11 @@ const checkIfGatewayIsOnline = async () => { return false; } }; + export const useHandleTutorials = () => { const [openTutorialModal, setOpenTutorialModal] = useState(null); const [shownTutorials, setShowTutorials] = useState(null); + const { t } = useTranslation(['core', 'tutorial']); useEffect(() => { try { @@ -104,7 +107,9 @@ export const useHandleTutorials = () => { setOpenTutorialModal({ multi: [ { - title: '1. Getting Started', + title: t('tutorial:1_getting_started', { + postProcess: 'capitalize', + }), resource: { name: 'a-test', service: 'VIDEO', @@ -113,7 +118,9 @@ export const useHandleTutorials = () => { }, }, { - title: '2. Overview', + title: t('tutorial:2_overview', { + postProcess: 'capitalize', + }), resource: { name: 'a-test', service: 'VIDEO', @@ -122,7 +129,9 @@ export const useHandleTutorials = () => { }, }, { - title: '3. Qortal Groups', + title: t('tutorial:3_groups', { + postProcess: 'capitalize', + }), resource: { name: 'a-test', service: 'VIDEO', @@ -131,7 +140,9 @@ export const useHandleTutorials = () => { }, }, { - title: '4. Obtaining Qort', + title: t('tutorial:4_obtain_qort', { + postProcess: 'capitalize', + }), resource: { name: 'a-test', service: 'VIDEO', @@ -151,7 +162,9 @@ export const useHandleTutorials = () => { setOpenTutorialModal({ multi: [ { - title: '1. Apps Dashboard', + title: t('tutorial:app.dashboard', { + postProcess: 'capitalize', + }), resource: { name: 'a-test', service: 'VIDEO', @@ -160,7 +173,9 @@ export const useHandleTutorials = () => { }, }, { - title: '2. Apps Navigation', + title: t('tutorial:app.navigation', { + postProcess: 'capitalize', + }), resource: { name: 'a-test', service: 'VIDEO', From 3bc139041df99e23283adfb1eabc3039f290e778 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 21 Apr 2025 20:06:57 +0200 Subject: [PATCH 216/717] Fix style for login buttons --- src/styles/App-styles.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/styles/App-styles.ts b/src/styles/App-styles.ts index 9b385bc..980657b 100644 --- a/src/styles/App-styles.ts +++ b/src/styles/App-styles.ts @@ -130,10 +130,10 @@ export const CustomButton = styled(Box)(({ theme }) => ({ transition: 'all 0.2s', width: 'fit-content', '&:hover': { - backgroundColor: theme.palette.background.default, - color: '#fff', + backgroundColor: theme.palette.background.paper, + color: theme.palette.text.secondary, 'svg path': { - fill: '#fff', + fill: theme.palette.background.paper, }, }, })); From af48dd7c063f0c8930f474712ce534d9f80e9481 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 21 Apr 2025 20:12:49 +0200 Subject: [PATCH 217/717] Change close button position (now right) --- src/ExtStates/NotAuthenticated.tsx | 37 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 44886e7..74b7798 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -936,7 +936,14 @@ export const NotAuthenticated = ({ )} + + {mode === 'list' && ( + + )} + {mode === 'list' && ( <> - )} {mode === 'add-node' && ( <> @@ -989,7 +991,7 @@ export const NotAuthenticated = ({ aria-describedby="alert-dialog-description" > - {t('auth:enter_apikey', { postProcess: 'capitalize' })} + {t('auth:apikey.enter', { postProcess: 'capitalize' })} - {t('auth:apikey_alternative', { postProcess: 'capitalize' })} + {t('auth:apikey.alternative', { postProcess: 'capitalize' })} - - + + + )} From c75b3de1cb9d9de8953c3b06e03ce1309bd7b6c3 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 21 Apr 2025 20:29:48 +0200 Subject: [PATCH 218/717] i18n for thingsToDoInitial --- public/locales/en/tutorial.json | 9 ++++++-- public/locales/it/core.json | 1 + src/components/Group/ThingsToDoInitial.tsx | 25 ++++++++++++++++------ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/public/locales/en/tutorial.json b/public/locales/en/tutorial.json index 8aeab89..2b632b3 100644 --- a/public/locales/en/tutorial.json +++ b/public/locales/en/tutorial.json @@ -3,10 +3,15 @@ "2_overview": "2. Overview", "3_groups": "3. Qortal Groups", "4_obtain_qort": "4. Obtaining Qort", - "account_creation": "Account Creation", - "important_info": "Important Information!", + "account_creation": "account creation", + "important_info": "important information!", "apps": { "dashboard": "1. Apps Dashboard", "navigation": "2. Apps Navigation" + }, + "initial": { + "6_qort": "Have at least 6 QORT in your wallet", + "getting_started": "getting started", + "register_name": "register a name" } } diff --git a/public/locales/it/core.json b/public/locales/it/core.json index 3bbac5b..673062f 100644 --- a/public/locales/it/core.json +++ b/public/locales/it/core.json @@ -6,6 +6,7 @@ "description": "descrizione", "edit": "modifica", "error": "si è verificato un errore", + "loading": "loading...", "save": "salva", "title": "titolo" } diff --git a/src/components/Group/ThingsToDoInitial.tsx b/src/components/Group/ThingsToDoInitial.tsx index 023cfde..6c315cd 100644 --- a/src/components/Group/ThingsToDoInitial.tsx +++ b/src/components/Group/ThingsToDoInitial.tsx @@ -4,10 +4,11 @@ import ListItem from '@mui/material/ListItem'; import ListItemButton from '@mui/material/ListItemButton'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; -import { Box, Typography } from '@mui/material'; +import { Box, Typography, useTheme } from '@mui/material'; import { Spacer } from '../../common/Spacer'; import { QMailMessages } from './QMailMessages'; import { executeEvent } from '../../utils/events'; +import { useTranslation } from 'react-i18next'; export const ThingsToDoInitial = ({ myAddress, @@ -18,6 +19,8 @@ export const ThingsToDoInitial = ({ }) => { const [checked1, setChecked1] = React.useState(false); const [checked2, setChecked2] = React.useState(false); + const { t } = useTranslation(['core', 'tutorial']); + const theme = useTheme(); React.useEffect(() => { if (balance && +balance >= 6) { @@ -72,7 +75,11 @@ export const ThingsToDoInitial = ({ fontWeight: 600, }} > - {!isLoaded ? 'Loading...' : 'Getting Started'} + {!isLoaded + ? t('core:loading', { postProcess: 'capitalize' }) + : t('tutorial:initial.getting_started', { + postProcess: 'capitalize', + })} @@ -80,12 +87,12 @@ export const ThingsToDoInitial = ({ {isLoaded && ( @@ -114,7 +121,9 @@ export const ThingsToDoInitial = ({ fontWeight: 400, }, }} - primary={`Have at least 6 QORT in your wallet`} + primary={t('tutorial:initial.6_qort', { + postProcess: 'capitalize', + })} /> Date: Mon, 21 Apr 2025 20:30:58 +0200 Subject: [PATCH 219/717] Remove unused file --- src/components/Group/Home.tsx | 112 ---------------------------------- 1 file changed, 112 deletions(-) delete mode 100644 src/components/Group/Home.tsx diff --git a/src/components/Group/Home.tsx b/src/components/Group/Home.tsx deleted file mode 100644 index a8c804c..0000000 --- a/src/components/Group/Home.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { Box, Button, Typography } from "@mui/material"; -import React from "react"; -import { Spacer } from "../../common/Spacer"; -import { ListOfThreadPostsWatched } from "./ListOfThreadPostsWatched"; -import { ThingsToDoInitial } from "./ThingsToDoInitial"; -import { GroupJoinRequests } from "./GroupJoinRequests"; -import { GroupInvites } from "./GroupInvites"; -import RefreshIcon from "@mui/icons-material/Refresh"; - -export const Home = ({ - refreshHomeDataFunc, - myAddress, - isLoadingGroups, - balance, - userInfo, - groups, - setGroupSection, - setSelectedGroup, - getTimestampEnterChat, - setOpenManageMembers, - setOpenAddGroup, - setMobileViewMode, - setDesktopViewMode -}) => { - return ( - - - 15 ? "16px" : "20px", - padding: '10px' - }} - > - Welcome - {userInfo?.name ? ( - {`, ${userInfo?.name}`} - ) : null} - - - - {/* - - */} - {!isLoadingGroups && ( - - - - - - - - )} - - - ); -}; From 59573a43abc0d32169d83515bef04223f3d263bf Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 21 Apr 2025 21:02:10 +0200 Subject: [PATCH 220/717] i18n for welcome page --- public/locales/en/core.json | 8 +- public/locales/en/tutorial.json | 6 +- public/locales/it/core.json | 3 +- src/components/Explore/Explore.tsx | 121 +++++------ src/components/Group/HomeDesktop.tsx | 306 +++++++++++++-------------- src/components/Home/QortPrice.tsx | 142 ++++++------- 6 files changed, 299 insertions(+), 287 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index bfc8b94..b9ea602 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -6,6 +6,12 @@ "description": "description", "edit": "edit", "error": "an error occurred", + "last_height": "last height", + "price": "price", "save": "save", - "title": "title" + "supply": "supply", + "title": "title", + "wallet": "wallet", + "wallet_many": "wallets", + "welcome": "welcome" } diff --git a/public/locales/en/tutorial.json b/public/locales/en/tutorial.json index 2b632b3..f022388 100644 --- a/public/locales/en/tutorial.json +++ b/public/locales/en/tutorial.json @@ -11,7 +11,11 @@ }, "initial": { "6_qort": "Have at least 6 QORT in your wallet", + "explore": "explore", + "general_chat": "general chat", "getting_started": "getting started", - "register_name": "register a name" + "register_name": "register a name", + "see_apps": "see apps", + "trade_qort": "trade QORT" } } diff --git a/public/locales/it/core.json b/public/locales/it/core.json index 673062f..db7e120 100644 --- a/public/locales/it/core.json +++ b/public/locales/it/core.json @@ -6,7 +6,8 @@ "description": "descrizione", "edit": "modifica", "error": "si è verificato un errore", - "loading": "loading...", + "loading": "caricamento...", "save": "salva", + "supply": "offerta", "title": "titolo" } diff --git a/src/components/Explore/Explore.tsx b/src/components/Explore/Explore.tsx index 61037d7..4ec6488 100644 --- a/src/components/Explore/Explore.tsx +++ b/src/components/Explore/Explore.tsx @@ -1,129 +1,130 @@ -import { Box, ButtonBase, Typography } from "@mui/material"; -import React from "react"; -import ChatIcon from "@mui/icons-material/Chat"; -import qTradeLogo from "../../assets/Icons/q-trade-logo.webp"; -import AppsIcon from "@mui/icons-material/Apps"; -import { executeEvent } from "../../utils/events"; +import { Box, ButtonBase, Typography, useTheme } from '@mui/material'; +import ChatIcon from '@mui/icons-material/Chat'; +import qTradeLogo from '../../assets/Icons/q-trade-logo.webp'; +import AppsIcon from '@mui/icons-material/Apps'; +import { executeEvent } from '../../utils/events'; import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; +import { useTranslation } from 'react-i18next'; +export const Explore = ({ setDesktopViewMode }) => { + const theme = useTheme(); + const { t } = useTranslation(['core', 'tutorial']); -export const Explore = ({setDesktopViewMode}) => { return ( { - executeEvent("addTab", { - data: { service: "APP", name: "q-trade" }, - }); - executeEvent("open-apps-mode", {}); - }} + executeEvent('addTab', { + data: { service: 'APP', name: 'q-trade' }, + }); + executeEvent('open-apps-mode', {}); + }} > - Trade QORT + {t('tutorial:initial.trade_qort', { postProcess: 'capitalize' })} + { + setDesktopViewMode('apps'); }} - onClick={()=> { - setDesktopViewMode('apps') - - }} > - See Apps + {t('tutorial:initial.see_apps', { postProcess: 'capitalize' })} + { - executeEvent("openGroupMessage", { - from: "0" , - }); - }} + executeEvent('openGroupMessage', { + from: '0', + }); + }} > - General Chat + {t('tutorial:initial.general_chat', { postProcess: 'capitalize' })} { - executeEvent("openWalletsApp", { - - }); - }} + executeEvent('openWalletsApp', {}); + }} > - Wallets + {t('core:wallet', { count: 100, postProcess: 'capitalize' })} diff --git a/src/components/Group/HomeDesktop.tsx b/src/components/Group/HomeDesktop.tsx index d83f774..0c6ca85 100644 --- a/src/components/Group/HomeDesktop.tsx +++ b/src/components/Group/HomeDesktop.tsx @@ -1,16 +1,16 @@ -import { Box, Button, Divider, Typography } from "@mui/material"; -import React from "react"; -import { Spacer } from "../../common/Spacer"; -import { ListOfThreadPostsWatched } from "./ListOfThreadPostsWatched"; -import { ThingsToDoInitial } from "./ThingsToDoInitial"; -import { GroupJoinRequests } from "./GroupJoinRequests"; -import { GroupInvites } from "./GroupInvites"; -import RefreshIcon from "@mui/icons-material/Refresh"; -import { ListOfGroupPromotions } from "./ListOfGroupPromotions"; -import { QortPrice } from "../Home/QortPrice"; -import ExploreIcon from "@mui/icons-material/Explore"; -import { Explore } from "../Explore/Explore"; -import { NewUsersCTA } from "../Home/NewUsersCTA"; +import { Box, Divider, Typography, useTheme } from '@mui/material'; +import React from 'react'; +import { Spacer } from '../../common/Spacer'; +import { ThingsToDoInitial } from './ThingsToDoInitial'; +import { GroupJoinRequests } from './GroupJoinRequests'; +import { GroupInvites } from './GroupInvites'; +import { ListOfGroupPromotions } from './ListOfGroupPromotions'; +import { QortPrice } from '../Home/QortPrice'; +import ExploreIcon from '@mui/icons-material/Explore'; +import { Explore } from '../Explore/Explore'; +import { NewUsersCTA } from '../Home/NewUsersCTA'; +import { useTranslation } from 'react-i18next'; + export const HomeDesktop = ({ refreshHomeDataFunc, myAddress, @@ -30,93 +30,97 @@ export const HomeDesktop = ({ }) => { const [checked1, setChecked1] = React.useState(false); const [checked2, setChecked2] = React.useState(false); - React.useEffect(() => { - if (balance && +balance >= 6) { - setChecked1(true); - } - }, [balance]); - - - React.useEffect(() => { - if (name) setChecked2(true); - }, [name]); - - - const isLoaded = React.useMemo(()=> { - if(userInfo !== null) return true - return false - }, [ userInfo]) - - const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(()=> { - if(isLoaded && checked1 && checked2) return true - return false - }, [checked1, isLoaded, checked2]) - + const { t } = useTranslation(['core']); + const theme = useTheme(); + + React.useEffect(() => { + if (balance && +balance >= 6) { + setChecked1(true); + } + }, [balance]); + + React.useEffect(() => { + if (name) setChecked2(true); + }, [name]); + + const isLoaded = React.useMemo(() => { + if (userInfo !== null) return true; + return false; + }, [userInfo]); + + const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(() => { + if (isLoaded && checked1 && checked2) return true; + return false; + }, [checked1, isLoaded, checked2]); + return ( + 15 ? "16px" : "20px", - padding: "10px", + fontSize: userInfo?.name?.length > 15 ? '16px' : '20px', + padding: '10px', }} > - Welcome + {t('core:welcome', { postProcess: 'capitalize' })} {userInfo?.name ? ( {`, ${userInfo?.name}`} ) : null} + + {!isLoadingGroups && ( item?.groupId !== "0").length !== 0 + groups?.filter((item) => item?.groupId !== '0').length !== 0 } /> - - {desktopViewMode === "home" && ( + + {desktopViewMode === 'home' && ( <> {/* */} - {hasDoneNameAndBalanceAndIsLoaded && ( - <> - - - - - - - - )} - + {hasDoneNameAndBalanceAndIsLoaded && ( + <> + + + + + + + + + )} )} )} - + {!isLoadingGroups && ( <> - - - {" "} - - Explore - {" "} - - - {!hasDoneNameAndBalanceAndIsLoaded && ( - - )} - - {hasDoneNameAndBalanceAndIsLoaded && ( - + + {' '} + + {t('tutorial:initial.explore', { postProcess: 'capitalize' })} + {' '} + + - )} - - - - - + {!hasDoneNameAndBalanceAndIsLoaded && } + + {hasDoneNameAndBalanceAndIsLoaded && } + + + + + - )} diff --git a/src/components/Home/QortPrice.tsx b/src/components/Home/QortPrice.tsx index f4586fb..2f118c1 100644 --- a/src/components/Home/QortPrice.tsx +++ b/src/components/Home/QortPrice.tsx @@ -1,8 +1,9 @@ -import React, { useCallback, useEffect, useState } from "react"; -import { getBaseApiReact } from "../../App"; -import { Box, Tooltip, Typography } from "@mui/material"; -import { BarSpinner } from "../../common/Spinners/BarSpinner/BarSpinner"; -import { formatDate } from "../../utils/time"; +import { useCallback, useEffect, useState } from 'react'; +import { getBaseApiReact } from '../../App'; +import { Box, Tooltip, Typography, useTheme } from '@mui/material'; +import { BarSpinner } from '../../common/Spinners/BarSpinner/BarSpinner'; +import { formatDate } from '../../utils/time'; +import { useTranslation } from 'react-i18next'; function getAverageLtcPerQort(trades) { let totalQort = 0; @@ -38,6 +39,8 @@ export const QortPrice = () => { const [supply, setSupply] = useState(null); const [lastBlock, setLastBlock] = useState(null); const [loading, setLoading] = useState(true); + const { t } = useTranslation(['core', 'tutorial']); + const theme = useTheme(); const getPrice = useCallback(async () => { try { @@ -101,64 +104,63 @@ export const QortPrice = () => { return () => clearInterval(interval); }, [getPrice]); - return ( + Based on the latest 20 trades } placement="bottom" arrow - sx={{ fontSize: "24" }} + sx={{ fontSize: '24' }} slotProps={{ tooltip: { sx: { - color: "#ffffff", - backgroundColor: "#444444", + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, }, }, arrow: { sx: { - color: "#444444", + color: theme.palette.text.primary, }, }, }} > - Price + {t('core:price', { postProcess: 'capitalize' })} + {!ltcPerQort ? ( ) : ( {ltcPerQort} LTC/QORT @@ -168,28 +170,28 @@ export const QortPrice = () => { - Supply + {t('core:supply', { postProcess: 'capitalize' })} + {!supply ? ( ) : ( {supply} QORT @@ -197,60 +199,58 @@ export const QortPrice = () => { )} - {lastBlock?.timestamp && formatDate(lastBlock?.timestamp)} - - } - placement="bottom" - arrow - sx={{ fontSize: "24" }} - slotProps={{ - tooltip: { - sx: { - color: "#ffffff", - backgroundColor: "#444444", - }, + title={ + + {lastBlock?.timestamp && formatDate(lastBlock?.timestamp)} + + } + placement="bottom" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, }, - arrow: { - sx: { - color: "#444444", - }, + }, + arrow: { + sx: { + color: theme.palette.text.primary, }, - }} - > - - + - Last height + {t('core:last_height', { postProcess: 'capitalize' })} + {!lastBlock?.height ? ( ) : ( {lastBlock?.height} )} - - + ); From aace10c85302698b1015156fed88ee3fae824772 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 21 Apr 2025 21:18:50 +0200 Subject: [PATCH 221/717] Set plural key --- public/locales/en/core.json | 3 ++- public/locales/it/core.json | 7 ++++++- src/components/Explore/Explore.tsx | 2 +- src/components/Group/HomeDesktop.tsx | 1 + src/components/Tutorials/useHandleTutorials.tsx | 4 ++-- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index b9ea602..0674642 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -7,11 +7,12 @@ "edit": "edit", "error": "an error occurred", "last_height": "last height", + "loading": "loading...", "price": "price", "save": "save", "supply": "supply", "title": "title", "wallet": "wallet", - "wallet_many": "wallets", + "wallet_other": "wallets", "welcome": "welcome" } diff --git a/public/locales/it/core.json b/public/locales/it/core.json index db7e120..6376e0a 100644 --- a/public/locales/it/core.json +++ b/public/locales/it/core.json @@ -6,8 +6,13 @@ "description": "descrizione", "edit": "modifica", "error": "si è verificato un errore", + "last_height": "ultimo blocco", "loading": "caricamento...", + "price": "prezzo", "save": "salva", "supply": "offerta", - "title": "titolo" + "title": "titolo", + "wallet": "wallet", + "wallet_other": "wallet", + "welcome": "benvenuto" } diff --git a/src/components/Explore/Explore.tsx b/src/components/Explore/Explore.tsx index 4ec6488..ae1988e 100644 --- a/src/components/Explore/Explore.tsx +++ b/src/components/Explore/Explore.tsx @@ -124,7 +124,7 @@ export const Explore = ({ setDesktopViewMode }) => { fontSize: '1rem', }} > - {t('core:wallet', { count: 100, postProcess: 'capitalize' })} + {t('core:wallet_other', { postProcess: 'capitalize' })} diff --git a/src/components/Group/HomeDesktop.tsx b/src/components/Group/HomeDesktop.tsx index 0c6ca85..d0f2459 100644 --- a/src/components/Group/HomeDesktop.tsx +++ b/src/components/Group/HomeDesktop.tsx @@ -241,6 +241,7 @@ export const HomeDesktop = ({ )} + {/* { setOpenTutorialModal({ multi: [ { - title: t('tutorial:app.dashboard', { + title: t('tutorial:apps.dashboard', { postProcess: 'capitalize', }), resource: { @@ -173,7 +173,7 @@ export const useHandleTutorials = () => { }, }, { - title: t('tutorial:app.navigation', { + title: t('tutorial:apps.navigation', { postProcess: 'capitalize', }), resource: { From 07ed170fd71c85b58d3655f2d9d773f1f5a94f96 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 21 Apr 2025 21:32:38 +0200 Subject: [PATCH 222/717] Use language detected from browser --- i18n.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/i18n.js b/i18n.js index e785beb..631d33a 100644 --- a/i18n.js +++ b/i18n.js @@ -25,10 +25,6 @@ i18n .use(initReactI18next) .use(capitalize) .init({ - debug: isDev, - fallbackLng: 'en', - ns: ['auth', 'core', 'tutorial'], - supportedLngs: ['en', 'it'], backend: { backends: [LocalStorageBackend, HttpBackend], backendOptions: [ @@ -40,9 +36,14 @@ i18n }, ], }, + debug: isDev, + fallbackLng: 'en', interpolation: { escapeValue: false, }, + lng: navigator.language, + ns: ['auth', 'core', 'tutorial'], + supportedLngs: ['en', 'it'], }); export default i18n; From ec7819e11da58b9a39880dbef8d6f34d695f2307 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 21 Apr 2025 22:32:30 +0200 Subject: [PATCH 223/717] Add translation keys --- i18n.js | 2 +- public/locales/en/auth.json | 12 +++- public/locales/en/core.json | 10 +++ public/locales/it/auth.json | 13 +++- public/locales/it/core.json | 10 +++ public/locales/it/tutorial.json | 21 ++++++ src/App.tsx | 89 ++++++++++++++++--------- src/components/GeneralNotifications.tsx | 5 +- src/components/QMailStatus.tsx | 7 +- 9 files changed, 127 insertions(+), 42 deletions(-) create mode 100644 public/locales/it/tutorial.json diff --git a/i18n.js b/i18n.js index 631d33a..ddda868 100644 --- a/i18n.js +++ b/i18n.js @@ -14,7 +14,7 @@ const isDev = process.env.NODE_ENV === 'development'; const capitalize = { type: 'postProcessor', name: 'capitalize', - process: (value, key, options, translator) => { + process: (value) => { return value.charAt(0).toUpperCase() + value.slice(1); }, }; diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json index 02c348d..36d2329 100644 --- a/public/locales/en/auth.json +++ b/public/locales/en/auth.json @@ -10,15 +10,23 @@ "key": "API key", "select_valid": "select a valid apikey" }, + "authenticate": "authenticate", "build_version": "build version", "create_account": "create account", - "return_to_list": "return to list", + "download_account": "download account", + "keep_secure": "Keep your account file secure", "node": { "choose": "choose custom node", "custom_many": "custom nodes", "use_custom": "use custom node", "use_local": "use local node", - "using": "using node" + "using": "using node", + "using_public": "using public node" }, + "password": "password", + "password_confirmation": "confirm password", + "return_to_list": "return to list", + "wallet_password_confirmation": "confirm wallet password", + "wallet_password": "wallet password", "welcome": "welcome to" } diff --git a/public/locales/en/core.json b/public/locales/en/core.json index 0674642..1f7bbab 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -1,17 +1,27 @@ { "add": "add", + "backup_wallet": "backup wallet", "cancel": "cancel", "choose": "choose", "close": "close", + "continue": "continue", "description": "description", "edit": "edit", "error": "an error occurred", "last_height": "last height", "loading": "loading...", + "logout": "logout", + "minting_status": "minting status", + "payment_notification": "payment notification", "price": "price", + "q_mail": "q-mail", "save": "save", + "settings": "settings", "supply": "supply", "title": "title", + "tutorial": "tutorial", + "your_account": "your account", + "user_lookup": "user lookup", "wallet": "wallet", "wallet_other": "wallets", "welcome": "welcome" diff --git a/public/locales/it/auth.json b/public/locales/it/auth.json index 306bd0f..6b820c7 100644 --- a/public/locales/it/auth.json +++ b/public/locales/it/auth.json @@ -10,15 +10,22 @@ "key": "chiave API", "select_valid": "selezione una chiave API valida" }, + "authenticate": "autenticazione", + "build_version": "versione build", + "download_account": "scarica account", + "keep_secure": "metti al sicuro il file del tuo account", "node": { "choose": "scegli un nodo custom", "custom_many": "nodi custom", "use_custom": "use nodo custom", "use_local": "usa nodo locale", - "using": "nodo in uso" + "using": "nodo in uso", + "using_public": "utilizzo nodo pubblico" }, - "build_version": "versione build", - "create_account": "crea un account", + "password": "password", + "password_confirmation": "confirma la password", + "wallet_password_confirmation": "conferma la password del wallet", "return_to_list": "ritorna alla lista", + "wallet_password": "password del wallet", "welcome": "benvenuto in" } diff --git a/public/locales/it/core.json b/public/locales/it/core.json index 6376e0a..bc6dd31 100644 --- a/public/locales/it/core.json +++ b/public/locales/it/core.json @@ -1,17 +1,27 @@ { "add": "aggiungi", + "backup_wallet": "backup wallet", "cancel": "cancella", "choose": "scegli", "close": "chiudi", + "continue": "continua", "description": "descrizione", "edit": "modifica", "error": "si è verificato un errore", "last_height": "ultimo blocco", "loading": "caricamento...", + "logout": "disconnetti", + "minting_status": "stato minting", + "payment_notification": "notifiche pagamenti", "price": "prezzo", + "q_mail": "q-mail", "save": "salva", + "settings": "impostazioni", "supply": "offerta", "title": "titolo", + "tutorial": "tutorial", + "your_account": "il tuo account", + "user_lookup": "ricerca utente", "wallet": "wallet", "wallet_other": "wallet", "welcome": "benvenuto" diff --git a/public/locales/it/tutorial.json b/public/locales/it/tutorial.json new file mode 100644 index 0000000..08fb1df --- /dev/null +++ b/public/locales/it/tutorial.json @@ -0,0 +1,21 @@ +{ + "1_getting_started": "1. Come iniziare", + "2_overview": "2. Overview", + "3_groups": "3. I gruppi in Qortal", + "4_obtain_qort": "4. Ottenere Qort", + "account_creation": "creazione account", + "important_info": "important information!", + "apps": { + "dashboard": "1. Dashboard delle app", + "navigation": "2. Navigation tra le app" + }, + "initial": { + "6_qort": "Avere almeno 6 QORT nel proprio wallet", + "explore": "esplora", + "general_chat": "chat generaleat", + "getting_started": "Come iniziare", + "register_name": "registra un nome", + "see_apps": "vedi le apps", + "trade_qort": "scambia i QORT" + } +} diff --git a/src/App.tsx b/src/App.tsx index 7d3a7c7..420e36f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -136,6 +136,7 @@ import { QortPayment } from './components/QortPayment'; import { GeneralNotifications } from './components/GeneralNotifications'; import { PdfViewer } from './common/PdfViewer'; import ThemeSelector from './components/Theme/ThemeSelector.tsx'; +import { useTranslation } from 'react-i18next'; type extStates = | 'not-authenticated' @@ -316,6 +317,8 @@ function App() { const [sendqortState, setSendqortState] = useState(null); const [isLoading, setIsLoading] = useState(false); const [isLoadingSendCoin, setIsLoadingSendCoin] = useState(false); + + const { t } = useTranslation(['auth', 'core']); const theme = useTheme(); const [ @@ -1550,9 +1553,10 @@ function App() { style={{ fontSize: '14px', fontWeight: 700, + textTransform: 'uppercase', }} > - LOGOUT + {t('core:logout')} } placement="left" @@ -1589,9 +1593,10 @@ function App() { style={{ fontSize: '14px', fontWeight: 700, + textTransform: 'uppercase', }} > - SETTINGS + {t('core:settings')} } placement="left" @@ -1628,9 +1633,10 @@ function App() { style={{ fontSize: '14px', fontWeight: 700, + textTransform: 'uppercase', }} > - USER LOOKUP + {t('core:user_lookup')} } placement="left" @@ -1667,9 +1673,10 @@ function App() { style={{ fontSize: '14px', fontWeight: 700, + textTransform: 'uppercase', }} > - WALLETS + {t('core:wallet_other')} } placement="left" @@ -1703,9 +1710,10 @@ function App() { style={{ fontSize: '14px', fontWeight: 700, + textTransform: 'uppercase', }} > - YOUR ACCOUNT + {t('core:your_account')} } placement="left" @@ -1813,9 +1821,10 @@ function App() { style={{ fontSize: '14px', fontWeight: 700, + textTransform: 'uppercase', }} > - MINTING STATUS + {t('core:minting_status')} } placement="left" @@ -1857,9 +1866,10 @@ function App() { style={{ fontSize: '14px', fontWeight: 700, + textTransform: 'uppercase', }} > - TUTORIAL + {t('core:tutorial')} } placement="left" @@ -1894,8 +1904,14 @@ function App() { > - BACKUP WALLET + + {t('core:backup_wallet')} } placement="left" @@ -2083,6 +2099,7 @@ function App() { {messageQortalRequest?.text1} + {messageQortalRequest?.text2 && ( <> @@ -2124,6 +2141,7 @@ function App() { > {messageQortalRequest?.text3} + @@ -2158,11 +2176,11 @@ function App() { {messageQortalRequest?.highlightedText} @@ -2379,17 +2397,7 @@ function App() { > {sendqortState?.amount} QORT - {/* - - Confirm Wallet Password - - - setPaymentPassword(e.target.value)} - /> */} - Create account + {t('auth:create_account', { postProcess: 'capitalize' })} )} @@ -2624,7 +2632,7 @@ function App() { fontWeight: 600, }} > - Authenticate + {t('auth:authenticate', { postProcess: 'capitalize' })} @@ -2632,7 +2640,7 @@ function App() { <> - Wallet Password + {t('auth:wallet_password', { postProcess: 'capitalize' })} @@ -2656,7 +2664,8 @@ function App() { fontSize: '12px', }} > - {'Using node: '} {currentNode?.url} + {t('auth:node.using', { postProcess: 'capitalize' })}:{' '} + {currentNode?.url} ) : ( @@ -2667,7 +2676,7 @@ function App() { fontSize: '12px', }} > - {'Using public node'} + {t('auth:node.using_public', { postProcess: 'capitalize' })} )} @@ -2675,7 +2684,7 @@ function App() { - Authenticate + {t('auth:authenticate', { postProcess: 'capitalize' })} {walletToBeDecryptedError} @@ -2703,7 +2712,9 @@ function App() { onClick={returnToMain} /> + +
    - Download Account + {t('auth:download_account', { postProcess: 'capitalize' })} @@ -2740,9 +2751,13 @@ function App() { {!walletToBeDownloaded && ( <> - Confirm Wallet Password + {t('auth:wallet_password_confirmation', { + postProcess: 'capitalize', + })} + + + + - Confirm password + {t('auth:password_confirmation', { + postProcess: 'capitalize', + })} {walletToBeDownloadedError} @@ -2764,11 +2783,15 @@ function App() { onClick={async () => { await saveFileToDiskFunc(); await showInfo({ - message: `Keep your account file secure.`, + message: t('auth:keep_secure', { + postProcess: 'capitalize', + }), }); }} > - Download account + {t('auth:download_account', { + postProcess: 'capitalize', + })} )} @@ -3024,7 +3047,7 @@ function App() { - Create Account + {t('auth:create_account', { postProcess: 'capitalize' })} {walletToBeDownloadedError} diff --git a/src/components/GeneralNotifications.tsx b/src/components/GeneralNotifications.tsx index 97c498b..6335dbf 100644 --- a/src/components/GeneralNotifications.tsx +++ b/src/components/GeneralNotifications.tsx @@ -14,6 +14,7 @@ import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; import { formatDate } from '../utils/time'; import { useHandlePaymentNotification } from '../hooks/useHandlePaymentNotification'; import { executeEvent } from '../utils/events'; +import { useTranslation } from 'react-i18next'; export const GeneralNotifications = ({ address }) => { const [anchorEl, setAnchorEl] = useState(null); @@ -31,6 +32,7 @@ export const GeneralNotifications = ({ address }) => { setAnchorEl(event.currentTarget); }; + const { t } = useTranslation(['core']); const theme = useTheme(); return ( @@ -48,9 +50,10 @@ export const GeneralNotifications = ({ address }) => { color: theme.palette.text.primary, fontSize: '14px', fontWeight: 700, + textTransform: 'uppercase', }} > - PAYMENT NOTIFICATION + {t('core:payment_notification')} } placement="left" diff --git a/src/components/QMailStatus.tsx b/src/components/QMailStatus.tsx index c760751..dd6f9be 100644 --- a/src/components/QMailStatus.tsx +++ b/src/components/QMailStatus.tsx @@ -1,13 +1,14 @@ import { useMemo } from 'react'; -import EmailIcon from '@mui/icons-material/Email'; import { useRecoilState } from 'recoil'; import { mailsAtom, qMailLastEnteredTimestampAtom } from '../atoms/global'; import { isLessThanOneWeekOld } from './Group/QMailMessages'; import { ButtonBase, Tooltip, useTheme } from '@mui/material'; import { executeEvent } from '../utils/events'; import { Mail } from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; export const QMailStatus = () => { + const { t } = useTranslation(['core']); const theme = useTheme(); const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState( @@ -63,7 +64,9 @@ export const QMailStatus = () => { fontWeight: 700, }} > - Q-MAIL + {t('core:q_mail', { + postProcess: 'capitalize', + })} } placement="left" From 801366a0fb490edb85635c7bbc1a889ae86722ce Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 21 Apr 2025 22:39:23 +0200 Subject: [PATCH 224/717] Add languages (fr, es, de) --- i18n.js | 2 +- public/locales/de/auth.json | 32 ++++++++++++++++++++++++++++++++ public/locales/de/core.json | 28 ++++++++++++++++++++++++++++ public/locales/de/tutorial.json | 21 +++++++++++++++++++++ public/locales/es/auth.json | 32 ++++++++++++++++++++++++++++++++ public/locales/es/core.json | 28 ++++++++++++++++++++++++++++ public/locales/es/tutorial.json | 21 +++++++++++++++++++++ public/locales/fr/auth.json | 32 ++++++++++++++++++++++++++++++++ public/locales/fr/core.json | 28 ++++++++++++++++++++++++++++ public/locales/fr/tutorial.json | 21 +++++++++++++++++++++ public/locales/it/auth.json | 1 + 11 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 public/locales/de/auth.json create mode 100644 public/locales/de/core.json create mode 100644 public/locales/de/tutorial.json create mode 100644 public/locales/es/auth.json create mode 100644 public/locales/es/core.json create mode 100644 public/locales/es/tutorial.json create mode 100644 public/locales/fr/auth.json create mode 100644 public/locales/fr/core.json create mode 100644 public/locales/fr/tutorial.json diff --git a/i18n.js b/i18n.js index ddda868..43f8663 100644 --- a/i18n.js +++ b/i18n.js @@ -43,7 +43,7 @@ i18n }, lng: navigator.language, ns: ['auth', 'core', 'tutorial'], - supportedLngs: ['en', 'it'], + supportedLngs: ['en', 'it', 'es', 'fr', 'de'], }); export default i18n; diff --git a/public/locales/de/auth.json b/public/locales/de/auth.json new file mode 100644 index 0000000..3e40c57 --- /dev/null +++ b/public/locales/de/auth.json @@ -0,0 +1,32 @@ +{ + "account_many": "Konten", + "account_one": "Konto", + "advanced_users": "für fortgeschrittene Benutzer", + "apikey": { + "alternative": "Alternative: Datei auswählen", + "change": "API-Schlüssel ändern", + "enter": "API-Schlüssel eingeben", + "import": "API-Schlüssel importieren", + "key": "API-Schlüssel", + "select_valid": "gültigen API-Schlüssel auswählen" + }, + "authenticate": "authentifizieren", + "build_version": "Build-Version", + "create_account": "Konto erstellen", + "download_account": "Konto herunterladen", + "keep_secure": "Bewahren Sie Ihre Kontodatei sicher auf", + "node": { + "choose": "benutzerdefinierten Node auswählen", + "custom_many": "benutzerdefinierte Nodes", + "use_custom": "benutzerdefinierten Node verwenden", + "use_local": "lokalen Node verwenden", + "using": "verwende Node", + "using_public": "öffentlichen Node verwenden" + }, + "password": "Passwort", + "password_confirmation": "Passwort bestätigen", + "return_to_list": "zurück zur Liste", + "wallet_password_confirmation": "Wallet-Passwort bestätigen", + "wallet_password": "Wallet-Passwort", + "welcome": "willkommen bei" +} diff --git a/public/locales/de/core.json b/public/locales/de/core.json new file mode 100644 index 0000000..be16346 --- /dev/null +++ b/public/locales/de/core.json @@ -0,0 +1,28 @@ +{ + "add": "hinzufügen", + "backup_wallet": "Wallet sichern", + "cancel": "abbrechen", + "choose": "auswählen", + "close": "schließen", + "continue": "fortfahren", + "description": "Beschreibung", + "edit": "bearbeiten", + "error": "ein Fehler ist aufgetreten", + "last_height": "letzte Höhe", + "loading": "wird geladen...", + "logout": "abmelden", + "minting_status": "Minting-Status", + "payment_notification": "Zahlungsbenachrichtigung", + "price": "Preis", + "q_mail": "q-Mail", + "save": "speichern", + "settings": "Einstellungen", + "supply": "Angebot", + "title": "Titel", + "tutorial": "Tutorial", + "your_account": "dein Konto", + "user_lookup": "Benutzersuche", + "wallet": "Wallet", + "wallet_other": "Wallets", + "welcome": "willkommen" +} diff --git a/public/locales/de/tutorial.json b/public/locales/de/tutorial.json new file mode 100644 index 0000000..f789e2b --- /dev/null +++ b/public/locales/de/tutorial.json @@ -0,0 +1,21 @@ +{ + "1_getting_started": "1. Erste Schritte", + "2_overview": "2. Überblick", + "3_groups": "3. Qortal-Gruppen", + "4_obtain_qort": "4. QORT erhalten", + "account_creation": "Kontoerstellung", + "important_info": "wichtige Informationen!", + "apps": { + "dashboard": "1. App-Dashboard", + "navigation": "2. App-Navigation" + }, + "initial": { + "6_qort": "Mindestens 6 QORT im Wallet haben", + "explore": "erkunden", + "general_chat": "allgemeiner Chat", + "getting_started": "erste Schritte", + "register_name": "einen Namen registrieren", + "see_apps": "Apps ansehen", + "trade_qort": "QORT handeln" + } +} diff --git a/public/locales/es/auth.json b/public/locales/es/auth.json new file mode 100644 index 0000000..93fd943 --- /dev/null +++ b/public/locales/es/auth.json @@ -0,0 +1,32 @@ +{ + "account_many": "cuentas", + "account_one": "cuenta", + "advanced_users": "para usuarios avanzados", + "apikey": { + "alternative": "alternativa: Seleccionar archivo", + "change": "cambiar clave API", + "enter": "ingresar clave API", + "import": "importar clave API", + "key": "clave API", + "select_valid": "selecciona una clave API válida" + }, + "authenticate": "autenticar", + "build_version": "versión de compilación", + "create_account": "crear cuenta", + "download_account": "descargar cuenta", + "keep_secure": "Mantén tu archivo de cuenta seguro", + "node": { + "choose": "elegir nodo personalizado", + "custom_many": "nodos personalizados", + "use_custom": "usar nodo personalizado", + "use_local": "usar nodo local", + "using": "usando nodo", + "using_public": "usando nodo público" + }, + "password": "contraseña", + "password_confirmation": "confirmar contraseña", + "return_to_list": "volver a la lista", + "wallet_password_confirmation": "confirmar contraseña del monedero", + "wallet_password": "contraseña del monedero", + "welcome": "bienvenido a" +} diff --git a/public/locales/es/core.json b/public/locales/es/core.json new file mode 100644 index 0000000..9da6f10 --- /dev/null +++ b/public/locales/es/core.json @@ -0,0 +1,28 @@ +{ + "add": "añadir", + "backup_wallet": "copia de seguridad del monedero", + "cancel": "cancelar", + "choose": "elegir", + "close": "cerrar", + "continue": "continuar", + "description": "descripción", + "edit": "editar", + "error": "ocurrió un error", + "last_height": "última altura", + "loading": "cargando...", + "logout": "cerrar sesión", + "minting_status": "estado de acuñación", + "payment_notification": "notificación de pago", + "price": "precio", + "q_mail": "q-mail", + "save": "guardar", + "settings": "configuración", + "supply": "suministro", + "title": "título", + "tutorial": "tutorial", + "your_account": "tu cuenta", + "user_lookup": "búsqueda de usuario", + "wallet": "monedero", + "wallet_other": "monederos", + "welcome": "bienvenido" +} diff --git a/public/locales/es/tutorial.json b/public/locales/es/tutorial.json new file mode 100644 index 0000000..8b89781 --- /dev/null +++ b/public/locales/es/tutorial.json @@ -0,0 +1,21 @@ +{ + "1_getting_started": "1. Comenzando", + "2_overview": "2. Visión general", + "3_groups": "3. Grupos de Qortal", + "4_obtain_qort": "4. Obtener QORT", + "account_creation": "creación de cuenta", + "important_info": "¡información importante!", + "apps": { + "dashboard": "1. Panel de aplicaciones", + "navigation": "2. Navegación de aplicaciones" + }, + "initial": { + "6_qort": "Tener al menos 6 QORT en tu monedero", + "explore": "explorar", + "general_chat": "chat general", + "getting_started": "comenzando", + "register_name": "registrar un nombre", + "see_apps": "ver aplicaciones", + "trade_qort": "intercambiar QORT" + } +} diff --git a/public/locales/fr/auth.json b/public/locales/fr/auth.json new file mode 100644 index 0000000..d6e4c16 --- /dev/null +++ b/public/locales/fr/auth.json @@ -0,0 +1,32 @@ +{ + "account_many": "comptes", + "account_one": "compte", + "advanced_users": "pour les utilisateurs avancés", + "apikey": { + "alternative": "alternative : Sélectionner un fichier", + "change": "changer la clé API", + "enter": "entrer la clé API", + "import": "importer la clé API", + "key": "clé API", + "select_valid": "sélectionnez une clé API valide" + }, + "authenticate": "authentifier", + "build_version": "version de build", + "create_account": "créer un compte", + "download_account": "télécharger le compte", + "keep_secure": "Gardez votre fichier de compte en sécurité", + "node": { + "choose": "choisir un nœud personnalisé", + "custom_many": "nœuds personnalisés", + "use_custom": "utiliser un nœud personnalisé", + "use_local": "utiliser un nœud local", + "using": "utilise le nœud", + "using_public": "utilise un nœud public" + }, + "password": "mot de passe", + "password_confirmation": "confirmer le mot de passe", + "return_to_list": "retour à la liste", + "wallet_password_confirmation": "confirmer le mot de passe du portefeuille", + "wallet_password": "mot de passe du portefeuille", + "welcome": "bienvenue sur" +} diff --git a/public/locales/fr/core.json b/public/locales/fr/core.json new file mode 100644 index 0000000..65191ab --- /dev/null +++ b/public/locales/fr/core.json @@ -0,0 +1,28 @@ +{ + "add": "ajouter", + "backup_wallet": "sauvegarde du portefeuille", + "cancel": "annuler", + "choose": "choisir", + "close": "fermer", + "continue": "continuer", + "description": "description", + "edit": "éditer", + "error": "une erreur est survenue", + "last_height": "hauteur finale", + "loading": "chargement...", + "logout": "se déconnecter", + "minting_status": "statut de minage", + "payment_notification": "notification de paiement", + "price": "prix", + "q_mail": "q-mail", + "save": "enregistrer", + "settings": "paramètres", + "supply": "approvisionnement", + "title": "titre", + "tutorial": "tutoriel", + "your_account": "votre compte", + "user_lookup": "recherche d'utilisateur", + "wallet": "portefeuille", + "wallet_other": "portefeuilles", + "welcome": "bienvenue" +} diff --git a/public/locales/fr/tutorial.json b/public/locales/fr/tutorial.json new file mode 100644 index 0000000..13a33cd --- /dev/null +++ b/public/locales/fr/tutorial.json @@ -0,0 +1,21 @@ +{ + "1_getting_started": "1. Démarrer", + "2_overview": "2. Aperçu", + "3_groups": "3. Groupes Qortal", + "4_obtain_qort": "4. Obtenir des QORT", + "account_creation": "création de compte", + "important_info": "informations importantes !", + "apps": { + "dashboard": "1. Tableau de bord des applications", + "navigation": "2. Navigation des applications" + }, + "initial": { + "6_qort": "Avoir au moins 6 QORT dans votre portefeuille", + "explore": "explorer", + "general_chat": "chat général", + "getting_started": "démarrer", + "register_name": "enregistrer un nom", + "see_apps": "voir les applications", + "trade_qort": "échanger des QORT" + } +} diff --git a/public/locales/it/auth.json b/public/locales/it/auth.json index 6b820c7..e6b35da 100644 --- a/public/locales/it/auth.json +++ b/public/locales/it/auth.json @@ -12,6 +12,7 @@ }, "authenticate": "autenticazione", "build_version": "versione build", + "create_account": "crea un account", "download_account": "scarica account", "keep_secure": "metti al sicuro il file del tuo account", "node": { From 2276e8020716e03cfaac7eca5fb9c515c55d99cc Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Tue, 22 Apr 2025 00:20:33 +0200 Subject: [PATCH 225/717] Add russian --- i18n.js | 2 +- public/locales/ru/auth.json | 32 ++++++++++++++++++++++++++++++++ public/locales/ru/core.json | 28 ++++++++++++++++++++++++++++ public/locales/ru/tutorial.json | 21 +++++++++++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 public/locales/ru/auth.json create mode 100644 public/locales/ru/core.json create mode 100644 public/locales/ru/tutorial.json diff --git a/i18n.js b/i18n.js index 43f8663..78df3c1 100644 --- a/i18n.js +++ b/i18n.js @@ -43,7 +43,7 @@ i18n }, lng: navigator.language, ns: ['auth', 'core', 'tutorial'], - supportedLngs: ['en', 'it', 'es', 'fr', 'de'], + supportedLngs: ['en', 'it', 'es', 'fr', 'de', 'ru'], }); export default i18n; diff --git a/public/locales/ru/auth.json b/public/locales/ru/auth.json new file mode 100644 index 0000000..5a47cc2 --- /dev/null +++ b/public/locales/ru/auth.json @@ -0,0 +1,32 @@ +{ + "account_many": "аккаунты", + "account_one": "аккаунт", + "advanced_users": "для продвинутых пользователей", + "apikey": { + "alternative": "альтернатива: выбрать файл", + "change": "изменить API-ключ", + "enter": "введите API-ключ", + "import": "импортировать API-ключ", + "key": "API-ключ", + "select_valid": "выберите действительный API-ключ" + }, + "authenticate": "аутентификация", + "build_version": "версия сборки", + "create_account": "создать аккаунт", + "download_account": "скачать аккаунт", + "keep_secure": "Храните файл аккаунта в безопасности", + "node": { + "choose": "выбрать пользовательский узел", + "custom_many": "пользовательские узлы", + "use_custom": "использовать пользовательский узел", + "use_local": "использовать локальный узел", + "using": "используется узел", + "using_public": "используется публичный узел" + }, + "password": "пароль", + "password_confirmation": "подтвердите пароль", + "return_to_list": "вернуться к списку", + "wallet_password_confirmation": "подтвердите пароль кошелька", + "wallet_password": "пароль кошелька", + "welcome": "добро пожаловать в" +} diff --git a/public/locales/ru/core.json b/public/locales/ru/core.json new file mode 100644 index 0000000..2dc0810 --- /dev/null +++ b/public/locales/ru/core.json @@ -0,0 +1,28 @@ +{ + "add": "добавить", + "backup_wallet": "резервная копия кошелька", + "cancel": "отмена", + "choose": "выбрать", + "close": "закрыть", + "continue": "продолжить", + "description": "описание", + "edit": "редактировать", + "error": "произошла ошибка", + "last_height": "последняя высота", + "loading": "загрузка...", + "logout": "выйти", + "minting_status": "статус майнинга", + "payment_notification": "уведомление о платеже", + "price": "цена", + "q_mail": "q-mail", + "save": "сохранить", + "settings": "настройки", + "supply": "предложение", + "title": "заголовок", + "tutorial": "руководство", + "your_account": "ваш аккаунт", + "user_lookup": "поиск пользователя", + "wallet": "кошелек", + "wallet_other": "кошельки", + "welcome": "добро пожаловать" +} diff --git a/public/locales/ru/tutorial.json b/public/locales/ru/tutorial.json new file mode 100644 index 0000000..ca669c5 --- /dev/null +++ b/public/locales/ru/tutorial.json @@ -0,0 +1,21 @@ +{ + "1_getting_started": "1. Начало работы", + "2_overview": "2. Обзор", + "3_groups": "3. Группы Qortal", + "4_obtain_qort": "4. Получение QORT", + "account_creation": "создание аккаунта", + "important_info": "важная информация!", + "apps": { + "dashboard": "1. Панель приложений", + "navigation": "2. Навигация по приложениям" + }, + "initial": { + "6_qort": "Иметь не менее 6 QORT в кошельке", + "explore": "исследовать", + "general_chat": "общий чат", + "getting_started": "начало работы", + "register_name": "зарегистрировать имя", + "see_apps": "посмотреть приложения", + "trade_qort": "обмен QORT" + } +} From b785cdac4d1375091bed48061e71e8fb32bd3dd4 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Tue, 22 Apr 2025 00:22:31 +0200 Subject: [PATCH 226/717] Set to lowercase --- public/locales/de/tutorial.json | 4 ++-- public/locales/en/tutorial.json | 2 +- public/locales/es/tutorial.json | 2 +- public/locales/fr/tutorial.json | 2 +- public/locales/ru/tutorial.json | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/public/locales/de/tutorial.json b/public/locales/de/tutorial.json index f789e2b..14a7021 100644 --- a/public/locales/de/tutorial.json +++ b/public/locales/de/tutorial.json @@ -10,12 +10,12 @@ "navigation": "2. App-Navigation" }, "initial": { - "6_qort": "Mindestens 6 QORT im Wallet haben", + "6_qort": "mindestens 6 QORT im Wallet haben", "explore": "erkunden", "general_chat": "allgemeiner Chat", "getting_started": "erste Schritte", "register_name": "einen Namen registrieren", - "see_apps": "Apps ansehen", + "see_apps": "apps ansehen", "trade_qort": "QORT handeln" } } diff --git a/public/locales/en/tutorial.json b/public/locales/en/tutorial.json index f022388..08c7628 100644 --- a/public/locales/en/tutorial.json +++ b/public/locales/en/tutorial.json @@ -10,7 +10,7 @@ "navigation": "2. Apps Navigation" }, "initial": { - "6_qort": "Have at least 6 QORT in your wallet", + "6_qort": "have at least 6 QORT in your wallet", "explore": "explore", "general_chat": "general chat", "getting_started": "getting started", diff --git a/public/locales/es/tutorial.json b/public/locales/es/tutorial.json index 8b89781..c60e829 100644 --- a/public/locales/es/tutorial.json +++ b/public/locales/es/tutorial.json @@ -10,7 +10,7 @@ "navigation": "2. Navegación de aplicaciones" }, "initial": { - "6_qort": "Tener al menos 6 QORT en tu monedero", + "6_qort": "tener al menos 6 QORT en tu monedero", "explore": "explorar", "general_chat": "chat general", "getting_started": "comenzando", diff --git a/public/locales/fr/tutorial.json b/public/locales/fr/tutorial.json index 13a33cd..0d3ef04 100644 --- a/public/locales/fr/tutorial.json +++ b/public/locales/fr/tutorial.json @@ -10,7 +10,7 @@ "navigation": "2. Navigation des applications" }, "initial": { - "6_qort": "Avoir au moins 6 QORT dans votre portefeuille", + "6_qort": "avoir au moins 6 QORT dans votre portefeuille", "explore": "explorer", "general_chat": "chat général", "getting_started": "démarrer", diff --git a/public/locales/ru/tutorial.json b/public/locales/ru/tutorial.json index ca669c5..405fdd0 100644 --- a/public/locales/ru/tutorial.json +++ b/public/locales/ru/tutorial.json @@ -10,7 +10,7 @@ "navigation": "2. Навигация по приложениям" }, "initial": { - "6_qort": "Иметь не менее 6 QORT в кошельке", + "6_qort": "иметь не менее 6 QORT в кошельке", "explore": "исследовать", "general_chat": "общий чат", "getting_started": "начало работы", From 284a8d01f9b4a0adbae3f22e86f6a32eb31e0bd7 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Tue, 22 Apr 2025 00:22:39 +0200 Subject: [PATCH 227/717] Set to lowercase --- public/locales/it/tutorial.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/locales/it/tutorial.json b/public/locales/it/tutorial.json index 08fb1df..27a270c 100644 --- a/public/locales/it/tutorial.json +++ b/public/locales/it/tutorial.json @@ -10,10 +10,10 @@ "navigation": "2. Navigation tra le app" }, "initial": { - "6_qort": "Avere almeno 6 QORT nel proprio wallet", + "6_qort": "avere almeno 6 QORT nel proprio wallet", "explore": "esplora", "general_chat": "chat generaleat", - "getting_started": "Come iniziare", + "getting_started": "come iniziare", "register_name": "registra un nome", "see_apps": "vedi le apps", "trade_qort": "scambia i QORT" From 247886ce856c63099d90c89697180a85c2e720e1 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 23 Apr 2025 08:55:50 +0200 Subject: [PATCH 228/717] Simplify selector with a unique icon --- src/App.tsx | 2 +- src/ExtStates/NotAuthenticated.tsx | 2 +- src/components/Apps/AppsHomeDesktop.tsx | 2 +- src/components/DesktopSideBar.tsx | 2 +- src/components/Theme/ThemeSelector.tsx | 76 +++++-------------------- 5 files changed, 17 insertions(+), 67 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 7d3a7c7..7ce75de 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3681,7 +3681,7 @@ function App() { /> )} - + ); } diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index a892414..017e7ae 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -1058,7 +1058,7 @@ export const NotAuthenticated = ({ /> - + ); }; diff --git a/src/components/Apps/AppsHomeDesktop.tsx b/src/components/Apps/AppsHomeDesktop.tsx index d9ad291..d525c26 100644 --- a/src/components/Apps/AppsHomeDesktop.tsx +++ b/src/components/Apps/AppsHomeDesktop.tsx @@ -157,7 +157,7 @@ export const AppsHomeDesktop = ({ /> - + ); }; diff --git a/src/components/DesktopSideBar.tsx b/src/components/DesktopSideBar.tsx index d1c8a7a..2244334 100644 --- a/src/components/DesktopSideBar.tsx +++ b/src/components/DesktopSideBar.tsx @@ -139,7 +139,7 @@ export const DesktopSideBar = ({ )} - + ); }; diff --git a/src/components/Theme/ThemeSelector.tsx b/src/components/Theme/ThemeSelector.tsx index dbf9d90..b82c072 100644 --- a/src/components/Theme/ThemeSelector.tsx +++ b/src/components/Theme/ThemeSelector.tsx @@ -1,75 +1,25 @@ import { useThemeContext } from './ThemeContext'; -import { styled, Switch } from '@mui/material'; +import { IconButton, Tooltip } from '@mui/material'; +import LightModeIcon from '@mui/icons-material/LightMode'; +import DarkModeIcon from '@mui/icons-material/DarkMode'; -const ThemeSwitch = styled(Switch)(({ theme }) => ({ - width: 62, - height: 34, - padding: 7, - '& .MuiSwitch-switchBase': { - margin: 1, - padding: 0, - transform: 'translateX(6px)', - '&.Mui-checked': { - color: '#fff', - transform: 'translateX(22px)', - '& .MuiSwitch-thumb:before': { - backgroundImage: `url('data:image/svg+xml;utf8,')`, - }, - '& + .MuiSwitch-track': { - opacity: 1, - backgroundColor: '#aab4be', - ...theme.applyStyles('dark', { - backgroundColor: '#8796A5', - }), - }, - }, - }, - '& .MuiSwitch-thumb': { - backgroundColor: '#fde402', - width: 32, - height: 32, - '&::before': { - content: "''", - position: 'absolute', - width: '100%', - height: '100%', - left: 0, - top: 0, - backgroundRepeat: 'no-repeat', - backgroundPosition: 'center', - backgroundImage: `url('data:image/svg+xml;utf8,')`, - }, - ...theme.applyStyles('dark', { - backgroundColor: '#003892', - }), - }, - '& .MuiSwitch-track': { - opacity: 1, - backgroundColor: '#aab4be', - borderRadius: 20 / 2, - ...theme.applyStyles('dark', { - backgroundColor: '#8796A5', - }), - }, -})); - -const ThemeSelector = ({ style }) => { +const ThemeSelector = () => { const { themeMode, toggleTheme } = useThemeContext(); + return (
    - + + + {themeMode === 'dark' ? : } + +
    ); }; From 104c4822bf2556532a18740f61d4edf28f3e5b9d Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 23 Apr 2025 09:05:07 +0200 Subject: [PATCH 229/717] Refactor icon position --- src/components/DesktopSideBar.tsx | 2 +- src/components/Theme/ThemeSelector.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/DesktopSideBar.tsx b/src/components/DesktopSideBar.tsx index 2244334..b25d206 100644 --- a/src/components/DesktopSideBar.tsx +++ b/src/components/DesktopSideBar.tsx @@ -118,7 +118,7 @@ export const DesktopSideBar = ({ - {/* */} + {isEnabledDevMode && ( { diff --git a/src/components/Theme/ThemeSelector.tsx b/src/components/Theme/ThemeSelector.tsx index b82c072..238ca00 100644 --- a/src/components/Theme/ThemeSelector.tsx +++ b/src/components/Theme/ThemeSelector.tsx @@ -9,10 +9,11 @@ const ThemeSelector = () => { return (
    From 0026b7c24997276c6c313e6e66e69b56c79668e0 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 23 Apr 2025 09:27:24 +0200 Subject: [PATCH 230/717] Save translations --- public/locales/en/core.json | 16 ++++++++++-- public/locales/it/tutorial.json | 2 +- src/components/Save/Save.tsx | 44 +++++++++++++++++++++++---------- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index 1f7bbab..01261da 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -7,7 +7,7 @@ "continue": "continue", "description": "description", "edit": "edit", - "error": "an error occurred", + "last_height": "last height", "loading": "loading...", "logout": "logout", @@ -15,7 +15,19 @@ "payment_notification": "payment notification", "price": "price", "q_mail": "q-mail", - "save": "save", + "result": { + "error": { + "generic": "an error occurred", + "save_qdn": "Unable to save to QDN" + }, + "success": { + "publish_qdn": "Sucessfully published to QDN" + } + }, + "save_options": { + "publish_qdn": "Would you like to publish your settings to QDN (encrypted) ?", + "save": "save" + }, "settings": "settings", "supply": "supply", "title": "title", diff --git a/public/locales/it/tutorial.json b/public/locales/it/tutorial.json index 27a270c..511b7cb 100644 --- a/public/locales/it/tutorial.json +++ b/public/locales/it/tutorial.json @@ -12,7 +12,7 @@ "initial": { "6_qort": "avere almeno 6 QORT nel proprio wallet", "explore": "esplora", - "general_chat": "chat generaleat", + "general_chat": "chat generale", "getting_started": "come iniziare", "register_name": "registra un nome", "see_apps": "vedi le apps", diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index c8196d8..9e35e66 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -1,6 +1,6 @@ -import React, { useContext, useEffect, useMemo, useState } from 'react'; +import { useContext, useEffect, useMemo, useState } from 'react'; import { useRecoilState, useSetRecoilState } from 'recoil'; -import isEqual from 'lodash/isEqual'; // Import deep comparison utility +import isEqual from 'lodash/isEqual'; // TODO Import deep comparison utility import { canSaveSettingToQdnAtom, hasSettingsChangedAtom, @@ -26,6 +26,7 @@ import { base64ToUint8Array, uint8ArrayToObject, } from '../../backgroundFunctions/encryption'; +import { useTranslation } from 'react-i18next'; export const handleImportClick = async () => { const fileInput = document.createElement('input'); @@ -77,6 +78,8 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { const [anchorEl, setAnchorEl] = useState(null); const { show } = useContext(MyContext); + const { t } = useTranslation(['core']); + const hasChanged = useMemo(() => { const newChanges = { sortablePinnedApps: pinnedApps.map((item) => { @@ -146,8 +149,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { const fee = await getFee('ARBITRARY'); await show({ - message: - 'Would you like to publish your settings to QDN (encrypted) ?', + message: t('core:save.publish_qnd', { postProcess: 'capitalize' }), publishFee: fee.fee + ' QORT', }); const response = await new Promise((res, rej) => { @@ -165,7 +167,10 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:result.error.generic', { postProcess: 'capitalize' }) + ); }); }); if (response?.identifier) { @@ -173,7 +178,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { setSettingsQdnLastUpdated(Date.now()); setInfoSnack({ type: 'success', - message: 'Sucessfully published to QDN', + message: t('core:result.success.publish_qdn', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); setAnchorEl(null); @@ -182,7 +189,11 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { } catch (error) { setInfoSnack({ type: 'error', - message: error?.message || 'Unable to save to QDN', + message: + error?.message || + t('core:result.error.save_qdn', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); } finally { @@ -214,7 +225,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { {isDesktop ? ( { )} + { {isUsingImportExportSettings && ( { }} > You are using the export/import way of saving settings. - + {' '} + // TODO: translate @@ -338,8 +341,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - You need a registered Qortal name to save your pinned apps to - QDN. + {t('core:save_options.register_name', { + postProcess: 'capitalize', + })} ) : ( @@ -358,8 +362,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - You have unsaved changes to your pinned apps. Save them to - QDN. + {t('core:save_options.unsaved_changes', { + postProcess: 'capitalize', + })} @@ -381,7 +386,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { onClick={saveToQdn} variant="contained" > - Save to QDN + {t('core:save_options.save_qdn', { + postProcess: 'capitalize', + })} {!isNaN(settingsQdnLastUpdated) && @@ -392,8 +399,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - Don't like your current local changes? Would you - like to reset to your saved QDN pinned apps? + {t('core:save_options.reset_qdn', { + postProcess: 'capitalize', + })} { }, }} > - Revert to QDN + {t('core:save_options.revert_qdn', { + postProcess: 'capitalize', + })} )} @@ -425,8 +435,10 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - Don't like your current local changes? Would you - like to reset to the default pinned apps? + {' '} + {t('core:save_options.reset_pinned', { + postProcess: 'capitalize', + })} { onClick={revertChanges} variant="contained" > - Revert to default + {t('core:save_options.revert_default', { + postProcess: 'capitalize', + })} )} @@ -456,8 +470,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - The app was unable to download your existing QDN-saved - pinned apps. Would you like to overwrite those changes? + {t('core:save_options.overwrite_changes', { + postProcess: 'capitalize', + })} { }, }} > - Overwrite to QDN + {t('core:save_options.overwrite_qdn', { + postProcess: 'capitalize', + })} )} @@ -495,7 +512,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - You currently do not have any changes to your pinned apps + {t('core:save_options.no_pinned_changes', { + postProcess: 'capitalize', + })} )} @@ -550,7 +569,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { } }} > - Import + {t('core:import', { + postProcess: 'capitalize', + })} { } }} > - Export + {t('core:export', { + postProcess: 'capitalize', + })} From d796a3f7c025205f70e0fd7040207d9b22a893e7 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 23 Apr 2025 20:49:30 +0200 Subject: [PATCH 236/717] Add comment TODO, format code and remove unused import --- src/components/Group/AddGroup.tsx | 2 +- src/components/Group/AddGroupList.tsx | 2 +- src/components/Group/BlockedUsersModal.tsx | 2 +- src/components/Group/Forum/DisplayHtml.tsx | 34 +- src/components/Group/Forum/GroupMail.tsx | 18 +- src/components/Group/Forum/Mail-styles.ts | 728 +++++++++--------- src/components/Group/Forum/NewThread.tsx | 2 +- src/components/Group/Forum/ReadOnlySlate.tsx | 87 +-- .../Group/Forum/ShowMessageWithoutModal.tsx | 300 ++++---- src/components/Group/Forum/TextEditor.tsx | 33 +- src/components/Group/Forum/Thread.tsx | 3 +- src/components/Group/Group.tsx | 75 +- src/components/Group/GroupInvites.tsx | 2 +- src/components/Group/GroupJoinRequests.tsx | 3 +- src/components/Group/GroupMenu.tsx | 274 ++++--- src/components/Group/InviteMember.tsx | 12 +- src/components/Group/ListOfBans.tsx | 138 ++-- .../Group/ListOfGroupPromotions.tsx | 2 +- src/components/Group/ListOfInvites.tsx | 145 ++-- src/components/Group/ListOfJoinRequests.tsx | 161 ++-- src/components/Group/ListOfMembers.tsx | 183 ++--- .../Group/ListOfThreadPostsWatched.tsx | 2 +- src/components/Group/ManageMembers.tsx | 2 +- src/components/Group/QMailMessages.tsx | 2 +- src/components/Group/Settings.tsx | 2 +- src/components/Group/UserListOfInvites.tsx | 434 ++++++----- src/components/Group/WalletsAppWrapper.tsx | 2 +- src/components/Group/WebsocketActive.tsx | 82 +- src/components/Group/useBlockUsers.tsx | 316 ++++---- src/components/Group/useHandleUserInfo.tsx | 40 +- src/components/Home/NewUsersCTA.tsx | 58 +- src/components/Minting/Minting.tsx | 3 +- 32 files changed, 1689 insertions(+), 1460 deletions(-) diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 470cd3f..91552cb 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -91,7 +91,7 @@ export const AddGroup = ({ address, open, setOpen }) => { if (!name) throw new Error('Please provide a name'); if (!description) throw new Error('Please provide a description'); - const fee = await getFee('CREATE_GROUP'); + const fee = await getFee('CREATE_GROUP'); // TODO translate await show({ message: 'Would you like to perform an CREATE_GROUP transaction?', publishFee: fee.fee + ' QORT', diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index 433d0e4..b80cc34 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -101,7 +101,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const handleJoinGroup = async (group, isOpen) => { try { const groupId = group.groupId; - const fee = await getFee('JOIN_GROUP'); + const fee = await getFee('JOIN_GROUP'); // TODO translate await show({ message: 'Would you like to perform an JOIN_GROUP transaction?', publishFee: fee.fee + ' QORT', diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index b2d58e7..80d9c4c 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -129,7 +129,7 @@ export const BlockedUsersModal = () => { executeEvent('updateChatMessagesWithBlocks', true); } } catch (error) { - setOpenSnackGlobal(true); + setOpenSnackGlobal(true); // TODO translate setInfoSnackCustom({ type: 'error', message: error?.message || 'Unable to block user', diff --git a/src/components/Group/Forum/DisplayHtml.tsx b/src/components/Group/Forum/DisplayHtml.tsx index 5bae4a7..786672a 100644 --- a/src/components/Group/Forum/DisplayHtml.tsx +++ b/src/components/Group/Forum/DisplayHtml.tsx @@ -1,21 +1,20 @@ -import { useMemo } from "react"; -import DOMPurify from "dompurify"; -import "react-quill/dist/quill.snow.css"; -import "react-quill/dist/quill.core.css"; -import "react-quill/dist/quill.bubble.css"; -import { Box, styled } from "@mui/material"; -import { convertQortalLinks } from "../../../utils/qortalLink"; - +import { useMemo } from 'react'; +import DOMPurify from 'dompurify'; +import 'react-quill/dist/quill.snow.css'; +import 'react-quill/dist/quill.core.css'; +import 'react-quill/dist/quill.bubble.css'; +import { Box, styled } from '@mui/material'; +import { convertQortalLinks } from '../../../utils/qortalLink'; const CrowdfundInlineContent = styled(Box)(({ theme }) => ({ - display: "flex", - fontFamily: "Mulish", - fontSize: "19px", - fontWeight: 400, - letterSpacing: 0, - color: theme.palette.text.primary, - width: '100%' - })); + display: 'flex', + fontFamily: 'Mulish', + fontSize: '19px', + fontWeight: 400, + letterSpacing: 0, + color: theme.palette.text.primary, + width: '100%', +})); export const DisplayHtml = ({ html, textColor }: any) => { const cleanContent = useMemo(() => { @@ -29,6 +28,7 @@ export const DisplayHtml = ({ html, textColor }: any) => { }, [html]); if (!cleanContent) return null; + return (
    { style={{ color: textColor || 'white', fontWeight: 400, - fontSize: '16px' + fontSize: '16px', }} dangerouslySetInnerHTML={{ __html: cleanContent }} /> diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index 544634b..3c8972f 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -45,7 +45,6 @@ import { formatDate, formatTimestamp } from '../../../utils/time'; import LazyLoad from '../../../common/LazyLoad'; import { delay } from '../../../utils/helpers'; import { NewThread } from './NewThread'; -import { getBaseApi } from '../../../background'; import { decryptPublishes, getTempPublish, @@ -55,15 +54,11 @@ import CheckSVG from '../../../assets/svgs/Check.svg'; import SortSVG from '../../../assets/svgs/Sort.svg'; import ArrowDownSVG from '../../../assets/svgs/ArrowDown.svg'; import { LoadingSnackbar } from '../../Snackbar/LoadingSnackbar'; -import { - executeEvent, - subscribeToEvent, - unsubscribeFromEvent, -} from '../../../utils/events'; +import { executeEvent } from '../../../utils/events'; import RefreshIcon from '@mui/icons-material/Refresh'; import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App'; -import { WrapperUserAction } from '../../WrapperUserAction'; import { addDataPublishesFunc, getDataPublishesFunc } from '../Group'; + const filterOptions = ['Recently active', 'Newest', 'Oldest']; export const threadIdentifier = 'DOCUMENT'; @@ -183,7 +178,7 @@ export const GroupMail = ({ }); }); } catch (error) { - } finally { + console.log(error); } }; @@ -266,7 +261,6 @@ export const GroupMail = ({ } else { sorted = fullArrayMsg.sort((a: any, b: any) => a.created - b.created); } - setAllThreads(sorted); } catch (error) { console.log({ error }); @@ -376,6 +370,7 @@ export const GroupMail = ({ ); setRecentThreads(sorted); } catch (error) { + console.log(error); } finally { setIsLoading(false); // dispatch(setIsLoadingCustom(null)); @@ -444,7 +439,6 @@ export const GroupMail = ({ } } } - setMembers(members); } catch (error) { console.log({ error }); @@ -754,7 +748,9 @@ export const GroupMail = ({ > {thread?.threadData?.title} + + {filterMode === 'Recently active' && (
    Last page diff --git a/src/components/Group/Forum/Mail-styles.ts b/src/components/Group/Forum/Mail-styles.ts index 534304d..368e4cb 100644 --- a/src/components/Group/Forum/Mail-styles.ts +++ b/src/components/Group/Forum/Mail-styles.ts @@ -1,52 +1,47 @@ -import { - AppBar, - Button, - Toolbar, - Typography, - Box, - TextField, -} from "@mui/material"; -import { styled } from "@mui/system"; +import { Typography, Box, TextField } from '@mui/material'; +import { styled } from '@mui/system'; export const InstanceContainer = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - width: "100%", - backgroundColor: "var(--color-instance)", - height: "59px", + display: 'flex', + alignItems: 'center', + width: '100%', + backgroundColor: 'var(--color-instance)', + height: '59px', flexShrink: 0, - justifyContent: "space-between", + justifyContent: 'space-between', })); + export const MailContainer = styled(Box)(({ theme }) => ({ - display: "flex", - flexDirection: "column", - width: "100%", - height: "calc(100vh - 78px)", - overflow: "hidden", + display: 'flex', + flexDirection: 'column', + width: '100%', + height: 'calc(100vh - 78px)', + overflow: 'hidden', })); export const MailBody = styled(Box)(({ theme }) => ({ - display: "flex", - flexDirection: "row", - width: "100%", - height: "calc(100% - 59px)", - // overflow: 'auto !important' + display: 'flex', + flexDirection: 'row', + width: '100%', + height: 'calc(100% - 59px)', })); + export const MailBodyInner = styled(Box)(({ theme }) => ({ - display: "flex", - flexDirection: "column", - width: "50%", - height: "100%", + display: 'flex', + flexDirection: 'column', + width: '50%', + height: '100%', })); + export const MailBodyInnerHeader = styled(Box)(({ theme }) => ({ - display: "flex", - width: "100%", - height: "25px", - marginTop: "50px", - marginBottom: "35px", - justifyContent: "center", - alignItems: "center", - gap: "11px", + display: 'flex', + width: '100%', + height: '25px', + marginTop: '50px', + marginBottom: '35px', + justifyContent: 'center', + alignItems: 'center', + gap: '11px', })); export const MailBodyInnerScroll = styled(Box)` @@ -84,163 +79,174 @@ export const MailBodyInnerScroll = styled(Box)` `; export const ComposeContainer = styled(Box)(({ theme }) => ({ - display: "flex", - width: "150px", - alignItems: "center", - gap: "7px", - height: "100%", - cursor: "pointer", - transition: "0.2s background-color", - justifyContent: "center", - "&:hover": { - backgroundColor: "rgba(67, 68, 72, 1)", + display: 'flex', + width: '150px', + alignItems: 'center', + gap: '7px', + height: '100%', + cursor: 'pointer', + transition: '0.2s background-color', + justifyContent: 'center', + '&:hover': { + backgroundColor: 'rgba(67, 68, 72, 1)', }, })); + export const ComposeContainerBlank = styled(Box)(({ theme }) => ({ - display: "flex", - width: "150px", - alignItems: "center", - gap: "7px", - height: "100%", + display: 'flex', + width: '150px', + alignItems: 'center', + gap: '7px', + height: '100%', })); + export const ComposeP = styled(Typography)(({ theme }) => ({ - fontSize: "15px", + fontSize: '15px', fontWeight: 500, })); -export const ComposeIcon = styled("img")({ - width: "auto", - height: "auto", - userSelect: "none", - objectFit: "contain", - cursor: "pointer", -}); -export const ArrowDownIcon = styled("img")({ - width: "auto", - height: "auto", - userSelect: "none", - objectFit: "contain", - cursor: "pointer", -}); -export const MailIconImg = styled("img")({ - width: "auto", - height: "auto", - userSelect: "none", - objectFit: "contain", +export const ComposeIcon = styled('img')({ + width: 'auto', + height: 'auto', + userSelect: 'none', + objectFit: 'contain', + cursor: 'pointer', }); -export const MailMessageRowInfoImg = styled("img")({ - width: "auto", - height: "auto", - userSelect: "none", - objectFit: "contain", +export const ArrowDownIcon = styled('img')({ + width: 'auto', + height: 'auto', + userSelect: 'none', + objectFit: 'contain', + cursor: 'pointer', +}); + +export const MailIconImg = styled('img')({ + width: 'auto', + height: 'auto', + userSelect: 'none', + objectFit: 'contain', +}); + +export const MailMessageRowInfoImg = styled('img')({ + width: 'auto', + height: 'auto', + userSelect: 'none', + objectFit: 'contain', }); export const SelectInstanceContainer = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - gap: "17px", + display: 'flex', + alignItems: 'center', + gap: '17px', })); + export const SelectInstanceContainerInner = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - gap: "3px", - cursor: "pointer", - padding: "8px", - transition: "all 0.2s", - "&:hover": { - borderRadius: "8px", - background: "#434448", + display: 'flex', + alignItems: 'center', + gap: '3px', + cursor: 'pointer', + padding: '8px', + transition: 'all 0.2s', + '&:hover': { + borderRadius: '8px', + background: '#434448', }, })); + export const SelectInstanceContainerFilterInner = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - gap: "3px", - cursor: "pointer", - padding: "8px", - transition: "all 0.2s" + display: 'flex', + alignItems: 'center', + gap: '3px', + cursor: 'pointer', + padding: '8px', + transition: 'all 0.2s', })); - export const InstanceLabel = styled(Typography)(({ theme }) => ({ - fontSize: "16px", + fontSize: '16px', fontWeight: 500, - color: "#FFFFFF33", + color: '#FFFFFF33', })); export const InstanceP = styled(Typography)(({ theme }) => ({ - fontSize: "16px", + fontSize: '16px', fontWeight: 500, })); export const MailMessageRowContainer = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - cursor: "pointer", - justifyContent: "space-between", - borderRadius: "56px 5px 10px 56px", - paddingRight: "15px", - transition: "background 0.2s", - gap: "10px", - "&:hover": { - background: "#434448", + display: 'flex', + alignItems: 'center', + cursor: 'pointer', + justifyContent: 'space-between', + borderRadius: '56px 5px 10px 56px', + paddingRight: '15px', + transition: 'background 0.2s', + gap: '10px', + '&:hover': { + background: '#434448', }, })); + export const MailMessageRowProfile = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - cursor: "pointer", - justifyContent: "flex-start", - gap: "10px", - width: "50%", - overflow: "hidden", + display: 'flex', + alignItems: 'center', + cursor: 'pointer', + justifyContent: 'flex-start', + gap: '10px', + width: '50%', + overflow: 'hidden', })); + export const MailMessageRowInfo = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - cursor: "pointer", - justifyContent: "flex-start", - gap: "7px", - width: "50%", + display: 'flex', + alignItems: 'center', + cursor: 'pointer', + justifyContent: 'flex-start', + gap: '7px', + width: '50%', })); + export const MailMessageRowInfoStatusNotDecrypted = styled(Typography)( ({ theme }) => ({ - fontSize: "16px", + fontSize: '16px', fontWeight: 900, - textTransform: "uppercase", - paddingTop: "2px", + textTransform: 'uppercase', + paddingTop: '2px', }) ); + export const MailMessageRowInfoStatusRead = styled(Typography)(({ theme }) => ({ - fontSize: "16px", + fontSize: '16px', fontWeight: 300, })); export const MessageExtraInfo = styled(Box)(({ theme }) => ({ - display: "flex", - flexDirection: "column", - gap: "2px", - overflow: "hidden", + display: 'flex', + flexDirection: 'column', + gap: '2px', + overflow: 'hidden', })); + export const MessageExtraName = styled(Typography)(({ theme }) => ({ - fontSize: "16px", + fontSize: '16px', fontWeight: 900, - whiteSpace: "nowrap", - textOverflow: "ellipsis", - overflow: "hidden", + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + overflow: 'hidden', })); + export const MessageExtraDate = styled(Typography)(({ theme }) => ({ - fontSize: "15px", + fontSize: '15px', fontWeight: 500, })); export const MessagesContainer = styled(Box)(({ theme }) => ({ - width: "460px", - maxWidth: "90%", - display: "flex", - flexDirection: "column", - gap: "12px", + width: '460px', + maxWidth: '90%', + display: 'flex', + flexDirection: 'column', + gap: '12px', })); export const InstanceListParent = styled(Box)` @@ -254,18 +260,21 @@ export const InstanceListParent = styled(Box)` background-color: var(--color-instance-popover-bg); border: 1px solid rgba(0, 0, 0, 0.1); `; + export const InstanceListHeader = styled(Box)` display: flex; flex-direction: column; width: 100%; background-color: var(--color-instance-popover-bg); `; + export const InstanceFooter = styled(Box)` display: flex; flex-direction: column; width: 100%; flex-shrink: 0; `; + export const InstanceListContainer = styled(Box)` width: 100%; display: flex; @@ -301,126 +310,132 @@ export const InstanceListContainer = styled(Box)` } } `; + export const InstanceListContainerRowLabelContainer = styled(Box)( ({ theme }) => ({ - width: "100%", - display: "flex", - alignItems: "center", - gap: "10px", - height: "50px", + width: '100%', + display: 'flex', + alignItems: 'center', + gap: '10px', + height: '50px', }) ); + export const InstanceListContainerRow = styled(Box)(({ theme }) => ({ - width: "100%", - display: "flex", - alignItems: "center", - gap: "10px", - height: "50px", - cursor: "pointer", - transition: "0.2s background", - "&:hover": { - background: "rgba(67, 68, 72, 1)", + width: '100%', + display: 'flex', + alignItems: 'center', + gap: '10px', + height: '50px', + cursor: 'pointer', + transition: '0.2s background', + '&:hover': { + background: 'rgba(67, 68, 72, 1)', }, flexShrink: 0, })); + export const InstanceListContainerRowCheck = styled(Box)(({ theme }) => ({ - width: "47px", - display: "flex", - alignItems: "center", - justifyContent: "center", + width: '47px', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', })); + export const InstanceListContainerRowMain = styled(Box)(({ theme }) => ({ - display: "flex", - justifyContent: "space-between", - width: "100%", - alignItems: "center", - paddingRight: "30px", - overflow: "hidden", + display: 'flex', + justifyContent: 'space-between', + width: '100%', + alignItems: 'center', + paddingRight: '30px', + overflow: 'hidden', })); + export const CloseParent = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - gap: "20px", + display: 'flex', + alignItems: 'center', + gap: '20px', })); + export const InstanceListContainerRowMainP = styled(Typography)( ({ theme }) => ({ fontWeight: 500, - fontSize: "16px", - textOverflow: "ellipsis", - overflow: "hidden", + fontSize: '16px', + textOverflow: 'ellipsis', + overflow: 'hidden', }) ); -export const InstanceListContainerRowCheckIcon = styled("img")({ - width: "auto", - height: "auto", - userSelect: "none", - objectFit: "contain", +export const InstanceListContainerRowCheckIcon = styled('img')({ + width: 'auto', + height: 'auto', + userSelect: 'none', + objectFit: 'contain', }); -export const InstanceListContainerRowGroupIcon = styled("img")({ - width: "auto", - height: "auto", - userSelect: "none", - objectFit: "contain", +export const InstanceListContainerRowGroupIcon = styled('img')({ + width: 'auto', + height: 'auto', + userSelect: 'none', + objectFit: 'contain', }); export const TypeInAliasTextfield = styled(TextField)({ - width: "340px", // Adjust the width as needed - borderRadius: "5px", - backgroundColor: "rgba(30, 30, 32, 1)", - border: "none", - outline: "none", + width: '340px', // Adjust the width as needed + borderRadius: '5px', + backgroundColor: 'rgba(30, 30, 32, 1)', + border: 'none', + outline: 'none', input: { fontSize: 16, - color: "white", - "&::placeholder": { + color: 'white', + '&::placeholder': { fontSize: 16, - color: "rgba(255, 255, 255, 0.2)", + color: 'rgba(255, 255, 255, 0.2)', }, - border: "none", - outline: "none", - padding: "10px", + border: 'none', + outline: 'none', + padding: '10px', }, - "& .MuiOutlinedInput-root": { - "& fieldset": { - border: "none", + '& .MuiOutlinedInput-root': { + '& fieldset': { + border: 'none', }, - "&:hover fieldset": { - border: "none", + '&:hover fieldset': { + border: 'none', }, - "&.Mui-focused fieldset": { - border: "none", + '&.Mui-focused fieldset': { + border: 'none', }, }, - "& .MuiInput-underline:before": { - borderBottom: "none", + '& .MuiInput-underline:before': { + borderBottom: 'none', }, - "& .MuiInput-underline:hover:not(.Mui-disabled):before": { - borderBottom: "none", + '& .MuiInput-underline:hover:not(.Mui-disabled):before': { + borderBottom: 'none', }, - "& .MuiInput-underline:after": { - borderBottom: "none", + '& .MuiInput-underline:after': { + borderBottom: 'none', }, }); -export const NewMessageCloseImg = styled("img")({ - width: "auto", - height: "auto", - userSelect: "none", - objectFit: "contain", - cursor: "pointer", +export const NewMessageCloseImg = styled('img')({ + width: 'auto', + height: 'auto', + userSelect: 'none', + objectFit: 'contain', + cursor: 'pointer', }); export const NewMessageHeaderP = styled(Typography)(({ theme }) => ({ - fontSize: "18px", + fontSize: '18px', fontWeight: 600, })); export const NewMessageInputRow = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - justifyContent: "space-between", - borderBottom: "3px solid rgba(237, 239, 241, 1)", - width: "100%", - paddingBottom: "6px", + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + borderBottom: '3px solid rgba(237, 239, 241, 1)', + width: '100%', + paddingBottom: '6px', })); export const NewMessageInputLabelP = styled(Typography)` color: rgba(84, 84, 84, 0.7); @@ -444,25 +459,25 @@ export const AliasLabelP = styled(Typography)` } `; export const NewMessageAliasContainer = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - gap: "12px", + display: 'flex', + alignItems: 'center', + gap: '12px', })); export const AttachmentContainer = styled(Box)(({ theme }) => ({ - height: "36px", - width: "100%", - display: "flex", - alignItems: "center", + height: '36px', + width: '100%', + display: 'flex', + alignItems: 'center', })); -export const NewMessageAttachmentImg = styled("img")({ - width: "auto", - height: "auto", - userSelect: "none", - objectFit: "contain", - cursor: "pointer", - padding: "10px", - border: "1px dashed #646464", +export const NewMessageAttachmentImg = styled('img')({ + width: 'auto', + height: 'auto', + userSelect: 'none', + objectFit: 'contain', + cursor: 'pointer', + padding: '10px', + border: '1px dashed #646464', }); export const NewMessageSendButton = styled(Box)` @@ -582,34 +597,36 @@ export const ShowMessageButtonP = styled(Typography)` color: white; `; -export const ShowMessageButtonImg = styled("img")({ - width: "auto", - height: "auto", - userSelect: "none", - objectFit: "contain", - cursor: "pointer", +export const ShowMessageButtonImg = styled('img')({ + width: 'auto', + height: 'auto', + userSelect: 'none', + objectFit: 'contain', + cursor: 'pointer', }); -export const MailAttachmentImg = styled("img")({ - width: "auto", - height: "auto", - userSelect: "none", - objectFit: "contain", +export const MailAttachmentImg = styled('img')({ + width: 'auto', + height: 'auto', + userSelect: 'none', + objectFit: 'contain', }); -export const AliasAvatarImg = styled("img")({ - width: "auto", - height: "auto", - userSelect: "none", - objectFit: "contain", + +export const AliasAvatarImg = styled('img')({ + width: 'auto', + height: 'auto', + userSelect: 'none', + objectFit: 'contain', }); -export const MoreImg = styled("img")({ - width: "auto", - height: "auto", - userSelect: "none", - objectFit: "contain", - transition: "0.2s all", - "&:hover": { - transform: "scale(1.3)", + +export const MoreImg = styled('img')({ + width: 'auto', + height: 'auto', + userSelect: 'none', + objectFit: 'contain', + transition: '0.2s all', + '&:hover': { + transform: 'scale(1.3)', }, }); @@ -625,17 +642,19 @@ export const MoreP = styled(Typography)` letter-spacing: -0.16px; white-space: nowrap; `; + export const ThreadContainerFullWidth = styled(Box)(({ theme }) => ({ - display: "flex", - flexDirection: "column", - width: "100%", - alignItems: "center", + display: 'flex', + flexDirection: 'column', + width: '100%', + alignItems: 'center', })); + export const ThreadContainer = styled(Box)(({ theme }) => ({ - display: "flex", - flexDirection: "column", - width: "100%", - maxWidth: "95%", + display: 'flex', + flexDirection: 'column', + width: '100%', + maxWidth: '95%', })); export const GroupNameP = styled(Typography)` @@ -648,130 +667,131 @@ export const GroupNameP = styled(Typography)` `; export const AllThreadP = styled(Typography)` - color: #FFF; -font-size: 20px; -font-style: normal; -font-weight: 400; -line-height: 120%; /* 24px */ -letter-spacing: 0.15px; + color: #fff; + font-size: 20px; + font-style: normal; + font-weight: 400; + line-height: 120%; /* 24px */ + letter-spacing: 0.15px; `; export const SingleThreadParent = styled(Box)` -border-radius: 35px 4px 4px 35px; -position: relative; -background: #434448; -display: flex; -padding: 13px; -cursor: pointer; -margin-bottom: 5px; -height: 76px; -align-items:center; -transition: 0.2s all; -&:hover { -background: rgba(255, 255, 255, 0.20) -} + border-radius: 35px 4px 4px 35px; + position: relative; + background: #434448; + display: flex; + padding: 13px; + cursor: pointer; + margin-bottom: 5px; + height: 76px; + align-items: center; + transition: 0.2s all; + &:hover { + background: rgba(255, 255, 255, 0.2); + } `; -export const SingleTheadMessageParent = styled(Box)` -border-radius: 35px 4px 4px 35px; -background: #434448; -display: flex; -padding: 13px; -cursor: pointer; -margin-bottom: 5px; -height: 76px; -align-items:center; +export const SingleTheadMessageParent = styled(Box)` + border-radius: 35px 4px 4px 35px; + background: #434448; + display: flex; + padding: 13px; + cursor: pointer; + margin-bottom: 5px; + height: 76px; + align-items: center; `; export const ThreadInfoColumn = styled(Box)(({ theme }) => ({ - display: "flex", - flexDirection: "column", - width: "170px", + display: 'flex', + flexDirection: 'column', + width: '170px', gap: '2px', marginLeft: '10px', height: '100%', - justifyContent: 'center' + justifyContent: 'center', })); - export const ThreadInfoColumnNameP = styled(Typography)` -color: #FFF; -font-family: Roboto; -font-size: 16px; -font-style: normal; -font-weight: 900; -line-height: normal; -white-space: nowrap; + color: #fff; + font-family: Roboto; + font-size: 16px; + font-style: normal; + font-weight: 900; + line-height: normal; + white-space: nowrap; text-overflow: ellipsis; overflow: hidden; `; + export const ThreadInfoColumnbyP = styled('span')` -color: rgba(255, 255, 255, 0.80); -font-family: Roboto; -font-size: 16px; -font-style: normal; -font-weight: 500; -line-height: normal; + color: rgba(255, 255, 255, 0.8); + font-family: Roboto; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: normal; `; export const ThreadInfoColumnTime = styled(Typography)` -color: rgba(255, 255, 255, 0.80); -font-family: Roboto; -font-size: 15px; -font-style: normal; -font-weight: 500; -line-height: normal; -` + color: rgba(255, 255, 255, 0.8); + font-family: Roboto; + font-size: 15px; + font-style: normal; + font-weight: 500; + line-height: normal; +`; + export const ThreadSingleTitle = styled(Typography)` -color: #FFF; -font-family: Roboto; -font-size: 23px; -font-style: normal; -font-weight: 700; -line-height: normal; -white-space: wrap; + color: #fff; + font-family: Roboto; + font-size: 23px; + font-style: normal; + font-weight: 700; + line-height: normal; + white-space: wrap; text-overflow: ellipsis; overflow: hidden; -` +`; + export const ThreadSingleLastMessageP = styled(Typography)` -color: #FFF; -font-family: Roboto; -font-size: 12px; -font-style: normal; -font-weight: 600; -line-height: normal; -` + color: #fff; + font-family: Roboto; + font-size: 12px; + font-style: normal; + font-weight: 600; + line-height: normal; +`; + export const ThreadSingleLastMessageSpanP = styled('span')` -color: #FFF; -font-family: Roboto; -font-size: 12px; -font-style: normal; -font-weight: 400; -line-height: normal; + color: #fff; + font-family: Roboto; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; `; export const GroupContainer = styled(Box)` -position: relative; - overflow: auto; - width: 100%; - - -` + position: relative; + overflow: auto; + width: 100%; +`; export const CloseContainer = styled(Box)(({ theme }) => ({ - display: "flex", - width: "50px", - overflow: "hidden", - alignItems: "center", - cursor: "pointer", - transition: "0.2s background-color", - justifyContent: "center", + display: 'flex', + width: '50px', + overflow: 'hidden', + alignItems: 'center', + cursor: 'pointer', + transition: '0.2s background-color', + justifyContent: 'center', position: 'absolute', top: '0px', right: '0px', height: '50px', borderRadius: '0px 12px 0px 0px', - "&:hover": { - backgroundColor: "rgba(162, 31, 31, 1)", + '&:hover': { + backgroundColor: 'rgba(162, 31, 31, 1)', }, -})); \ No newline at end of file +})); diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index dc987d9..d5c4384 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -191,7 +191,7 @@ export const NewThread = ({ } if (!groupInfo) { errorMsg = 'Cannot access group information'; - } + } // TODO translate // if (!description) missingFields.push('subject') if (missingFields.length > 0) { diff --git a/src/components/Group/Forum/ReadOnlySlate.tsx b/src/components/Group/Forum/ReadOnlySlate.tsx index d64cf9e..5724f92 100644 --- a/src/components/Group/Forum/ReadOnlySlate.tsx +++ b/src/components/Group/Forum/ReadOnlySlate.tsx @@ -1,18 +1,24 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { createEditor} from 'slate'; -import { withReact, Slate, Editable, RenderElementProps, RenderLeafProps } from 'slate-react'; +import { createEditor } from 'slate'; +import { + withReact, + Slate, + Editable, + RenderElementProps, + RenderLeafProps, +} from 'slate-react'; -type ExtendedRenderElementProps = RenderElementProps & { mode?: string } +type ExtendedRenderElementProps = RenderElementProps & { mode?: string }; export const renderElement = ({ attributes, children, element, - mode + mode, }: ExtendedRenderElementProps) => { switch (element.type) { case 'block-quote': - return
    {children}
    + return
    {children}
    ; case 'heading-2': return (

    {children}

    - ) + ); case 'heading-3': return (

    {children}

    - ) + ); case 'code-block': return (
               {children}
             
    - ) + ); case 'code-line': - return
    {children}
    + return
    {children}
    ; case 'link': return ( {children} - ) + ); default: return (

    {children}

    - ) + ); } -} - +}; export const renderLeaf = ({ attributes, children, leaf }: RenderLeafProps) => { - let el = children + let el = children; if (leaf.bold) { - el = {el} + el = {el}; } if (leaf.italic) { - el = {el} + el = {el}; } if (leaf.underline) { - el = {el} + el = {el}; } if (leaf.link) { @@ -81,39 +86,35 @@ export const renderLeaf = ({ attributes, children, leaf }: RenderLeafProps) => { {el} - ) + ); } - return {el} -} + return {el}; +}; interface ReadOnlySlateProps { - content: any - mode?: string + content: any; + mode?: string; } const ReadOnlySlate: React.FC = ({ content, mode }) => { - const [load, setLoad] = useState(false) - const editor = useMemo(() => withReact(createEditor()), []) - const value = useMemo(() => content, [content]) + const [load, setLoad] = useState(false); + const editor = useMemo(() => withReact(createEditor()), []); + const value = useMemo(() => content, [content]); - const performUpdate = useCallback(async()=> { - setLoad(true) - await new Promise((res)=> { + const performUpdate = useCallback(async () => { + setLoad(true); + await new Promise((res) => { setTimeout(() => { - res() + res(); }, 250); - }) - setLoad(false) - }, []) - useEffect(()=> { + }); + setLoad(false); + }, []); + useEffect(() => { + performUpdate(); + }, [value]); - - - - performUpdate() - }, [value]) - - if(load) return null + if (load) return null; return ( {}}> @@ -123,7 +124,7 @@ const ReadOnlySlate: React.FC = ({ content, mode }) => { renderLeaf={renderLeaf} /> - ) -} + ); +}; -export default ReadOnlySlate; \ No newline at end of file +export default ReadOnlySlate; diff --git a/src/components/Group/Forum/ShowMessageWithoutModal.tsx b/src/components/Group/Forum/ShowMessageWithoutModal.tsx index 9a079d6..7a4e594 100644 --- a/src/components/Group/Forum/ShowMessageWithoutModal.tsx +++ b/src/components/Group/Forum/ShowMessageWithoutModal.tsx @@ -1,8 +1,8 @@ -import React, { useState } from "react"; -import { Avatar, Box, IconButton } from "@mui/material"; -import DOMPurify from "dompurify"; +import { useState } from 'react'; +import { Avatar, Box, IconButton } from '@mui/material'; +import DOMPurify from 'dompurify'; import FormatQuoteIcon from '@mui/icons-material/FormatQuote'; -import MoreSVG from '../../../assets/svgs/More.svg' +import MoreSVG from '../../../assets/svgs/More.svg'; import { MoreImg, @@ -11,20 +11,18 @@ import { ThreadInfoColumn, ThreadInfoColumnNameP, ThreadInfoColumnTime, -} from "./Mail-styles"; -import { Spacer } from "../../../common/Spacer"; -import { DisplayHtml } from "./DisplayHtml"; -import { formatTimestampForum } from "../../../utils/time"; -import ReadOnlySlate from "./ReadOnlySlate"; -import { MessageDisplay } from "../../Chat/MessageDisplay"; -import { getBaseApi } from "../../../background"; -import { getBaseApiReact } from "../../../App"; -import { WrapperUserAction } from "../../WrapperUserAction"; +} from './Mail-styles'; +import { Spacer } from '../../../common/Spacer'; +import { formatTimestampForum } from '../../../utils/time'; +import ReadOnlySlate from './ReadOnlySlate'; +import { MessageDisplay } from '../../Chat/MessageDisplay'; +import { getBaseApiReact } from '../../../App'; +import { WrapperUserAction } from '../../WrapperUserAction'; export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { const [expandAttachments, setExpandAttachments] = useState(false); - let cleanHTML = ""; + let cleanHTML = ''; if (message?.htmlContent) { cleanHTML = DOMPurify.sanitize(message.htmlContent); } @@ -32,79 +30,94 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { return ( - - {message?.name?.charAt(0)} - + + + {message?.name?.charAt(0)} + + - - - {message?.name} + + {message?.name} {formatTimestampForum(message?.created)}
    - {message?.attachments?.length > 0 && ( - - {message?.attachments - .map((file: any, index: number) => { - const isFirst = index === 0 - return ( - + {message?.attachments?.length > 0 && ( + + {message?.attachments.map((file: any, index: number) => { + const isFirst = index === 0; + return ( - {/* + {/* { {file?.originalFilename || file?.filename} */} - {message?.attachments?.length > 1 && isFirst && ( - { - setExpandAttachments(prev => !prev); - }} - > - 1 && isFirst && ( + - - {expandAttachments ? 'hide' : `(${message?.attachments?.length - 1} more)`} - - - - )} + onClick={() => { + setExpandAttachments((prev) => !prev); + }} + > + + + {expandAttachments + ? 'hide' + : `(${message?.attachments?.length - 1} more)`} + + + )} + - - ); - }) - } - - )} - -
    + ); + })} +
    + )} +
    + + {message?.reply?.textContentV2 && ( <> - + - - {message?.reply?.name?.charAt(0)} - - {message?.reply?.name} - - - - - - + sx={{ + display: 'flex', + alignItems: 'flex-start', + gap: '10px', + }} + > + + {message?.reply?.name?.charAt(0)} + + + + {message?.reply?.name} + + + + + + - )} - + {message?.textContent && ( )} @@ -208,22 +230,18 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { {message?.htmlContent && (
    )} - - openNewPostWithQuote(message)} - + - - + openNewPostWithQuote(message)}> + + - - - ); }; diff --git a/src/components/Group/Forum/TextEditor.tsx b/src/components/Group/Forum/TextEditor.tsx index 874f2e3..ac26275 100644 --- a/src/components/Group/Forum/TextEditor.tsx +++ b/src/components/Group/Forum/TextEditor.tsx @@ -1,32 +1,33 @@ -import React from "react"; -import ReactQuill, { Quill } from "react-quill"; -import "react-quill/dist/quill.snow.css"; -import ImageResize from "quill-image-resize-module-react"; -import './texteditor.css' -Quill.register("modules/imageResize", ImageResize); +import ReactQuill, { Quill } from 'react-quill'; +import 'react-quill/dist/quill.snow.css'; +import ImageResize from 'quill-image-resize-module-react'; +import './texteditor.css'; + +Quill.register('modules/imageResize', ImageResize); const modules = { imageResize: { - parchment: Quill.import("parchment"), - modules: ["Resize", "DisplaySize"], + parchment: Quill.import('parchment'), + modules: ['Resize', 'DisplaySize'], }, toolbar: [ - ["bold", "italic", "underline", "strike"], // styled text - ["blockquote", "code-block"], // blocks + ['bold', 'italic', 'underline', 'strike'], // styled text + ['blockquote', 'code-block'], // blocks [{ header: 1 }, { header: 2 }], // custom button values - [{ list: "ordered" }, { list: "bullet" }], // lists - [{ script: "sub" }, { script: "super" }], // superscript/subscript - [{ indent: "-1" }, { indent: "+1" }], // outdent/indent - [{ direction: "rtl" }], // text direction - [{ size: ["small", false, "large", "huge"] }], // custom dropdown + [{ list: 'ordered' }, { list: 'bullet' }], // lists + [{ script: 'sub' }, { script: 'super' }], // superscript/subscript + [{ indent: '-1' }, { indent: '+1' }], // outdent/indent + [{ direction: 'rtl' }], // text direction + [{ size: ['small', false, 'large', 'huge'] }], // custom dropdown [{ header: [1, 2, 3, 4, 5, 6, false] }], // custom button values [{ color: [] }, { background: [] }], // dropdown with defaults [{ font: [] }], // font family [{ align: [] }], // text align - ["clean"], // remove formatting + ['clean'], // remove formatting // ["image"], // image ], }; + export const TextEditor = ({ inlineContent, setInlineContent }: any) => { return ( Previous diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 4573022..a4629ce 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -2218,7 +2218,8 @@ export const Group = ({ }} > No group selected - + {' '} + // TODO translate )} @@ -2235,39 +2236,39 @@ export const Group = ({ : '0px', }} > - - + - + + /> { Group Invites{' '} {groupsWithJoinRequests?.length > 0 && diff --git a/src/components/Group/GroupJoinRequests.tsx b/src/components/Group/GroupJoinRequests.tsx index 1847bd0..4bc6b21 100644 --- a/src/components/Group/GroupJoinRequests.tsx +++ b/src/components/Group/GroupJoinRequests.tsx @@ -82,6 +82,7 @@ export const GroupJoinRequests = ({ ); setGroupsWithJoinRequests(res); } catch (error) { + console.log(error); } finally { setLoading(false); } @@ -138,7 +139,7 @@ export const GroupJoinRequests = ({ Join Requests{' '} {filteredJoinRequests?.filter((group) => group?.data?.length > 0) diff --git a/src/components/Group/GroupMenu.tsx b/src/components/Group/GroupMenu.tsx index a44c480..0d81cd2 100644 --- a/src/components/Group/GroupMenu.tsx +++ b/src/components/Group/GroupMenu.tsx @@ -1,22 +1,27 @@ -import React, { useState } from "react"; +import { useState } from 'react'; import { Button, Menu, MenuItem, ListItemIcon, ListItemText, - Badge, Box, -} from "@mui/material"; -import ForumIcon from "@mui/icons-material/Forum"; -import GroupIcon from "@mui/icons-material/Group"; -import { ArrowDownIcon } from "../../assets/Icons/ArrowDownIcon"; -import { NotificationIcon2 } from "../../assets/Icons/NotificationIcon2"; -import { ChatIcon } from "../../assets/Icons/ChatIcon"; -import { ThreadsIcon } from "../../assets/Icons/ThreadsIcon"; -import { MembersIcon } from "../../assets/Icons/MembersIcon"; +} from '@mui/material'; +import { ArrowDownIcon } from '../../assets/Icons/ArrowDownIcon'; +import { NotificationIcon2 } from '../../assets/Icons/NotificationIcon2'; +import { ChatIcon } from '../../assets/Icons/ChatIcon'; +import { ThreadsIcon } from '../../assets/Icons/ThreadsIcon'; +import { MembersIcon } from '../../assets/Icons/MembersIcon'; -export const GroupMenu = ({ setGroupSection, groupSection, setOpenManageMembers, goToAnnouncements, goToChat, hasUnreadChat, hasUnreadAnnouncements }) => { +export const GroupMenu = ({ + setGroupSection, + groupSection, + setOpenManageMembers, + goToAnnouncements, + goToChat, + hasUnreadChat, + hasUnreadAnnouncements, +}) => { const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); @@ -31,170 +36,217 @@ export const GroupMenu = ({ setGroupSection, groupSection, setOpenManageMembers, return ( + { - goToChat() + goToChat(); handleClose(); }} > - - + + - + { - goToAnnouncements() + goToAnnouncements(); handleClose(); }} > - - + + - + { - setGroupSection("forum"); + setGroupSection('forum'); handleClose(); }} > - - - + + - + { - setOpenManageMembers(true) + setOpenManageMembers(true); handleClose(); }} > - - - + + - + diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx index 40b44ac..de3dc20 100644 --- a/src/components/Group/InviteMember.tsx +++ b/src/components/Group/InviteMember.tsx @@ -1,13 +1,6 @@ import { LoadingButton } from '@mui/lab'; -import { - Box, - Button, - Input, - MenuItem, - Select, - SelectChangeEvent, -} from '@mui/material'; -import React, { useState } from 'react'; +import { Box, Input, MenuItem, Select, SelectChangeEvent } from '@mui/material'; +import { useState } from 'react'; import { Spacer } from '../../common/Spacer'; import { Label } from './AddGroup'; import { getFee } from '../../background'; @@ -34,6 +27,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { }) .then((response) => { if (!response?.error) { + // TODO translate setInfoSnack({ type: 'success', message: `Successfully invited ${value}. It may take a couple of minutes for the changes to propagate`, diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index db4c6ba..850ce13 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -1,16 +1,31 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { Avatar, Box, Button, ListItem, ListItemAvatar, ListItemButton, ListItemText, Popover } from '@mui/material'; -import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtualized'; +import { useEffect, useRef, useState } from 'react'; +import { + Avatar, + Box, + ListItem, + ListItemAvatar, + ListItemButton, + ListItemText, + Popover, +} from '@mui/material'; +import { + AutoSizer, + CellMeasurer, + CellMeasurerCache, + List, +} from 'react-virtualized'; import { getNameInfo } from './Group'; -import { getBaseApi, getFee } from '../../background'; +import { getFee } from '../../background'; import { LoadingButton } from '@mui/lab'; import { getBaseApiReact } from '../../App'; export const getMemberInvites = async (groupNumber) => { - const response = await fetch(`${getBaseApiReact()}/groups/bans/${groupNumber}?limit=0`); + const response = await fetch( + `${getBaseApiReact()}/groups/bans/${groupNumber}?limit=0` + ); const groupData = await response.json(); return groupData; -} +}; const getNames = async (listOfMembers, includeNoNames) => { let members = []; @@ -20,14 +35,14 @@ const getNames = async (listOfMembers, includeNoNames) => { const name = await getNameInfo(member.offender); if (name) { members.push({ ...member, name }); - } else if(includeNoNames){ - members.push({ ...member, name: name || "" }); + } else if (includeNoNames) { + members.push({ ...member, name: name || '' }); } } } } return members; -} +}; const cache = new CellMeasurerCache({ fixedWidth: true, @@ -49,7 +64,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { } catch (error) { console.error(error); } - } + }; useEffect(() => { if (groupId) { @@ -67,33 +82,36 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { setOpenPopoverIndex(null); }; - const handleCancelBan = async (address)=> { + const handleCancelBan = async (address) => { try { - const fee = await getFee('CANCEL_GROUP_BAN') + // TODO translate + const fee = await getFee('CANCEL_GROUP_BAN'); await show({ - message: "Would you like to perform a CANCEL_GROUP_BAN transaction?" , - publishFee: fee.fee + ' QORT' - }) - setIsLoadingUnban(true) - new Promise((res, rej)=> { - window.sendMessage("cancelBan", { - groupId, - qortalAddress: address, - }) + message: 'Would you like to perform a CANCEL_GROUP_BAN transaction?', + publishFee: fee.fee + ' QORT', + }); + setIsLoadingUnban(true); + new Promise((res, rej) => { + window + .sendMessage('cancelBan', { + groupId, + qortalAddress: address, + }) .then((response) => { if (!response?.error) { res(response); setIsLoadingUnban(false); setInfoSnack({ - type: "success", - message: "Successfully unbanned user. It may take a couple of minutes for the changes to propagate", + type: 'success', + message: + 'Successfully unbanned user. It may take a couple of minutes for the changes to propagate', }); handlePopoverClose(); setOpenSnack(true); return; } setInfoSnack({ - type: "error", + type: 'error', message: response?.error, }); setOpenSnack(true); @@ -101,24 +119,22 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { }) .catch((error) => { setInfoSnack({ - type: "error", - message: error.message || "An error occurred", + type: 'error', + message: error.message || 'An error occurred', }); setOpenSnack(true); rej(error); }); - - }) + }); } catch (error) { - } finally { - setIsLoadingUnban(false) + setIsLoadingUnban(false); } - } + }; const rowRenderer = ({ index, key, parent, style }) => { const member = bans[index]; - + return ( { anchorEl={popoverAnchor} onClose={handlePopoverClose} anchorOrigin={{ - vertical: "bottom", - horizontal: "center", + vertical: 'bottom', + horizontal: 'center', }} transformOrigin={{ - vertical: "top", - horizontal: "center", + vertical: 'top', + horizontal: 'center', }} - style={{ marginTop: "8px" }} + style={{ marginTop: '8px' }} > - - handleCancelBan(member?.offender)}>Cancel Ban + variant="contained" + onClick={() => handleCancelBan(member?.offender)} + > + Cancel Ban + - handlePopoverOpen(event, index)}> + handlePopoverOpen(event, index)} + > @@ -179,7 +206,16 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { return (

    Ban list

    -
    +
    {({ height, width }) => ( {
    ); -} +}; diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index 9a8c170..35ac442 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -230,7 +230,7 @@ export const ListOfGroupPromotions = () => { .catch((error) => { rej(error.message || 'An error occurred'); }); - }); + }); // TODO translate setInfoSnack({ type: 'success', message: diff --git a/src/components/Group/ListOfInvites.tsx b/src/components/Group/ListOfInvites.tsx index a41bc47..a571775 100644 --- a/src/components/Group/ListOfInvites.tsx +++ b/src/components/Group/ListOfInvites.tsx @@ -1,16 +1,31 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { Avatar, Box, Button, ListItem, ListItemAvatar, ListItemButton, ListItemText, Popover } from '@mui/material'; -import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtualized'; +import { useEffect, useRef, useState } from 'react'; +import { + Avatar, + Box, + ListItem, + ListItemAvatar, + ListItemButton, + ListItemText, + Popover, +} from '@mui/material'; +import { + AutoSizer, + CellMeasurer, + CellMeasurerCache, + List, +} from 'react-virtualized'; import { getNameInfo } from './Group'; -import { getBaseApi, getFee } from '../../background'; +import { getFee } from '../../background'; import { LoadingButton } from '@mui/lab'; import { getBaseApiReact } from '../../App'; export const getMemberInvites = async (groupNumber) => { - const response = await fetch(`${getBaseApiReact()}/groups/invites/group/${groupNumber}?limit=0`); + const response = await fetch( + `${getBaseApiReact()}/groups/invites/group/${groupNumber}?limit=0` + ); const groupData = await response.json(); return groupData; -} +}; const getNames = async (listOfMembers, includeNoNames) => { let members = []; @@ -20,21 +35,26 @@ const getNames = async (listOfMembers, includeNoNames) => { const name = await getNameInfo(member.invitee); if (name) { members.push({ ...member, name }); - } else if(includeNoNames){ - members.push({ ...member, name: name || "" }); + } else if (includeNoNames) { + members.push({ ...member, name: name || '' }); } } } } return members; -} +}; const cache = new CellMeasurerCache({ fixedWidth: true, defaultHeight: 50, }); -export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) => { +export const ListOfInvites = ({ + groupId, + setInfoSnack, + setOpenSnack, + show, +}) => { const [invites, setInvites] = useState([]); const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open @@ -50,7 +70,7 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) => } catch (error) { console.error(error); } - } + }; useEffect(() => { if (groupId) { @@ -68,24 +88,27 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) => setOpenPopoverIndex(null); }; - const handleCancelInvitation = async (address)=> { + const handleCancelInvitation = async (address) => { try { - const fee = await getFee('CANCEL_GROUP_INVITE') + // TODO translate + const fee = await getFee('CANCEL_GROUP_INVITE'); await show({ - message: "Would you like to perform a CANCEL_GROUP_INVITE transaction?" , - publishFee: fee.fee + ' QORT' - }) - setIsLoadingCancelInvite(true) - await new Promise((res, rej)=> { - window.sendMessage("cancelInvitationToGroup", { - groupId, - qortalAddress: address, - }) + message: 'Would you like to perform a CANCEL_GROUP_INVITE transaction?', + publishFee: fee.fee + ' QORT', + }); + setIsLoadingCancelInvite(true); + await new Promise((res, rej) => { + window + .sendMessage('cancelInvitationToGroup', { + groupId, + qortalAddress: address, + }) .then((response) => { if (!response?.error) { setInfoSnack({ - type: "success", - message: "Successfully canceled invitation. It may take a couple of minutes for the changes to propagate", + type: 'success', + message: + 'Successfully canceled invitation. It may take a couple of minutes for the changes to propagate', }); setOpenSnack(true); handlePopoverClose(); @@ -94,7 +117,7 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) => return; } setInfoSnack({ - type: "error", + type: 'error', message: response?.error, }); setOpenSnack(true); @@ -102,24 +125,22 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) => }) .catch((error) => { setInfoSnack({ - type: "error", - message: error.message || "An error occurred", + type: 'error', + message: error.message || 'An error occurred', }); setOpenSnack(true); rej(error); }); - - }) + }); } catch (error) { - } finally { - setIsLoadingCancelInvite(false) + setIsLoadingCancelInvite(false); } - } + }; const rowRenderer = ({ index, key, parent, style }) => { const member = invites[index]; - + return ( anchorEl={popoverAnchor} onClose={handlePopoverClose} anchorOrigin={{ - vertical: "bottom", - horizontal: "center", + vertical: 'bottom', + horizontal: 'center', }} transformOrigin={{ - vertical: "top", - horizontal: "center", + vertical: 'top', + horizontal: 'center', }} - style={{ marginTop: "8px" }} + style={{ marginTop: '8px' }} > - - handleCancelInvitation(member?.invitee)}>Cancel Invitation + variant="contained" + onClick={() => handleCancelInvitation(member?.invitee)} + > + Cancel Invitation + - handlePopoverOpen(event, index)}> + handlePopoverOpen(event, index)} + > @@ -180,7 +212,16 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) => return (

    Invitees list

    -
    +
    {({ height, width }) => (
    ); -} +}; diff --git a/src/components/Group/ListOfJoinRequests.tsx b/src/components/Group/ListOfJoinRequests.tsx index 0f4bd81..d2bd5ad 100644 --- a/src/components/Group/ListOfJoinRequests.tsx +++ b/src/components/Group/ListOfJoinRequests.tsx @@ -1,16 +1,31 @@ -import React, { useContext, useEffect, useRef, useState } from 'react'; -import { Avatar, Box, Button, ListItem, ListItemAvatar, ListItemButton, ListItemText, Popover } from '@mui/material'; -import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtualized'; +import { useContext, useEffect, useRef, useState } from 'react'; +import { + Avatar, + Box, + ListItem, + ListItemAvatar, + ListItemButton, + ListItemText, + Popover, +} from '@mui/material'; +import { + AutoSizer, + CellMeasurer, + CellMeasurerCache, + List, +} from 'react-virtualized'; import { getNameInfo } from './Group'; import { getBaseApi, getFee } from '../../background'; import { LoadingButton } from '@mui/lab'; import { MyContext, getBaseApiReact } from '../../App'; export const getMemberInvites = async (groupNumber) => { - const response = await fetch(`${getBaseApiReact()}/groups/joinrequests/${groupNumber}?limit=0`); + const response = await fetch( + `${getBaseApiReact()}/groups/joinrequests/${groupNumber}?limit=0` + ); const groupData = await response.json(); return groupData; -} +}; const getNames = async (listOfMembers, includeNoNames) => { let members = []; @@ -19,24 +34,29 @@ const getNames = async (listOfMembers, includeNoNames) => { if (member.joiner) { const name = await getNameInfo(member.joiner); if (name) { - members.push({ ...member, name: name || "" }); - } else if(includeNoNames){ - members.push({ ...member, name: name || "" }); + members.push({ ...member, name: name || '' }); + } else if (includeNoNames) { + members.push({ ...member, name: name || '' }); } } } } return members; -} +}; const cache = new CellMeasurerCache({ fixedWidth: true, defaultHeight: 50, }); -export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }) => { +export const ListOfJoinRequests = ({ + groupId, + setInfoSnack, + setOpenSnack, + show, +}) => { const [invites, setInvites] = useState([]); - const {txList, setTxList} = useContext(MyContext) + const { txList, setTxList } = useContext(MyContext); const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open @@ -51,7 +71,7 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show } } catch (error) { console.error(error); } - } + }; useEffect(() => { if (groupId) { @@ -69,31 +89,33 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show } setOpenPopoverIndex(null); }; - const handleAcceptJoinRequest = async (address)=> { + const handleAcceptJoinRequest = async (address) => { try { - const fee = await getFee('GROUP_INVITE') + const fee = await getFee('GROUP_INVITE'); // TODO translate await show({ - message: "Would you like to perform a GROUP_INVITE transaction?" , - publishFee: fee.fee + ' QORT' - }) - setIsLoadingAccept(true) - await new Promise((res, rej)=> { - window.sendMessage("inviteToGroup", { - groupId, - qortalAddress: address, - inviteTime: 10800, - }) + message: 'Would you like to perform a GROUP_INVITE transaction?', + publishFee: fee.fee + ' QORT', + }); + setIsLoadingAccept(true); + await new Promise((res, rej) => { + window + .sendMessage('inviteToGroup', { + groupId, + qortalAddress: address, + inviteTime: 10800, + }) .then((response) => { if (!response?.error) { setIsLoadingAccept(false); setInfoSnack({ - type: "success", - message: "Successfully accepted join request. It may take a couple of minutes for the changes to propagate", + type: 'success', + message: + 'Successfully accepted join request. It may take a couple of minutes for the changes to propagate', }); setOpenSnack(true); handlePopoverClose(); res(response); - + setTxList((prev) => [ { ...response, @@ -106,12 +128,12 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show } }, ...prev, ]); - + return; } - + setInfoSnack({ - type: "error", + type: 'error', message: response?.error, }); setOpenSnack(true); @@ -119,25 +141,28 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show } }) .catch((error) => { setInfoSnack({ - type: "error", - message: error?.message || "An error occurred", + type: 'error', + message: error?.message || 'An error occurred', }); setOpenSnack(true); rej(error); }); - - }) + }); } catch (error) { - } finally { - setIsLoadingAccept(false) + setIsLoadingAccept(false); } - } + }; const rowRenderer = ({ index, key, parent, style }) => { const member = invites[index]; - const findJoinRequsetInTxList = txList?.find((tx)=> tx?.groupId === groupId && tx?.qortalAddress === member?.joiner && tx?.type === 'join-request-accept') - if(findJoinRequsetInTxList) return null + const findJoinRequsetInTxList = txList?.find( + (tx) => + tx?.groupId === groupId && + tx?.qortalAddress === member?.joiner && + tx?.type === 'join-request-accept' + ); + if (findJoinRequsetInTxList) return null; return ( - - handleAcceptJoinRequest(member?.joiner)}>Accept + variant="contained" + onClick={() => handleAcceptJoinRequest(member?.joiner)} + > + Accept + - handlePopoverOpen(event, index)}> + handlePopoverOpen(event, index)} + > @@ -198,7 +234,16 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show } return (

    Join request list

    -
    +
    {({ height, width }) => (
    ); -} +}; diff --git a/src/components/Group/ListOfMembers.tsx b/src/components/Group/ListOfMembers.tsx index 9910453..0f227f8 100644 --- a/src/components/Group/ListOfMembers.tsx +++ b/src/components/Group/ListOfMembers.tsx @@ -1,29 +1,29 @@ import { Avatar, Box, - Button, ListItem, ListItemAvatar, ListItemButton, ListItemText, Popover, Typography, -} from "@mui/material"; -import React, { useRef, useState } from "react"; +} from '@mui/material'; +import { useRef, useState } from 'react'; import { AutoSizer, CellMeasurer, CellMeasurerCache, List, -} from "react-virtualized"; -import { LoadingButton } from "@mui/lab"; -import { getBaseApi, getFee } from "../../background"; -import { getBaseApiReact } from "../../App"; +} from 'react-virtualized'; +import { LoadingButton } from '@mui/lab'; +import { getFee } from '../../background'; +import { getBaseApiReact } from '../../App'; const cache = new CellMeasurerCache({ fixedWidth: true, defaultHeight: 50, }); + const ListOfMembers = ({ members, groupId, @@ -40,7 +40,6 @@ const ListOfMembers = ({ const [isLoadingMakeAdmin, setIsLoadingMakeAdmin] = useState(false); const [isLoadingRemoveAdmin, setIsLoadingRemoveAdmin] = useState(false); - const listRef = useRef(); const handlePopoverOpen = (event, index) => { @@ -55,23 +54,25 @@ const ListOfMembers = ({ const handleKick = async (address) => { try { - const fee = await getFee("GROUP_KICK"); + const fee = await getFee('GROUP_KICK'); await show({ - message: "Would you like to perform a GROUP_KICK transaction?", - publishFee: fee.fee + " QORT", + message: 'Would you like to perform a GROUP_KICK transaction?', + publishFee: fee.fee + ' QORT', }); setIsLoadingKick(true); new Promise((res, rej) => { - window.sendMessage("kickFromGroup", { - groupId, - qortalAddress: address, - }) + window + .sendMessage('kickFromGroup', { + groupId, + qortalAddress: address, + }) .then((response) => { if (!response?.error) { setInfoSnack({ - type: "success", - message: "Successfully kicked member from group. It may take a couple of minutes for the changes to propagate", + type: 'success', + message: + 'Successfully kicked member from group. It may take a couple of minutes for the changes to propagate', }); setOpenSnack(true); handlePopoverClose(); @@ -79,7 +80,7 @@ const ListOfMembers = ({ return; } setInfoSnack({ - type: "error", + type: 'error', message: response?.error, }); setOpenSnack(true); @@ -87,38 +88,40 @@ const ListOfMembers = ({ }) .catch((error) => { setInfoSnack({ - type: "error", - message: error.message || "An error occurred", + type: 'error', + message: error.message || 'An error occurred', }); setOpenSnack(true); rej(error); }); - }); } catch (error) { + console.log(error); } finally { setIsLoadingKick(false); } }; const handleBan = async (address) => { try { - const fee = await getFee("GROUP_BAN"); + const fee = await getFee('GROUP_BAN'); // TODO translate await show({ - message: "Would you like to perform a GROUP_BAN transaction?", - publishFee: fee.fee + " QORT", + message: 'Would you like to perform a GROUP_BAN transaction?', + publishFee: fee.fee + ' QORT', }); setIsLoadingBan(true); await new Promise((res, rej) => { - window.sendMessage("banFromGroup", { - groupId, - qortalAddress: address, - rBanTime: 0, - }) + window + .sendMessage('banFromGroup', { + groupId, + qortalAddress: address, + rBanTime: 0, + }) .then((response) => { if (!response?.error) { setInfoSnack({ - type: "success", - message: "Successfully banned member from group. It may take a couple of minutes for the changes to propagate", + type: 'success', + message: + 'Successfully banned member from group. It may take a couple of minutes for the changes to propagate', }); setOpenSnack(true); handlePopoverClose(); @@ -126,7 +129,7 @@ const ListOfMembers = ({ return; } setInfoSnack({ - type: "error", + type: 'error', message: response?.error, }); setOpenSnack(true); @@ -134,13 +137,12 @@ const ListOfMembers = ({ }) .catch((error) => { setInfoSnack({ - type: "error", - message: error.message || "An error occurred", + type: 'error', + message: error.message || 'An error occurred', }); setOpenSnack(true); rej(error); }); - }); } catch (error) { } finally { @@ -150,22 +152,24 @@ const ListOfMembers = ({ const makeAdmin = async (address) => { try { - const fee = await getFee("ADD_GROUP_ADMIN"); + const fee = await getFee('ADD_GROUP_ADMIN'); await show({ - message: "Would you like to perform a ADD_GROUP_ADMIN transaction?", - publishFee: fee.fee + " QORT", + message: 'Would you like to perform a ADD_GROUP_ADMIN transaction?', + publishFee: fee.fee + ' QORT', }); setIsLoadingMakeAdmin(true); await new Promise((res, rej) => { - window.sendMessage("makeAdmin", { - groupId, - qortalAddress: address, - }) + window + .sendMessage('makeAdmin', { + groupId, + qortalAddress: address, + }) .then((response) => { if (!response?.error) { setInfoSnack({ - type: "success", - message: "Successfully made member an admin. It may take a couple of minutes for the changes to propagate", + type: 'success', + message: + 'Successfully made member an admin. It may take a couple of minutes for the changes to propagate', }); setOpenSnack(true); handlePopoverClose(); @@ -173,7 +177,7 @@ const ListOfMembers = ({ return; } setInfoSnack({ - type: "error", + type: 'error', message: response?.error, }); setOpenSnack(true); @@ -181,13 +185,12 @@ const ListOfMembers = ({ }) .catch((error) => { setInfoSnack({ - type: "error", - message: error.message || "An error occurred", + type: 'error', + message: error.message || 'An error occurred', }); setOpenSnack(true); rej(error); }); - }); } catch (error) { } finally { @@ -197,22 +200,24 @@ const ListOfMembers = ({ const removeAdmin = async (address) => { try { - const fee = await getFee("REMOVE_GROUP_ADMIN"); + const fee = await getFee('REMOVE_GROUP_ADMIN'); await show({ - message: "Would you like to perform a REMOVE_GROUP_ADMIN transaction?", - publishFee: fee.fee + " QORT", + message: 'Would you like to perform a REMOVE_GROUP_ADMIN transaction?', + publishFee: fee.fee + ' QORT', }); setIsLoadingRemoveAdmin(true); await new Promise((res, rej) => { - window.sendMessage("removeAdmin", { - groupId, - qortalAddress: address, - }) + window + .sendMessage('removeAdmin', { + groupId, + qortalAddress: address, + }) .then((response) => { if (!response?.error) { setInfoSnack({ - type: "success", - message: "Successfully removed member as an admin. It may take a couple of minutes for the changes to propagate", + type: 'success', + message: + 'Successfully removed member as an admin. It may take a couple of minutes for the changes to propagate', }); setOpenSnack(true); handlePopoverClose(); @@ -220,7 +225,7 @@ const ListOfMembers = ({ return; } setInfoSnack({ - type: "error", + type: 'error', message: response?.error, }); setOpenSnack(true); @@ -228,13 +233,12 @@ const ListOfMembers = ({ }) .catch((error) => { setInfoSnack({ - type: "error", - message: error.message || "An error occurred", + type: 'error', + message: error.message || 'An error occurred', }); setOpenSnack(true); rej(error); }); - }); } catch (error) { } finally { @@ -260,24 +264,24 @@ const ListOfMembers = ({ anchorEl={popoverAnchor} onClose={handlePopoverClose} anchorOrigin={{ - vertical: "bottom", - horizontal: "center", + vertical: 'bottom', + horizontal: 'center', }} transformOrigin={{ - vertical: "top", - horizontal: "center", + vertical: 'top', + horizontal: 'center', }} - style={{ marginTop: "8px" }} + style={{ marginTop: '8px' }} > {isOwner && ( @@ -336,21 +340,28 @@ const ListOfMembers = ({ {member?.isAdmin && ( - Admin - )} + + Admin + + )} -
    )} @@ -363,11 +374,11 @@ const ListOfMembers = ({

    Member list

    diff --git a/src/components/Group/ListOfThreadPostsWatched.tsx b/src/components/Group/ListOfThreadPostsWatched.tsx index e0a9d00..1cf0d2a 100644 --- a/src/components/Group/ListOfThreadPostsWatched.tsx +++ b/src/components/Group/ListOfThreadPostsWatched.tsx @@ -42,7 +42,7 @@ export const ListOfThreadPostsWatched = () => { rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej(error.message || 'An error occurred'); // TODO translate }); }); } catch (error) { diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index a69dde4..05d30aa 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -105,7 +105,7 @@ export const ManageMembers = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej(error.message || 'An error occurred'); // TODO translate }); }); } catch (error) { diff --git a/src/components/Group/QMailMessages.tsx b/src/components/Group/QMailMessages.tsx index 857b0db..8673110 100644 --- a/src/components/Group/QMailMessages.tsx +++ b/src/components/Group/QMailMessages.tsx @@ -87,7 +87,7 @@ export const QMailMessages = ({ userName, userAddress }) => { rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej(error.message || 'An error occurred'); // TODO translate }); }); } catch (error) { diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index d3ff1f7..fd2f9c2 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -79,7 +79,7 @@ export const Settings = ({ address, open, setOpen }) => { if (response?.error) { console.error('Error adding user settings:', response.error); } else { - console.log('User settings added successfully'); + console.log('User settings added successfully'); // TODO translate } }) .catch((error) => { diff --git a/src/components/Group/UserListOfInvites.tsx b/src/components/Group/UserListOfInvites.tsx index 2a63896..58efd9a 100644 --- a/src/components/Group/UserListOfInvites.tsx +++ b/src/components/Group/UserListOfInvites.tsx @@ -1,240 +1,266 @@ -import { Box, Button, ListItem, ListItemButton, ListItemText, Popover, Typography } from '@mui/material'; -import React, { useContext, useEffect, useRef, useState } from 'react' -import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtualized'; +import { + Box, + ListItem, + ListItemButton, + ListItemText, + Popover, + Typography, +} from '@mui/material'; +import { useContext, useEffect, useRef, useState } from 'react'; +import { + AutoSizer, + CellMeasurer, + CellMeasurerCache, + List, +} from 'react-virtualized'; import { MyContext, getBaseApiReact } from '../../App'; import { LoadingButton } from '@mui/lab'; -import { getBaseApi, getFee } from '../../background'; +import { getFee } from '../../background'; import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; -import { Spacer } from "../../common/Spacer"; +import { Spacer } from '../../common/Spacer'; const cache = new CellMeasurerCache({ - fixedWidth: true, - defaultHeight: 50, - }); + fixedWidth: true, + defaultHeight: 50, +}); - - -const getGroupInfo = async (groupId)=> { +const getGroupInfo = async (groupId) => { const response = await fetch(`${getBaseApiReact()}/groups/` + groupId); const groupData = await response.json(); if (groupData) { - return groupData - } -} - export const getGroupNames = async (listOfGroups) => { - let groups = []; - if (listOfGroups && Array.isArray(listOfGroups)) { - for (const group of listOfGroups) { - - const groupInfo = await getGroupInfo(group.groupId); - if (groupInfo) { - groups.push({ ...group, ...groupInfo }); - - } - } - } - return groups; + return groupData; } - -export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => { - const {txList, setTxList, show} = useContext(MyContext) - const [invites, setInvites] = useState([]); - const [isLoading, setIsLoading] = useState(false); - - const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to - const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open - const listRef = useRef(); - - const getRequests = async () => { - try { - const response = await fetch(`${getBaseApiReact()}/groups/invites/${myAddress}/?limit=0`); - const inviteData = await response.json(); - - const resMoreData = await getGroupNames(inviteData) - setInvites(resMoreData); - } catch (error) { - console.error(error); +}; +export const getGroupNames = async (listOfGroups) => { + let groups = []; + if (listOfGroups && Array.isArray(listOfGroups)) { + for (const group of listOfGroups) { + const groupInfo = await getGroupInfo(group.groupId); + if (groupInfo) { + groups.push({ ...group, ...groupInfo }); } } - - useEffect(() => { - - getRequests(); - - }, []); - - const handlePopoverOpen = (event, index) => { - setPopoverAnchor(event.currentTarget); - setOpenPopoverIndex(index); - }; - - const handlePopoverClose = () => { - setPopoverAnchor(null); - setOpenPopoverIndex(null); - }; - - const handleJoinGroup = async (groupId, groupName)=> { - try { - - const fee = await getFee('JOIN_GROUP') - await show({ - message: "Would you like to perform an JOIN_GROUP transaction?" , - publishFee: fee.fee + ' QORT' - }) + } + return groups; +}; - setIsLoading(true); +export const UserListOfInvites = ({ + myAddress, + setInfoSnack, + setOpenSnack, +}) => { + const { txList, setTxList, show } = useContext(MyContext); + const [invites, setInvites] = useState([]); + const [isLoading, setIsLoading] = useState(false); - await new Promise((res, rej)=> { - window.sendMessage("joinGroup", { + const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to + const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open + const listRef = useRef(); + + const getRequests = async () => { + try { + const response = await fetch( + `${getBaseApiReact()}/groups/invites/${myAddress}/?limit=0` + ); + const inviteData = await response.json(); + + const resMoreData = await getGroupNames(inviteData); + setInvites(resMoreData); + } catch (error) { + console.error(error); + } + }; + + useEffect(() => { + getRequests(); + }, []); + + const handlePopoverOpen = (event, index) => { + setPopoverAnchor(event.currentTarget); + setOpenPopoverIndex(index); + }; + + const handlePopoverClose = () => { + setPopoverAnchor(null); + setOpenPopoverIndex(null); + }; + + const handleJoinGroup = async (groupId, groupName) => { + try { + const fee = await getFee('JOIN_GROUP'); // TODO translate + await show({ + message: 'Would you like to perform an JOIN_GROUP transaction?', + publishFee: fee.fee + ' QORT', + }); + + setIsLoading(true); + + await new Promise((res, rej) => { + window + .sendMessage('joinGroup', { groupId, }) - .then((response) => { - if (!response?.error) { - setTxList((prev) => [ - { - ...response, - type: 'joined-group', - label: `Joined Group ${groupName}: awaiting confirmation`, - labelDone: `Joined Group ${groupName}: success!`, - done: false, - groupId, - }, - ...prev, - ]); - res(response); - setInfoSnack({ - type: "success", - message: "Successfully requested to join group. It may take a couple of minutes for the changes to propagate", - }); - setOpenSnack(true); - handlePopoverClose(); - return; - } + .then((response) => { + if (!response?.error) { + setTxList((prev) => [ + { + ...response, + type: 'joined-group', + label: `Joined Group ${groupName}: awaiting confirmation`, + labelDone: `Joined Group ${groupName}: success!`, + done: false, + groupId, + }, + ...prev, + ]); + res(response); setInfoSnack({ - type: "error", - message: response?.error, + type: 'success', + message: + 'Successfully requested to join group. It may take a couple of minutes for the changes to propagate', }); setOpenSnack(true); - rej(response.error); - }) - .catch((error) => { - setInfoSnack({ - type: "error", - message: error.message || "An error occurred", - }); - setOpenSnack(true); - rej(error); + handlePopoverClose(); + return; + } + setInfoSnack({ + type: 'error', + message: response?.error, }); - - }) - - } catch (error) { - - } finally { - setIsLoading(false); - - } + setOpenSnack(true); + rej(response.error); + }) + .catch((error) => { + setInfoSnack({ + type: 'error', + message: error.message || 'An error occurred', + }); + setOpenSnack(true); + rej(error); + }); + }); + } catch (error) { + } finally { + setIsLoading(false); } - - const rowRenderer = ({ index, key, parent, style }) => { - const invite = invites[index]; - - return ( - - {({ measure }) => ( -
    - - - { + const invite = invites[index]; + + return ( + + {({ measure }) => ( +
    + + + - Join {invite?.groupName} - Join {invite?.groupName} + handleJoinGroup(invite?.groupId, invite?.groupName)}>Join group - - - handlePopoverOpen(event, index)}> + variant="contained" + onClick={() => + handleJoinGroup(invite?.groupId, invite?.groupName) + } + > + Join group + + + + handlePopoverOpen(event, index)} + > {invite?.isOpen === false && ( - + + )} + {invite?.isOpen === true && ( + + )} + + + + +
    )} - {invite?.isOpen === true && ( - - )} - - - -
    -
    - )} -
    - ); - }; - - return ( - + ); + }; + + return ( + -

    Invite list

    -
    +

    Invite list

    +
    - - {({ height, width }) => ( - - )} - -
    - - ); -} + + {({ height, width }) => ( + + )} + +
    +
    + ); +}; diff --git a/src/components/Group/WalletsAppWrapper.tsx b/src/components/Group/WalletsAppWrapper.tsx index c569425..111e216 100644 --- a/src/components/Group/WalletsAppWrapper.tsx +++ b/src/components/Group/WalletsAppWrapper.tsx @@ -88,7 +88,7 @@ export const WalletsAppWrapper = () => { justifyContent: 'space-between', }} > - Q-Wallets + Q-Wallets // TODO translate { const socketRef = useRef(null); // WebSocket reference const timeoutIdRef = useRef(null); // Timeout ID reference const groupSocketTimeoutRef = useRef(null); // Group Socket Timeout reference - const initiateRef = useRef(null) + const initiateRef = useRef(null); const forceCloseWebSocket = () => { if (socketRef.current) { clearTimeout(timeoutIdRef.current); @@ -17,21 +21,20 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => { }; const logoutEventFunc = () => { - forceCloseWebSocket() + forceCloseWebSocket(); }; useEffect(() => { - subscribeToEvent("logout-event", logoutEventFunc); + subscribeToEvent('logout-event', logoutEventFunc); return () => { - unsubscribeFromEvent("logout-event", logoutEventFunc); + unsubscribeFromEvent('logout-event', logoutEventFunc); }; }, []); useEffect(() => { if (!myAddress) return; // Only proceed if myAddress is set - const pingHeads = () => { try { if (socketRef.current?.readyState === WebSocket.OPEN) { @@ -53,10 +56,9 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => { const currentAddress = myAddress; try { - if(!initiateRef.current) { - setIsLoadingGroups(true) - pauseAllQueues() - + if (!initiateRef.current) { + setIsLoadingGroups(true); + pauseAllQueues(); } const socketLink = `${getBaseApiReactSocket()}/websockets/chat/active/${currentAddress}?encoding=BASE64&haschatreference=false`; socketRef.current = new WebSocket(socketLink); @@ -71,34 +73,46 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => { clearTimeout(timeoutIdRef.current); groupSocketTimeoutRef.current = setTimeout(pingHeads, 45000); // Ping every 45 seconds } else { - if(!initiateRef.current) { - setIsLoadingGroups(false) - initiateRef.current = true - resumeAllQueues() - + if (!initiateRef.current) { + setIsLoadingGroups(false); + initiateRef.current = true; + resumeAllQueues(); } const data = JSON.parse(e.data); - const copyGroups = [...(data?.groups || [])] - const findIndex = copyGroups?.findIndex(item => item?.groupId === 0) - if(findIndex !== -1){ + const copyGroups = [...(data?.groups || [])]; + const findIndex = copyGroups?.findIndex( + (item) => item?.groupId === 0 + ); + if (findIndex !== -1) { copyGroups[findIndex] = { ...(copyGroups[findIndex] || {}), - groupId: "0" - } + groupId: '0', + }; } - const filteredGroups = copyGroups - const sortedGroups = filteredGroups.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0)); - const sortedDirects = (data?.direct || []).filter(item => - item?.name !== 'extension-proxy' && item?.address !== 'QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH' - ).sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0)); + const filteredGroups = copyGroups; + const sortedGroups = filteredGroups.sort( + (a, b) => (b.timestamp || 0) - (a.timestamp || 0) + ); + const sortedDirects = (data?.direct || []) + .filter( + (item) => + item?.name !== 'extension-proxy' && + item?.address !== 'QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH' + ) + .sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0)); - window.sendMessage("handleActiveGroupDataFromSocket", { - groups: sortedGroups, - directs: sortedDirects, - }).catch((error) => { - console.error("Failed to handle active group data from socket:", error.message || "An error occurred"); + window + .sendMessage('handleActiveGroupDataFromSocket', { + groups: sortedGroups, + directs: sortedDirects, + }) + .catch((error) => { + // TODO translate + console.error( + 'Failed to handle active group data from socket:', + error.message || 'An error occurred' + ); }); - } } catch (error) { console.error('Error parsing onmessage data:', error); @@ -127,9 +141,7 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => { } }; - - initWebsocketMessageGroup(); // Initialize WebSocket on component mount - + initWebsocketMessageGroup(); // Initialize WebSocket on component mount return () => { forceCloseWebSocket(); // Clean up WebSocket on component unmount diff --git a/src/components/Group/useBlockUsers.tsx b/src/components/Group/useBlockUsers.tsx index eeb5361..178c358 100644 --- a/src/components/Group/useBlockUsers.tsx +++ b/src/components/Group/useBlockUsers.tsx @@ -1,218 +1,194 @@ -import React, { useCallback, useEffect, useRef } from "react"; -import { getBaseApiReact } from "../../App"; -import { truncate } from "lodash"; - - +import { useCallback, useEffect, useRef } from 'react'; export const useBlockedAddresses = () => { - const userBlockedRef = useRef({}) - const userNamesBlockedRef = useRef({}) - - const getAllBlockedUsers = useCallback(()=> { + const userBlockedRef = useRef({}); + const userNamesBlockedRef = useRef({}); + const getAllBlockedUsers = useCallback(() => { return { names: userNamesBlockedRef.current, - addresses: userBlockedRef.current - } - }, []) + addresses: userBlockedRef.current, + }; + }, []); - const isUserBlocked = useCallback((address, name)=> { + const isUserBlocked = useCallback((address, name) => { try { - if(!address) return false - if(userBlockedRef.current[address]) return true - return false - - + if (!address) return false; + if (userBlockedRef.current[address]) return true; + return false; } catch (error) { - //error + //error } - }, []) + }, []); - useEffect(()=> { - const fetchBlockedList = async ()=> { + useEffect(() => { + const fetchBlockedList = async () => { try { - const response = await new Promise((res, rej) => { - window.sendMessage("listActions", { - + const response = await new Promise((res, rej) => { + window + .sendMessage('listActions', { type: 'get', listName: `blockedAddresses`, - - }) - .then((response) => { - if (response.error) { - rej(response?.message); - return; - } else { - res(response); - } - }) - .catch((error) => { - console.error("Failed qortalRequest", error); - }); - }) - const blockedUsers = {} - response?.forEach((item)=> { - blockedUsers[item] = true - }) - userBlockedRef.current = blockedUsers + }) + .then((response) => { + if (response.error) { + rej(response?.message); + return; + } else { + res(response); + } + }) + .catch((error) => { + console.error('Failed qortalRequest', error); + }); + }); + const blockedUsers = {}; + response?.forEach((item) => { + blockedUsers[item] = true; + }); + userBlockedRef.current = blockedUsers; - const response2 = await new Promise((res, rej) => { - window.sendMessage("listActions", { - + const response2 = await new Promise((res, rej) => { + window + .sendMessage('listActions', { type: 'get', listName: `blockedNames`, - + }) + .then((response) => { + if (response.error) { + rej(response?.message); + return; + } else { + res(response); + } + }) + .catch((error) => { + console.error('Failed qortalRequest', error); + }); + }); + const blockedUsers2 = {}; + response2?.forEach((item) => { + blockedUsers2[item] = true; + }); + userNamesBlockedRef.current = blockedUsers2; + } catch (error) { + console.error(error); + } + }; + fetchBlockedList(); + }, []); + + const removeBlockFromList = useCallback(async (address, name) => { + if (name) { + await new Promise((res, rej) => { + window + .sendMessage('listActions', { + type: 'remove', + items: [name], + listName: 'blockedNames', }) .then((response) => { if (response.error) { rej(response?.message); return; } else { + const copyObject = { ...userNamesBlockedRef.current }; + delete copyObject[name]; + userNamesBlockedRef.current = copyObject; + res(response); } }) .catch((error) => { - console.error("Failed qortalRequest", error); + console.error('Failed qortalRequest', error); }); - }) - const blockedUsers2 = {} - response2?.forEach((item)=> { - blockedUsers2[item] = true - }) - userNamesBlockedRef.current = blockedUsers2 - - - } catch (error) { - console.error(error) - } - } - fetchBlockedList() - }, []) - - const removeBlockFromList = useCallback(async (address, name)=> { - if(name){ - await new Promise((res, rej) => { - window.sendMessage("listActions", { - - type: 'remove', - items: [name] , - listName: 'blockedNames' - - }) - .then((response) => { - if (response.error) { - rej(response?.message); - return; - } else { - - const copyObject = {...userNamesBlockedRef.current} - delete copyObject[name] - userNamesBlockedRef.current = copyObject - - - res(response); - } - }) - .catch((error) => { - console.error("Failed qortalRequest", error); - }); - }) + }); } - if(address){ + if (address) { await new Promise((res, rej) => { - window.sendMessage("listActions", { - + window + .sendMessage('listActions', { type: 'remove', items: [address], - listName: 'blockedAddresses' - - }) - .then((response) => { - if (response.error) { - rej(response?.message); - return; - } else { - - const copyObject = {...userBlockedRef.current} - delete copyObject[address] - userBlockedRef.current = copyObject - - - res(response); - } - }) - .catch((error) => { - console.error("Failed qortalRequest", error); - }); - }) - } - - - }, []) + listName: 'blockedAddresses', + }) + .then((response) => { + if (response.error) { + rej(response?.message); + return; + } else { + const copyObject = { ...userBlockedRef.current }; + delete copyObject[address]; + userBlockedRef.current = copyObject; - const addToBlockList = useCallback(async (address, name)=> { - if(name){ + res(response); + } + }) + .catch((error) => { + console.error('Failed qortalRequest', error); + }); + }); + } + }, []); + + const addToBlockList = useCallback(async (address, name) => { + if (name) { await new Promise((res, rej) => { - window.sendMessage("listActions", { - + window + .sendMessage('listActions', { type: 'add', items: [name], - listName: 'blockedNames' - - }) - .then((response) => { - if (response.error) { - rej(response?.message); - return; - } else { - const copyObject = {...userNamesBlockedRef.current} - copyObject[name] = true - userNamesBlockedRef.current = copyObject - - - res(response); - } - }) - .catch((error) => { - console.error("Failed qortalRequest", error); - }); - }) + listName: 'blockedNames', + }) + .then((response) => { + if (response.error) { + rej(response?.message); + return; + } else { + const copyObject = { ...userNamesBlockedRef.current }; + copyObject[name] = true; + userNamesBlockedRef.current = copyObject; + + res(response); + } + }) + .catch((error) => { + console.error('Failed qortalRequest', error); + }); + }); } - if(address){ + if (address) { await new Promise((res, rej) => { - window.sendMessage("listActions", { - + window + .sendMessage('listActions', { type: 'add', items: [address], - listName: 'blockedAddresses' - - }) - .then((response) => { - if (response.error) { - rej(response?.message); - return; - } else { - - const copyObject = {...userBlockedRef.current} - copyObject[address] = true - userBlockedRef.current = copyObject - - res(response); - } - }) - .catch((error) => { - console.error("Failed qortalRequest", error); - }); - }) + listName: 'blockedAddresses', + }) + .then((response) => { + if (response.error) { + rej(response?.message); + return; + } else { + const copyObject = { ...userBlockedRef.current }; + copyObject[address] = true; + userBlockedRef.current = copyObject; + + res(response); + } + }) + .catch((error) => { + console.error('Failed qortalRequest', error); + }); + }); } - - }, []) + }, []); return { isUserBlocked, addToBlockList, removeBlockFromList, - getAllBlockedUsers + getAllBlockedUsers, }; }; diff --git a/src/components/Group/useHandleUserInfo.tsx b/src/components/Group/useHandleUserInfo.tsx index a497259..622e737 100644 --- a/src/components/Group/useHandleUserInfo.tsx +++ b/src/components/Group/useHandleUserInfo.tsx @@ -1,32 +1,30 @@ -import React, { useCallback, useRef } from "react"; -import { getBaseApiReact } from "../../App"; - - +import { useCallback, useRef } from 'react'; +import { getBaseApiReact } from '../../App'; export const useHandleUserInfo = () => { - const userInfoRef = useRef({}) + const userInfoRef = useRef({}); - - const getIndividualUserInfo = useCallback(async (address)=> { + const getIndividualUserInfo = useCallback(async (address) => { try { - if(!address) return null - if(userInfoRef.current[address] !== undefined) return userInfoRef.current[address] + if (!address) return null; + if (userInfoRef.current[address] !== undefined) + return userInfoRef.current[address]; const url = `${getBaseApiReact()}/addresses/${address}`; - const response = await fetch(url); - if (!response.ok) { - throw new Error("network error"); - } - const data = await response.json(); - userInfoRef.current = { - ...userInfoRef.current, - [address]: data?.level - } - return data?.level + const response = await fetch(url); + if (!response.ok) { + throw new Error('network error'); + } + const data = await response.json(); + userInfoRef.current = { + ...userInfoRef.current, + [address]: data?.level, + }; + return data?.level; } catch (error) { - //error + //error } - }, []) + }, []); return { getIndividualUserInfo, diff --git a/src/components/Home/NewUsersCTA.tsx b/src/components/Home/NewUsersCTA.tsx index 4ad5c79..30dea8a 100644 --- a/src/components/Home/NewUsersCTA.tsx +++ b/src/components/Home/NewUsersCTA.tsx @@ -1,40 +1,40 @@ -import { Box, ButtonBase, Typography } from "@mui/material"; -import React from "react"; -import { Spacer } from "../../common/Spacer"; +import { Box, ButtonBase, Typography } from '@mui/material'; +import { Spacer } from '../../common/Spacer'; export const NewUsersCTA = ({ balance }) => { if (balance === undefined || +balance > 0) return null; return ( Are you a new user? - + {' '} + // TODO translate Please message us on Telegram or Discord if you need 4 QORT to start @@ -43,25 +43,25 @@ export const NewUsersCTA = ({ balance }) => { { if (window?.electronAPI?.openExternal) { window.electronAPI.openExternal( - "https://link.qortal.dev/telegram-invite" + 'https://link.qortal.dev/telegram-invite' ); } else { window.open( - "https://link.qortal.dev/telegram-invite", - "_blank" + 'https://link.qortal.dev/telegram-invite', + '_blank' ); } }} @@ -70,15 +70,15 @@ export const NewUsersCTA = ({ balance }) => { { if (window?.electronAPI?.openExternal) { window.electronAPI.openExternal( - "https://link.qortal.dev/discord-invite" + 'https://link.qortal.dev/discord-invite' ); } else { - window.open("https://link.qortal.dev/discord-invite", "_blank"); + window.open('https://link.qortal.dev/discord-invite', '_blank'); } }} > diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index 84697c8..7187986 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -206,7 +206,6 @@ export const Minting = ({ window .sendMessage( 'ADMIN_ACTION', - { type: 'addmintingaccount', value: val, @@ -266,7 +265,7 @@ export const Minting = ({ rej({ message: response.error }); }) .catch((error) => { - rej({ message: error.message || 'An error occurred' }); + rej({ message: error.message || 'An error occurred' }); //TODO translate }); }); } catch (error) { From 072f98c25ec29e7c2b5d9f85e9b0b79c82d60a99 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 23 Apr 2025 23:35:56 +0200 Subject: [PATCH 237/717] Update language translations --- public/locales/de/auth.json | 13 +++++--- public/locales/de/core.json | 63 +++++++++++++++++++++++++++++++------ public/locales/en/core.json | 2 +- public/locales/es/auth.json | 13 +++++--- public/locales/es/core.json | 59 +++++++++++++++++++++++++++++----- public/locales/fr/auth.json | 13 +++++--- public/locales/fr/core.json | 59 +++++++++++++++++++++++++++++----- public/locales/it/auth.json | 15 ++++++--- public/locales/it/core.json | 63 +++++++++++++++++++++++++++++++------ public/locales/ru/auth.json | 13 +++++--- public/locales/ru/core.json | 59 +++++++++++++++++++++++++++++----- 11 files changed, 306 insertions(+), 66 deletions(-) diff --git a/public/locales/de/auth.json b/public/locales/de/auth.json index 3e40c57..fc83d42 100644 --- a/public/locales/de/auth.json +++ b/public/locales/de/auth.json @@ -1,6 +1,9 @@ { - "account_many": "Konten", - "account_one": "Konto", + "account": { + "your": "ihr Konto", + "account_many": "Konten", + "account_one": "Konto" + }, "advanced_users": "für fortgeschrittene Benutzer", "apikey": { "alternative": "Alternative: Datei auswählen", @@ -26,7 +29,9 @@ "password": "Passwort", "password_confirmation": "Passwort bestätigen", "return_to_list": "zurück zur Liste", - "wallet_password_confirmation": "Wallet-Passwort bestätigen", - "wallet_password": "Wallet-Passwort", + "wallet": { + "password_confirmation": "Wallet-Passwort bestätigen", + "password": "Wallet-Passwort" + }, "welcome": "willkommen bei" } diff --git a/public/locales/de/core.json b/public/locales/de/core.json index be16346..c2b7a29 100644 --- a/public/locales/de/core.json +++ b/public/locales/de/core.json @@ -1,28 +1,71 @@ { "add": "hinzufügen", - "backup_wallet": "Wallet sichern", "cancel": "abbrechen", "choose": "auswählen", "close": "schließen", "continue": "fortfahren", + "core": { + "block_height": "Blockhöhe", + "information": "Kerninformationen", + "peers": "verbundene Peers", + "version": "Kernversion" + }, "description": "Beschreibung", "edit": "bearbeiten", - "error": "ein Fehler ist aufgetreten", + "export": "exportieren", + "import": "importieren", "last_height": "letzte Höhe", - "loading": "wird geladen...", + "loading": "Lade...", "logout": "abmelden", - "minting_status": "Minting-Status", + "minting_status": "Präge-Status", "payment_notification": "Zahlungsbenachrichtigung", "price": "Preis", - "q_mail": "q-Mail", - "save": "speichern", + "q_mail": "Q-Mail", + "result": { + "error": { + "generic": "Ein Fehler ist aufgetreten", + "incorrect_password": "Falsches Passwort", + "save_qdn": "Speichern in QDN nicht möglich" + }, + "status": { + "minting": "(Prägung)", + "not_minting": "(keine Prägung)", + "synchronized": "synchronisiert", + "synchronizing": "synchronisiere" + }, + "success": { + "publish_qdn": "Erfolgreich in QDN veröffentlicht" + } + }, + "save_options": { + "no_pinned_changes": "Derzeit keine Änderungen an Ihren angehefteten Apps", + "overwrite_changes": "Die App konnte Ihre vorhandenen in QDN gespeicherten angehefteten Apps nicht herunterladen. Möchten Sie diese Änderungen überschreiben?", + "overwrite_qdn": "In QDN überschreiben", + "publish_qdn": "Möchten Sie Ihre Einstellungen in QDN (verschlüsselt) veröffentlichen?", + "qdn": "QDN-Speicherung verwenden", + "register_name": "Sie benötigen einen registrierten Qortal-Namen, um Ihre angehefteten Apps in QDN zu speichern.", + "reset_pinned": "Gefällt Ihnen Ihre aktuellen lokalen Änderungen nicht? Möchten Sie zu den Standard-Anheftungen zurückkehren?", + "reset_qdn": "Gefällt Ihnen Ihre aktuellen lokalen Änderungen nicht? Möchten Sie zu Ihren in QDN gespeicherten Anheftungen zurückkehren?", + "revert_default": "Auf Standard zurücksetzen", + "revert_qdn": "Auf QDN zurücksetzen", + "save_qdn": "In QDN speichern", + "save": "speichern", + "settings": "Sie verwenden die Export/Import-Methode zum Speichern von Einstellungen.", + "unsaved_changes": "Sie haben nicht gespeicherte Änderungen an Ihren angehefteten Apps. Speichern Sie sie in QDN." + }, "settings": "Einstellungen", "supply": "Angebot", + "theme": { + "dark": "Dunkelmodus", + "light": "Hellmodus" + }, "title": "Titel", "tutorial": "Tutorial", - "your_account": "dein Konto", "user_lookup": "Benutzersuche", - "wallet": "Wallet", - "wallet_other": "Wallets", - "welcome": "willkommen" + "wallet": { + "backup_wallet": "Wallet sichern", + "wallet": "Wallet", + "wallet_other": "Wallets" + }, + "welcome": "Willkommen" } diff --git a/public/locales/en/core.json b/public/locales/en/core.json index 37048be..8988c4a 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -51,7 +51,7 @@ "save_qdn": "save to QDN", "save": "save", "settings": "you are using the export/import way of saving settings.", - "unsaved_changes": " You have unsaved changes to your pinned apps. Save them to QDN." + "unsaved_changes": " you have unsaved changes to your pinned apps. Save them to QDN." }, "settings": "settings", "supply": "supply", diff --git a/public/locales/es/auth.json b/public/locales/es/auth.json index 9cf1910..3c62c24 100644 --- a/public/locales/es/auth.json +++ b/public/locales/es/auth.json @@ -1,6 +1,9 @@ { - "account_many": "cuentas", - "account_one": "cuenta", + "account": { + "your": "tu cuenta", + "account_many": "cuentas", + "account_one": "cuenta" + }, "advanced_users": "para usuarios avanzados", "apikey": { "alternative": "alternativa: Seleccionar archivo", @@ -26,7 +29,9 @@ "password": "contraseña", "password_confirmation": "confirmar contraseña", "return_to_list": "volver a la lista", - "wallet_password_confirmation": "confirmar contraseña del monedero", - "wallet_password": "contraseña del monedero", + "wallet": { + "password_confirmation": "confirmar contraseña del monedero", + "password": "contraseña del monedero" + }, "welcome": "bienvenido a" } diff --git a/public/locales/es/core.json b/public/locales/es/core.json index 9da6f10..78b06e9 100644 --- a/public/locales/es/core.json +++ b/public/locales/es/core.json @@ -1,13 +1,19 @@ { - "add": "añadir", - "backup_wallet": "copia de seguridad del monedero", + "add": "agregar", "cancel": "cancelar", "choose": "elegir", "close": "cerrar", "continue": "continuar", + "core": { + "block_height": "altura de bloque", + "information": "información del núcleo", + "peers": "pares conectados", + "version": "versión del núcleo" + }, "description": "descripción", "edit": "editar", - "error": "ocurrió un error", + "export": "exportar", + "import": "importar", "last_height": "última altura", "loading": "cargando...", "logout": "cerrar sesión", @@ -15,14 +21,51 @@ "payment_notification": "notificación de pago", "price": "precio", "q_mail": "q-mail", - "save": "guardar", - "settings": "configuración", + "result": { + "error": { + "generic": "ocurrió un error", + "incorrect_password": "contraseña incorrecta", + "save_qdn": "no se pudo guardar en QDN" + }, + "status": { + "minting": "(acuñando)", + "not_minting": "(no acuñando)", + "synchronized": "sincronizado", + "synchronizing": "sincronizando" + }, + "success": { + "publish_qdn": "publicado exitosamente en QDN" + } + }, + "save_options": { + "no_pinned_changes": "actualmente no tienes cambios en tus aplicaciones fijadas", + "overwrite_changes": "la aplicación no pudo descargar tus aplicaciones fijadas existentes guardadas en QDN. ¿Deseas sobrescribir esos cambios?", + "overwrite_qdn": "sobrescribir en QDN", + "publish_qdn": "¿Deseas publicar tus configuraciones en QDN (cifrado)?", + "qdn": "usar guardado en QDN", + "register_name": "necesitas un nombre Qortal registrado para guardar tus aplicaciones fijadas en QDN.", + "reset_pinned": "¿No te gustan tus cambios locales actuales? ¿Deseas restablecer las aplicaciones fijadas predeterminadas?", + "reset_qdn": "¿No te gustan tus cambios locales actuales? ¿Deseas restablecer tus aplicaciones fijadas guardadas en QDN?", + "revert_default": "restablecer a predeterminado", + "revert_qdn": "restablecer a QDN", + "save_qdn": "guardar en QDN", + "save": "guardar", + "settings": "estás utilizando el método de exportación/importación para guardar configuraciones.", + "unsaved_changes": "tienes cambios no guardados en tus aplicaciones fijadas. Guárdalos en QDN." + }, + "settings": "configuraciones", "supply": "suministro", + "theme": { + "dark": "modo oscuro", + "light": "modo claro" + }, "title": "título", "tutorial": "tutorial", - "your_account": "tu cuenta", "user_lookup": "búsqueda de usuario", - "wallet": "monedero", - "wallet_other": "monederos", + "wallet": { + "backup_wallet": "respaldar billetera", + "wallet": "billetera", + "wallet_other": "billeteras" + }, "welcome": "bienvenido" } diff --git a/public/locales/fr/auth.json b/public/locales/fr/auth.json index d6e4c16..06d08d5 100644 --- a/public/locales/fr/auth.json +++ b/public/locales/fr/auth.json @@ -1,6 +1,9 @@ { - "account_many": "comptes", - "account_one": "compte", + "account": { + "your": "ton compte", + "account_many": "comptes", + "account_one": "compte" + }, "advanced_users": "pour les utilisateurs avancés", "apikey": { "alternative": "alternative : Sélectionner un fichier", @@ -26,7 +29,9 @@ "password": "mot de passe", "password_confirmation": "confirmer le mot de passe", "return_to_list": "retour à la liste", - "wallet_password_confirmation": "confirmer le mot de passe du portefeuille", - "wallet_password": "mot de passe du portefeuille", + "wallet": { + "password_confirmation": "confirmer le mot de passe du portefeuille", + "password": "mot de passe du portefeuille" + }, "welcome": "bienvenue sur" } diff --git a/public/locales/fr/core.json b/public/locales/fr/core.json index 65191ab..61d231d 100644 --- a/public/locales/fr/core.json +++ b/public/locales/fr/core.json @@ -1,28 +1,71 @@ { "add": "ajouter", - "backup_wallet": "sauvegarde du portefeuille", "cancel": "annuler", "choose": "choisir", "close": "fermer", "continue": "continuer", + "core": { + "block_height": "hauteur de bloc", + "information": "informations du noyau", + "peers": "pairs connectés", + "version": "version du noyau" + }, "description": "description", "edit": "éditer", - "error": "une erreur est survenue", - "last_height": "hauteur finale", + "export": "exporter", + "import": "importer", + "last_height": "dernière hauteur", "loading": "chargement...", "logout": "se déconnecter", - "minting_status": "statut de minage", + "minting_status": "statut de frappe", "payment_notification": "notification de paiement", "price": "prix", "q_mail": "q-mail", - "save": "enregistrer", + "result": { + "error": { + "generic": "une erreur s'est produite", + "incorrect_password": "mot de passe incorrect", + "save_qdn": "impossible d'enregistrer dans QDN" + }, + "status": { + "minting": "(frappe en cours)", + "not_minting": "(pas de frappe)", + "synchronized": "synchronisé", + "synchronizing": "synchronisation en cours" + }, + "success": { + "publish_qdn": "publié avec succès dans QDN" + } + }, + "save_options": { + "no_pinned_changes": "vous n'avez actuellement aucune modification de vos applications épinglées", + "overwrite_changes": "l'application n'a pas pu télécharger vos applications épinglées existantes enregistrées dans QDN. Voulez-vous écraser ces modifications ?", + "overwrite_qdn": "écraser dans QDN", + "publish_qdn": "souhaitez-vous publier vos paramètres dans QDN (chiffré) ?", + "qdn": "utiliser l'enregistrement QDN", + "register_name": "vous devez avoir un nom Qortal enregistré pour enregistrer vos applications épinglées dans QDN.", + "reset_pinned": "vous n'aimez pas vos modifications locales actuelles ? Voulez-vous réinitialiser les applications épinglées par défaut ?", + "reset_qdn": "vous n'aimez pas vos modifications locales actuelles ? Voulez-vous réinitialiser vos applications épinglées enregistrées dans QDN ?", + "revert_default": "revenir aux paramètres par défaut", + "revert_qdn": "revenir à QDN", + "save_qdn": "enregistrer dans QDN", + "save": "enregistrer", + "settings": "vous utilisez la méthode d'exportation/importation pour enregistrer les paramètres.", + "unsaved_changes": "vous avez des modifications non enregistrées de vos applications épinglées. Enregistrez-les dans QDN." + }, "settings": "paramètres", "supply": "approvisionnement", + "theme": { + "dark": "mode sombre", + "light": "mode clair" + }, "title": "titre", "tutorial": "tutoriel", - "your_account": "votre compte", "user_lookup": "recherche d'utilisateur", - "wallet": "portefeuille", - "wallet_other": "portefeuilles", + "wallet": { + "backup_wallet": "sauvegarder le portefeuille", + "wallet": "portefeuille", + "wallet_other": "portefeuilles" + }, "welcome": "bienvenue" } diff --git a/public/locales/it/auth.json b/public/locales/it/auth.json index e6b35da..345c154 100644 --- a/public/locales/it/auth.json +++ b/public/locales/it/auth.json @@ -1,6 +1,9 @@ { - "account_many": "account", - "account_one": "account", + "account": { + "your": "il tuo account", + "account_many": "account", + "account_one": "account" + }, "advanced_users": "per utenti avanzati", "apikey": { "alternative": "alternativa: seleziona un file", @@ -23,10 +26,12 @@ "using": "nodo in uso", "using_public": "utilizzo nodo pubblico" }, - "password": "password", "password_confirmation": "confirma la password", - "wallet_password_confirmation": "conferma la password del wallet", + "password": "password", + "wallet": { + "password_confirmation": "conferma la password del wallet", + "password": "password del wallet" + }, "return_to_list": "ritorna alla lista", - "wallet_password": "password del wallet", "welcome": "benvenuto in" } diff --git a/public/locales/it/core.json b/public/locales/it/core.json index bc6dd31..8a6c4a2 100644 --- a/public/locales/it/core.json +++ b/public/locales/it/core.json @@ -1,28 +1,71 @@ { "add": "aggiungi", - "backup_wallet": "backup wallet", - "cancel": "cancella", + "cancel": "annulla", "choose": "scegli", "close": "chiudi", "continue": "continua", + "core": { + "block_height": "altezza del blocco", + "information": "informazioni core", + "peers": "peer connessi", + "version": "versione core" + }, "description": "descrizione", "edit": "modifica", - "error": "si è verificato un errore", - "last_height": "ultimo blocco", + "export": "esporta", + "import": "importa", + "last_height": "ultima altezza", "loading": "caricamento...", "logout": "disconnetti", - "minting_status": "stato minting", - "payment_notification": "notifiche pagamenti", + "minting_status": "stato del conio", + "payment_notification": "notifica di pagamento", "price": "prezzo", "q_mail": "q-mail", - "save": "salva", + "result": { + "error": { + "generic": "si è verificato un errore", + "incorrect_password": "password errata", + "save_qdn": "impossibile salvare su QDN" + }, + "status": { + "minting": "(conio in corso)", + "not_minting": "(conio non attivo)", + "synchronized": "sincronizzato", + "synchronizing": "sincronizzazione in corso" + }, + "success": { + "publish_qdn": "pubblicato con successo su QDN" + } + }, + "save_options": { + "no_pinned_changes": "attualmente non hai modifiche alle tue app appuntate", + "overwrite_changes": "l'app non è riuscita a scaricare le tue app appuntate salvate su QDN. Vuoi sovrascrivere queste modifiche?", + "overwrite_qdn": "sovrascrivi su QDN", + "publish_qdn": "vuoi pubblicare le tue impostazioni su QDN (crittografate)?", + "qdn": "usa il salvataggio QDN", + "register_name": "hai bisogno di un nome Qortal registrato per salvare le tue app appuntate su QDN.", + "reset_pinned": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le app appuntate predefinite?", + "reset_qdn": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le tue app appuntate salvate su QDN?", + "revert_default": "ripristina predefinite", + "revert_qdn": "ripristina da QDN", + "save_qdn": "salva su QDN", + "save": "salva", + "settings": "stai utilizzando il metodo esporta/importa per salvare le impostazioni.", + "unsaved_changes": "hai modifiche non salvate alle tue app appuntate. Salvale su QDN." + }, "settings": "impostazioni", "supply": "offerta", + "theme": { + "dark": "modalità scura", + "light": "modalità chiara" + }, "title": "titolo", "tutorial": "tutorial", - "your_account": "il tuo account", "user_lookup": "ricerca utente", - "wallet": "wallet", - "wallet_other": "wallet", + "wallet": { + "backup_wallet": "backup portafoglio", + "wallet": "portafoglio", + "wallet_other": "portafogli" + }, "welcome": "benvenuto" } diff --git a/public/locales/ru/auth.json b/public/locales/ru/auth.json index 5a47cc2..3ae2327 100644 --- a/public/locales/ru/auth.json +++ b/public/locales/ru/auth.json @@ -1,6 +1,9 @@ { - "account_many": "аккаунты", - "account_one": "аккаунт", + "account": { + "your": "Ваш аккаунт", + "account_many": "аккаунты", + "account_one": "аккаунт" + }, "advanced_users": "для продвинутых пользователей", "apikey": { "alternative": "альтернатива: выбрать файл", @@ -26,7 +29,9 @@ "password": "пароль", "password_confirmation": "подтвердите пароль", "return_to_list": "вернуться к списку", - "wallet_password_confirmation": "подтвердите пароль кошелька", - "wallet_password": "пароль кошелька", + "wallet": { + "password_confirmation": "подтвердите пароль кошелька", + "password": "пароль кошелька" + }, "welcome": "добро пожаловать в" } diff --git a/public/locales/ru/core.json b/public/locales/ru/core.json index 2dc0810..04bf898 100644 --- a/public/locales/ru/core.json +++ b/public/locales/ru/core.json @@ -1,28 +1,71 @@ { "add": "добавить", - "backup_wallet": "резервная копия кошелька", "cancel": "отмена", "choose": "выбрать", "close": "закрыть", "continue": "продолжить", + "core": { + "block_height": "высота блока", + "information": "информация ядра", + "peers": "подключенные узлы", + "version": "версия ядра" + }, "description": "описание", "edit": "редактировать", - "error": "произошла ошибка", + "export": "экспорт", + "import": "импорт", "last_height": "последняя высота", "loading": "загрузка...", "logout": "выйти", - "minting_status": "статус майнинга", + "minting_status": "статус чеканки", "payment_notification": "уведомление о платеже", "price": "цена", "q_mail": "q-mail", - "save": "сохранить", + "result": { + "error": { + "generic": "произошла ошибка", + "incorrect_password": "неверный пароль", + "save_qdn": "не удалось сохранить в QDN" + }, + "status": { + "minting": "(чеканка)", + "not_minting": "(не чеканится)", + "synchronized": "синхронизировано", + "synchronizing": "синхронизация" + }, + "success": { + "publish_qdn": "успешно опубликовано в QDN" + } + }, + "save_options": { + "no_pinned_changes": "у вас нет изменений в закреплённых приложениях", + "overwrite_changes": "приложению не удалось загрузить ваши закреплённые приложения, сохранённые в QDN. Хотите перезаписать эти изменения?", + "overwrite_qdn": "перезаписать в QDN", + "publish_qdn": "хотите опубликовать свои настройки в QDN (зашифровано)?", + "qdn": "использовать сохранение в QDN", + "register_name": "вам необходимо зарегистрированное имя Qortal для сохранения закреплённых приложений в QDN.", + "reset_pinned": "не устраивают текущие локальные изменения? Хотите сбросить до приложений по умолчанию?", + "reset_qdn": "не устраивают текущие локальные изменения? Хотите сбросить до сохранённых в QDN приложений?", + "revert_default": "сбросить до стандартных", + "revert_qdn": "сбросить до QDN", + "save_qdn": "сохранить в QDN", + "save": "сохранить", + "settings": "вы используете метод экспорта/импорта для сохранения настроек.", + "unsaved_changes": "у вас есть несохранённые изменения в закреплённых приложениях. Сохраните их в QDN." + }, "settings": "настройки", "supply": "предложение", + "theme": { + "dark": "тёмная тема", + "light": "светлая тема" + }, "title": "заголовок", - "tutorial": "руководство", - "your_account": "ваш аккаунт", + "tutorial": "учебник", "user_lookup": "поиск пользователя", - "wallet": "кошелек", - "wallet_other": "кошельки", + "wallet": { + "backup_wallet": "резервная копия кошелька", + "wallet": "кошелёк", + "wallet_other": "кошельки" + }, "welcome": "добро пожаловать" } From a70d77c17e5e99ad4f262f707e16f36e79de69a3 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 23 Apr 2025 23:57:55 +0200 Subject: [PATCH 238/717] Add group namespace --- i18n.js | 2 +- public/locales/en/core.json | 3 +++ public/locales/en/group.json | 34 ++++++++++++++++++++++++ src/components/Group/Forum/GroupMail.tsx | 30 ++++++++++++--------- src/components/Group/Forum/NewThread.tsx | 22 +++++++++------ 5 files changed, 69 insertions(+), 22 deletions(-) create mode 100644 public/locales/en/group.json diff --git a/i18n.js b/i18n.js index 78df3c1..376f28d 100644 --- a/i18n.js +++ b/i18n.js @@ -42,7 +42,7 @@ i18n escapeValue: false, }, lng: navigator.language, - ns: ['auth', 'core', 'tutorial'], + ns: ['auth', 'core', 'group', 'tutorial'], supportedLngs: ['en', 'it', 'es', 'fr', 'de', 'ru'], }); diff --git a/public/locales/en/core.json b/public/locales/en/core.json index 8988c4a..c2089a9 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -18,6 +18,9 @@ "loading": "loading...", "logout": "logout", "minting_status": "minting status", + "page": { + "last": "last" + }, "payment_notification": "payment notification", "price": "price", "q_mail": "q-mail", diff --git a/public/locales/en/group.json b/public/locales/en/group.json new file mode 100644 index 0000000..453c1f6 --- /dev/null +++ b/public/locales/en/group.json @@ -0,0 +1,34 @@ +{ + "provide_thread": "please provide a thread title", + "result": { + "cannot": { + "access_name": "Cannot send a message without a access to your name", + "group_info": "Cannot access group information" + } + }, + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "" +} diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index 3c8972f..0de9921 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -1,5 +1,4 @@ import React, { - FC, useCallback, useEffect, useMemo, @@ -17,7 +16,6 @@ import { ComposeIcon, ComposeP, GroupContainer, - GroupNameP, InstanceFooter, InstanceListContainer, InstanceListContainerRow, @@ -58,10 +56,12 @@ import { executeEvent } from '../../../utils/events'; import RefreshIcon from '@mui/icons-material/Refresh'; import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App'; import { addDataPublishesFunc, getDataPublishesFunc } from '../Group'; +import { useTranslation } from 'react-i18next'; const filterOptions = ['Recently active', 'Newest', 'Oldest']; export const threadIdentifier = 'DOCUMENT'; + export const GroupMail = ({ selectedGroup, userInfo, @@ -82,6 +82,7 @@ export const GroupMail = ({ const anchorElInstanceFilter = useRef(null); const [tempPublishedList, setTempPublishedList] = useState([]); const dataPublishes = useRef({}); + const { t } = useTranslation(['core']); const [isLoading, setIsLoading] = useState(false); const groupIdRef = useRef(null); @@ -627,9 +628,9 @@ export const GroupMail = ({ @@ -682,6 +683,7 @@ export const GroupMail = ({ }} /> + {combinedListTempAndReal.map((thread) => { @@ -754,8 +756,8 @@ export const GroupMail = ({ {filterMode === 'Recently active' && (
    @@ -776,16 +778,16 @@ export const GroupMail = ({ }, 300); }} sx={{ - position: 'absolute', - bottom: '2px', - right: '2px', - borderRadius: '5px', + alignItems: 'center', backgroundColor: '#27282c', + borderRadius: '5px', + bottom: '2px', + cursor: 'pointer', display: 'flex', gap: '10px', - alignItems: 'center', padding: '5px', - cursor: 'pointer', + position: 'absolute', + right: '2px', '&:hover': { background: 'rgba(255, 255, 255, 0.60)', }, @@ -795,9 +797,11 @@ export const GroupMail = ({ sx={{ color: 'white', fontSize: '12px', - }} // TODO translate + }} > - Last page + {t('core:page.last', { + postProcess: 'capitalize', + })} { console.log(error); } }; + export const NewThread = ({ groupInfo, members, @@ -143,8 +143,8 @@ export const NewThread = ({ setPostReply, isPrivate, }: NewMessageProps) => { + const { t } = useTranslation(['core', 'group']); const { show } = React.useContext(MyContext); - const [isOpen, setIsOpen] = useState(false); const [value, setValue] = useState(''); const [isSending, setIsSending] = useState(false); @@ -183,21 +183,27 @@ export const NewThread = ({ const missingFields: string[] = []; if (!isMessage && !threadTitle) { - errorMsg = 'Please provide a thread title'; + errorMsg = t('group:provide_thread', { + postProcess: 'capitalize', + }); } if (!name) { - errorMsg = 'Cannot send a message without a access to your name'; + errorMsg = t('group:result.cannot.access_name', { + postProcess: 'capitalize', + }); } if (!groupInfo) { - errorMsg = 'Cannot access group information'; - } // TODO translate + errorMsg = t('group:result.cannot.group_info', { + postProcess: 'capitalize', + }); + } // if (!description) missingFields.push('subject') if (missingFields.length > 0) { const missingFieldsString = missingFields.join(', '); const errMsg = `Missing: ${missingFieldsString}`; - errorMsg = errMsg; + errorMsg = errMsg; // TODO translate } if (errorMsg) { From d627c6a1427ad5c1f79811b1e6d695fc07fbabbc Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 09:12:33 +0200 Subject: [PATCH 239/717] Add language selector --- i18n.js | 11 ++- src/App.tsx | 2 + src/ExtStates/NotAuthenticated.tsx | 2 + src/components/Apps/AppsHomeDesktop.tsx | 2 + src/components/DesktopSideBar.tsx | 2 + src/components/Language/LanguageSelector.tsx | 73 ++++++++++++++++++++ src/components/Theme/ThemeSelector.tsx | 2 +- 7 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/components/Language/LanguageSelector.tsx diff --git a/i18n.js b/i18n.js index 376f28d..12d8a9d 100644 --- a/i18n.js +++ b/i18n.js @@ -19,6 +19,15 @@ const capitalize = { }, }; +export const supportedLanguages = { + de: { name: 'Deutsch', flag: '🇩🇪' }, + en: { name: 'English', flag: '🇬🇧' }, + es: { name: 'Español', flag: '🇪🇸' }, + fr: { name: 'Français', flag: '🇫🇷' }, + it: { name: 'Italiano', flag: '🇮🇹' }, + ru: { name: 'Русский', flag: '🇷🇺' }, +}; + i18n .use(HttpApi) .use(LanguageDetector) @@ -43,7 +52,7 @@ i18n }, lng: navigator.language, ns: ['auth', 'core', 'group', 'tutorial'], - supportedLngs: ['en', 'it', 'es', 'fr', 'de', 'ru'], + supportedLngs: Object.keys(supportedLanguages), }); export default i18n; diff --git a/src/App.tsx b/src/App.tsx index ba57d99..cfefb34 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -137,6 +137,7 @@ import { GeneralNotifications } from './components/GeneralNotifications'; import { PdfViewer } from './common/PdfViewer'; import ThemeSelector from './components/Theme/ThemeSelector.tsx'; import { useTranslation } from 'react-i18next'; +import LanguageSelector from './components/Language/LanguageSelector.tsx'; type extStates = | 'not-authenticated' @@ -3704,6 +3705,7 @@ function App() { /> )} + ); diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 28892a9..2574f96 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -31,6 +31,7 @@ import { GlobalContext } from '../App'; import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip'; import ThemeSelector from '../components/Theme/ThemeSelector'; import { useTranslation } from 'react-i18next'; +import LanguageSelector from '../components/Language/LanguageSelector'; const manifestData = { version: '0.5.3', @@ -1097,6 +1098,7 @@ export const NotAuthenticated = ({ /> + ); diff --git a/src/components/Apps/AppsHomeDesktop.tsx b/src/components/Apps/AppsHomeDesktop.tsx index d525c26..c77865c 100644 --- a/src/components/Apps/AppsHomeDesktop.tsx +++ b/src/components/Apps/AppsHomeDesktop.tsx @@ -15,6 +15,7 @@ import { extractComponents } from '../Chat/MessageDisplay'; import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward'; import { AppsPrivate } from './AppsPrivate'; import ThemeSelector from '../Theme/ThemeSelector'; +import LanguageSelector from '../Language/LanguageSelector'; export const AppsHomeDesktop = ({ setMode, @@ -157,6 +158,7 @@ export const AppsHomeDesktop = ({ /> + ); diff --git a/src/components/DesktopSideBar.tsx b/src/components/DesktopSideBar.tsx index b25d206..53937ae 100644 --- a/src/components/DesktopSideBar.tsx +++ b/src/components/DesktopSideBar.tsx @@ -8,6 +8,7 @@ import { enabledDevModeAtom } from '../atoms/global'; import { AppsIcon } from '../assets/Icons/AppsIcon'; import ThemeSelector from './Theme/ThemeSelector'; import { CoreSyncStatus } from './CoreSyncStatus'; +import LanguageSelector from './Language/LanguageSelector'; export const DesktopSideBar = ({ goToHome, @@ -139,6 +140,7 @@ export const DesktopSideBar = ({ )} + ); diff --git a/src/components/Language/LanguageSelector.tsx b/src/components/Language/LanguageSelector.tsx new file mode 100644 index 0000000..b89836d --- /dev/null +++ b/src/components/Language/LanguageSelector.tsx @@ -0,0 +1,73 @@ +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { supportedLanguages } from '../../../i18n'; +import { Tooltip } from '@mui/material'; + +const LanguageSelector = () => { + const { i18n } = useTranslation(); + const [showSelect, setShowSelect] = useState(false); + + const handleChange = (e) => { + const newLang = e.target.value; + i18n.changeLanguage(newLang); + setShowSelect(false); + }; + + const currentLang = i18n.language; + const { name, flag } = + supportedLanguages[currentLang] || supportedLanguages['en']; + + return ( +
    + + {showSelect ? ( + + ) : ( + + )} + +
    + ); +}; + +export default LanguageSelector; diff --git a/src/components/Theme/ThemeSelector.tsx b/src/components/Theme/ThemeSelector.tsx index 42b75c0..b962e1f 100644 --- a/src/components/Theme/ThemeSelector.tsx +++ b/src/components/Theme/ThemeSelector.tsx @@ -14,7 +14,7 @@ const ThemeSelector = () => { bottom: '1%', display: 'flex', gap: '12px', - left: '1.5vh', + left: '1.2vh', position: 'absolute', }} > From 501f35fa70a5b225cfd91f9891de26cf2d7f87e5 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 09:25:59 +0200 Subject: [PATCH 240/717] Refactor structure --- public/locales/en/core.json | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index c2089a9..ef134a7 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -1,9 +1,16 @@ { - "add": "add", - "cancel": "cancel", - "choose": "choose", - "close": "close", - "continue": "continue", + "action": { + "add": "add", + "cancel": "cancel", + "change": "change", + "choose": "choose", + "close": "close", + "continue": "continue", + "edit": "edit", + "export": "export", + "import": "import", + "logout": "logout" + }, "core": { "block_height": "block height", "information": "core information", @@ -11,12 +18,8 @@ "version": "core version" }, "description": "description", - "edit": "edit", - "export": "export", - "import": "import", "last_height": "last height", "loading": "loading...", - "logout": "logout", "minting_status": "minting status", "page": { "last": "last" From 7d45d54216c9a2cb079f77c0ffb9978e2a02f0eb Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 09:26:31 +0200 Subject: [PATCH 241/717] Refactor structure of translation core file --- src/App.tsx | 4 ++-- src/ExtStates/NotAuthenticated.tsx | 16 ++++++++++------ src/components/QMailStatus.tsx | 1 + src/components/Save/Save.tsx | 4 ++-- src/components/Tutorials/Tutorials.tsx | 4 ++-- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index cfefb34..0d0460a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1557,7 +1557,7 @@ function App() { textTransform: 'uppercase', }} > - {t('core:logout')} + {t('core:action.logout')} } placement="left" @@ -3585,7 +3585,7 @@ function App() { I have read this request - + // TODO translate } /> )} diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 2574f96..338a657 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -817,7 +817,7 @@ export const NotAuthenticated = ({ }} variant="contained" > - {t('core:choose', { postProcess: 'capitalize' })} + {t('core:action.choose', { postProcess: 'capitalize' })} @@ -876,7 +876,9 @@ export const NotAuthenticated = ({ }} variant="contained" > - {t('core:choose', { postProcess: 'capitalize' })} + {t('core:action.choose', { + postProcess: 'capitalize', + })} )} @@ -954,7 +958,7 @@ export const NotAuthenticated = ({ }} autoFocus > - {t('core:close', { postProcess: 'capitalize' })} + {t('core:action.close', { postProcess: 'capitalize' })} )} @@ -1076,7 +1080,7 @@ export const NotAuthenticated = ({ setShowSelectApiKey(false); }} > - {t('core:close', { postProcess: 'capitalize' })} + {t('core:action.close', { postProcess: 'capitalize' })} diff --git a/src/components/QMailStatus.tsx b/src/components/QMailStatus.tsx index dd6f9be..b733261 100644 --- a/src/components/QMailStatus.tsx +++ b/src/components/QMailStatus.tsx @@ -62,6 +62,7 @@ export const QMailStatus = () => { color: theme.palette.text.primary, fontSize: '14px', fontWeight: 700, + textTransform: 'uppercase', }} > {t('core:q_mail', { diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index f0fbc4f..3835c3e 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -569,7 +569,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { } }} > - {t('core:import', { + {t('core:action.import', { postProcess: 'capitalize', })} @@ -594,7 +594,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { } }} > - {t('core:export', { + {t('core:action.export', { postProcess: 'capitalize', })} diff --git a/src/components/Tutorials/Tutorials.tsx b/src/components/Tutorials/Tutorials.tsx index 06274e7..522324f 100644 --- a/src/components/Tutorials/Tutorials.tsx +++ b/src/components/Tutorials/Tutorials.tsx @@ -91,7 +91,7 @@ export const Tutorials = () => { @@ -138,7 +138,7 @@ export const Tutorials = () => { From 6b1e15a58df4b17304d547997714cc7a2777fbf1 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 20:15:13 +0200 Subject: [PATCH 242/717] Add some translations --- public/locales/en/core.json | 17 +++++- src/components/Group/Forum/NewThread.tsx | 1 + .../Group/Forum/ShowMessageWithoutModal.tsx | 9 ++- src/components/Group/Forum/Thread.tsx | 59 ++++++++++--------- 4 files changed, 51 insertions(+), 35 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index ef134a7..d2caa8f 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -18,14 +18,25 @@ "version": "core version" }, "description": "description", + "page": { + "last": "last", + "first": "first", + "previous": "previous" + }, + "downloading_qdn": "downloading from QDN", + "edit": "edit", + "export": "export", + "import": "import", "last_height": "last height", "loading": "loading...", + "loading_posts": "loading posts... please wait.", + "logout": "logout", "minting_status": "minting status", - "page": { - "last": "last" - }, + "next": "next", "payment_notification": "payment notification", "price": "price", + "refetch_page": "refetch page", + "return_to_thread": "return to Threads", "q_mail": "q-mail", "result": { "error": { diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index 3909686..f83aed9 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -193,6 +193,7 @@ export const NewThread = ({ postProcess: 'capitalize', }); } + if (!groupInfo) { errorMsg = t('group:result.cannot.group_info', { postProcess: 'capitalize', diff --git a/src/components/Group/Forum/ShowMessageWithoutModal.tsx b/src/components/Group/Forum/ShowMessageWithoutModal.tsx index 7a4e594..914b9c5 100644 --- a/src/components/Group/Forum/ShowMessageWithoutModal.tsx +++ b/src/components/Group/Forum/ShowMessageWithoutModal.tsx @@ -3,7 +3,6 @@ import { Avatar, Box, IconButton } from '@mui/material'; import DOMPurify from 'dompurify'; import FormatQuoteIcon from '@mui/icons-material/FormatQuote'; import MoreSVG from '../../../assets/svgs/More.svg'; - import { MoreImg, MoreP, @@ -38,16 +37,16 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { > @@ -67,6 +66,7 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { {message?.name?.charAt(0)} + { > {message?.name} + {formatTimestampForum(message?.created)} @@ -205,6 +206,7 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { > {message?.reply?.name?.charAt(0)} + { + diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 2bf2318..2d44b98 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -1,20 +1,11 @@ import React, { - FC, useCallback, useEffect, useMemo, useRef, useState, } from 'react'; -import { - Avatar, - Box, - Button, - ButtonBase, - IconButton, - Skeleton, - Typography, -} from '@mui/material'; +import { Avatar, Box, Button, ButtonBase, Typography } from '@mui/material'; import { ShowMessage } from './ShowMessageWithoutModal'; import { ComposeP, @@ -51,8 +42,12 @@ import { RequestQueueWithPromise } from '../../../utils/queue/queue'; import { CustomLoader } from '../../../common/CustomLoader'; import { WrapperUserAction } from '../../WrapperUserAction'; import { formatTimestampForum } from '../../../utils/time'; +import { useTranslation } from 'react-i18next'; + const requestQueueSaveToLocal = new RequestQueueWithPromise(1); + const requestQueueDownloadPost = new RequestQueueWithPromise(3); + interface ThreadProps { currentThread: any; groupInfo: any; @@ -120,6 +115,7 @@ export const Thread = ({ const [isLoading, setIsLoading] = useState(true); const [postReply, setPostReply] = useState(null); const [hasLastPage, setHasLastPage] = useState(false); + const { t } = useTranslation(['core']); // Update: Use a new ref for the scrollable container const threadContainerRef = useRef(null); @@ -251,6 +247,7 @@ export const Thread = ({ 'Content-Type': 'application/json', }, }); + const responseData = await response.json(); let fullArrayMsg = [...responseData]; @@ -431,6 +428,7 @@ export const Thread = ({ } const newArray = responseData.slice(0, findMessage).reverse(); let fullArrayMsg = [...messages]; + for (const message of newArray) { try { const responseDataMessage = await getEncryptedResource({ @@ -468,7 +466,6 @@ export const Thread = ({ setMessages(fullArrayMsg); } catch (error) { console.log(error); - } finally { } }, [messages] @@ -565,20 +562,20 @@ export const Thread = ({ return ( - Return to Threads + + {t('core:return_to_thread', { postProcess: 'capitalize' })} + {/* Conditionally render the scroll buttons */} {showScrollButton && @@ -658,6 +657,7 @@ export const Thread = ({ > {currentThread?.threadData?.title} + - First + {t(core:page.first', { postProcess: 'capitalize' })} + @@ -925,7 +926,7 @@ export const Thread = ({ color: 'white', }} > - Downloading from QDN + {t('core:downloading_qdn', { postProcess: 'capitalize' })} @@ -959,7 +960,7 @@ export const Thread = ({ color: 'white', }} > - Refetch page + {t('core:refetch_page', { postProcess: 'capitalize' })} @@ -997,7 +998,7 @@ export const Thread = ({ disabled={!hasFirstPage} variant="contained" > - First + {t(core:page.first', { postProcess: 'capitalize' })} @@ -1063,7 +1064,7 @@ export const Thread = ({ From 02ac18767514b7903a4172094dbb30ea94545e6a Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 20:32:00 +0200 Subject: [PATCH 243/717] Add translations to app --- public/locales/en/core.json | 24 ++++++++----- src/App.tsx | 51 +++++++++++++++++++-------- src/components/Group/AddGroup.tsx | 15 +++++--- src/components/Group/Forum/Thread.tsx | 4 +-- 4 files changed, 64 insertions(+), 30 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index d2caa8f..f2191be 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -1,15 +1,20 @@ { "action": { "add": "add", + "accept": "accept", "cancel": "cancel", "change": "change", "choose": "choose", "close": "close", "continue": "continue", + "continue_logout": "continue to logout", + "decline": "decline", "edit": "edit", "export": "export", "import": "import", - "logout": "logout" + "logout": "logout", + "refetch_page": "refetch page", + "return_to_thread": "return to threads" }, "core": { "block_height": "block height", @@ -18,25 +23,23 @@ "version": "core version" }, "description": "description", + "fee": { + "payment": "payment fee", + "publish": "publish fee" + }, "page": { "last": "last", "first": "first", "previous": "previous" }, "downloading_qdn": "downloading from QDN", - "edit": "edit", - "export": "export", - "import": "import", "last_height": "last height", "loading": "loading...", "loading_posts": "loading posts... please wait.", - "logout": "logout", "minting_status": "minting status", "next": "next", "payment_notification": "payment notification", "price": "price", - "refetch_page": "refetch page", - "return_to_thread": "return to Threads", "q_mail": "q-mail", "result": { "error": { @@ -51,9 +54,14 @@ "synchronizing": "synchronizing" }, "success": { - "publish_qdn": "successfully published to QDN" + "group_creation": "successfully created group. It may take a couple of minutes for the changes to propagate", + "publish_qdn": "successfully published to QDN", + "request_read": "I have read this request" } }, + "question": { + "perform_create_group": "would you like to perform an CREATE_GROUP transaction?" + }, "save_options": { "no_pinned_changes": "you currently do not have any changes to your pinned apps", "overwrite_changes": "the app was unable to download your existing QDN-saved pinned apps. Would you like to overwrite those changes?", diff --git a/src/App.tsx b/src/App.tsx index 0d0460a..e74f168 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2079,7 +2079,6 @@ function App() { /> )} - {isShowQortalRequest && !isMainWindow && ( <> @@ -2362,7 +2361,6 @@ function App() { {sendPaymentError} )} - {extState === 'web-app-request-payment' && !isMainWindow && ( <> @@ -3176,8 +3174,9 @@ function App() { > Close - + // TODO translate )} + {countdown && ( {message?.paymentFee && ( - payment fee: {message.paymentFee} + {t('core:fee.payment', { + postProcess: 'capitalize', + })} + : {message.paymentFee} )} {message?.publishFee && ( - publish fee: {message.publishFee} + {t('core:fee.publish', { + postProcess: 'capitalize', + })} + : {message.publishFee} )} @@ -3247,7 +3252,9 @@ function App() { onClick={onOk} autoFocus > - accept + {t('core:action.accept', { + postProcess: 'capitalize', + })} @@ -3285,7 +3294,9 @@ function App() { @@ -3304,14 +3315,18 @@ function App() { @@ -3582,10 +3597,12 @@ function App() { label={ - I have read this request + {t('core:result.success.request_read', { + postProcess: 'capitalize', + })} - // TODO translate + } /> )} @@ -3593,8 +3610,8 @@ function App() { @@ -3630,7 +3647,9 @@ function App() { onOkQortalRequestExtension('accepted'); }} > - accept + {t('core:action.accept', { + postProcess: 'capitalize', + })} onCancelQortalRequestExtension()} > - decline + {t('core:action.decline', { + postProcess: 'capitalize', + })} {sendPaymentError} diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 91552cb..2358d3d 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -28,6 +28,7 @@ import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { getFee } from '../../background'; import { MyContext } from '../../App'; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; +import { useTranslation } from 'react-i18next'; export const Label = styled('label')` display: block; @@ -84,6 +85,7 @@ export const AddGroup = ({ address, open, setOpen }) => { setMaxBlock(event.target.value as string); }; + const { t } = useTranslation(['core']); const theme = useTheme(); const handleCreateGroup = async () => { @@ -91,9 +93,11 @@ export const AddGroup = ({ address, open, setOpen }) => { if (!name) throw new Error('Please provide a name'); if (!description) throw new Error('Please provide a description'); - const fee = await getFee('CREATE_GROUP'); // TODO translate + const fee = await getFee('CREATE_GROUP'); await show({ - message: 'Would you like to perform an CREATE_GROUP transaction?', + message: t('core:question.perform_create_group', { + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); @@ -111,10 +115,11 @@ export const AddGroup = ({ address, open, setOpen }) => { if (!response?.error) { setInfoSnack({ type: 'success', - message: - 'Successfully created group. It may take a couple of minutes for the changes to propagate', + message: t('core:result.success.group_creation', { + postProcess: 'capitalize', + }), }); - setOpenSnack(true); + setOpenSnack(true); // TODO translate setTxList((prev) => [ { ...response, diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 2d44b98..476db83 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -608,7 +608,7 @@ export const Thread = ({ > - {t('core:return_to_thread', { postProcess: 'capitalize' })} + {t('core:action.return_to_thread', { postProcess: 'capitalize' })} {/* Conditionally render the scroll buttons */} @@ -960,7 +960,7 @@ export const Thread = ({ color: 'white', }} > - {t('core:refetch_page', { postProcess: 'capitalize' })} + {t('core:action.refetch_page', { postProcess: 'capitalize' })} From b0f0ec57a98a11b3492c18bdecf0d1157931d520 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 21:33:04 +0200 Subject: [PATCH 244/717] Add translations to App --- public/locales/en/core.json | 7 ++-- src/App.tsx | 65 ++++++++++++++++--------------------- 2 files changed, 33 insertions(+), 39 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index f2191be..bb56264 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -2,6 +2,8 @@ "action": { "add": "add", "accept": "accept", + "backup_account": "backup account", + "backup_wallet": "backup wallet", "cancel": "cancel", "change": "change", "choose": "choose", @@ -55,8 +57,10 @@ }, "success": { "group_creation": "successfully created group. It may take a couple of minutes for the changes to propagate", + "order_submitted": "your buy order was submitted", "publish_qdn": "successfully published to QDN", - "request_read": "I have read this request" + "request_read": "I have read this request", + "transfer": "the transfer was succesful!" } }, "question": { @@ -88,7 +92,6 @@ "tutorial": "tutorial", "user_lookup": "user lookup", "wallet": { - "backup_wallet": "backup wallet", "wallet": "wallet", "wallet_other": "wallets" }, diff --git a/src/App.tsx b/src/App.tsx index e74f168..38b8337 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -257,14 +257,7 @@ export const getBaseApiReact = (customApi?: string) => { return groupApi; } }; -// export const getArbitraryEndpointReact = () => { -// if (globalApiKey) { -// return `/arbitrary/resources/search`; -// } else { -// return `/arbitrary/resources/searchsimple`; -// } -// }; export const getArbitraryEndpointReact = () => { if (globalApiKey) { return `/arbitrary/resources/searchsimple`; @@ -538,7 +531,7 @@ function App() { console.error( 'Failed to get API key:', error?.message || 'An error occurred' - ); + ); // TODO translate }) .finally(() => { window @@ -593,26 +586,6 @@ function App() { isFocusedRef.current = isFocused; }, [isFocused]); - // const checkIfUserHasLocalNode = useCallback(async () => { - // try { - // const url = `http://127.0.0.1:12391/admin/status`; - // const response = await fetch(url, { - // method: "GET", - // headers: { - // "Content-Type": "application/json", - // }, - // }); - // const data = await response.json(); - // if (data?.isSynchronizing === false && data?.syncPercent === 100) { - // setHasLocalNode(true); - // } - // } catch (error) {} - // }, []); - - // useEffect(() => { - // checkIfUserHasLocalNode(); - // }, [extState]); - const address = useMemo(() => { if (!rawWallet?.address0) return ''; return rawWallet.address0; @@ -1050,7 +1023,7 @@ function App() { await showUnsavedChanges({ message: 'Your settings have changed. If you logout you will lose your changes. Click on the save button in the header to keep your changed settings.', - }); + }); // TODO translate } else if (extState === 'authenticated') { await showUnsavedChanges({ message: 'Are you sure you would like to logout?', @@ -1354,19 +1327,24 @@ function App() { )} + {authenticatedMode === 'ltc' ? ( <> + + {rawWallet?.ltcAddress?.slice(0, 6)}... {rawWallet?.ltcAddress?.slice(-4)} + + {ltcBalanceLoading && ( )} @@ -1388,6 +1366,7 @@ function App() { > {ltcBalance} LTC + + + {userInfo?.name} + + {rawWallet?.address0?.slice(0, 6)}... @@ -1912,7 +1895,7 @@ function App() { textTransform: 'uppercase', }} > - {t('core:backup_wallet')} + {t('core:action.backup_wallet')} } placement="left" @@ -3090,7 +3073,9 @@ function App() { }); }} > - Backup Account + {t('core:action.backup_account', { + postProcess: 'capitalize', + })} )} @@ -3118,7 +3103,9 @@ function App() { lineHeight: '15px', }} > - The transfer was succesful! + {t('core:result.success.transfer', { + postProcess: 'capitalize', + })} - Continue + {t('core:action.continue', { postProcess: 'capitalize' })} )} @@ -3141,7 +3128,9 @@ function App() { lineHeight: '15px', }} > - The transfer was succesful! + {t('core:result.success.transfer', { + postProcess: 'capitalize', + })} - Continue + {t('core:action.continue', { postProcess: 'capitalize' })} )} @@ -3164,7 +3153,9 @@ function App() { lineHeight: '15px', }} > - Your buy order was submitted + {t('core:result.success.order_submitted', { + postProcess: 'capitalize', + })} - Close + {t('core:action.close', { postProcess: 'capitalize' })} // TODO translate )} From 230cc25a3b00bfaa730aa9fe9c32adbb396b3004 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 21:45:42 +0200 Subject: [PATCH 245/717] Add translation to group --- public/locales/en/core.json | 3 +-- public/locales/en/group.json | 9 +++++++-- src/components/Group/Forum/GroupMail.tsx | 4 +++- src/components/Group/Forum/Thread.tsx | 2 +- src/components/Group/Group.tsx | 4 ++-- src/components/Group/GroupInvites.tsx | 16 ++++++++-------- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index bb56264..576f952 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -15,8 +15,7 @@ "export": "export", "import": "import", "logout": "logout", - "refetch_page": "refetch page", - "return_to_thread": "return to threads" + "refetch_page": "refetch page" }, "core": { "block_height": "block height", diff --git a/public/locales/en/group.json b/public/locales/en/group.json index 453c1f6..21d11c4 100644 --- a/public/locales/en/group.json +++ b/public/locales/en/group.json @@ -1,12 +1,17 @@ { + "action": { + "return_to_thread": "return to threads" + }, + "group_invites": "group invites", "provide_thread": "please provide a thread title", "result": { "cannot": { "access_name": "Cannot send a message without a access to your name", "group_info": "Cannot access group information" - } + }, + "loading_threads": "loading threads... please wait." }, - "": "", + "": "", "": "", "": "", diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index 0de9921..c575cf1 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -121,7 +121,9 @@ export const GroupMail = ({ }); setTempPublishedList(tempData); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; const getEncryptedResource = async ( diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 476db83..1a4075e 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -608,7 +608,7 @@ export const Thread = ({ > - {t('core:action.return_to_thread', { postProcess: 'capitalize' })} + {t('group:action.return_to_thread', { postProcess: 'capitalize' })} {/* Conditionally render the scroll buttons */} diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index a4629ce..f0eff94 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -55,8 +55,6 @@ import { RequestQueueWithPromise } from '../../utils/queue/queue'; import { WebSocketActive } from './WebsocketActive'; import { useMessageQueue } from '../../MessageQueueContext'; import { ContextMenu } from '../ContextMenu'; -import { ReturnIcon } from '../../assets/Icons/ReturnIcon'; -import { ExitIcon } from '../../assets/Icons/ExitIcon'; import { HomeDesktop } from './HomeDesktop'; import { IconWrapper } from '../Desktop/DesktopFooter'; import { DesktopHeader } from '../Desktop/DesktopHeader'; @@ -80,6 +78,7 @@ import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { BlockedUsersModal } from './BlockedUsersModal'; import { WalletsAppWrapper } from './WalletsAppWrapper'; +import { useTranslation } from 'react-i18next'; export const getPublishesFromAdmins = async (admins: string[], groupId) => { const queryString = admins.map((name) => `name=${name}`).join('&'); @@ -450,6 +449,7 @@ export const Group = ({ const [isOpenSideViewGroups, setIsOpenSideViewGroups] = useState(false); const [isForceShowCreationKeyPopup, setIsForceShowCreationKeyPopup] = useState(false); + const { t } = useTranslation(['core', 'group']); const [groupsProperties, setGroupsProperties] = useRecoilState(groupsPropertiesAtom); diff --git a/src/components/Group/GroupInvites.tsx b/src/components/Group/GroupInvites.tsx index ecbf88a..c753fdb 100644 --- a/src/components/Group/GroupInvites.tsx +++ b/src/components/Group/GroupInvites.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { useEffect, useState } from 'react'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemButton from '@mui/material/ListItemButton'; @@ -12,14 +12,13 @@ import { CustomLoader } from '../../common/CustomLoader'; import { getBaseApiReact } from '../../App'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import { useTranslation } from 'react-i18next'; export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { - const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState( - [] - ); - const [isExpanded, setIsExpanded] = React.useState(false); + const [groupsWithJoinRequests, setGroupsWithJoinRequests] = useState([]); + const [isExpanded, setIsExpanded] = useState(false); - const [loading, setLoading] = React.useState(true); + const [loading, setLoading] = useState(true); const getJoinRequests = async () => { try { @@ -38,9 +37,10 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { } }; + const { t } = useTranslation(['core', 'group']); const theme = useTheme(); - React.useEffect(() => { + useEffect(() => { if (myAddress) { getJoinRequests(); } @@ -71,7 +71,7 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { fontSize: '1rem', }} // TODO translate > - Group Invites{' '} + {t('group:group_invites', { postProcess: 'capitalize' })}{' '} {groupsWithJoinRequests?.length > 0 && ` (${groupsWithJoinRequests?.length})`} From 340fe551d6e1901ebefba341497e164a1f3e847c Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 25 Apr 2025 09:05:32 +0200 Subject: [PATCH 246/717] Add translation to language selector --- public/locales/en/core.json | 1 + src/components/Group/Forum/Thread.tsx | 16 ++++++----- src/components/Language/LanguageSelector.tsx | 29 +++++++++++--------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index 576f952..1b6529f 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -6,6 +6,7 @@ "backup_wallet": "backup wallet", "cancel": "cancel", "change": "change", + "change_language": "change language", "choose": "choose", "close": "close", "continue": "continue", diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 1a4075e..9472443 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -608,7 +608,9 @@ export const Thread = ({ > - {t('group:action.return_to_thread', { postProcess: 'capitalize' })} + {t('group:action.return_to_thread', { + postProcess: 'capitalize', + })} {/* Conditionally render the scroll buttons */} @@ -685,7 +687,7 @@ export const Thread = ({ disabled={!hasFirstPage} variant="contained" > - {t(core:page.first', { postProcess: 'capitalize' })} + {t('core:page.first', { postProcess: 'capitalize' })} @@ -998,7 +1000,7 @@ export const Thread = ({ disabled={!hasFirstPage} variant="contained" > - {t(core:page.first', { postProcess: 'capitalize' })} + {t('core:page.first', { postProcess: 'capitalize' })} diff --git a/src/components/Language/LanguageSelector.tsx b/src/components/Language/LanguageSelector.tsx index b89836d..6241347 100644 --- a/src/components/Language/LanguageSelector.tsx +++ b/src/components/Language/LanguageSelector.tsx @@ -1,11 +1,12 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { supportedLanguages } from '../../../i18n'; -import { Tooltip } from '@mui/material'; +import { Tooltip, useTheme } from '@mui/material'; const LanguageSelector = () => { - const { i18n } = useTranslation(); + const { i18n, t } = useTranslation(['core']); const [showSelect, setShowSelect] = useState(false); + const theme = useTheme(); const handleChange = (e) => { const newLang = e.target.value; @@ -28,19 +29,21 @@ const LanguageSelector = () => { }} > {showSelect ? ( setName(e.target.value)} /> @@ -303,10 +326,16 @@ export const AddGroup = ({ address, open, setOpen }) => { gap: '5px', }} > - + setDescription(e.target.value)} /> @@ -318,7 +347,13 @@ export const AddGroup = ({ address, open, setOpen }) => { gap: '5px', }} > - + + @@ -341,7 +382,11 @@ export const AddGroup = ({ address, open, setOpen }) => { }} onClick={() => setOpenAdvance((prev) => !prev)} > - Advanced options + + {t('group:advanced_options', { + postProcess: 'capitalize', + })} + {openAdvance ? : } @@ -355,8 +400,9 @@ export const AddGroup = ({ address, open, setOpen }) => { }} > { }} > { }} > @@ -449,7 +552,9 @@ export const AddGroup = ({ address, open, setOpen }) => { color="primary" onClick={handleCreateGroup} > - Create Group + {t('group.action.create', { + postProcess: 'capitalize', + })} diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index f83aed9..6f0c466 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -183,19 +183,19 @@ export const NewThread = ({ const missingFields: string[] = []; if (!isMessage && !threadTitle) { - errorMsg = t('group:provide_thread', { + errorMsg = t('group:question.provide_thread', { postProcess: 'capitalize', }); } if (!name) { - errorMsg = t('group:result.cannot.access_name', { + errorMsg = t('group:result.error.access_name', { postProcess: 'capitalize', }); } if (!groupInfo) { - errorMsg = t('group:result.cannot.group_info', { + errorMsg = t('group:result.error.group_info', { postProcess: 'capitalize', }); } From 7f2102e5e09c37d120d58f4bd6b5a06d5df37ffa Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 07:41:07 +0200 Subject: [PATCH 253/717] Add missing translation --- public/locales/en/group.json | 6 ++++-- src/components/Group/AddGroup.tsx | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/public/locales/en/group.json b/public/locales/en/group.json index 3bc248e..4333ff6 100644 --- a/public/locales/en/group.json +++ b/public/locales/en/group.json @@ -25,8 +25,10 @@ }, "result": { "error": { - "access_name": "Cannot send a message without a access to your name", - "group_info": "Cannot access group information" + "access_name": "cannot send a message without a access to your name", + "description_required": "please provide a description", + "group_info": "cannot access group information", + "name_required": "please provide a name" }, "loading_threads": "loading threads... please wait.", "success": { diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 6b45d00..a82dfb1 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -98,8 +98,18 @@ export const AddGroup = ({ address, open, setOpen }) => { const handleCreateGroup = async () => { try { - if (!name) throw new Error('Please provide a name'); - if (!description) throw new Error('Please provide a description'); + if (!name) + throw new Error( + t('group:result.error.name_required', { + postProcess: 'capitalize', + }) + ); + if (!description) + throw new Error( + t('group:result.error.description_required', { + postProcess: 'capitalize', + }) + ); const fee = await getFee('CREATE_GROUP'); @@ -128,7 +138,7 @@ export const AddGroup = ({ address, open, setOpen }) => { postProcess: 'capitalize', }), }); - setOpenSnack(true); // TODO translate + setOpenSnack(true); setTxList((prev) => [ { ...response, From 8a653d9c155b4222feef14cfd54fa2f8a0dafd58 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 07:50:16 +0200 Subject: [PATCH 254/717] Add translations --- public/locales/en/auth.json | 5 +++++ src/ExtStates/NotAuthenticated.tsx | 23 ++++++----------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json index 26cab11..ebc4fa8 100644 --- a/public/locales/en/auth.json +++ b/public/locales/en/auth.json @@ -29,6 +29,11 @@ "password": "password", "password_confirmation": "confirm password", "return_to_list": "return to list", + "tips": { + "digital_id": "your wallet is like your digital ID on Qortal, and is how you will login to the Qortal User Interface. It holds your public address and the Qortal name you will eventually choose. Every transaction you make is linked to your ID, and this is where you manage all your QORT and other tradeable cryptocurrencies on Qortal.", + "new_account": "creating an account means creating a new wallet and digital ID to start using Qortal. Once you have made your account, you can start doing things like obtaining some QORT, buying a name and avatar, publishing videos and blogs, and much more.", + "new_users": "new users start here!" + }, "wallet": { "password_confirmation": "confirm wallet password", "password": "wallet password" diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 338a657..2e3d598 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -511,14 +511,8 @@ export const NotAuthenticated = ({ fontSize: '16px', }} > - Your wallet is like your digital ID on Qortal, and is how you - will login to the Qortal User Interface. It holds your public - address and the Qortal name you will eventually choose. Every - transaction you make is linked to your ID, and this is where you - manage all your QORT and other tradeable cryptocurrencies on - Qortal. - {' '} - // TODO translate + {t('auth:tips.digital_id', { postProcess: 'capitalize' })} + } > @@ -548,9 +542,8 @@ export const NotAuthenticated = ({ fontSize: '18px', }} > - New users start here! - {' '} - // TODO translate + {t('auth:tips.new_users', { postProcess: 'capitalize' })} + - Creating an account means creating a new wallet and digital ID - to start using Qortal. Once you have made your account, you can - start doing things like obtaining some QORT, buying a name and - avatar, publishing videos and blogs, and much more. - {' '} - // TODO translate + {t('auth:tips.new_account', { postProcess: 'capitalize' })} + } > From 629aa8f3f59b0005f0b38b385be456c0c184b08f Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 07:59:26 +0200 Subject: [PATCH 255/717] Remove commented code --- src/components/Group/ThingsToDoInitial.tsx | 52 ---------------------- 1 file changed, 52 deletions(-) diff --git a/src/components/Group/ThingsToDoInitial.tsx b/src/components/Group/ThingsToDoInitial.tsx index 6c315cd..84c806b 100644 --- a/src/components/Group/ThingsToDoInitial.tsx +++ b/src/components/Group/ThingsToDoInitial.tsx @@ -141,21 +141,6 @@ export const ThingsToDoInitial = ({ outline: '1px solid rgba(9, 182, 232, 1)', }} /> - {/* */} @@ -163,15 +148,6 @@ export const ThingsToDoInitial = ({ sx={{ marginBottom: '20px', }} - // secondaryAction={ - // - // - // - // } disablePadding > - {/* - - - - - - - - */} )} From 0e3c18b21a919f48834ad8f921ecdd5dbb51a552 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 08:00:19 +0200 Subject: [PATCH 256/717] Remove unused function --- src/components/Group/Settings.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index fd2f9c2..f5a952c 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -11,13 +11,6 @@ import { Box, FormControlLabel, Switch, styled, useTheme } from '@mui/material'; import { enabledDevModeAtom } from '../../atoms/global'; import { useRecoilState } from 'recoil'; -function a11yProps(index: number) { - return { - id: `simple-tab-${index}`, - 'aria-controls': `simple-tabpanel-${index}`, - }; -} - const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ padding: 8, '& .MuiSwitch-track': { From 2327efa537ba63c878028e077e7e1b688b6cf9aa Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 08:00:29 +0200 Subject: [PATCH 257/717] Format code --- src/components/Group/WalletsAppWrapper.tsx | 2 +- src/components/Group/WebsocketActive.tsx | 1 - src/components/Group/useBlockUsers.tsx | 8 +++++++- src/components/Group/useHandleUserInfo.tsx | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/Group/WalletsAppWrapper.tsx b/src/components/Group/WalletsAppWrapper.tsx index 111e216..c569425 100644 --- a/src/components/Group/WalletsAppWrapper.tsx +++ b/src/components/Group/WalletsAppWrapper.tsx @@ -88,7 +88,7 @@ export const WalletsAppWrapper = () => { justifyContent: 'space-between', }} > - Q-Wallets // TODO translate + Q-Wallets { directs: sortedDirects, }) .catch((error) => { - // TODO translate console.error( 'Failed to handle active group data from socket:', error.message || 'An error occurred' diff --git a/src/components/Group/useBlockUsers.tsx b/src/components/Group/useBlockUsers.tsx index 178c358..4165260 100644 --- a/src/components/Group/useBlockUsers.tsx +++ b/src/components/Group/useBlockUsers.tsx @@ -17,7 +17,7 @@ export const useBlockedAddresses = () => { if (userBlockedRef.current[address]) return true; return false; } catch (error) { - //error + console.log(error); } }, []); @@ -42,10 +42,13 @@ export const useBlockedAddresses = () => { console.error('Failed qortalRequest', error); }); }); + const blockedUsers = {}; + response?.forEach((item) => { blockedUsers[item] = true; }); + userBlockedRef.current = blockedUsers; const response2 = await new Promise((res, rej) => { @@ -66,10 +69,13 @@ export const useBlockedAddresses = () => { console.error('Failed qortalRequest', error); }); }); + const blockedUsers2 = {}; + response2?.forEach((item) => { blockedUsers2[item] = true; }); + userNamesBlockedRef.current = blockedUsers2; } catch (error) { console.error(error); diff --git a/src/components/Group/useHandleUserInfo.tsx b/src/components/Group/useHandleUserInfo.tsx index 622e737..da7ee13 100644 --- a/src/components/Group/useHandleUserInfo.tsx +++ b/src/components/Group/useHandleUserInfo.tsx @@ -22,7 +22,7 @@ export const useHandleUserInfo = () => { }; return data?.level; } catch (error) { - //error + console.log(error); } }, []); From db211ee2b9940ede5e5b897eec0689c0b635ecd2 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 09:10:06 +0200 Subject: [PATCH 258/717] Add translatios to addGroup --- public/locales/en/core.json | 1 + public/locales/en/group.json | 9 +++-- src/components/Group/AddGroup.tsx | 2 ++ src/components/Group/AddGroupList.tsx | 42 +++++++++++++++++------- src/components/Group/Forum/GroupMail.tsx | 4 ++- 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index c640b7b..5b75a16 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -15,6 +15,7 @@ "edit": "edit", "export": "export", "import": "import", + "join": "join", "logout": "logout", "refetch_page": "refetch page" }, diff --git a/public/locales/en/group.json b/public/locales/en/group.json index 4333ff6..766b506 100644 --- a/public/locales/en/group.json +++ b/public/locales/en/group.json @@ -2,6 +2,7 @@ "action": { "create_group": "create group", "find_group": "find group", + "join_group": "join group", "return_to_thread": "return to threads" }, "advanced_options": "advanced options", @@ -21,6 +22,7 @@ }, "question": { "create_group": "would you like to perform an CREATE_GROUP transaction?", + "join_group": "would you like to perform an JOIN_GROUP transaction?", "provide_thread": "please provide a thread title" }, "result": { @@ -30,11 +32,14 @@ "group_info": "cannot access group information", "name_required": "please provide a name" }, - "loading_threads": "loading threads... please wait.", "success": { "group_creation": "successfully created group. It may take a couple of minutes for the changes to propagate", "group_creation_name": "created group {{group_name}}: awaiting confirmation", - "group_creation_label": "created group {{name}}: success!" + "group_creation_label": "created group {{name}}: success!", + "join_creation": "successfully requested to join group. It may take a couple of minutes for the changes to propagate", + "group_join_name": "joined group {{group_name}}: awaiting confirmation", + "group_join_label": "joined group {{name}}: success!", + "loading_threads": "loading threads... please wait." } } } diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index a82dfb1..2bdf81a 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -350,6 +350,7 @@ export const AddGroup = ({ address, open, setOpen }) => { onChange={(e) => setDescription(e.target.value)} /> + { + { const { memberGroups, show, setTxList } = useContext(MyContext); - + const { t } = useTranslation(['core', 'group']); const [groups, setGroups] = useState([]); const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open @@ -101,12 +102,17 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const handleJoinGroup = async (group, isOpen) => { try { const groupId = group.groupId; - const fee = await getFee('JOIN_GROUP'); // TODO translate + + const fee = await getFee('JOIN_GROUP'); + await show({ - message: 'Would you like to perform an JOIN_GROUP transaction?', + message: t('group:question.join_group', { + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); setIsLoading(true); + await new Promise((res, rej) => { window .sendMessage('joinGroup', { @@ -116,8 +122,9 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { if (!response?.error) { setInfoSnack({ type: 'success', - message: - 'Successfully requested to join group. It may take a couple of minutes for the changes to propagate', + message: t('group:result.success.join_group', { + postProcess: 'capitalize', + }), }); if (isOpen) { @@ -125,8 +132,14 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { { ...response, type: 'joined-group', - label: `Joined Group ${group?.groupName}: awaiting confirmation`, - labelDone: `Joined Group ${group?.groupName}: success!`, + label: t('group:result.success.group_join_label', { + group_name: group?.groupName, + postProcess: 'capitalize', + }), + labelDone: t('group:result.success.group_join_label', { + group_name: group?.groupName, + postProcess: 'capitalize', + }), done: false, groupId, }, @@ -215,7 +228,10 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { padding: '10px', }} > - Join {group?.groupName} + + {t('core:action.join', { postProcess: 'capitalize' })}{' '} + {group?.groupName} + {group?.isOpen === false && 'This is a closed/private group, so you will need to wait until an admin accepts your request'} @@ -226,7 +242,9 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { variant="contained" onClick={() => handleJoinGroup(group, group?.isOpen)} > - Join group + {t('group:action.join_group', { + postProcess: 'capitalize', + })} diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index c575cf1..985644e 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -834,7 +834,9 @@ export const GroupMail = ({ From 4e8eb3b2c31665584a4242d70d890e8fb6d69a6c Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 10:34:15 +0300 Subject: [PATCH 259/717] added the option to change the wallet's password --- public/locales/de/auth.json | 8 +- public/locales/en/auth.json | 8 +- public/locales/es/auth.json | 8 +- public/locales/fr/auth.json | 8 +- public/locales/it/auth.json | 8 +- public/locales/ru/auth.json | 8 +- src/App.tsx | 136 +------------- src/ExtStates/NotAuthenticated.tsx | 2 +- src/components/Auth/DownloadWallet.tsx | 248 +++++++++++++++++++++++++ src/components/Explore/Explore.tsx | 2 +- 10 files changed, 302 insertions(+), 134 deletions(-) create mode 100644 src/components/Auth/DownloadWallet.tsx diff --git a/public/locales/de/auth.json b/public/locales/de/auth.json index fc83d42..5b14183 100644 --- a/public/locales/de/auth.json +++ b/public/locales/de/auth.json @@ -31,7 +31,13 @@ "return_to_list": "zurück zur Liste", "wallet": { "password_confirmation": "Wallet-Passwort bestätigen", - "password": "Wallet-Passwort" + "password": "Wallet-Passwort", + "keep_password": "aktuelles Passwort beibehalten", + "new_password": "neues Passwort", + "error": { + "missing_new_password": "bitte neues Passwort eingeben", + "missing_password": "bitte Passwort eingeben" + } }, "welcome": "willkommen bei" } diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json index 26cab11..2ee037e 100644 --- a/public/locales/en/auth.json +++ b/public/locales/en/auth.json @@ -31,7 +31,13 @@ "return_to_list": "return to list", "wallet": { "password_confirmation": "confirm wallet password", - "password": "wallet password" + "password": "wallet password", + "keep_password": "keep current password", + "new_password": "new password", + "error": { + "missing_new_password": "please enter a new password", + "missing_password": "please enter your password" + } }, "welcome": "welcome to" } diff --git a/public/locales/es/auth.json b/public/locales/es/auth.json index 3c62c24..5d4b8b6 100644 --- a/public/locales/es/auth.json +++ b/public/locales/es/auth.json @@ -31,7 +31,13 @@ "return_to_list": "volver a la lista", "wallet": { "password_confirmation": "confirmar contraseña del monedero", - "password": "contraseña del monedero" + "password": "contraseña del monedero", + "keep_password": "mantener la contraseña actual", + "new_password": "nueva contraseña", + "error": { + "missing_new_password": "por favor ingresa una nueva contraseña", + "missing_password": "por favor ingresa tu contraseña" + } }, "welcome": "bienvenido a" } diff --git a/public/locales/fr/auth.json b/public/locales/fr/auth.json index 06d08d5..f314d3e 100644 --- a/public/locales/fr/auth.json +++ b/public/locales/fr/auth.json @@ -31,7 +31,13 @@ "return_to_list": "retour à la liste", "wallet": { "password_confirmation": "confirmer le mot de passe du portefeuille", - "password": "mot de passe du portefeuille" + "password": "mot de passe du portefeuille", + "keep_password": "garder le mot de passe actuel", + "new_password": "nouveau mot de passe", + "error": { + "missing_new_password": "veuillez entrer un nouveau mot de passe", + "missing_password": "veuillez entrer votre mot de passe" + } }, "welcome": "bienvenue sur" } diff --git a/public/locales/it/auth.json b/public/locales/it/auth.json index 345c154..ad00833 100644 --- a/public/locales/it/auth.json +++ b/public/locales/it/auth.json @@ -30,7 +30,13 @@ "password": "password", "wallet": { "password_confirmation": "conferma la password del wallet", - "password": "password del wallet" + "password": "password del wallet", + "keep_password": "mantieni la password attuale", + "new_password": "nuova password", + "error": { + "missing_new_password": "per favore inserisci una nuova password", + "missing_password": "per favore inserisci la tua password" + } }, "return_to_list": "ritorna alla lista", "welcome": "benvenuto in" diff --git a/public/locales/ru/auth.json b/public/locales/ru/auth.json index 3ae2327..797220b 100644 --- a/public/locales/ru/auth.json +++ b/public/locales/ru/auth.json @@ -31,7 +31,13 @@ "return_to_list": "вернуться к списку", "wallet": { "password_confirmation": "подтвердите пароль кошелька", - "password": "пароль кошелька" + "password": "пароль кошелька", + "keep_password": "сохранить текущий пароль", + "new_password": "новый пароль", + "error": { + "missing_new_password": "пожалуйста, введите новый пароль", + "missing_password": "пожалуйста, введите ваш пароль" + } }, "welcome": "добро пожаловать в" } diff --git a/src/App.tsx b/src/App.tsx index ba57d99..7c66d9d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -137,6 +137,7 @@ import { GeneralNotifications } from './components/GeneralNotifications'; import { PdfViewer } from './common/PdfViewer'; import ThemeSelector from './components/Theme/ThemeSelector.tsx'; import { useTranslation } from 'react-i18next'; +import { DownloadWallet } from './components/Auth/DownloadWallet.tsx'; type extStates = | 'not-authenticated' @@ -919,27 +920,6 @@ function App() { } }, [authenticatedMode]); - const confirmPasswordToDownload = async () => { - try { - setWalletToBeDownloadedError(''); - if (!walletToBeDownloadedPassword) { - setSendPaymentError('Please enter your password'); - return; - } - setIsLoading(true); - await new Promise((res) => { - setTimeout(() => { - res(); - }, 250); - }); - const res = await saveWalletFunc(walletToBeDownloadedPassword); - } catch (error: any) { - setWalletToBeDownloadedError(error?.message); - } finally { - setIsLoading(false); - } - }; - const saveFileToDiskFunc = async () => { try { await saveFileToDisk( @@ -1676,7 +1656,7 @@ function App() { textTransform: 'uppercase', }} > - {t('core:wallet_other')} + {t('core:wallet.wallet_other')} } placement="left" @@ -2692,110 +2672,14 @@ function App() { )} {extState === 'download-wallet' && ( - <> - - - - - - - -
    - -
    - - - - - - {t('auth:download_account', { postProcess: 'capitalize' })} - - - - - - {!walletToBeDownloaded && ( - <> - - {t('auth:wallet.password_confirmation', { - postProcess: 'capitalize', - })} - - - - - - setWalletToBeDownloadedPassword(e.target.value) - } - /> - - - - - {t('auth:password_confirmation', { - postProcess: 'capitalize', - })} - - {walletToBeDownloadedError} - - )} - - {walletToBeDownloaded && ( - <> - { - await saveFileToDiskFunc(); - await showInfo({ - message: t('auth:keep_secure', { - postProcess: 'capitalize', - }), - }); - }} - > - {t('auth:download_account', { - postProcess: 'capitalize', - })} - - - )} - + )} {extState === 'create-wallet' && ( <> diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 28892a9..3c8afd9 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -703,7 +703,7 @@ export const NotAuthenticated = ({ visibility: importedApiKey ? 'visible' : 'hidden', }} > - {t('auth:apikey.key', { postProcess: 'capitalize' })}: $ + {t('auth:apikey.key', { postProcess: 'capitalize' })}:{' '} {importedApiKey} diff --git a/src/components/Auth/DownloadWallet.tsx b/src/components/Auth/DownloadWallet.tsx new file mode 100644 index 0000000..12b46f4 --- /dev/null +++ b/src/components/Auth/DownloadWallet.tsx @@ -0,0 +1,248 @@ +import { Box, Checkbox, FormControlLabel, Typography } from '@mui/material'; +import { Spacer } from '../../common/Spacer'; +import { Return } from '../../assets/Icons/Return'; +import { CustomButton, CustomLabel, TextP } from '../../styles/App-styles'; +import { PasswordField } from '../PasswordField/PasswordField'; +import { ErrorText } from '../ErrorText/ErrorText'; +import Logo1Dark from '../../assets/svgs/Logo1Dark.svg'; +import { useTranslation } from 'react-i18next'; +import { saveFileToDisk } from '../../utils/generateWallet/generateWallet'; +import { useState } from 'react'; +import { decryptStoredWallet } from '../../utils/decryptWallet'; +import PhraseWallet from '../../utils/generateWallet/phrase-wallet'; +import { crypto, walletVersion } from '../../constants/decryptWallet'; + +export const DownloadWallet = ({ + returnToMain, + setIsLoading, + showInfo, + rawWallet, + setWalletToBeDownloaded, + walletToBeDownloaded, +}) => { + const [walletToBeDownloadedPassword, setWalletToBeDownloadedPassword] = + useState(''); + const [newPassword, setNewPassword] = useState(''); + const [keepCurrentPassword, setKeepCurrentPassword] = useState(true); + + const [walletToBeDownloadedError, setWalletToBeDownloadedError] = + useState(''); + + const { t } = useTranslation(['auth']); + + const saveFileToDiskFunc = async () => { + try { + await saveFileToDisk( + walletToBeDownloaded.wallet, + walletToBeDownloaded.qortAddress + ); + } catch (error: any) { + setWalletToBeDownloadedError(error?.message); + } + }; + + const saveWalletFunc = async (password: string, newPassword) => { + let wallet = structuredClone(rawWallet); + + const res = await decryptStoredWallet(password, wallet); + const wallet2 = new PhraseWallet(res, wallet?.version || walletVersion); + const passwordToUse = newPassword || password; + wallet = await wallet2.generateSaveWalletData( + passwordToUse, + crypto.kdfThreads, + () => {} + ); + + setWalletToBeDownloaded({ + wallet, + qortAddress: rawWallet.address0, + }); + return { + wallet, + qortAddress: rawWallet.address0, + }; + }; + + const confirmPasswordToDownload = async () => { + try { + setWalletToBeDownloadedError(''); + if (!keepCurrentPassword && !newPassword) { + setWalletToBeDownloadedError( + t('auth:wallet.error.missing_new_password', { + postProcess: 'capitalize', + }) + ); + return; + } + if (!walletToBeDownloadedPassword) { + setWalletToBeDownloadedError( + t('auth:wallet.error.missing_password', { postProcess: 'capitalize' }) + ); + return; + } + setIsLoading(true); + await new Promise((res) => { + setTimeout(() => { + res(); + }, 250); + }); + const newPasswordForWallet = !keepCurrentPassword ? newPassword : null; + const res = await saveWalletFunc( + walletToBeDownloadedPassword, + newPasswordForWallet + ); + } catch (error: any) { + setWalletToBeDownloadedError(error?.message); + } finally { + setIsLoading(false); + } + }; + + return ( + <> + + + + + + + +
    + +
    + + + + + + {t('auth:download_account', { postProcess: 'capitalize' })} + + + + + + {!walletToBeDownloaded && ( + <> + + {t('auth:wallet.password_confirmation', { + postProcess: 'capitalize', + })} + + + + + setWalletToBeDownloadedPassword(e.target.value)} + /> + + + + setKeepCurrentPassword(e.target.checked)} + checked={keepCurrentPassword} + edge="start" + tabIndex={-1} + disableRipple + /> + } + label={ + + + {t('auth:wallet.keep_password', { + postProcess: 'capitalize', + })} + + + } + /> + + {!keepCurrentPassword && ( + <> + + {t('auth:wallet.new_password', { + postProcess: 'capitalize', + })} + + + + setNewPassword(e.target.value)} + /> + + + )} + + + {t('auth:password_confirmation', { + postProcess: 'capitalize', + })} + + + {walletToBeDownloadedError} + + )} + + {walletToBeDownloaded && ( + <> + { + await saveFileToDiskFunc(); + await showInfo({ + message: t('auth:keep_secure', { + postProcess: 'capitalize', + }), + }); + }} + > + {t('auth:download_account', { + postProcess: 'capitalize', + })} + + + )} + + ); +}; diff --git a/src/components/Explore/Explore.tsx b/src/components/Explore/Explore.tsx index ae1988e..266fdd5 100644 --- a/src/components/Explore/Explore.tsx +++ b/src/components/Explore/Explore.tsx @@ -124,7 +124,7 @@ export const Explore = ({ setDesktopViewMode }) => { fontSize: '1rem', }} > - {t('core:wallet_other', { postProcess: 'capitalize' })} + {t('core:wallet.wallet_other', { postProcess: 'capitalize' })}
    From 3c242a688c907ce85161b346e540ec58aa18f8a3 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 12:33:55 +0300 Subject: [PATCH 260/717] remove full screen on double click --- src/App.tsx | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index ba57d99..2b3eee5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -102,7 +102,6 @@ import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil'; import { canSaveSettingToQdnAtom, enabledDevModeAtom, - fullScreenAtom, groupsPropertiesAtom, hasSettingsChangedAtom, isDisabledEditorEnterAtom, @@ -115,7 +114,6 @@ import { settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom, } from './atoms/global'; -import { useAppFullScreen } from './useAppFullscreen'; import { NotAuthenticated } from './ExtStates/NotAuthenticated'; import { handleGetFileFromIndexedDB } from './utils/indexedDB'; import { Wallets } from './Wallets'; @@ -407,12 +405,10 @@ function App() { const qortalRequestCheckbox1Ref = useRef(null); useRetrieveDataLocalStorage(userInfo?.address); useQortalGetSaveSettings(userInfo?.name, extState === 'authenticated'); - const [fullScreen, setFullScreen] = useRecoilState(fullScreenAtom); const [isEnabledDevMode, setIsEnabledDevMode] = useRecoilState(enabledDevModeAtom); const setIsDisabledEditorEnter = useSetRecoilState(isDisabledEditorEnterAtom); const [isOpenMinting, setIsOpenMinting] = useState(false); - const { toggleFullScreen } = useAppFullScreen(setFullScreen); const generatorRef = useRef(null); const exportSeedphrase = () => { @@ -456,24 +452,6 @@ function App() { } }, [extState, walletToBeDownloaded, shownTutorialsInitiated]); - useEffect(() => { - // Attach a global event listener for double-click - const handleDoubleClick = () => { - toggleFullScreen(); - }; - - // Add the event listener to the root HTML document - document.documentElement.addEventListener('dblclick', handleDoubleClick); - - // Clean up the event listener on unmount - return () => { - document.documentElement.removeEventListener( - 'dblclick', - handleDoubleClick - ); - }; - }, [toggleFullScreen]); - //resets for recoil const resetAtomSortablePinnedAppsAtom = useResetRecoilState( sortablePinnedAppsAtom From 9f0f6e96775dfc8662e14651efae42510eb4ec0d Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 12:44:52 +0300 Subject: [PATCH 261/717] save theme to localstorage for persistance --- src/components/Theme/ThemeContext.tsx | 38 +++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/components/Theme/ThemeContext.tsx b/src/components/Theme/ThemeContext.tsx index 5a5abb8..d2e7048 100644 --- a/src/components/Theme/ThemeContext.tsx +++ b/src/components/Theme/ThemeContext.tsx @@ -1,4 +1,11 @@ -import { createContext, useContext, useState, useMemo } from 'react'; +import { + createContext, + useContext, + useState, + useMemo, + useEffect, + useCallback, +} from 'react'; import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles'; import { darkTheme } from '../../styles/theme-dark'; import { lightTheme } from '../../styles/theme-light'; @@ -17,9 +24,36 @@ export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { ); const toggleTheme = () => { - setThemeMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light')); + setThemeMode((prevMode) => { + const newMode = prevMode === 'light' ? 'dark' : 'light'; + + const themeProperties = { + mode: newMode, + }; + + localStorage.setItem('saved_ui_theme', JSON.stringify(themeProperties)); + + return newMode; + }); }; + const getSavedTheme = useCallback(async () => { + try { + const themeProperties = JSON.parse( + localStorage.getItem(`saved_ui_theme`) || '{}' + ); + + const theme = themeProperties?.mode || 'light'; + setThemeMode(theme); + } catch (error) { + console.log('error', error); + } + }, []); + + useEffect(() => { + getSavedTheme(); + }, [getSavedTheme]); + return ( {children} From d220c0753a07bc1a543108e07693331d2ec0116a Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 15:42:13 +0300 Subject: [PATCH 262/717] fix editor options not showing --- src/components/Chat/TipTap.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 00425cd..3c5403f 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -493,14 +493,12 @@ export default ({ > - ) + } extensions={[...extensionsFiltered, ...additionalExtensions]} content={content} From 1dbcd0149267f6b5817e4dd3942bd41215c8546d Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 15:42:54 +0300 Subject: [PATCH 263/717] fix colors for selected items --- src/assets/Icons/AppsIcon.tsx | 22 ++++---- src/assets/Icons/HomeIcon.tsx | 5 +- src/components/Apps/AppsDesktop.tsx | 49 +++--------------- src/components/Apps/AppsDevMode.tsx | 33 ++++++++---- src/components/Chat/AnnouncementItem.tsx | 2 +- src/components/Desktop/DesktopFooter.tsx | 4 +- src/components/Desktop/DesktopHeader.tsx | 65 +++++++++++++----------- src/components/DesktopSideBar.tsx | 14 +++-- src/components/Group/Group.tsx | 18 ++++--- src/components/Save/Save.tsx | 30 +++++++++-- 10 files changed, 128 insertions(+), 114 deletions(-) diff --git a/src/assets/Icons/AppsIcon.tsx b/src/assets/Icons/AppsIcon.tsx index 3f58604..631c2e2 100644 --- a/src/assets/Icons/AppsIcon.tsx +++ b/src/assets/Icons/AppsIcon.tsx @@ -1,8 +1,10 @@ import { useTheme } from '@mui/material'; -export const AppsIcon = ({ height = 31, width = 31 }) => { +export const AppsIcon = ({ height = 31, width = 31, color }) => { const theme = useTheme(); + const setColor = color ? color : theme.palette.text.primary; + return ( { > ); diff --git a/src/assets/Icons/HomeIcon.tsx b/src/assets/Icons/HomeIcon.tsx index 0d3c4fe..4432c42 100644 --- a/src/assets/Icons/HomeIcon.tsx +++ b/src/assets/Icons/HomeIcon.tsx @@ -1,8 +1,9 @@ import { useTheme } from '@mui/material'; -export const HomeIcon = ({ height = 20, width = 23 }) => { +export const HomeIcon = ({ height = 20, width = 23, color }) => { const theme = useTheme(); + const setColor = color ? color : theme.palette.text.primary; return ( { > ); diff --git a/src/components/Apps/AppsDesktop.tsx b/src/components/Apps/AppsDesktop.tsx index c7bb5c6..e81e1a3 100644 --- a/src/components/Apps/AppsDesktop.tsx +++ b/src/components/Apps/AppsDesktop.tsx @@ -363,7 +363,7 @@ export const AppsDesktop = ({ goToHome(); }} > - + - + @@ -385,7 +385,9 @@ export const AppsDesktop = ({ color={ hasUnreadDirects || hasUnreadGroups ? 'var(--unread)' - : theme.palette.text.primary + : desktopViewMode === 'chat' + ? theme.palette.text.primary + : theme.palette.text.secondary } label="Chat" disableWidth @@ -395,48 +397,13 @@ export const AppsDesktop = ({ color={ hasUnreadDirects || hasUnreadGroups ? 'var(--unread)' - : theme.palette.text.primary + : desktopViewMode === 'chat' + ? theme.palette.text.primary + : theme.palette.text.secondary } /> - {/* { - setDesktopSideView("directs"); - toggleSideViewDirects() - }} - > - - - - - { - setDesktopSideView("groups"); - toggleSideViewGroups() - }} - > - - - */} {isEnabledDevMode && ( { setTimeout(() => { @@ -266,7 +267,9 @@ export const AppsDevMode = ({ @@ -276,13 +279,19 @@ export const AppsDevMode = ({ }} > @@ -296,8 +305,8 @@ export const AppsDevMode = ({ hasUnreadDirects || hasUnreadGroups ? 'var(--unread)' : desktopViewMode === 'chat' - ? 'white' - : 'rgba(250, 250, 250, 0.5)' + ? theme.palette.text.primary + : theme.palette.text.secondary } label="Chat" disableWidth @@ -308,8 +317,8 @@ export const AppsDevMode = ({ hasUnreadDirects || hasUnreadGroups ? 'var(--unread)' : desktopViewMode === 'chat' - ? 'white' - : 'rgba(250, 250, 250, 0.5)' + ? theme.palette.text.primary + : theme.palette.text.secondary } /> @@ -322,14 +331,18 @@ export const AppsDevMode = ({ > diff --git a/src/components/Chat/AnnouncementItem.tsx b/src/components/Chat/AnnouncementItem.tsx index 73faf24..e63c7a3 100644 --- a/src/components/Chat/AnnouncementItem.tsx +++ b/src/components/Chat/AnnouncementItem.tsx @@ -51,7 +51,7 @@ export const AnnouncementItem = ({ return (
    { const [value, setValue] = React.useState(0); + const theme = useTheme(); return ( @@ -177,10 +170,12 @@ export const DesktopHeader = ({ }} > @@ -203,16 +198,24 @@ export const DesktopHeader = ({ }} > @@ -222,7 +225,7 @@ export const DesktopHeader = ({ }} > @@ -242,21 +245,21 @@ export const DesktopHeader = ({ diff --git a/src/components/DesktopSideBar.tsx b/src/components/DesktopSideBar.tsx index b25d206..b422f81 100644 --- a/src/components/DesktopSideBar.tsx +++ b/src/components/DesktopSideBar.tsx @@ -28,6 +28,8 @@ export const DesktopSideBar = ({ const theme = useTheme(); + console.log('test', desktopViewMode === 'home'); + return ( { setDesktopViewMode('apps'); - // setIsOpenSideViewDirects(false) - // setIsOpenSideViewGroups(false) }} > @@ -134,7 +138,7 @@ export const DesktopSideBar = ({ label="Dev" disableWidth > - + )} diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index a4629ce..eaa89e6 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -1679,9 +1679,6 @@ export const Group = ({ width: '100%', flexDirection: 'column', cursor: 'pointer', - borderColor: theme.palette.primary, - borderWidth: '1px', - borderStyle: 'solid', padding: '2px', borderRadius: '2px', background: @@ -1721,6 +1718,7 @@ export const Group = ({ theme.palette.text.primary, textWrap: 'wrap', overflow: 'hidden', + fontSize: '16px', }, }} // Change the color of the primary text secondaryTypographyProps={{ @@ -1781,7 +1779,7 @@ export const Group = ({
    ); }; - + console.log('groupsProperties', groupsProperties); const renderGroups = () => { return (
    + {/* */} { const [oldPinnedApps, setOldPinnedApps] = useRecoilState(oldPinnedAppsAtom); const [anchorEl, setAnchorEl] = useState(null); const { show } = useContext(MyContext); - + const theme = useTheme(); const { t } = useTranslation(['core']); const hasChanged = useMemo(() => { @@ -229,13 +236,28 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { postProcess: 'capitalize', })} selected={false} + color={ + hasChanged && !isLoading + ? '#5EB049' + : theme.palette.text.secondary + } > ) : ( - + )} From c734b8016cc37210d4ccd7998a9963d223770b76 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 23 Apr 2025 08:55:50 +0200 Subject: [PATCH 264/717] Simplify selector with a unique icon --- src/components/Theme/ThemeSelector.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Theme/ThemeSelector.tsx b/src/components/Theme/ThemeSelector.tsx index b962e1f..bab403c 100644 --- a/src/components/Theme/ThemeSelector.tsx +++ b/src/components/Theme/ThemeSelector.tsx @@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next'; const ThemeSelector = () => { const { t } = useTranslation(['core']); + const { themeMode, toggleTheme } = useThemeContext(); return ( From 9a5970731aed419d482fe5edb530bcf590110eb9 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 12:33:55 +0300 Subject: [PATCH 265/717] remove full screen on double click --- src/App.tsx | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 38b8337..0f2d4b1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -102,7 +102,6 @@ import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil'; import { canSaveSettingToQdnAtom, enabledDevModeAtom, - fullScreenAtom, groupsPropertiesAtom, hasSettingsChangedAtom, isDisabledEditorEnterAtom, @@ -115,7 +114,6 @@ import { settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom, } from './atoms/global'; -import { useAppFullScreen } from './useAppFullscreen'; import { NotAuthenticated } from './ExtStates/NotAuthenticated'; import { handleGetFileFromIndexedDB } from './utils/indexedDB'; import { Wallets } from './Wallets'; @@ -401,12 +399,10 @@ function App() { const qortalRequestCheckbox1Ref = useRef(null); useRetrieveDataLocalStorage(userInfo?.address); useQortalGetSaveSettings(userInfo?.name, extState === 'authenticated'); - const [fullScreen, setFullScreen] = useRecoilState(fullScreenAtom); const [isEnabledDevMode, setIsEnabledDevMode] = useRecoilState(enabledDevModeAtom); const setIsDisabledEditorEnter = useSetRecoilState(isDisabledEditorEnterAtom); const [isOpenMinting, setIsOpenMinting] = useState(false); - const { toggleFullScreen } = useAppFullScreen(setFullScreen); const generatorRef = useRef(null); const exportSeedphrase = () => { @@ -450,24 +446,6 @@ function App() { } }, [extState, walletToBeDownloaded, shownTutorialsInitiated]); - useEffect(() => { - // Attach a global event listener for double-click - const handleDoubleClick = () => { - toggleFullScreen(); - }; - - // Add the event listener to the root HTML document - document.documentElement.addEventListener('dblclick', handleDoubleClick); - - // Clean up the event listener on unmount - return () => { - document.documentElement.removeEventListener( - 'dblclick', - handleDoubleClick - ); - }; - }, [toggleFullScreen]); - //resets for recoil const resetAtomSortablePinnedAppsAtom = useResetRecoilState( sortablePinnedAppsAtom From a81910cf3d9b3d93324b7df34a516f983f262df0 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 12:44:52 +0300 Subject: [PATCH 266/717] save theme to localstorage for persistance --- src/components/Theme/ThemeContext.tsx | 38 +++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/components/Theme/ThemeContext.tsx b/src/components/Theme/ThemeContext.tsx index 5a5abb8..d2e7048 100644 --- a/src/components/Theme/ThemeContext.tsx +++ b/src/components/Theme/ThemeContext.tsx @@ -1,4 +1,11 @@ -import { createContext, useContext, useState, useMemo } from 'react'; +import { + createContext, + useContext, + useState, + useMemo, + useEffect, + useCallback, +} from 'react'; import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles'; import { darkTheme } from '../../styles/theme-dark'; import { lightTheme } from '../../styles/theme-light'; @@ -17,9 +24,36 @@ export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { ); const toggleTheme = () => { - setThemeMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light')); + setThemeMode((prevMode) => { + const newMode = prevMode === 'light' ? 'dark' : 'light'; + + const themeProperties = { + mode: newMode, + }; + + localStorage.setItem('saved_ui_theme', JSON.stringify(themeProperties)); + + return newMode; + }); }; + const getSavedTheme = useCallback(async () => { + try { + const themeProperties = JSON.parse( + localStorage.getItem(`saved_ui_theme`) || '{}' + ); + + const theme = themeProperties?.mode || 'light'; + setThemeMode(theme); + } catch (error) { + console.log('error', error); + } + }, []); + + useEffect(() => { + getSavedTheme(); + }, [getSavedTheme]); + return ( {children} From 5030723285c0621e125ab19cc09ccc398e7b9e00 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 10:34:15 +0300 Subject: [PATCH 267/717] added the option to change the wallet's password --- public/locales/de/auth.json | 8 +- public/locales/en/auth.json | 8 +- public/locales/es/auth.json | 8 +- public/locales/fr/auth.json | 8 +- public/locales/it/auth.json | 8 +- public/locales/ru/auth.json | 8 +- src/App.tsx | 136 +------------- src/ExtStates/NotAuthenticated.tsx | 2 +- src/components/Auth/DownloadWallet.tsx | 248 +++++++++++++++++++++++++ src/components/Explore/Explore.tsx | 2 +- 10 files changed, 302 insertions(+), 134 deletions(-) create mode 100644 src/components/Auth/DownloadWallet.tsx diff --git a/public/locales/de/auth.json b/public/locales/de/auth.json index fc83d42..5b14183 100644 --- a/public/locales/de/auth.json +++ b/public/locales/de/auth.json @@ -31,7 +31,13 @@ "return_to_list": "zurück zur Liste", "wallet": { "password_confirmation": "Wallet-Passwort bestätigen", - "password": "Wallet-Passwort" + "password": "Wallet-Passwort", + "keep_password": "aktuelles Passwort beibehalten", + "new_password": "neues Passwort", + "error": { + "missing_new_password": "bitte neues Passwort eingeben", + "missing_password": "bitte Passwort eingeben" + } }, "welcome": "willkommen bei" } diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json index ebc4fa8..6591afe 100644 --- a/public/locales/en/auth.json +++ b/public/locales/en/auth.json @@ -36,7 +36,13 @@ }, "wallet": { "password_confirmation": "confirm wallet password", - "password": "wallet password" + "password": "wallet password", + "keep_password": "keep current password", + "new_password": "new password", + "error": { + "missing_new_password": "please enter a new password", + "missing_password": "please enter your password" + } }, "welcome": "welcome to" } diff --git a/public/locales/es/auth.json b/public/locales/es/auth.json index 3c62c24..5d4b8b6 100644 --- a/public/locales/es/auth.json +++ b/public/locales/es/auth.json @@ -31,7 +31,13 @@ "return_to_list": "volver a la lista", "wallet": { "password_confirmation": "confirmar contraseña del monedero", - "password": "contraseña del monedero" + "password": "contraseña del monedero", + "keep_password": "mantener la contraseña actual", + "new_password": "nueva contraseña", + "error": { + "missing_new_password": "por favor ingresa una nueva contraseña", + "missing_password": "por favor ingresa tu contraseña" + } }, "welcome": "bienvenido a" } diff --git a/public/locales/fr/auth.json b/public/locales/fr/auth.json index 06d08d5..f314d3e 100644 --- a/public/locales/fr/auth.json +++ b/public/locales/fr/auth.json @@ -31,7 +31,13 @@ "return_to_list": "retour à la liste", "wallet": { "password_confirmation": "confirmer le mot de passe du portefeuille", - "password": "mot de passe du portefeuille" + "password": "mot de passe du portefeuille", + "keep_password": "garder le mot de passe actuel", + "new_password": "nouveau mot de passe", + "error": { + "missing_new_password": "veuillez entrer un nouveau mot de passe", + "missing_password": "veuillez entrer votre mot de passe" + } }, "welcome": "bienvenue sur" } diff --git a/public/locales/it/auth.json b/public/locales/it/auth.json index 345c154..ad00833 100644 --- a/public/locales/it/auth.json +++ b/public/locales/it/auth.json @@ -30,7 +30,13 @@ "password": "password", "wallet": { "password_confirmation": "conferma la password del wallet", - "password": "password del wallet" + "password": "password del wallet", + "keep_password": "mantieni la password attuale", + "new_password": "nuova password", + "error": { + "missing_new_password": "per favore inserisci una nuova password", + "missing_password": "per favore inserisci la tua password" + } }, "return_to_list": "ritorna alla lista", "welcome": "benvenuto in" diff --git a/public/locales/ru/auth.json b/public/locales/ru/auth.json index 3ae2327..797220b 100644 --- a/public/locales/ru/auth.json +++ b/public/locales/ru/auth.json @@ -31,7 +31,13 @@ "return_to_list": "вернуться к списку", "wallet": { "password_confirmation": "подтвердите пароль кошелька", - "password": "пароль кошелька" + "password": "пароль кошелька", + "keep_password": "сохранить текущий пароль", + "new_password": "новый пароль", + "error": { + "missing_new_password": "пожалуйста, введите новый пароль", + "missing_password": "пожалуйста, введите ваш пароль" + } }, "welcome": "добро пожаловать в" } diff --git a/src/App.tsx b/src/App.tsx index 0f2d4b1..fe375b8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -136,6 +136,7 @@ import { PdfViewer } from './common/PdfViewer'; import ThemeSelector from './components/Theme/ThemeSelector.tsx'; import { useTranslation } from 'react-i18next'; import LanguageSelector from './components/Language/LanguageSelector.tsx'; +import { DownloadWallet } from './components/Auth/DownloadWallet.tsx'; type extStates = | 'not-authenticated' @@ -871,27 +872,6 @@ function App() { } }, [authenticatedMode]); - const confirmPasswordToDownload = async () => { - try { - setWalletToBeDownloadedError(''); - if (!walletToBeDownloadedPassword) { - setSendPaymentError('Please enter your password'); - return; - } - setIsLoading(true); - await new Promise((res) => { - setTimeout(() => { - res(); - }, 250); - }); - const res = await saveWalletFunc(walletToBeDownloadedPassword); - } catch (error: any) { - setWalletToBeDownloadedError(error?.message); - } finally { - setIsLoading(false); - } - }; - const saveFileToDiskFunc = async () => { try { await saveFileToDisk( @@ -1638,7 +1618,7 @@ function App() { textTransform: 'uppercase', }} > - {t('core:wallet_other')} + {t('core:wallet.wallet_other')} } placement="left" @@ -2652,110 +2632,14 @@ function App() { )} {extState === 'download-wallet' && ( - <> - - - - - - - -
    - -
    - - - - - - {t('auth:download_account', { postProcess: 'capitalize' })} - - - - - - {!walletToBeDownloaded && ( - <> - - {t('auth:wallet.password_confirmation', { - postProcess: 'capitalize', - })} - - - - - - setWalletToBeDownloadedPassword(e.target.value) - } - /> - - - - - {t('auth:password_confirmation', { - postProcess: 'capitalize', - })} - - {walletToBeDownloadedError} - - )} - - {walletToBeDownloaded && ( - <> - { - await saveFileToDiskFunc(); - await showInfo({ - message: t('auth:keep_secure', { - postProcess: 'capitalize', - }), - }); - }} - > - {t('auth:download_account', { - postProcess: 'capitalize', - })} - - - )} - + )} {extState === 'create-wallet' && ( <> diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 2e3d598..1f58360 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -693,7 +693,7 @@ export const NotAuthenticated = ({ visibility: importedApiKey ? 'visible' : 'hidden', }} > - {t('auth:apikey.key', { postProcess: 'capitalize' })}: $ + {t('auth:apikey.key', { postProcess: 'capitalize' })}:{' '} {importedApiKey} diff --git a/src/components/Auth/DownloadWallet.tsx b/src/components/Auth/DownloadWallet.tsx new file mode 100644 index 0000000..12b46f4 --- /dev/null +++ b/src/components/Auth/DownloadWallet.tsx @@ -0,0 +1,248 @@ +import { Box, Checkbox, FormControlLabel, Typography } from '@mui/material'; +import { Spacer } from '../../common/Spacer'; +import { Return } from '../../assets/Icons/Return'; +import { CustomButton, CustomLabel, TextP } from '../../styles/App-styles'; +import { PasswordField } from '../PasswordField/PasswordField'; +import { ErrorText } from '../ErrorText/ErrorText'; +import Logo1Dark from '../../assets/svgs/Logo1Dark.svg'; +import { useTranslation } from 'react-i18next'; +import { saveFileToDisk } from '../../utils/generateWallet/generateWallet'; +import { useState } from 'react'; +import { decryptStoredWallet } from '../../utils/decryptWallet'; +import PhraseWallet from '../../utils/generateWallet/phrase-wallet'; +import { crypto, walletVersion } from '../../constants/decryptWallet'; + +export const DownloadWallet = ({ + returnToMain, + setIsLoading, + showInfo, + rawWallet, + setWalletToBeDownloaded, + walletToBeDownloaded, +}) => { + const [walletToBeDownloadedPassword, setWalletToBeDownloadedPassword] = + useState(''); + const [newPassword, setNewPassword] = useState(''); + const [keepCurrentPassword, setKeepCurrentPassword] = useState(true); + + const [walletToBeDownloadedError, setWalletToBeDownloadedError] = + useState(''); + + const { t } = useTranslation(['auth']); + + const saveFileToDiskFunc = async () => { + try { + await saveFileToDisk( + walletToBeDownloaded.wallet, + walletToBeDownloaded.qortAddress + ); + } catch (error: any) { + setWalletToBeDownloadedError(error?.message); + } + }; + + const saveWalletFunc = async (password: string, newPassword) => { + let wallet = structuredClone(rawWallet); + + const res = await decryptStoredWallet(password, wallet); + const wallet2 = new PhraseWallet(res, wallet?.version || walletVersion); + const passwordToUse = newPassword || password; + wallet = await wallet2.generateSaveWalletData( + passwordToUse, + crypto.kdfThreads, + () => {} + ); + + setWalletToBeDownloaded({ + wallet, + qortAddress: rawWallet.address0, + }); + return { + wallet, + qortAddress: rawWallet.address0, + }; + }; + + const confirmPasswordToDownload = async () => { + try { + setWalletToBeDownloadedError(''); + if (!keepCurrentPassword && !newPassword) { + setWalletToBeDownloadedError( + t('auth:wallet.error.missing_new_password', { + postProcess: 'capitalize', + }) + ); + return; + } + if (!walletToBeDownloadedPassword) { + setWalletToBeDownloadedError( + t('auth:wallet.error.missing_password', { postProcess: 'capitalize' }) + ); + return; + } + setIsLoading(true); + await new Promise((res) => { + setTimeout(() => { + res(); + }, 250); + }); + const newPasswordForWallet = !keepCurrentPassword ? newPassword : null; + const res = await saveWalletFunc( + walletToBeDownloadedPassword, + newPasswordForWallet + ); + } catch (error: any) { + setWalletToBeDownloadedError(error?.message); + } finally { + setIsLoading(false); + } + }; + + return ( + <> + + + + + + + +
    + +
    + + + + + + {t('auth:download_account', { postProcess: 'capitalize' })} + + + + + + {!walletToBeDownloaded && ( + <> + + {t('auth:wallet.password_confirmation', { + postProcess: 'capitalize', + })} + + + + + setWalletToBeDownloadedPassword(e.target.value)} + /> + + + + setKeepCurrentPassword(e.target.checked)} + checked={keepCurrentPassword} + edge="start" + tabIndex={-1} + disableRipple + /> + } + label={ + + + {t('auth:wallet.keep_password', { + postProcess: 'capitalize', + })} + + + } + /> + + {!keepCurrentPassword && ( + <> + + {t('auth:wallet.new_password', { + postProcess: 'capitalize', + })} + + + + setNewPassword(e.target.value)} + /> + + + )} + + + {t('auth:password_confirmation', { + postProcess: 'capitalize', + })} + + + {walletToBeDownloadedError} + + )} + + {walletToBeDownloaded && ( + <> + { + await saveFileToDiskFunc(); + await showInfo({ + message: t('auth:keep_secure', { + postProcess: 'capitalize', + }), + }); + }} + > + {t('auth:download_account', { + postProcess: 'capitalize', + })} + + + )} + + ); +}; diff --git a/src/components/Explore/Explore.tsx b/src/components/Explore/Explore.tsx index ae1988e..266fdd5 100644 --- a/src/components/Explore/Explore.tsx +++ b/src/components/Explore/Explore.tsx @@ -124,7 +124,7 @@ export const Explore = ({ setDesktopViewMode }) => { fontSize: '1rem', }} > - {t('core:wallet_other', { postProcess: 'capitalize' })} + {t('core:wallet.wallet_other', { postProcess: 'capitalize' })} From 53facb9f2cf4d61ddaa472b28ecdc293f09f4461 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 15:39:45 +0200 Subject: [PATCH 268/717] Detect click outside of the component (manage no language selection) --- src/components/Language/LanguageSelector.tsx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/components/Language/LanguageSelector.tsx b/src/components/Language/LanguageSelector.tsx index 6241347..751c0a9 100644 --- a/src/components/Language/LanguageSelector.tsx +++ b/src/components/Language/LanguageSelector.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { supportedLanguages } from '../../../i18n'; import { Tooltip, useTheme } from '@mui/material'; @@ -7,6 +7,7 @@ const LanguageSelector = () => { const { i18n, t } = useTranslation(['core']); const [showSelect, setShowSelect] = useState(false); const theme = useTheme(); + const selectorRef = useRef(null); const handleChange = (e) => { const newLang = e.target.value; @@ -18,8 +19,23 @@ const LanguageSelector = () => { const { name, flag } = supportedLanguages[currentLang] || supportedLanguages['en']; + // Detect clicks outside the component + useEffect(() => { + const handleClickOutside = (event) => { + if (selectorRef.current && !selectorRef.current.contains(event.target)) { + setShowSelect(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + return (
    { }} value={currentLang} onChange={handleChange} - onBlur={() => setShowSelect(false)} + autoFocus > {Object.entries(supportedLanguages).map(([code, { name }]) => (
    )} @@ -2341,18 +2342,23 @@ export const Group = ({ > {' '} - You are not part of the encrypted group of members. Wait - until an admin re-encrypts the keys. + {t('group:message.generic.not_part_group', { + postProcess: 'capitalize', + })} - Only unencrypted messages will be displayed. + {t('group:message.generic.only_encrypted', { + postProcess: 'capitalize', + })} - Try notifying an admin from the list of admins below: + {t('group:message.generic.notify_admins', { + postProcess: 'capitalize', + })} {adminsWithNames.map((admin) => { @@ -2372,7 +2378,9 @@ export const Group = ({ variant="contained" onClick={() => notifyAdmin(admin)} > - Notify + {t('core:action.notify', { + postProcess: 'capitalize', + })} ); @@ -2592,14 +2600,19 @@ export const Group = ({ open={isLoadingGroup} info={{ message: - isLoadingGroupMessage || 'Setting up group... please wait.', + isLoadingGroupMessage || + t('group:message.generic.setting_group', { + postProcess: 'capitalize', + }), }} /> diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index 3835c3e..af81863 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -169,7 +169,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { .catch((error) => { rej( error.message || - t('core:result.error.generic', { postProcess: 'capitalize' }) + t('core:message.error.generic', { postProcess: 'capitalize' }) ); }); }); @@ -178,7 +178,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { setSettingsQdnLastUpdated(Date.now()); setInfoSnack({ type: 'success', - message: t('core:result.success.publish_qdn', { + message: t('core:message.success.publish_qdn', { postProcess: 'capitalize', }), }); @@ -191,7 +191,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { type: 'error', message: error?.message || - t('core:result.error.save_qdn', { + t('core:message.error.save_qdn', { postProcess: 'capitalize', }), }); From d89a98ec4f89a74cb393f4782c7e3d8842493927 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 16:45:31 +0200 Subject: [PATCH 271/717] Translate inviteMember --- public/locales/en/core.json | 1 + public/locales/en/group.json | 4 +++ src/components/Group/InviteMember.tsx | 43 ++++++++++++++++----------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index 407571c..5ba27e8 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -15,6 +15,7 @@ "edit": "edit", "export": "export", "import": "import", + "invite": "invite", "join": "join", "logout": "logout", "notify": "notify" diff --git a/public/locales/en/group.json b/public/locales/en/group.json index 220aa9f..2514468 100644 --- a/public/locales/en/group.json +++ b/public/locales/en/group.json @@ -3,6 +3,7 @@ "create_group": "create group", "find_group": "find group", "join_group": "join group", + "invite_member": "invite member", "refetch_page": "refetch page", "return_to_thread": "return to threads" }, @@ -21,8 +22,10 @@ "open": "open (public)", "type": "group type" }, + "invitation_expiry": "invitation Expiry Time", "question": { "create_group": "would you like to perform an CREATE_GROUP transaction?", + "group_invite": "would you like to perform a GROUP_INVITE transaction?", "join_group": "would you like to perform an JOIN_GROUP transaction?", "provide_thread": "please provide a thread title" }, @@ -45,6 +48,7 @@ "group_creation": "successfully created group. It may take a couple of minutes for the changes to propagate", "group_creation_name": "created group {{group_name}}: awaiting confirmation", "group_creation_label": "created group {{name}}: success!", + "group_invite": "successfully invited {{value}}. It may take a couple of minutes for the changes to propagate", "join_creation": "successfully requested to join group. It may take a couple of minutes for the changes to propagate", "group_join_name": "joined group {{group_name}}: awaiting confirmation", "group_join_label": "joined group {{name}}: success!", diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx index de3dc20..db01b59 100644 --- a/src/components/Group/InviteMember.tsx +++ b/src/components/Group/InviteMember.tsx @@ -4,16 +4,21 @@ import { useState } from 'react'; import { Spacer } from '../../common/Spacer'; import { Label } from './AddGroup'; import { getFee } from '../../background'; +import { useTranslation } from 'react-i18next'; export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const [value, setValue] = useState(''); const [expiryTime, setExpiryTime] = useState('259200'); const [isLoadingInvite, setIsLoadingInvite] = useState(false); + const { t } = useTranslation(['core', 'group']); + const inviteMember = async () => { try { const fee = await getFee('GROUP_INVITE'); await show({ - message: 'Would you like to perform a GROUP_INVITE transaction?', + message: t('group:question.group_invite', { + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); setIsLoadingInvite(true); @@ -27,10 +32,12 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { }) .then((response) => { if (!response?.error) { - // TODO translate setInfoSnack({ type: 'success', - message: `Successfully invited ${value}. It may take a couple of minutes for the changes to propagate`, + message: t('group:message.success.group_invite', { + value: value, + postProcess: 'capitalize', + }), }); setOpenSnack(true); res(response); @@ -72,7 +79,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { flexDirection: 'column', }} > - Invite member + {t('group:action.invite_member', { postProcess: 'capitalize' })} { onChange={(e) => setValue(e.target.value)} /> - + { loading={isLoadingInvite} onClick={inviteMember} > - Invite + {t('core:action.invite', { postProcess: 'capitalize' })} ); From ea6b9bf93d6856dc02f15b2098ab9392db9f1bf5 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 16:53:17 +0200 Subject: [PATCH 272/717] Translate groupJoinRequests --- public/locales/en/group.json | 3 +++ src/components/Group/GroupInvites.tsx | 11 +++++++--- src/components/Group/GroupJoinRequests.tsx | 24 ++++++++++++---------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/public/locales/en/group.json b/public/locales/en/group.json index 2514468..d59e6db 100644 --- a/public/locales/en/group.json +++ b/public/locales/en/group.json @@ -23,6 +23,7 @@ "type": "group type" }, "invitation_expiry": "invitation Expiry Time", + "join_requests": "join requests", "question": { "create_group": "would you like to perform an CREATE_GROUP transaction?", "group_invite": "would you like to perform a GROUP_INVITE transaction?", @@ -32,6 +33,8 @@ "message": { "generic": { "encryption_key": "the group's first common encryption key is in the process of creation. Please wait a few minutes for it to be retrieved by the network. Checking every 2 minutes...", + "group_invited_you": "{{group}} has invited you", + "no_display": "nothing to display", "no_selection": "no group selected", "not_part_group": "you are not part of the encrypted group of members. Wait until an admin re-encrypts the keys.", "only_encrypted": "only unencrypted messages will be displayed.", diff --git a/src/components/Group/GroupInvites.tsx b/src/components/Group/GroupInvites.tsx index c753fdb..9883911 100644 --- a/src/components/Group/GroupInvites.tsx +++ b/src/components/Group/GroupInvites.tsx @@ -69,7 +69,7 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { {t('group:group_invites', { postProcess: 'capitalize' })}{' '} {groupsWithJoinRequests?.length > 0 && @@ -130,7 +130,9 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { fontWeight: 400, }} > - Nothing to display + {t('group:message.generic.no_display', { + postProcess: 'capitalize', + })} )} @@ -177,7 +179,10 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { fontWeight: 400, }, }} - primary={`${group?.groupName} has invited you`} + primary={t('group:message.generic.group_invited_you', { + group: group?.groupName, + postProcess: 'capitalize', + })} /> diff --git a/src/components/Group/GroupJoinRequests.tsx b/src/components/Group/GroupJoinRequests.tsx index 4bc6b21..c7bb32e 100644 --- a/src/components/Group/GroupJoinRequests.tsx +++ b/src/components/Group/GroupJoinRequests.tsx @@ -14,6 +14,7 @@ import { myGroupsWhereIAmAdminAtom } from '../../atoms/global'; import { useSetRecoilState } from 'recoil'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import { useTranslation } from 'react-i18next'; export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2); export const GroupJoinRequests = ({ @@ -27,7 +28,7 @@ export const GroupJoinRequests = ({ setDesktopViewMode, }) => { const [isExpanded, setIsExpanded] = React.useState(false); - + const { t } = useTranslation(['core', 'group']); const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState( [] ); @@ -139,9 +140,9 @@ export const GroupJoinRequests = ({ - Join Requests{' '} + {t('group:join_requests', { postProcess: 'capitalize' })}{' '} {filteredJoinRequests?.filter((group) => group?.data?.length > 0) ?.length > 0 && ` (${filteredJoinRequests?.filter((group) => group?.data?.length > 0)?.length})`} @@ -163,14 +164,13 @@ export const GroupJoinRequests = ({ {loading && filteredJoinRequests.length === 0 && ( @@ -204,18 +204,20 @@ export const GroupJoinRequests = ({ color: 'rgba(255, 255, 255, 0.2)', }} > - Nothing to display + {t('group:message.generic.no_display', { + postProcess: 'capitalize', + })} )} {filteredJoinRequests?.map((group) => { From 5daf76ace60e9354d570f8fa4b96585ff2b099e0 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 16:58:02 +0200 Subject: [PATCH 273/717] Translate listOfBans --- public/locales/en/group.json | 6 +++++- src/components/Group/ListOfBans.tsx | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/public/locales/en/group.json b/public/locales/en/group.json index d59e6db..345c6d0 100644 --- a/public/locales/en/group.json +++ b/public/locales/en/group.json @@ -1,5 +1,6 @@ { "action": { + "cancel_ban": "cancel ban", "create_group": "create group", "find_group": "find group", "join_group": "join group", @@ -9,6 +10,7 @@ }, "advanced_options": "advanced options", "approval_threshold": "group Approval Threshold (number / percentage of Admins that must approve a transaction)", + "ban_list": "ban list", "block_delay": { "minimum": "minimum Block delay for Group Transaction Approvals", "maximum": "maximum Block delay for Group Transaction Approvals" @@ -25,6 +27,7 @@ "invitation_expiry": "invitation Expiry Time", "join_requests": "join requests", "question": { + "cancel_ban": "would you like to perform a CANCEL_GROUP_BAN transaction?", "create_group": "would you like to perform an CREATE_GROUP transaction?", "group_invite": "would you like to perform a GROUP_INVITE transaction?", "join_group": "would you like to perform an JOIN_GROUP transaction?", @@ -55,7 +58,8 @@ "join_creation": "successfully requested to join group. It may take a couple of minutes for the changes to propagate", "group_join_name": "joined group {{group_name}}: awaiting confirmation", "group_join_label": "joined group {{name}}: success!", - "loading_threads": "loading threads... please wait." + "loading_threads": "loading threads... please wait.", + "unbanned_user": "successfully unbanned user. It may take a couple of minutes for the changes to propagate" } } } diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index 850ce13..0c522e0 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -18,6 +18,7 @@ import { getNameInfo } from './Group'; import { getFee } from '../../background'; import { LoadingButton } from '@mui/lab'; import { getBaseApiReact } from '../../App'; +import { useTranslation } from 'react-i18next'; export const getMemberInvites = async (groupNumber) => { const response = await fetch( @@ -55,6 +56,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const listRef = useRef(); const [isLoadingUnban, setIsLoadingUnban] = useState(false); + const { t } = useTranslation(['core', 'group']); const getInvites = async (groupId) => { try { @@ -87,7 +89,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { // TODO translate const fee = await getFee('CANCEL_GROUP_BAN'); await show({ - message: 'Would you like to perform a CANCEL_GROUP_BAN transaction?', + message: t('group:question.cancel_ban', { postProcess: 'capitalize' }), publishFee: fee.fee + ' QORT', }); setIsLoadingUnban(true); @@ -103,8 +105,9 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { setIsLoadingUnban(false); setInfoSnack({ type: 'success', - message: - 'Successfully unbanned user. It may take a couple of minutes for the changes to propagate', + message: t('group:message.success.unbanned_user', { + postProcess: 'capitalize', + }), }); handlePopoverClose(); setOpenSnack(true); @@ -127,6 +130,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { }); }); } catch (error) { + console.log(error); } finally { setIsLoadingUnban(false); } @@ -177,10 +181,13 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { variant="contained" onClick={() => handleCancelBan(member?.offender)} > - Cancel Ban + {t('group:action.cancel_ban', { + postProcess: 'capitalize', + })} + handlePopoverOpen(event, index)} > @@ -205,7 +212,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { return (
    -

    Ban list

    +

    {t('group:ban_list', { postProcess: 'capitalize' })}

    Date: Sat, 26 Apr 2025 16:59:39 +0200 Subject: [PATCH 274/717] Import react components --- src/components/Group/Settings.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index f5a952c..4af646b 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { forwardRef, Fragment, ReactElement, Ref, useEffect } from 'react'; import Dialog from '@mui/material/Dialog'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; @@ -44,11 +44,11 @@ const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ }, })); -const Transition = React.forwardRef(function Transition( +const Transition = forwardRef(function Transition( props: TransitionProps & { - children: React.ReactElement; + children: ReactElement; }, - ref: React.Ref + ref: Ref ) { return ; }); @@ -111,12 +111,12 @@ export const Settings = ({ address, open, setOpen }) => { } }; - React.useEffect(() => { + useEffect(() => { getUserSettings(); }, []); return ( - + { )} - + ); }; From fa63ac493eaf130bf776f77f384cf7053fabafb6 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 17:00:20 +0200 Subject: [PATCH 275/717] Remove comment --- src/components/Group/ListOfBans.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index 0c522e0..355f1d8 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -86,7 +86,6 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const handleCancelBan = async (address) => { try { - // TODO translate const fee = await getFee('CANCEL_GROUP_BAN'); await show({ message: t('group:question.cancel_ban', { postProcess: 'capitalize' }), From 38c3cc92aace516745ba3f1993052d7e5dc38631 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 18:03:27 +0300 Subject: [PATCH 276/717] improving bg colors and adding borders --- src/App.tsx | 1 + src/components/Chat/AnnouncementItem.tsx | 1 - src/components/Chat/ChatGroup.tsx | 5 +- src/components/Chat/ChatList.tsx | 10 +- src/components/Chat/ChatOptions.tsx | 1 - src/components/Chat/MessageItem.tsx | 11 +- src/components/DesktopSideBar.tsx | 2 + src/components/Embeds/AttachmentEmbed.tsx | 499 +++++++++--------- src/components/Embeds/ImageEmbed.tsx | 434 +++++++-------- src/components/Group/Group.tsx | 6 +- .../Group/ListOfGroupPromotions.tsx | 3 - src/styles/theme-dark.ts | 7 +- src/styles/theme-light.ts | 11 +- src/styles/theme.d.ts | 19 + 14 files changed, 527 insertions(+), 483 deletions(-) create mode 100644 src/styles/theme.d.ts diff --git a/src/App.tsx b/src/App.tsx index a4f60f6..35dc80f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1487,6 +1487,7 @@ function App() { sx={{ height: '100%', justifyContent: 'space-between', + borderLeft: `1px solid ${theme.palette.border.subtle}`, }} > {message?.name} diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index e79bffe..2a86f1d 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -997,6 +997,7 @@ export const ChatGroup = ({ opacity: hide ? 0 : 1, position: hide ? 'absolute' : 'relative', width: '100%', + padding: '10px', }} >
    )} {showScrollDownButton && !showScrollButton && ( - + )}
    {enableMentions && (hasSecretKey || isPrivate === false) && ( diff --git a/src/components/Chat/ChatOptions.tsx b/src/components/Chat/ChatOptions.tsx index e95fa5b..ddb3a3f 100644 --- a/src/components/Chat/ChatOptions.tsx +++ b/src/components/Chat/ChatOptions.tsx @@ -767,7 +767,6 @@ const ShowMessage = ({ message, goToMessage, messages }) => { sx={{ fontWight: 600, fontFamily: 'Inter', - color: 'cadetBlue', }} > {message?.senderName} diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index a40fca8..b365d73 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -248,7 +248,6 @@ export const MessageItem = React.memo( sx={{ fontWight: 600, fontFamily: 'Inter', - color: 'cadetBlue', }} > {message?.senderName || message?.sender} @@ -304,7 +303,7 @@ export const MessageItem = React.memo( { return ( { > { + resourceData, + resourceDetails, + owner, + refresh, + openExternal, + external, + isLoadingParent, + errorMsg, + encryptionType, + selectedGroupId, +}) => { + const [isOpen, setIsOpen] = useState(true); + const { downloadResource } = useContext(MyContext); - const [isOpen, setIsOpen] = useState(true); - const { downloadResource } = useContext(MyContext); - - const saveToDisk = async ()=> { - const { name, service, identifier } = resourceData; - - const url = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}`; - fetch(url) - .then(response => response.blob()) - .then(async blob => { - await saveFileToDiskGeneric(blob, resourceData?.fileName) - }) - .catch(error => { - console.error("Error fetching the video:", error); - }); - } - - const saveToDiskEncrypted = async ()=> { - let blobUrl + const saveToDisk = async () => { + const { name, service, identifier } = resourceData; + + const url = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}`; + fetch(url) + .then((response) => response.blob()) + .then(async (blob) => { + await saveFileToDiskGeneric(blob, resourceData?.fileName); + }) + .catch((error) => { + console.error('Error fetching the video:', error); + }); + }; + + const saveToDiskEncrypted = async () => { + let blobUrl; + try { + const { name, service, identifier, key } = resourceData; + + const url = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?encoding=base64`; + const res = await fetch(url); + const data = await res.text(); + let decryptedData; try { - const { name, service, identifier,key } = resourceData; - - const url = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?encoding=base64`; - const res = await fetch(url) - const data = await res.text(); - let decryptedData - try { - if(key && encryptionType === 'private'){ - decryptedData = await window.sendMessage( - "DECRYPT_DATA_WITH_SHARING_KEY", - - { - encryptedData: data, - key: decodeURIComponent(key), - } - - ); - } - if(encryptionType === 'group'){ - decryptedData = await window.sendMessage( - "DECRYPT_QORTAL_GROUP_DATA", - - { - data64: data, - groupId: selectedGroupId, - } - - ); - } - } catch (error) { - throw new Error('Unable to decrypt') + if (key && encryptionType === 'private') { + decryptedData = await window.sendMessage( + 'DECRYPT_DATA_WITH_SHARING_KEY', + + { + encryptedData: data, + key: decodeURIComponent(key), + } + ); + } + if (encryptionType === 'group') { + decryptedData = await window.sendMessage( + 'DECRYPT_QORTAL_GROUP_DATA', + + { + data64: data, + groupId: selectedGroupId, + } + ); } - - if (!decryptedData || decryptedData?.error) throw new Error("Could not decrypt data"); - blobUrl = base64ToBlobUrl(decryptedData, resourceData?.mimeType) - const response = await fetch(blobUrl); - const blob = await response.blob(); - await saveFileToDiskGeneric(blob, resourceData?.fileName) - } catch (error) { - console.error(error) - } finally { - if(blobUrl){ - URL.revokeObjectURL(blobUrl); - } - + throw new Error('Unable to decrypt'); + } + + if (!decryptedData || decryptedData?.error) + throw new Error('Could not decrypt data'); + blobUrl = base64ToBlobUrl(decryptedData, resourceData?.mimeType); + const response = await fetch(blobUrl); + const blob = await response.blob(); + await saveFileToDiskGeneric(blob, resourceData?.fileName); + } catch (error) { + console.error(error); + } finally { + if (blobUrl) { + URL.revokeObjectURL(blobUrl); } } - return ( - + - - + ATTACHMENT embed + + + + - ATTACHMENT embed - - + + {external && ( - - {external && ( - - - - )} - - - - - Created by {decodeIfEncoded(owner)} - - - {encryptionType === 'private' ? "ENCRYPTED" : encryptionType === 'group' ? 'GROUP ENCRYPTED' : "Not encrypted"} - - - - - - - {isLoadingParent && isOpen && ( - - {" "} - {" "} - )} - {errorMsg && ( - + + + + Created by {decodeIfEncoded(owner)} + + + {encryptionType === 'private' + ? 'ENCRYPTED' + : encryptionType === 'group' + ? 'GROUP ENCRYPTED' + : 'Not encrypted'} + + + + + {isLoadingParent && isOpen && ( + + {' '} + {' '} + + )} + {errorMsg && ( + + {' '} + - {" "} + {errorMsg} + {' '} + + )} + + + + + {resourceData?.fileName && ( + <> - {errorMsg} - {" "} - + {resourceData?.fileName} + + + )} - - - - - {resourceData?.fileName && ( + { + if (resourceDetails?.status?.status === 'READY') { + if (encryptionType) { + saveToDiskEncrypted(); + return; + } + saveToDisk(); + return; + } + downloadResource(resourceData); + }} + > + + + {resourceDetails?.status?.status === 'DOWNLOADED' + ? 'BUILDING' + : resourceDetails?.status?.status} + + {!resourceDetails && ( <> - {resourceData?.fileName} - + + Download File )} - { - if(resourceDetails?.status?.status === 'READY'){ - if(encryptionType){ - saveToDiskEncrypted() - return - } - saveToDisk() - return - } - downloadResource(resourceData) - }}> - - - {resourceDetails?.status?.status === 'DOWNLOADED' ? 'BUILDING' : resourceDetails?.status?.status} - {!resourceDetails && ( - <> - - Download File - - - )} - {resourceDetails && resourceDetails?.status?.status !== 'READY' && resourceDetails?.status?.status !== 'FAILED_TO_DOWNLOAD' && ( - <> - - Downloading: {resourceDetails?.status?.percentLoaded || '0'}% - - - )} - {resourceDetails && resourceDetails?.status?.status === 'READY' && ( - <> - - Save to Disk - - - )} - - - - - - - - - ); - }; \ No newline at end of file + {resourceDetails && + resourceDetails?.status?.status !== 'READY' && + resourceDetails?.status?.status !== 'FAILED_TO_DOWNLOAD' && ( + <> + + + Downloading:{' '} + {resourceDetails?.status?.percentLoaded || '0'}% + + + )} + {resourceDetails && + resourceDetails?.status?.status === 'READY' && ( + <> + + Save to Disk + + )} + + + + + + ); +}; diff --git a/src/components/Embeds/ImageEmbed.tsx b/src/components/Embeds/ImageEmbed.tsx index f1cc859..4122dfb 100644 --- a/src/components/Embeds/ImageEmbed.tsx +++ b/src/components/Embeds/ImageEmbed.tsx @@ -1,265 +1,265 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState } from 'react'; import { Card, CardContent, Typography, - Box, ButtonBase, Divider, Dialog, IconButton, +} from '@mui/material'; -} from "@mui/material"; - -import RefreshIcon from "@mui/icons-material/Refresh"; -import OpenInNewIcon from "@mui/icons-material/OpenInNew"; -import { CustomLoader } from "../../common/CustomLoader"; -import ImageIcon from "@mui/icons-material/Image"; -import CloseIcon from "@mui/icons-material/Close"; -import { decodeIfEncoded } from "../../utils/decode"; +import RefreshIcon from '@mui/icons-material/Refresh'; +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; +import { CustomLoader } from '../../common/CustomLoader'; +import ImageIcon from '@mui/icons-material/Image'; +import CloseIcon from '@mui/icons-material/Close'; +import { decodeIfEncoded } from '../../utils/decode'; export const ImageCard = ({ - image, - fetchImage, - owner, - refresh, - openExternal, - external, - isLoadingParent, - errorMsg, - encryptionType, - }) => { - const [isOpen, setIsOpen] = useState(true); - const [height, setHeight] = useState('400px') - useEffect(() => { - if (isOpen) { - fetchImage(); - } - }, [isOpen]); - - // useEffect(()=> { - // if(errorMsg){ - // setHeight('300px') - // } - // }, [errorMsg]) - - return ( - { + const [isOpen, setIsOpen] = useState(true); + const [height, setHeight] = useState('400px'); + useEffect(() => { + if (isOpen) { + fetchImage(); + } + }, [isOpen]); + + // useEffect(()=> { + // if(errorMsg){ + // setHeight('300px') + // } + // }, [errorMsg]) + + return ( + + - - + IMAGE embed + + + + - IMAGE embed - - + + {external && ( - - {external && ( - - - - )} + )} + + + + + Created by {decodeIfEncoded(owner)} + + + {encryptionType === 'private' + ? 'ENCRYPTED' + : encryptionType === 'group' + ? 'GROUP ENCRYPTED' + : 'Not encrypted'} + + + + + {isLoadingParent && isOpen && ( + + {' '} + {' '} - - - - Created by {decodeIfEncoded(owner)} - - - {encryptionType === 'private' ? "ENCRYPTED" : encryptionType === 'group' ? 'GROUP ENCRYPTED' : "Not encrypted"} - - - - - - {isLoadingParent && isOpen && ( - - {" "} - {" "} - - )} - {errorMsg && ( - - {" "} - - {errorMsg} - {" "} - - )} - - - - - - - - - ); - }; + {errorMsg} + {' '} + + )} + - export function ImageViewer({ src, alt = "" }) { - const [isFullscreen, setIsFullscreen] = useState(false); - - const handleOpenFullscreen = () => setIsFullscreen(true); - const handleCloseFullscreen = () => setIsFullscreen(false); - - return ( - <> - {/* Image in container */} + + + + + + + ); +}; + +export function ImageViewer({ src, alt = '' }) { + const [isFullscreen, setIsFullscreen] = useState(false); + + const handleOpenFullscreen = () => setIsFullscreen(true); + const handleCloseFullscreen = () => setIsFullscreen(false); + + return ( + <> + {/* Image in container */} + + {alt} + + + {/* Fullscreen Viewer */} + + {/* Close Button */} + + + + + {/* Fullscreen Image */} {alt} - - {/* Fullscreen Viewer */} - - - {/* Close Button */} - - - - - {/* Fullscreen Image */} - {alt} - - - - ); - } \ No newline at end of file + + + ); +} diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index eaa89e6..a32812c 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -1556,12 +1556,13 @@ export const Group = ({
    { sx={{ fontWight: 600, fontFamily: 'Inter', - color: 'cadetBlue', }} > {promotion?.name} @@ -698,7 +697,6 @@ export const ListOfGroupPromotions = () => { sx={{ fontWight: 600, fontFamily: 'Inter', - color: 'cadetBlue', }} > {promotion?.groupName} @@ -746,7 +744,6 @@ export const ListOfGroupPromotions = () => { sx={{ fontWight: 600, fontFamily: 'Inter', - color: 'cadetBlue', }} > {promotion?.data} diff --git a/src/styles/theme-dark.ts b/src/styles/theme-dark.ts index 9c9e610..fb348a8 100644 --- a/src/styles/theme-dark.ts +++ b/src/styles/theme-dark.ts @@ -15,12 +15,17 @@ const darkThemeOptions: ThemeOptions = { }, background: { default: 'rgb(49, 51, 56)', - paper: 'rgb(96, 96, 97)', + paper: 'rgb(62, 64, 68)', + surface: 'rgb(58, 60, 65)', }, text: { primary: 'rgb(255, 255, 255)', secondary: 'rgb(179, 179, 179)', }, + border: { + main: 'rgba(255, 255, 255, 0.12)', + subtle: 'rgba(255, 255, 255, 0.08)', + }, }, components: { MuiCard: { diff --git a/src/styles/theme-light.ts b/src/styles/theme-light.ts index 1bd114d..db025d3 100644 --- a/src/styles/theme-light.ts +++ b/src/styles/theme-light.ts @@ -6,21 +6,26 @@ const lightThemeOptions: ThemeOptions = { palette: { mode: 'light', primary: { - main: 'rgba(244, 244, 251, 1)', + main: 'rgb(162, 162, 221)', // old light becomes main dark: 'rgb(113, 198, 212)', - light: 'rgb(162, 162, 221)', + light: 'rgba(244, 244, 251, 1)', // former main becomes light }, secondary: { main: 'rgba(194, 222, 236, 1)', }, background: { default: 'rgba(250, 250, 250, 1)', - paper: 'rgb(228, 228, 228)', + paper: 'rgb(220, 220, 220)', // darker card background + surface: 'rgb(240, 240, 240)', // optional middle gray for replies, side panels }, text: { primary: 'rgba(0, 0, 0, 1)', secondary: 'rgba(82, 82, 82, 1)', }, + border: { + main: 'rgba(0, 0, 0, 0.12)', + subtle: 'rgba(0, 0, 0, 0.08)', + }, }, components: { MuiCard: { diff --git a/src/styles/theme.d.ts b/src/styles/theme.d.ts new file mode 100644 index 0000000..762b526 --- /dev/null +++ b/src/styles/theme.d.ts @@ -0,0 +1,19 @@ +import '@mui/material/styles'; + +declare module '@mui/material/styles' { + interface TypeBackground { + surface: string; + } + interface Palette { + border: { + main: string; + subtle: string; + }; + } + interface PaletteOptions { + border?: { + main?: string; + subtle?: string; + }; + } +} From cf998ed04cc916853f4b071df3876e42fe7d7715 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 17:06:23 +0200 Subject: [PATCH 277/717] Update italian translation --- public/locales/it/auth.json | 37 ++++++++------- public/locales/it/core.json | 92 ++++++++++++++++++++++++++---------- public/locales/it/group.json | 65 +++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 42 deletions(-) create mode 100644 public/locales/it/group.json diff --git a/public/locales/it/auth.json b/public/locales/it/auth.json index ad00833..513378f 100644 --- a/public/locales/it/auth.json +++ b/public/locales/it/auth.json @@ -6,38 +6,43 @@ }, "advanced_users": "per utenti avanzati", "apikey": { - "alternative": "alternativa: seleziona un file", - "change": "cambia la chiave API", - "enter": "inserisci la chiave API", - "import": "importa chiave API", + "alternative": "alternativa: selezione file", + "change": "cambia APIkey", + "enter": "inserisci APIkey", + "import": "importa APIkey", "key": "chiave API", - "select_valid": "selezione una chiave API valida" + "select_valid": "seleziona una APIkey valida" }, - "authenticate": "autenticazione", + "authenticate": "autentica", "build_version": "versione build", - "create_account": "crea un account", + "create_account": "crea account", "download_account": "scarica account", - "keep_secure": "metti al sicuro il file del tuo account", + "keep_secure": "mantieni sicuro il file del tuo account", "node": { - "choose": "scegli un nodo custom", - "custom_many": "nodi custom", - "use_custom": "use nodo custom", + "choose": "scegli nodo personalizzato", + "custom_many": "nodi personalizzati", + "use_custom": "usa nodo personalizzato", "use_local": "usa nodo locale", - "using": "nodo in uso", + "using": "utilizzo nodo", "using_public": "utilizzo nodo pubblico" }, - "password_confirmation": "confirma la password", "password": "password", + "password_confirmation": "conferma password", + "return_to_list": "torna alla lista", + "tips": { + "digital_id": "il tuo wallet è come la tua identità digitale su Qortal ed è il modo in cui accederai all'interfaccia utente di Qortal. Contiene il tuo indirizzo pubblico e il nome Qortal che sceglierai. Ogni transazione che esegui è collegata alla tua identità ed è qui che gestisci tutti i tuoi QORT e altre criptovalute scambiabili su Qortal.", + "new_account": "creare un account significa creare un nuovo wallet e un'identità digitale per iniziare a usare Qortal. Una volta creato l'account, potrai iniziare a ottenere QORT, acquistare un nome e un avatar, pubblicare video e blog, e molto altro.", + "new_users": "i nuovi utenti iniziano qui!" + }, "wallet": { - "password_confirmation": "conferma la password del wallet", + "password_confirmation": "conferma password del wallet", "password": "password del wallet", - "keep_password": "mantieni la password attuale", + "keep_password": "mantieni password corrente", "new_password": "nuova password", "error": { "missing_new_password": "per favore inserisci una nuova password", "missing_password": "per favore inserisci la tua password" } }, - "return_to_list": "ritorna alla lista", "welcome": "benvenuto in" } diff --git a/public/locales/it/core.json b/public/locales/it/core.json index 8a6c4a2..8319b22 100644 --- a/public/locales/it/core.json +++ b/public/locales/it/core.json @@ -1,71 +1,111 @@ { - "add": "aggiungi", - "cancel": "annulla", - "choose": "scegli", - "close": "chiudi", - "continue": "continua", + "action": { + "add": "aggiungi", + "accept": "accetta", + "backup_account": "backup account", + "backup_wallet": "backup wallet", + "cancel": "annulla", + "change": "cambia", + "change_language": "cambia lingua", + "choose": "scegli", + "close": "chiudi", + "continue": "continua", + "continue_logout": "continua con il logout", + "decline": "rifiuta", + "edit": "modifica", + "export": "esporta", + "import": "importa", + "invite": "invita", + "join": "unisciti", + "logout": "esci", + "notify": "notifica" + }, "core": { - "block_height": "altezza del blocco", + "block_height": "altezza blocco", "information": "informazioni core", "peers": "peer connessi", "version": "versione core" }, + "count": { + "none": "nessuno", + "one": "uno" + }, "description": "descrizione", - "edit": "modifica", - "export": "esporta", - "import": "importa", + "fee": { + "payment": "commissione di pagamento", + "publish": "commissione di pubblicazione" + }, + "page": { + "last": "ultimo", + "first": "primo", + "next": "successivo", + "previous": "precedente" + }, + "downloading_qdn": "scaricamento da QDN", "last_height": "ultima altezza", "loading": "caricamento...", - "logout": "disconnetti", - "minting_status": "stato del conio", + "loading_posts": "caricamento post... attendere prego.", + "message_us": "per favore scrivici su Telegram o Discord se hai bisogno di 4 QORT per iniziare a chattare senza limitazioni", + "minting_status": "stato minting", + "new_user": "sei un nuovo utente?", "payment_notification": "notifica di pagamento", "price": "prezzo", "q_mail": "q-mail", - "result": { + "message": { "error": { "generic": "si è verificato un errore", "incorrect_password": "password errata", "save_qdn": "impossibile salvare su QDN" }, "status": { - "minting": "(conio in corso)", - "not_minting": "(conio non attivo)", + "minting": "(minting)", + "not_minting": "(non minting)", "synchronized": "sincronizzato", "synchronizing": "sincronizzazione in corso" }, "success": { - "publish_qdn": "pubblicato con successo su QDN" + "order_submitted": "il tuo ordine di acquisto è stato inviato", + "publish_qdn": "pubblicato su QDN con successo", + "request_read": "ho letto questa richiesta", + "transfer": "il trasferimento è stato effettuato con successo!" } }, "save_options": { "no_pinned_changes": "attualmente non hai modifiche alle tue app appuntate", - "overwrite_changes": "l'app non è riuscita a scaricare le tue app appuntate salvate su QDN. Vuoi sovrascrivere queste modifiche?", + "overwrite_changes": "l'app non è riuscita a scaricare le tue app appuntate salvate su QDN. Vuoi sovrascrivere le modifiche?", "overwrite_qdn": "sovrascrivi su QDN", - "publish_qdn": "vuoi pubblicare le tue impostazioni su QDN (crittografate)?", - "qdn": "usa il salvataggio QDN", - "register_name": "hai bisogno di un nome Qortal registrato per salvare le tue app appuntate su QDN.", + "publish_qdn": "vuoi pubblicare le tue impostazioni su QDN (crittografato)?", + "qdn": "usa il salvataggio su QDN", + "register_name": "devi avere un nome Qortal registrato per salvare le tue app appuntate su QDN.", "reset_pinned": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le app appuntate predefinite?", - "reset_qdn": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le tue app appuntate salvate su QDN?", - "revert_default": "ripristina predefinite", + "reset_qdn": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le app appuntate salvate su QDN?", + "revert_default": "ripristina predefinito", "revert_qdn": "ripristina da QDN", "save_qdn": "salva su QDN", "save": "salva", - "settings": "stai utilizzando il metodo esporta/importa per salvare le impostazioni.", + "settings": "stai usando il metodo di esportazione/importazione per salvare le impostazioni.", "unsaved_changes": "hai modifiche non salvate alle tue app appuntate. Salvale su QDN." }, "settings": "impostazioni", - "supply": "offerta", + "supply": "disponibilità", "theme": { "dark": "modalità scura", "light": "modalità chiara" }, + "time": { + "day_one": "{{count}} giorno", + "day_other": "{{count}} giorni", + "hour_one": "{{count}} ora", + "hour_other": "{{count}} ore", + "minute_one": "{{count}} minuto", + "minute_other": "{{count}} minuti" + }, "title": "titolo", "tutorial": "tutorial", "user_lookup": "ricerca utente", "wallet": { - "backup_wallet": "backup portafoglio", - "wallet": "portafoglio", - "wallet_other": "portafogli" + "wallet": "wallet", + "wallet_other": "wallet" }, "welcome": "benvenuto" } diff --git a/public/locales/it/group.json b/public/locales/it/group.json new file mode 100644 index 0000000..862496d --- /dev/null +++ b/public/locales/it/group.json @@ -0,0 +1,65 @@ +{ + "action": { + "cancel_ban": "annulla ban", + "create_group": "crea gruppo", + "find_group": "trova gruppo", + "join_group": "unisciti al gruppo", + "invite_member": "invita membro", + "refetch_page": "ricarica pagina", + "return_to_thread": "torna ai thread" + }, + "advanced_options": "opzioni avanzate", + "approval_threshold": "soglia di Approvazione del gruppo (numero/percentuale di Admin che devono approvare una transazione)", + "ban_list": "lista ban", + "block_delay": { + "minimum": "ritardo minimo dei blocchi per Approvazione di Transazioni di Gruppo", + "maximum": "ritardo massimo dei blocchi per Approvazione di Transazioni di Gruppo" + }, + "group": { + "closed": "chiuso (privato) - gli utenti necessitano di permesso per unirsi", + "description": "descrizione del gruppo", + "invites": "inviti del gruppo", + "management": "gestione del gruppo", + "name": "nome del gruppo", + "open": "aperto (pubblico)", + "type": "tipo di gruppo" + }, + "invitation_expiry": "tempo di scadenza dell'invito", + "join_requests": "richieste di adesione", + "question": { + "cancel_ban": "vuoi eseguire una transazione CANCEL_GROUP_BAN?", + "create_group": "vuoi eseguire una transazione CREATE_GROUP?", + "group_invite": "vuoi eseguire una transazione GROUP_INVITE?", + "join_group": "vuoi eseguire una transazione JOIN_GROUP?", + "provide_thread": "per favore fornisci un titolo per il thread" + }, + "message": { + "generic": { + "encryption_key": "la prima chiave di cifratura comune del gruppo è in fase di creazione. Attendere alcuni minuti affinché venga recuperata dalla rete. Controllo ogni 2 minuti...", + "group_invited_you": "{{group}} ti ha invitato", + "no_display": "niente da visualizzare", + "no_selection": "nessun gruppo selezionato", + "not_part_group": "non fai parte del gruppo cifrato dei membri. Attendi che un amministratore ri-codifichi le chiavi.", + "only_encrypted": "verranno visualizzati solo i messaggi non cifrati.", + "setting_group": "configurazione del gruppo... attendere prego." + }, + "error": { + "access_name": "impossibile inviare un messaggio senza accesso al tuo nome", + "description_required": "per favore fornisci una descrizione", + "group_info": "impossibile accedere alle informazioni del gruppo", + "name_required": "per favore fornisci un nome", + "notify_admins": "prova a notificare un admin dalla lista di amministratori qui sotto:" + }, + "success": { + "group_creation": "gruppo creato con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", + "group_creation_name": "gruppo {{group_name}} creato: in attesa di conferma", + "group_creation_label": "gruppo {{name}} creato: successo!", + "group_invite": "invito inviato con successo a {{value}}. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", + "join_creation": "richiesta di adesione al gruppo inviata con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", + "group_join_name": "entrato nel gruppo {{group_name}}: in attesa di conferma", + "group_join_label": "entrato nel gruppo {{name}}: successo!", + "loading_threads": "caricamento thread... attendere prego.", + "unbanned_user": "utente sbannato con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino" + } + } +} From c7b19c0d720e7d3924c045e7d3f73f76ba85d956 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 19:20:00 +0300 Subject: [PATCH 278/717] change tooltip bg color --- src/App.tsx | 16 ++++++++-------- src/ExtStates/NotAuthenticated.tsx | 9 --------- src/components/Chat/ChatOptions.tsx | 4 ++-- src/components/GeneralNotifications.tsx | 2 +- src/components/QMailStatus.tsx | 2 +- src/styles/App-styles.ts | 3 +-- 6 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 35dc80f..b215b5e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1525,7 +1525,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1565,7 +1565,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1605,7 +1605,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1645,7 +1645,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1682,7 +1682,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1793,7 +1793,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1838,7 +1838,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1880,7 +1880,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 3c8afd9..bbb2b3d 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -641,15 +641,6 @@ export const NotAuthenticated = ({ }} control={ { if (event.target.checked) { diff --git a/src/components/Chat/ChatOptions.tsx b/src/components/Chat/ChatOptions.tsx index ddb3a3f..ed808f2 100644 --- a/src/components/Chat/ChatOptions.tsx +++ b/src/components/Chat/ChatOptions.tsx @@ -358,14 +358,14 @@ export const ChatOptions = ({ return ( { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { diff --git a/src/components/QMailStatus.tsx b/src/components/QMailStatus.tsx index dd6f9be..b171be2 100644 --- a/src/components/QMailStatus.tsx +++ b/src/components/QMailStatus.tsx @@ -76,7 +76,7 @@ export const QMailStatus = () => { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { diff --git a/src/styles/App-styles.ts b/src/styles/App-styles.ts index 980657b..8031321 100644 --- a/src/styles/App-styles.ts +++ b/src/styles/App-styles.ts @@ -131,9 +131,8 @@ export const CustomButton = styled(Box)(({ theme }) => ({ width: 'fit-content', '&:hover': { backgroundColor: theme.palette.background.paper, - color: theme.palette.text.secondary, 'svg path': { - fill: theme.palette.background.paper, + fill: theme.palette.background.secondary, }, }, })); From c4520b960d4e451427e9e4dcc1c1aa502a45743f Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 18:03:27 +0300 Subject: [PATCH 279/717] improving bg colors and adding borders --- src/App.tsx | 1 + src/components/Chat/AnnouncementItem.tsx | 1 - src/components/Chat/ChatGroup.tsx | 5 +- src/components/Chat/ChatList.tsx | 10 +- src/components/Chat/ChatOptions.tsx | 1 - src/components/Chat/MessageItem.tsx | 11 +- src/components/DesktopSideBar.tsx | 2 + src/components/Embeds/AttachmentEmbed.tsx | 499 +++++++++--------- src/components/Embeds/ImageEmbed.tsx | 434 +++++++-------- src/components/Group/Group.tsx | 6 +- .../Group/ListOfGroupPromotions.tsx | 3 - src/styles/theme-dark.ts | 7 +- src/styles/theme-light.ts | 11 +- src/styles/theme.d.ts | 19 + 14 files changed, 527 insertions(+), 483 deletions(-) create mode 100644 src/styles/theme.d.ts diff --git a/src/App.tsx b/src/App.tsx index b3d11d0..f7240b9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1471,6 +1471,7 @@ function App() { sx={{ height: '100%', justifyContent: 'space-between', + borderLeft: `1px solid ${theme.palette.border.subtle}`, }} > {message?.name} diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index e79bffe..2a86f1d 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -997,6 +997,7 @@ export const ChatGroup = ({ opacity: hide ? 0 : 1, position: hide ? 'absolute' : 'relative', width: '100%', + padding: '10px', }} >
    )} {showScrollDownButton && !showScrollButton && ( - + )}
    {enableMentions && (hasSecretKey || isPrivate === false) && ( diff --git a/src/components/Chat/ChatOptions.tsx b/src/components/Chat/ChatOptions.tsx index e95fa5b..ddb3a3f 100644 --- a/src/components/Chat/ChatOptions.tsx +++ b/src/components/Chat/ChatOptions.tsx @@ -767,7 +767,6 @@ const ShowMessage = ({ message, goToMessage, messages }) => { sx={{ fontWight: 600, fontFamily: 'Inter', - color: 'cadetBlue', }} > {message?.senderName} diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index a40fca8..b365d73 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -248,7 +248,6 @@ export const MessageItem = React.memo( sx={{ fontWight: 600, fontFamily: 'Inter', - color: 'cadetBlue', }} > {message?.senderName || message?.sender} @@ -304,7 +303,7 @@ export const MessageItem = React.memo( { return ( { > { + resourceData, + resourceDetails, + owner, + refresh, + openExternal, + external, + isLoadingParent, + errorMsg, + encryptionType, + selectedGroupId, +}) => { + const [isOpen, setIsOpen] = useState(true); + const { downloadResource } = useContext(MyContext); - const [isOpen, setIsOpen] = useState(true); - const { downloadResource } = useContext(MyContext); - - const saveToDisk = async ()=> { - const { name, service, identifier } = resourceData; - - const url = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}`; - fetch(url) - .then(response => response.blob()) - .then(async blob => { - await saveFileToDiskGeneric(blob, resourceData?.fileName) - }) - .catch(error => { - console.error("Error fetching the video:", error); - }); - } - - const saveToDiskEncrypted = async ()=> { - let blobUrl + const saveToDisk = async () => { + const { name, service, identifier } = resourceData; + + const url = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}`; + fetch(url) + .then((response) => response.blob()) + .then(async (blob) => { + await saveFileToDiskGeneric(blob, resourceData?.fileName); + }) + .catch((error) => { + console.error('Error fetching the video:', error); + }); + }; + + const saveToDiskEncrypted = async () => { + let blobUrl; + try { + const { name, service, identifier, key } = resourceData; + + const url = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?encoding=base64`; + const res = await fetch(url); + const data = await res.text(); + let decryptedData; try { - const { name, service, identifier,key } = resourceData; - - const url = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?encoding=base64`; - const res = await fetch(url) - const data = await res.text(); - let decryptedData - try { - if(key && encryptionType === 'private'){ - decryptedData = await window.sendMessage( - "DECRYPT_DATA_WITH_SHARING_KEY", - - { - encryptedData: data, - key: decodeURIComponent(key), - } - - ); - } - if(encryptionType === 'group'){ - decryptedData = await window.sendMessage( - "DECRYPT_QORTAL_GROUP_DATA", - - { - data64: data, - groupId: selectedGroupId, - } - - ); - } - } catch (error) { - throw new Error('Unable to decrypt') + if (key && encryptionType === 'private') { + decryptedData = await window.sendMessage( + 'DECRYPT_DATA_WITH_SHARING_KEY', + + { + encryptedData: data, + key: decodeURIComponent(key), + } + ); + } + if (encryptionType === 'group') { + decryptedData = await window.sendMessage( + 'DECRYPT_QORTAL_GROUP_DATA', + + { + data64: data, + groupId: selectedGroupId, + } + ); } - - if (!decryptedData || decryptedData?.error) throw new Error("Could not decrypt data"); - blobUrl = base64ToBlobUrl(decryptedData, resourceData?.mimeType) - const response = await fetch(blobUrl); - const blob = await response.blob(); - await saveFileToDiskGeneric(blob, resourceData?.fileName) - } catch (error) { - console.error(error) - } finally { - if(blobUrl){ - URL.revokeObjectURL(blobUrl); - } - + throw new Error('Unable to decrypt'); + } + + if (!decryptedData || decryptedData?.error) + throw new Error('Could not decrypt data'); + blobUrl = base64ToBlobUrl(decryptedData, resourceData?.mimeType); + const response = await fetch(blobUrl); + const blob = await response.blob(); + await saveFileToDiskGeneric(blob, resourceData?.fileName); + } catch (error) { + console.error(error); + } finally { + if (blobUrl) { + URL.revokeObjectURL(blobUrl); } } - return ( - + - - + ATTACHMENT embed + + + + - ATTACHMENT embed - - + + {external && ( - - {external && ( - - - - )} - - - - - Created by {decodeIfEncoded(owner)} - - - {encryptionType === 'private' ? "ENCRYPTED" : encryptionType === 'group' ? 'GROUP ENCRYPTED' : "Not encrypted"} - - - - - - - {isLoadingParent && isOpen && ( - - {" "} - {" "} - )} - {errorMsg && ( - + + + + Created by {decodeIfEncoded(owner)} + + + {encryptionType === 'private' + ? 'ENCRYPTED' + : encryptionType === 'group' + ? 'GROUP ENCRYPTED' + : 'Not encrypted'} + + + + + {isLoadingParent && isOpen && ( + + {' '} + {' '} + + )} + {errorMsg && ( + + {' '} + - {" "} + {errorMsg} + {' '} + + )} + + + + + {resourceData?.fileName && ( + <> - {errorMsg} - {" "} - + {resourceData?.fileName} + + + )} - - - - - {resourceData?.fileName && ( + { + if (resourceDetails?.status?.status === 'READY') { + if (encryptionType) { + saveToDiskEncrypted(); + return; + } + saveToDisk(); + return; + } + downloadResource(resourceData); + }} + > + + + {resourceDetails?.status?.status === 'DOWNLOADED' + ? 'BUILDING' + : resourceDetails?.status?.status} + + {!resourceDetails && ( <> - {resourceData?.fileName} - + + Download File )} - { - if(resourceDetails?.status?.status === 'READY'){ - if(encryptionType){ - saveToDiskEncrypted() - return - } - saveToDisk() - return - } - downloadResource(resourceData) - }}> - - - {resourceDetails?.status?.status === 'DOWNLOADED' ? 'BUILDING' : resourceDetails?.status?.status} - {!resourceDetails && ( - <> - - Download File - - - )} - {resourceDetails && resourceDetails?.status?.status !== 'READY' && resourceDetails?.status?.status !== 'FAILED_TO_DOWNLOAD' && ( - <> - - Downloading: {resourceDetails?.status?.percentLoaded || '0'}% - - - )} - {resourceDetails && resourceDetails?.status?.status === 'READY' && ( - <> - - Save to Disk - - - )} - - - - - - - - - ); - }; \ No newline at end of file + {resourceDetails && + resourceDetails?.status?.status !== 'READY' && + resourceDetails?.status?.status !== 'FAILED_TO_DOWNLOAD' && ( + <> + + + Downloading:{' '} + {resourceDetails?.status?.percentLoaded || '0'}% + + + )} + {resourceDetails && + resourceDetails?.status?.status === 'READY' && ( + <> + + Save to Disk + + )} + + + + + + ); +}; diff --git a/src/components/Embeds/ImageEmbed.tsx b/src/components/Embeds/ImageEmbed.tsx index f1cc859..4122dfb 100644 --- a/src/components/Embeds/ImageEmbed.tsx +++ b/src/components/Embeds/ImageEmbed.tsx @@ -1,265 +1,265 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState } from 'react'; import { Card, CardContent, Typography, - Box, ButtonBase, Divider, Dialog, IconButton, +} from '@mui/material'; -} from "@mui/material"; - -import RefreshIcon from "@mui/icons-material/Refresh"; -import OpenInNewIcon from "@mui/icons-material/OpenInNew"; -import { CustomLoader } from "../../common/CustomLoader"; -import ImageIcon from "@mui/icons-material/Image"; -import CloseIcon from "@mui/icons-material/Close"; -import { decodeIfEncoded } from "../../utils/decode"; +import RefreshIcon from '@mui/icons-material/Refresh'; +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; +import { CustomLoader } from '../../common/CustomLoader'; +import ImageIcon from '@mui/icons-material/Image'; +import CloseIcon from '@mui/icons-material/Close'; +import { decodeIfEncoded } from '../../utils/decode'; export const ImageCard = ({ - image, - fetchImage, - owner, - refresh, - openExternal, - external, - isLoadingParent, - errorMsg, - encryptionType, - }) => { - const [isOpen, setIsOpen] = useState(true); - const [height, setHeight] = useState('400px') - useEffect(() => { - if (isOpen) { - fetchImage(); - } - }, [isOpen]); - - // useEffect(()=> { - // if(errorMsg){ - // setHeight('300px') - // } - // }, [errorMsg]) - - return ( - { + const [isOpen, setIsOpen] = useState(true); + const [height, setHeight] = useState('400px'); + useEffect(() => { + if (isOpen) { + fetchImage(); + } + }, [isOpen]); + + // useEffect(()=> { + // if(errorMsg){ + // setHeight('300px') + // } + // }, [errorMsg]) + + return ( + + - - + IMAGE embed + + + + - IMAGE embed - - + + {external && ( - - {external && ( - - - - )} + )} + + + + + Created by {decodeIfEncoded(owner)} + + + {encryptionType === 'private' + ? 'ENCRYPTED' + : encryptionType === 'group' + ? 'GROUP ENCRYPTED' + : 'Not encrypted'} + + + + + {isLoadingParent && isOpen && ( + + {' '} + {' '} - - - - Created by {decodeIfEncoded(owner)} - - - {encryptionType === 'private' ? "ENCRYPTED" : encryptionType === 'group' ? 'GROUP ENCRYPTED' : "Not encrypted"} - - - - - - {isLoadingParent && isOpen && ( - - {" "} - {" "} - - )} - {errorMsg && ( - - {" "} - - {errorMsg} - {" "} - - )} - - - - - - - - - ); - }; + {errorMsg} + {' '} + + )} + - export function ImageViewer({ src, alt = "" }) { - const [isFullscreen, setIsFullscreen] = useState(false); - - const handleOpenFullscreen = () => setIsFullscreen(true); - const handleCloseFullscreen = () => setIsFullscreen(false); - - return ( - <> - {/* Image in container */} + + + + + + + ); +}; + +export function ImageViewer({ src, alt = '' }) { + const [isFullscreen, setIsFullscreen] = useState(false); + + const handleOpenFullscreen = () => setIsFullscreen(true); + const handleCloseFullscreen = () => setIsFullscreen(false); + + return ( + <> + {/* Image in container */} + + {alt} + + + {/* Fullscreen Viewer */} + + {/* Close Button */} + + + + + {/* Fullscreen Image */} {alt} - - {/* Fullscreen Viewer */} - - - {/* Close Button */} - - - - - {/* Fullscreen Image */} - {alt} - - - - ); - } \ No newline at end of file + + + ); +} diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 17ee3e6..fdd57e0 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -1556,12 +1556,13 @@ export const Group = ({
    { sx={{ fontWight: 600, fontFamily: 'Inter', - color: 'cadetBlue', }} > {promotion?.name} @@ -698,7 +697,6 @@ export const ListOfGroupPromotions = () => { sx={{ fontWight: 600, fontFamily: 'Inter', - color: 'cadetBlue', }} > {promotion?.groupName} @@ -746,7 +744,6 @@ export const ListOfGroupPromotions = () => { sx={{ fontWight: 600, fontFamily: 'Inter', - color: 'cadetBlue', }} > {promotion?.data} diff --git a/src/styles/theme-dark.ts b/src/styles/theme-dark.ts index 9c9e610..fb348a8 100644 --- a/src/styles/theme-dark.ts +++ b/src/styles/theme-dark.ts @@ -15,12 +15,17 @@ const darkThemeOptions: ThemeOptions = { }, background: { default: 'rgb(49, 51, 56)', - paper: 'rgb(96, 96, 97)', + paper: 'rgb(62, 64, 68)', + surface: 'rgb(58, 60, 65)', }, text: { primary: 'rgb(255, 255, 255)', secondary: 'rgb(179, 179, 179)', }, + border: { + main: 'rgba(255, 255, 255, 0.12)', + subtle: 'rgba(255, 255, 255, 0.08)', + }, }, components: { MuiCard: { diff --git a/src/styles/theme-light.ts b/src/styles/theme-light.ts index 1bd114d..db025d3 100644 --- a/src/styles/theme-light.ts +++ b/src/styles/theme-light.ts @@ -6,21 +6,26 @@ const lightThemeOptions: ThemeOptions = { palette: { mode: 'light', primary: { - main: 'rgba(244, 244, 251, 1)', + main: 'rgb(162, 162, 221)', // old light becomes main dark: 'rgb(113, 198, 212)', - light: 'rgb(162, 162, 221)', + light: 'rgba(244, 244, 251, 1)', // former main becomes light }, secondary: { main: 'rgba(194, 222, 236, 1)', }, background: { default: 'rgba(250, 250, 250, 1)', - paper: 'rgb(228, 228, 228)', + paper: 'rgb(220, 220, 220)', // darker card background + surface: 'rgb(240, 240, 240)', // optional middle gray for replies, side panels }, text: { primary: 'rgba(0, 0, 0, 1)', secondary: 'rgba(82, 82, 82, 1)', }, + border: { + main: 'rgba(0, 0, 0, 0.12)', + subtle: 'rgba(0, 0, 0, 0.08)', + }, }, components: { MuiCard: { diff --git a/src/styles/theme.d.ts b/src/styles/theme.d.ts new file mode 100644 index 0000000..762b526 --- /dev/null +++ b/src/styles/theme.d.ts @@ -0,0 +1,19 @@ +import '@mui/material/styles'; + +declare module '@mui/material/styles' { + interface TypeBackground { + surface: string; + } + interface Palette { + border: { + main: string; + subtle: string; + }; + } + interface PaletteOptions { + border?: { + main?: string; + subtle?: string; + }; + } +} From 6f20bdd684c00d308b432b18c023a7049f7b5edb Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 19:20:00 +0300 Subject: [PATCH 280/717] change tooltip bg color --- src/App.tsx | 16 ++++++++-------- src/ExtStates/NotAuthenticated.tsx | 9 --------- src/components/Chat/ChatOptions.tsx | 4 ++-- src/components/GeneralNotifications.tsx | 2 +- src/components/QMailStatus.tsx | 2 +- src/styles/App-styles.ts | 3 +-- 6 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index f7240b9..6297047 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1509,7 +1509,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1549,7 +1549,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1589,7 +1589,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1629,7 +1629,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1666,7 +1666,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1777,7 +1777,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1822,7 +1822,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -1864,7 +1864,7 @@ function App() { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 1f58360..a99a970 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -631,15 +631,6 @@ export const NotAuthenticated = ({ }} control={ { if (event.target.checked) { diff --git a/src/components/Chat/ChatOptions.tsx b/src/components/Chat/ChatOptions.tsx index ddb3a3f..ed808f2 100644 --- a/src/components/Chat/ChatOptions.tsx +++ b/src/components/Chat/ChatOptions.tsx @@ -358,14 +358,14 @@ export const ChatOptions = ({ return ( { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { diff --git a/src/components/QMailStatus.tsx b/src/components/QMailStatus.tsx index b733261..6600211 100644 --- a/src/components/QMailStatus.tsx +++ b/src/components/QMailStatus.tsx @@ -77,7 +77,7 @@ export const QMailStatus = () => { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { diff --git a/src/styles/App-styles.ts b/src/styles/App-styles.ts index 980657b..8031321 100644 --- a/src/styles/App-styles.ts +++ b/src/styles/App-styles.ts @@ -131,9 +131,8 @@ export const CustomButton = styled(Box)(({ theme }) => ({ width: 'fit-content', '&:hover': { backgroundColor: theme.palette.background.paper, - color: theme.palette.text.secondary, 'svg path': { - fill: theme.palette.background.paper, + fill: theme.palette.background.secondary, }, }, })); From c8e501ddee38e673db7fa71dbfec0b75741d93eb Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sun, 27 Apr 2025 15:38:06 +0300 Subject: [PATCH 281/717] fixing styles --- src/App.tsx | 67 +++++++++++++++----- src/assets/Icons/ChatIcon.tsx | 26 +++++--- src/assets/Icons/CopyIcon.tsx | 25 ++++++++ src/assets/Icons/MessagingIcon2.tsx | 14 ----- src/assets/Icons/MessagingIconFilled.tsx | 24 ++++++++ src/assets/Icons/QappDevelopText.tsx | 4 +- src/components/Apps/AppPublish.tsx | 72 ++++++++++------------ src/components/Apps/AppViewerContainer.tsx | 4 +- src/components/Apps/Apps-styles.tsx | 29 +++++---- src/components/Apps/AppsDesktop.tsx | 5 +- src/components/Apps/AppsDevMode.tsx | 21 ++----- src/components/Apps/AppsDevModeNavBar.tsx | 4 +- src/components/Apps/AppsLibraryDesktop.tsx | 8 ++- src/components/Apps/AppsNavBarDesktop.tsx | 4 +- src/components/Apps/AppsPrivate.tsx | 6 ++ src/components/Auth/DownloadWallet.tsx | 8 +++ src/components/Chat/ChatGroup.tsx | 2 +- src/components/Chat/ChatOptions.tsx | 4 +- src/components/Chat/TipTap.tsx | 11 +++- src/components/DesktopSideBar.tsx | 4 +- src/components/Embeds/AttachmentEmbed.tsx | 17 ++--- src/components/Embeds/ImageEmbed.tsx | 17 ++--- src/components/Embeds/PollEmbed.tsx | 24 +++----- src/components/GeneralNotifications.tsx | 2 +- src/components/Group/GroupInvites.tsx | 2 +- src/components/Group/ManageMembers.tsx | 28 +++++---- src/components/QMailStatus.tsx | 6 +- src/styles/App-styles.ts | 3 - src/styles/theme-dark.ts | 14 +++++ src/styles/theme-light.ts | 18 +++++- 30 files changed, 291 insertions(+), 182 deletions(-) create mode 100644 src/assets/Icons/CopyIcon.tsx delete mode 100644 src/assets/Icons/MessagingIcon2.tsx create mode 100644 src/assets/Icons/MessagingIconFilled.tsx diff --git a/src/App.tsx b/src/App.tsx index 6297047..f8e4146 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -30,7 +30,6 @@ import { CountdownCircleTimer } from 'react-countdown-circle-timer'; import Logo1Dark from './assets/svgs/Logo1Dark.svg'; import RefreshIcon from '@mui/icons-material/Refresh'; import DownloadIcon from '@mui/icons-material/Download'; -import Copy from './assets/svgs/Copy.svg'; import ltcLogo from './assets/ltc.png'; import PersonSearchIcon from '@mui/icons-material/PersonSearch'; import qortLogo from './assets/qort.png'; @@ -137,6 +136,7 @@ import ThemeSelector from './components/Theme/ThemeSelector.tsx'; import { useTranslation } from 'react-i18next'; import LanguageSelector from './components/Language/LanguageSelector.tsx'; import { DownloadWallet } from './components/Auth/DownloadWallet.tsx'; +import { CopyIcon } from './assets/Icons/CopyIcon.tsx'; type extStates = | 'not-authenticated' @@ -1297,7 +1297,8 @@ function App() { {rawWallet?.ltcAddress?.slice(0, 6)}... - {rawWallet?.ltcAddress?.slice(-4)} + {rawWallet?.ltcAddress?.slice(-4)}{' '} + @@ -1362,7 +1363,8 @@ function App() { {rawWallet?.address0?.slice(0, 6)}... - {rawWallet?.address0?.slice(-4)} + {rawWallet?.address0?.slice(-4)}{' '} + @@ -1519,7 +1521,11 @@ function App() { }, }} > - + @@ -1559,7 +1565,11 @@ function App() { }, }} > - + @@ -1599,7 +1609,11 @@ function App() { }, }} > - + @@ -1639,7 +1653,11 @@ function App() { }, }} > - + @@ -1681,7 +1699,10 @@ function App() { setIsOpenDrawerProfile(true); }} > - + @@ -1787,7 +1808,11 @@ function App() { }, }} > - + @@ -1832,7 +1857,11 @@ function App() { }, }} > - + )} @@ -1874,7 +1903,11 @@ function App() { }, }} > - + @@ -2165,10 +2198,10 @@ function App() { defaultChecked={messageQortalRequest?.checkbox1?.value} sx={{ '&.Mui-checked': { - color: 'white', // Customize the color when checked + color: theme.palette.text.secondary, // Customize the color when checked }, '& .MuiSvgIcon-root': { - color: 'white', + color: theme.palette.text.secondary, }, }} /> @@ -3407,10 +3440,10 @@ function App() { } sx={{ '&.Mui-checked': { - color: 'white', // Customize the color when checked + color: theme.palette.text.secondary, // Customize the color when checked }, '& .MuiSvgIcon-root': { - color: 'white', + color: theme.palette.text.secondary, }, }} /> @@ -3436,10 +3469,10 @@ function App() { disableRipple sx={{ '&.Mui-checked': { - color: 'white', + color: theme.palette.text.secondary, }, '& .MuiSvgIcon-root': { - color: 'white', + color: theme.palette.text.secondary, }, }} /> diff --git a/src/assets/Icons/ChatIcon.tsx b/src/assets/Icons/ChatIcon.tsx index f68ed35..8546949 100644 --- a/src/assets/Icons/ChatIcon.tsx +++ b/src/assets/Icons/ChatIcon.tsx @@ -1,12 +1,18 @@ import React from 'react'; -export const ChatIcon= ({ color = 'white', height = 15, width = 15 }) => { - return ( - - - - - - ); - }; - \ No newline at end of file +export const ChatIcon = ({ color = 'white', height = 15, width = 15 }) => { + return ( + + + + ); +}; diff --git a/src/assets/Icons/CopyIcon.tsx b/src/assets/Icons/CopyIcon.tsx new file mode 100644 index 0000000..67ede2d --- /dev/null +++ b/src/assets/Icons/CopyIcon.tsx @@ -0,0 +1,25 @@ +import { useTheme } from '@mui/material'; +import React from 'react'; + +export const CopyIcon = ({ color, height = 11, width = 10 }) => { + const theme = useTheme(); + + const setColor = color ? color : theme.palette.text.primary; + return ( + + + + ); +}; diff --git a/src/assets/Icons/MessagingIcon2.tsx b/src/assets/Icons/MessagingIcon2.tsx deleted file mode 100644 index da367cd..0000000 --- a/src/assets/Icons/MessagingIcon2.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -export const MessagingIcon2= ({ color = '#8F8F91', height = 24, width =24 }) => { - return ( - - - - - - - - ); - }; - \ No newline at end of file diff --git a/src/assets/Icons/MessagingIconFilled.tsx b/src/assets/Icons/MessagingIconFilled.tsx new file mode 100644 index 0000000..5dc6e04 --- /dev/null +++ b/src/assets/Icons/MessagingIconFilled.tsx @@ -0,0 +1,24 @@ +import { useTheme } from '@mui/material'; +import React from 'react'; + +export const MessagingIconFilled = ({ color, height = 31, width = 31 }) => { + const theme = useTheme(); + + const setColor = color ? color : theme.palette.text.primary; + return ( + + + + ); +}; diff --git a/src/assets/Icons/QappDevelopText.tsx b/src/assets/Icons/QappDevelopText.tsx index e0856cf..9bd30f9 100644 --- a/src/assets/Icons/QappDevelopText.tsx +++ b/src/assets/Icons/QappDevelopText.tsx @@ -14,8 +14,8 @@ export const QappDevelopText: React.FC = ({ return ( { @@ -74,7 +80,7 @@ export const AppPublish = ({ names, categories }) => { const [appType, setAppType] = useState('APP'); const [file, setFile] = useState(null); const { show } = useContext(MyContext); - + const theme = useTheme(); const [tag1, setTag1] = useState(''); const [tag2, setTag2] = useState(''); const [tag3, setTag3] = useState(''); @@ -263,9 +269,7 @@ export const AppPublish = ({ names, categories }) => { - + Name/App @@ -278,11 +282,11 @@ export const AppPublish = ({ names, categories }) => { Select Name/App - {' '} + {/* This is the placeholder item */} {names.map((name) => { @@ -292,9 +296,7 @@ export const AppPublish = ({ names, categories }) => { - + App service type @@ -307,12 +309,11 @@ export const AppPublish = ({ names, categories }) => { Select App Type - {' '} - {/* This is the placeholder item */} + App Website @@ -320,9 +321,7 @@ export const AppPublish = ({ names, categories }) => { - + Title @@ -330,7 +329,7 @@ export const AppPublish = ({ names, categories }) => { value={title} onChange={(e) => setTitle(e.target.value)} sx={{ - border: '0.5px solid var(--50-white, #FFFFFF80)', + border: `0.5px solid ${theme.palette.action.disabled}`, padding: '0px 15px', borderRadius: '5px', height: '36px', @@ -347,9 +346,7 @@ export const AppPublish = ({ names, categories }) => { - + Description @@ -357,7 +354,7 @@ export const AppPublish = ({ names, categories }) => { value={description} onChange={(e) => setDescription(e.target.value)} sx={{ - border: '0.5px solid var(--50-white, #FFFFFF80)', + border: `0.5px solid ${theme.palette.action.disabled}`, padding: '0px 15px', borderRadius: '5px', height: '36px', @@ -374,9 +371,7 @@ export const AppPublish = ({ names, categories }) => { - + Category @@ -389,12 +384,11 @@ export const AppPublish = ({ names, categories }) => { Select Category - {' '} - {/* This is the placeholder item */} + {categories?.map((category) => { return ( @@ -407,9 +401,7 @@ export const AppPublish = ({ names, categories }) => { - + Tags @@ -418,7 +410,7 @@ export const AppPublish = ({ names, categories }) => { value={tag1} onChange={(e) => setTag1(e.target.value)} sx={{ - border: '0.5px solid var(--50-white, #FFFFFF80)', + border: `0.5px solid ${theme.palette.action.disabled}`, padding: '0px 15px', borderRadius: '5px', height: '36px', @@ -435,7 +427,7 @@ export const AppPublish = ({ names, categories }) => { value={tag2} onChange={(e) => setTag2(e.target.value)} sx={{ - border: '0.5px solid var(--50-white, #FFFFFF80)', + border: `0.5px solid ${theme.palette.action.disabled}`, padding: '0px 15px', borderRadius: '5px', height: '36px', @@ -452,7 +444,7 @@ export const AppPublish = ({ names, categories }) => { value={tag3} onChange={(e) => setTag3(e.target.value)} sx={{ - border: '0.5px solid var(--50-white, #FFFFFF80)', + border: `0.5px solid ${theme.palette.action.disabled}`, padding: '0px 15px', borderRadius: '5px', height: '36px', @@ -469,7 +461,7 @@ export const AppPublish = ({ names, categories }) => { value={tag4} onChange={(e) => setTag4(e.target.value)} sx={{ - border: '0.5px solid var(--50-white, #FFFFFF80)', + border: `0.5px solid ${theme.palette.action.disabled}`, padding: '0px 15px', borderRadius: '5px', height: '36px', @@ -486,7 +478,7 @@ export const AppPublish = ({ names, categories }) => { value={tag5} onChange={(e) => setTag5(e.target.value)} sx={{ - border: '0.5px solid var(--50-white, #FFFFFF80)', + border: `0.5px solid ${theme.palette.action.disabled}`, padding: '0px 15px', borderRadius: '5px', height: '36px', diff --git a/src/components/Apps/AppViewerContainer.tsx b/src/components/Apps/AppViewerContainer.tsx index daa3a7e..43c661f 100644 --- a/src/components/Apps/AppViewerContainer.tsx +++ b/src/components/Apps/AppViewerContainer.tsx @@ -27,7 +27,7 @@ const AppViewerContainer = React.forwardRef( } .frame-content { overflow: hidden; - height: '100vh'; + height: 100vh; } `} @@ -35,7 +35,7 @@ const AppViewerContainer = React.forwardRef( } style={{ border: 'none', - height: '100vh', + height: customHeight || '100vh', left: (!isSelected || hide) && '-200vw', overflow: 'hidden', position: (!isSelected || hide) && 'fixed', diff --git a/src/components/Apps/Apps-styles.tsx b/src/components/Apps/Apps-styles.tsx index a335810..40192a3 100644 --- a/src/components/Apps/Apps-styles.tsx +++ b/src/components/Apps/Apps-styles.tsx @@ -141,7 +141,7 @@ export const AppLibrarySubTitle = styled(Typography)(({ theme }) => ({ export const AppCircle = styled(Box)(({ theme }) => ({ alignItems: 'center', - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.surface, borderColor: theme.palette.mode === 'dark' ? 'rgb(209, 209, 209)' @@ -283,7 +283,7 @@ export const TabParent = styled(Box)(({ theme }) => ({ export const PublishQAppCTAParent = styled(Box)(({ theme }) => ({ alignItems: 'center', - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, color: theme.palette.text.primary, display: 'flex', justifyContent: 'space-between', @@ -292,7 +292,7 @@ export const PublishQAppCTAParent = styled(Box)(({ theme }) => ({ export const PublishQAppCTALeft = styled(Box)(({ theme }) => ({ alignItems: 'center', - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, color: theme.palette.text.primary, display: 'flex', justifyContent: 'flex-start', @@ -300,7 +300,7 @@ export const PublishQAppCTALeft = styled(Box)(({ theme }) => ({ export const PublishQAppCTARight = styled(Box)(({ theme }) => ({ alignItems: 'center', - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, color: theme.palette.text.primary, display: 'flex', justifyContent: 'flex-end', @@ -308,8 +308,8 @@ export const PublishQAppCTARight = styled(Box)(({ theme }) => ({ export const PublishQAppCTAButton = styled(ButtonBase)(({ theme }) => ({ alignItems: 'center', - backgroundColor: theme.palette.background.paper, - borderColor: theme.palette.background.default, + backgroundColor: theme.palette.background.default, + borderColor: theme.palette.text.primary, borderRadius: '25px', borderStyle: 'solid', borderWidth: '1px', @@ -322,10 +322,10 @@ export const PublishQAppCTAButton = styled(ButtonBase)(({ theme }) => ({ export const PublishQAppDotsBG = styled(Box)(({ theme }) => ({ alignItems: 'center', - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, color: theme.palette.text.primary, display: 'flex', - height: '60px', + height: '80px', justifyContent: 'center', width: '60px', })); @@ -333,7 +333,7 @@ export const PublishQAppDotsBG = styled(Box)(({ theme }) => ({ export const PublishQAppInfo = styled(Typography)(({ theme }) => ({ backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, - fontSize: '10px', + fontSize: '16px', fontStyle: 'italic', fontWeight: 400, lineHeight: 1.2, @@ -341,15 +341,18 @@ export const PublishQAppInfo = styled(Typography)(({ theme }) => ({ export const PublishQAppChoseFile = styled(ButtonBase)(({ theme }) => ({ alignItems: 'center', - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, borderRadius: '5px', color: theme.palette.text.primary, display: 'flex', - fontSize: '10px', + fontSize: '16px', fontWeight: 600, - height: '30px', + height: '40px', justifyContent: 'center', - width: '101px', + width: '120px', + '&:hover': { + backgroundColor: 'action.hover', // background on hover + }, })); export const AppsCategoryInfo = styled(Box)(({ theme }) => ({ diff --git a/src/components/Apps/AppsDesktop.tsx b/src/components/Apps/AppsDesktop.tsx index e81e1a3..32098e2 100644 --- a/src/components/Apps/AppsDesktop.tsx +++ b/src/components/Apps/AppsDesktop.tsx @@ -24,6 +24,7 @@ import { useRecoilState } from 'recoil'; import { enabledDevModeAtom } from '../../atoms/global'; import { AppsIcon } from '../../assets/Icons/AppsIcon'; import { CoreSyncStatus } from '../CoreSyncStatus'; +import { MessagingIconFilled } from '../../assets/Icons/MessagingIconFilled'; const uid = new ShortUniqueId({ length: 8 }); @@ -342,6 +343,8 @@ export const AppsDesktop = ({ gap: '25px', height: '100vh', width: '60px', + backgroundColor: theme.palette.background.surface, + borderRight: `1px solid ${theme.palette.border.subtle}`, }} > - - { sx={{ position: 'relative', flexDirection: 'column', - width: '60px', + width: '59px', height: 'unset', maxHeight: '70vh', borderRadius: '0px 30px 30px 0px', @@ -116,7 +116,7 @@ export const AppsDevModeNavBar = () => { '& .MuiTabs-indicator': { backgroundColor: 'white', }, - maxHeight: `320px`, // Ensure the tabs container fits within the available space + maxHeight: `275px`, // Ensure the tabs container fits within the available space overflow: 'hidden', // Prevents overflow on small screens }} > diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index 1c6206e..f09e7c1 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -467,11 +467,13 @@ export const AppsLibraryDesktop = ({ borderRadius: '6px', borderStyle: 'solid', borderWidth: '4px', - boxShadow: '2px 4px 0px 0px #000000', display: 'flex', height: '50px', justifyContent: 'center', padding: '0px 20px', + '&:hover': { + backgroundColor: 'action.hover', // background on hover + }, }} > All @@ -495,11 +497,13 @@ export const AppsLibraryDesktop = ({ borderRadius: '6px', borderStyle: 'solid', borderWidth: '4px', - boxShadow: '2px 4px 0px 0px #000000', display: 'flex', height: '50px', justifyContent: 'center', padding: '0px 20px', + '&:hover': { + backgroundColor: 'action.hover', // background on hover + }, }} > {category?.name} diff --git a/src/components/Apps/AppsNavBarDesktop.tsx b/src/components/Apps/AppsNavBarDesktop.tsx index 890c297..3c6d91c 100644 --- a/src/components/Apps/AppsNavBarDesktop.tsx +++ b/src/components/Apps/AppsNavBarDesktop.tsx @@ -160,7 +160,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => { maxHeight: '70vh', padding: '10px', position: 'relative', - width: '60px', + width: '59px', }} > { '& .MuiTabs-indicator': { backgroundColor: theme.palette.background.default, }, - maxHeight: `320px`, // Ensure the tabs container fits within the available space + maxHeight: `275px`, // Ensure the tabs container fits within the available space overflow: 'hidden', // Prevents overflow on small screens }} > diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index 4d3e852..4a39b63 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -261,6 +261,12 @@ export const AppsPrivate = ({ myName }) => { }} maxWidth="md" fullWidth={true} + PaperProps={{ + style: { + backgroundColor: theme.palette.background.paper, + boxShadow: 'none', + }, + }} > } label={ diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 2a86f1d..503ec3a 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -1217,7 +1217,7 @@ export const ChatGroup = ({ > diff --git a/src/components/Chat/ChatOptions.tsx b/src/components/Chat/ChatOptions.tsx index ed808f2..c70dbff 100644 --- a/src/components/Chat/ChatOptions.tsx +++ b/src/components/Chat/ChatOptions.tsx @@ -576,7 +576,7 @@ export const ChatOptions = ({ diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 3c5403f..01155b5 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -365,6 +365,7 @@ export default ({ membersWithNames, enableMentions, }) => { + const theme = useTheme(); const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useRecoilState( isDisabledEditorEnterAtom ); @@ -483,12 +484,16 @@ export default ({ }, []); return ( -
    -
    +
    ); }; diff --git a/src/components/DesktopSideBar.tsx b/src/components/DesktopSideBar.tsx index e632b06..f1ccf22 100644 --- a/src/components/DesktopSideBar.tsx +++ b/src/components/DesktopSideBar.tsx @@ -1,6 +1,5 @@ import { Box, ButtonBase, useTheme } from '@mui/material'; import { HomeIcon } from '../assets/Icons/HomeIcon'; -import { MessagingIcon } from '../assets/Icons/MessagingIcon'; import { Save } from './Save/Save'; import { IconWrapper } from './Desktop/DesktopFooter'; import { useRecoilState } from 'recoil'; @@ -9,6 +8,7 @@ import { AppsIcon } from '../assets/Icons/AppsIcon'; import ThemeSelector from './Theme/ThemeSelector'; import { CoreSyncStatus } from './CoreSyncStatus'; import LanguageSelector from './Language/LanguageSelector'; +import { MessagingIconFilled } from '../assets/Icons/MessagingIconFilled'; export const DesktopSideBar = ({ goToHome, @@ -111,7 +111,7 @@ export const DesktopSideBar = ({ label="Chat" disableWidth > - { const [isOpen, setIsOpen] = useState(true); const { downloadResource } = useContext(MyContext); + const theme = useTheme(); const saveToDisk = async () => { const { name, service, identifier } = resourceData; @@ -110,7 +112,7 @@ export const AttachmentCard = ({ return ( ATTACHMENT embed @@ -149,7 +151,7 @@ export const AttachmentCard = ({ onClick={refresh} sx={{ fontSize: '24px', - color: 'white', + color: theme.palette.text.primary, }} /> @@ -159,7 +161,7 @@ export const AttachmentCard = ({ onClick={openExternal} sx={{ fontSize: '24px', - color: 'white', + color: theme.palette.text.primary, }} /> @@ -174,7 +176,6 @@ export const AttachmentCard = ({ Created by {decodeIfEncoded(owner)} @@ -281,10 +282,10 @@ export const AttachmentCard = ({ resourceDetails?.status?.status !== 'FAILED_TO_DOWNLOAD' && ( <> Downloading:{' '} diff --git a/src/components/Embeds/ImageEmbed.tsx b/src/components/Embeds/ImageEmbed.tsx index 4122dfb..24cab97 100644 --- a/src/components/Embeds/ImageEmbed.tsx +++ b/src/components/Embeds/ImageEmbed.tsx @@ -8,6 +8,7 @@ import { Divider, Dialog, IconButton, + useTheme, } from '@mui/material'; import RefreshIcon from '@mui/icons-material/Refresh'; @@ -28,6 +29,7 @@ export const ImageCard = ({ errorMsg, encryptionType, }) => { + const theme = useTheme(); const [isOpen, setIsOpen] = useState(true); const [height, setHeight] = useState('400px'); useEffect(() => { @@ -45,7 +47,7 @@ export const ImageCard = ({ return ( IMAGE embed @@ -84,7 +86,7 @@ export const ImageCard = ({ onClick={refresh} sx={{ fontSize: '24px', - color: 'white', + color: theme.palette.text.primary, }} /> @@ -94,7 +96,7 @@ export const ImageCard = ({ onClick={openExternal} sx={{ fontSize: '24px', - color: 'white', + color: theme.palette.text.primary, }} /> @@ -109,7 +111,6 @@ export const ImageCard = ({ Created by {decodeIfEncoded(owner)} @@ -182,7 +183,7 @@ export function ImageViewer({ src, alt = '' }) { const handleOpenFullscreen = () => setIsFullscreen(true); const handleCloseFullscreen = () => setIsFullscreen(false); - + const theme = useTheme(); return ( <> {/* Image in container */} @@ -231,7 +232,7 @@ export function ImageViewer({ src, alt = '' }) { display: 'flex', justifyContent: 'center', alignItems: 'center', - backgroundColor: '#000', // Optional: dark background for fullscreen mode + backgroundColor: theme.palette.background.paper, // Optional: dark background for fullscreen mode }} > {/* Close Button */} @@ -242,7 +243,7 @@ export function ImageViewer({ src, alt = '' }) { top: 8, right: 8, zIndex: 10, - color: 'white', + color: theme.palette.text.primary, }} > diff --git a/src/components/Embeds/PollEmbed.tsx b/src/components/Embeds/PollEmbed.tsx index 65e5983..4adda15 100644 --- a/src/components/Embeds/PollEmbed.tsx +++ b/src/components/Embeds/PollEmbed.tsx @@ -12,6 +12,7 @@ import { Box, ButtonBase, Divider, + useTheme, } from '@mui/material'; import { getNameInfo } from '../Group/Group'; import PollIcon from '@mui/icons-material/Poll'; @@ -37,6 +38,7 @@ export const PollCard = ({ const [isOpen, setIsOpen] = useState(false); const { show, userInfo } = useContext(MyContext); const [isLoadingSubmit, setIsLoadingSubmit] = useState(false); + const theme = useTheme(); const handleVote = async () => { const fee = await getFee('VOTE_ON_POLL'); @@ -103,7 +105,7 @@ export const PollCard = ({ return ( @@ -124,7 +126,7 @@ export const PollCard = ({ > POLL embed @@ -141,7 +143,7 @@ export const PollCard = ({ onClick={refresh} sx={{ fontSize: '24px', - color: 'white', + color: theme.palette.text.primary, }} /> @@ -151,7 +153,7 @@ export const PollCard = ({ onClick={openExternal} sx={{ fontSize: '24px', - color: 'white', + color: theme.palette.text.primary, }} /> @@ -186,9 +188,6 @@ export const PollCard = ({
    )}
    - { setTimeout(() => { executeEvent('threadFetchMode', { @@ -781,7 +782,6 @@ export const GroupMail = ({ }} sx={{ alignItems: 'center', - backgroundColor: '#27282c', borderRadius: '5px', bottom: '2px', cursor: 'pointer', @@ -790,14 +790,11 @@ export const GroupMail = ({ padding: '5px', position: 'absolute', right: '2px', - '&:hover': { - background: 'rgba(255, 255, 255, 0.60)', - }, + minWidth: 'unset', }} > @@ -807,11 +804,11 @@ export const GroupMail = ({ - + ); })} diff --git a/src/components/Group/Forum/Mail-styles.ts b/src/components/Group/Forum/Mail-styles.ts index 368e4cb..6fc12de 100644 --- a/src/components/Group/Forum/Mail-styles.ts +++ b/src/components/Group/Forum/Mail-styles.ts @@ -1,4 +1,4 @@ -import { Typography, Box, TextField } from '@mui/material'; +import { Typography, Box } from '@mui/material'; import { styled } from '@mui/system'; export const InstanceContainer = styled(Box)(({ theme }) => ({ @@ -88,7 +88,7 @@ export const ComposeContainer = styled(Box)(({ theme }) => ({ transition: '0.2s background-color', justifyContent: 'center', '&:hover': { - backgroundColor: 'rgba(67, 68, 72, 1)', + backgroundColor: theme.palette.action.hover, }, })); @@ -141,19 +141,6 @@ export const SelectInstanceContainer = styled(Box)(({ theme }) => ({ gap: '17px', })); -export const SelectInstanceContainerInner = styled(Box)(({ theme }) => ({ - display: 'flex', - alignItems: 'center', - gap: '3px', - cursor: 'pointer', - padding: '8px', - transition: 'all 0.2s', - '&:hover': { - borderRadius: '8px', - background: '#434448', - }, -})); - export const SelectInstanceContainerFilterInner = styled(Box)(({ theme }) => ({ display: 'flex', alignItems: 'center', @@ -174,81 +161,6 @@ export const InstanceP = styled(Typography)(({ theme }) => ({ fontWeight: 500, })); -export const MailMessageRowContainer = styled(Box)(({ theme }) => ({ - display: 'flex', - alignItems: 'center', - cursor: 'pointer', - justifyContent: 'space-between', - borderRadius: '56px 5px 10px 56px', - paddingRight: '15px', - transition: 'background 0.2s', - gap: '10px', - '&:hover': { - background: '#434448', - }, -})); - -export const MailMessageRowProfile = styled(Box)(({ theme }) => ({ - display: 'flex', - alignItems: 'center', - cursor: 'pointer', - justifyContent: 'flex-start', - gap: '10px', - width: '50%', - overflow: 'hidden', -})); - -export const MailMessageRowInfo = styled(Box)(({ theme }) => ({ - display: 'flex', - alignItems: 'center', - cursor: 'pointer', - justifyContent: 'flex-start', - gap: '7px', - width: '50%', -})); - -export const MailMessageRowInfoStatusNotDecrypted = styled(Typography)( - ({ theme }) => ({ - fontSize: '16px', - fontWeight: 900, - textTransform: 'uppercase', - paddingTop: '2px', - }) -); - -export const MailMessageRowInfoStatusRead = styled(Typography)(({ theme }) => ({ - fontSize: '16px', - fontWeight: 300, -})); - -export const MessageExtraInfo = styled(Box)(({ theme }) => ({ - display: 'flex', - flexDirection: 'column', - gap: '2px', - overflow: 'hidden', -})); - -export const MessageExtraName = styled(Typography)(({ theme }) => ({ - fontSize: '16px', - fontWeight: 900, - whiteSpace: 'nowrap', - textOverflow: 'ellipsis', - overflow: 'hidden', -})); - -export const MessageExtraDate = styled(Typography)(({ theme }) => ({ - fontSize: '15px', - fontWeight: 500, -})); - -export const MessagesContainer = styled(Box)(({ theme }) => ({ - width: '460px', - maxWidth: '90%', - display: 'flex', - flexDirection: 'column', - gap: '12px', -})); - export const InstanceListParent = styled(Box)` display: flex; flex-direction: column; @@ -378,44 +290,6 @@ export const InstanceListContainerRowGroupIcon = styled('img')({ userSelect: 'none', objectFit: 'contain', }); -export const TypeInAliasTextfield = styled(TextField)({ - width: '340px', // Adjust the width as needed - borderRadius: '5px', - backgroundColor: 'rgba(30, 30, 32, 1)', - border: 'none', - outline: 'none', - input: { - fontSize: 16, - color: 'white', - '&::placeholder': { - fontSize: 16, - color: 'rgba(255, 255, 255, 0.2)', - }, - border: 'none', - outline: 'none', - padding: '10px', - }, - '& .MuiOutlinedInput-root': { - '& fieldset': { - border: 'none', - }, - '&:hover fieldset': { - border: 'none', - }, - '&.Mui-focused fieldset': { - border: 'none', - }, - }, - '& .MuiInput-underline:before': { - borderBottom: 'none', - }, - '& .MuiInput-underline:hover:not(.Mui-disabled):before': { - borderBottom: 'none', - }, - '& .MuiInput-underline:after': { - borderBottom: 'none', - }, -}); export const NewMessageCloseImg = styled('img')({ width: 'auto', @@ -427,6 +301,7 @@ export const NewMessageCloseImg = styled('img')({ export const NewMessageHeaderP = styled(Typography)(({ theme }) => ({ fontSize: '18px', fontWeight: 600, + color: theme.palette.text.primary, })); export const NewMessageInputRow = styled(Box)(({ theme }) => ({ @@ -480,29 +355,24 @@ export const NewMessageAttachmentImg = styled('img')({ border: '1px dashed #646464', }); -export const NewMessageSendButton = styled(Box)` - border-radius: 4px; - border: 1px solid rgba(0, 0, 0, 0.9); - display: inline-flex; - padding: 8px 16px 8px 12px; - justify-content: center; - align-items: center; - gap: 8px; - width: fit-content; - transition: all 0.2s; - color: black; - min-width: 120px; - gap: 8px; - position: relative; - cursor: pointer; - &:hover { - background-color: rgba(41, 41, 43, 1); - color: white; - svg path { - fill: white; // Fill color changes to white on hover - } - } -`; +export const NewMessageSendButton = styled(Box)(({ theme }) => ({ + borderRadius: '4px', + border: `1px solid ${theme.palette.border.main}`, // you can replace with theme.palette.divider or whatever you want later + display: 'inline-flex', + padding: '8px 16px 8px 12px', + justifyContent: 'center', + alignItems: 'center', + width: 'fit-content', + transition: 'all 0.2s', + color: theme.palette.text.primary, // replace later with theme.palette.text.primary if needed + minWidth: '120px', + position: 'relative', + gap: '8px', + cursor: 'pointer', + '&:hover': { + backgroundColor: theme.palette.action.hover, // replace with theme value if needed + }, +})); export const NewMessageSendP = styled(Typography)` font-family: Roboto; @@ -524,14 +394,7 @@ export const ShowMessageNameP = styled(Typography)` text-overflow: ellipsis; overflow: hidden; `; -export const ShowMessageTimeP = styled(Typography)` - color: rgba(255, 255, 255, 0.5); - font-family: Roboto; - font-size: 15px; - font-style: normal; - font-weight: 500; - line-height: normal; -`; + export const ShowMessageSubjectP = styled(Typography)` font-family: Roboto; font-size: 16px; @@ -541,61 +404,44 @@ export const ShowMessageSubjectP = styled(Typography)` text-align: left; `; -export const ShowMessageButton = styled(Box)` -display: inline-flex; -padding: 8px 16px 8px 16px; -align-items: center; -justify-content: center; -gap: 8px; -width: fit-content; -transition: all 0.2s; -color: white; -background-color: rgba(41, 41, 43, 1) -min-width: 120px; -gap: 8px; -border-radius: 4px; -border: 0.5px solid rgba(255, 255, 255, 0.70); -font-family: Roboto; +export const ShowMessageButton = styled(Box)(({ theme }) => ({ + display: 'inline-flex', + padding: '8px 16px', + alignItems: 'center', + justifyContent: 'center', + width: 'fit-content', + transition: 'all 0.2s', + color: theme.palette.text.primary, // you'll replace with theme value + minWidth: '120px', + gap: '8px', + borderRadius: '4px', + border: theme.palette.border.main, // you'll replace + fontFamily: 'Roboto', + cursor: 'pointer', + '&:hover': { + background: theme.palette.action.hover, // you'll replace + borderRadius: '4px', + }, +})); -min-width: 120px; -cursor: pointer; -&:hover { - border-radius: 4px; -border: 0.5px solid rgba(255, 255, 255, 0.70); -background: #434448; -} -`; -export const ShowMessageReturnButton = styled(Box)` -display: inline-flex; -padding: 8px 16px 8px 16px; -align-items: center; -justify-content: center; -gap: 8px; -width: fit-content; -transition: all 0.2s; -color: white; -background-color: rgba(41, 41, 43, 1) -min-width: 120px; -gap: 8px; -border-radius: 4px; -font-family: Roboto; - -min-width: 120px; -cursor: pointer; -&:hover { - border-radius: 4px; -background: #434448; -} -`; - -export const ShowMessageButtonP = styled(Typography)` - font-size: 16px; - font-style: normal; - font-weight: 500; - line-height: 120%; /* 19.2px */ - letter-spacing: -0.16px; - color: white; -`; +export const ShowMessageReturnButton = styled(Box)(({ theme }) => ({ + display: 'inline-flex', + padding: '8px 16px', + alignItems: 'center', + justifyContent: 'center', + width: 'fit-content', + transition: 'all 0.2s', + color: theme.palette.text.primary, // you'll replace with theme value + minWidth: '120px', + gap: '8px', + borderRadius: '4px', + fontFamily: 'Roboto', + cursor: 'pointer', + '&:hover': { + background: theme.palette.action.hover, // you'll replace + borderRadius: '4px', + }, +})); export const ShowMessageButtonImg = styled('img')({ width: 'auto', @@ -630,18 +476,16 @@ export const MoreImg = styled('img')({ }, }); -export const MoreP = styled(Typography)` - color: rgba(255, 255, 255, 0.5); - - /* Attachments */ - font-family: Roboto; - font-size: 16px; - font-style: normal; - font-weight: 400; - line-height: 120%; /* 19.2px */ - letter-spacing: -0.16px; - white-space: nowrap; -`; +export const MoreP = styled(Typography)(({ theme }) => ({ + color: theme.palette.text.primary, // Now dynamic + fontFamily: 'Roboto', + fontSize: '16px', + fontStyle: 'normal', + fontWeight: 400, + lineHeight: '120%', // 19.2px + letterSpacing: '-0.16px', + whiteSpace: 'nowrap', +})); export const ThreadContainerFullWidth = styled(Box)(({ theme }) => ({ display: 'flex', @@ -658,7 +502,6 @@ export const ThreadContainer = styled(Box)(({ theme }) => ({ })); export const GroupNameP = styled(Typography)` - color: #fff; font-size: 25px; font-style: normal; font-weight: 700; @@ -667,7 +510,6 @@ export const GroupNameP = styled(Typography)` `; export const AllThreadP = styled(Typography)` - color: #fff; font-size: 20px; font-style: normal; font-weight: 400; @@ -675,32 +517,32 @@ export const AllThreadP = styled(Typography)` letter-spacing: 0.15px; `; -export const SingleThreadParent = styled(Box)` - border-radius: 35px 4px 4px 35px; - position: relative; - background: #434448; - display: flex; - padding: 13px; - cursor: pointer; - margin-bottom: 5px; - height: 76px; - align-items: center; - transition: 0.2s all; - &:hover { - background: rgba(255, 255, 255, 0.2); - } -`; +export const SingleThreadParent = styled(Box)(({ theme }) => ({ + borderRadius: '35px 4px 4px 35px', + position: 'relative', + display: 'flex', + padding: '13px', + cursor: 'pointer', + marginBottom: '5px', + height: '76px', + alignItems: 'center', + transition: '0.2s all', + backgroundColor: theme.palette.background.paper, // or remove if you want no background by default + '&:hover': { + backgroundColor: theme.palette.action.hover, + }, +})); -export const SingleTheadMessageParent = styled(Box)` - border-radius: 35px 4px 4px 35px; - background: #434448; - display: flex; - padding: 13px; - cursor: pointer; - margin-bottom: 5px; - height: 76px; - align-items: center; -`; +export const SingleTheadMessageParent = styled(Box)(({ theme }) => ({ + borderRadius: '35px 4px 4px 35px', + background: theme.palette.background.paper, + display: 'flex', + padding: '13px', + cursor: 'pointer', + marginBottom: '5px', + height: '76px', + alignItems: 'center', +})); export const ThreadInfoColumn = styled(Box)(({ theme }) => ({ display: 'flex', @@ -713,7 +555,6 @@ export const ThreadInfoColumn = styled(Box)(({ theme }) => ({ })); export const ThreadInfoColumnNameP = styled(Typography)` - color: #fff; font-family: Roboto; font-size: 16px; font-style: normal; @@ -724,26 +565,25 @@ export const ThreadInfoColumnNameP = styled(Typography)` overflow: hidden; `; -export const ThreadInfoColumnbyP = styled('span')` - color: rgba(255, 255, 255, 0.8); - font-family: Roboto; - font-size: 16px; - font-style: normal; - font-weight: 500; - line-height: normal; -`; +export const ThreadInfoColumnbyP = styled('span')(({ theme }) => ({ + color: theme.palette.text.secondary, + fontFamily: 'Roboto', + fontSize: '16px', + fontStyle: 'normal', + fontWeight: 500, + lineHeight: 'normal', +})); -export const ThreadInfoColumnTime = styled(Typography)` - color: rgba(255, 255, 255, 0.8); - font-family: Roboto; - font-size: 15px; - font-style: normal; - font-weight: 500; - line-height: normal; -`; +export const ThreadInfoColumnTime = styled(Typography)(({ theme }) => ({ + color: theme.palette.text.secondary, + fontFamily: 'Roboto', + fontSize: '15px', + fontStyle: 'normal', + fontWeight: 500, + lineHeight: 'normal', +})); export const ThreadSingleTitle = styled(Typography)` - color: #fff; font-family: Roboto; font-size: 23px; font-style: normal; @@ -755,7 +595,6 @@ export const ThreadSingleTitle = styled(Typography)` `; export const ThreadSingleLastMessageP = styled(Typography)` - color: #fff; font-family: Roboto; font-size: 12px; font-style: normal; @@ -764,7 +603,6 @@ export const ThreadSingleLastMessageP = styled(Typography)` `; export const ThreadSingleLastMessageSpanP = styled('span')` - color: #fff; font-family: Roboto; font-size: 12px; font-style: normal; @@ -792,6 +630,6 @@ export const CloseContainer = styled(Box)(({ theme }) => ({ height: '50px', borderRadius: '0px 12px 0px 0px', '&:hover': { - backgroundColor: 'rgba(162, 31, 31, 1)', + backgroundColor: theme.palette.action.hover, }, })); diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index ddcafbb..74ce7f4 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -1,12 +1,9 @@ import React, { useEffect, useRef, useState } from 'react'; -import { Box, CircularProgress, Input } from '@mui/material'; +import { Box, CircularProgress, Input, useTheme } from '@mui/material'; import ShortUniqueId from 'short-unique-id'; -import ModalCloseSVG from '../../../assets/svgs/ModalClose.svg'; -import ComposeIconSVG from '../../../assets/svgs/ComposeIcon.svg'; import { CloseContainer, ComposeContainer, - ComposeIcon, ComposeP, InstanceFooter, InstanceListContainer, @@ -29,6 +26,8 @@ import { MessageDisplay } from '../../Chat/MessageDisplay'; import { CustomizedSnackbars } from '../../Snackbar/Snackbar'; import { saveTempPublish } from '../../Chat/GroupAnnouncements'; import { useTranslation } from 'react-i18next'; +import { ComposeIcon } from '../../../assets/Icons/ComposeIcon'; +import CloseIcon from '@mui/icons-material/Close'; const uid = new ShortUniqueId({ length: 8 }); @@ -152,6 +151,7 @@ export const NewThread = ({ const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); const editorRef = useRef(null); + const theme = useTheme(); const setEditorRef = (editorInstance) => { editorRef.current = editorInstance; }; @@ -406,7 +406,7 @@ export const NewThread = ({ }} onClick={() => setIsOpen(true)} > - + {currentThread ? 'New Post' : 'New Thread'} @@ -417,7 +417,7 @@ export const NewThread = ({ maxWidth: '950px', height: '700px', borderRadius: '12px 12px 0px 0px', - background: '#434448', + background: theme.palette.background.paper, padding: '0px', gap: '0px', }} @@ -429,7 +429,7 @@ export const NewThread = ({ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', - backgroundColor: '#434448', + backgroundColor: theme.palette.background.paper, }} > @@ -441,12 +441,16 @@ export const NewThread = ({ }} onClick={closeModal} > - + - +
    )} @@ -552,9 +559,14 @@ export const NewThread = ({ {isMessage ? ( - + ) : ( - + )} diff --git a/src/components/Group/Forum/ShowMessageWithoutModal.tsx b/src/components/Group/Forum/ShowMessageWithoutModal.tsx index 914b9c5..57d920d 100644 --- a/src/components/Group/Forum/ShowMessageWithoutModal.tsx +++ b/src/components/Group/Forum/ShowMessageWithoutModal.tsx @@ -20,7 +20,7 @@ import { WrapperUserAction } from '../../WrapperUserAction'; export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { const [expandAttachments, setExpandAttachments] = useState(false); - + console.log('message', message); let cleanHTML = ''; if (message?.htmlContent) { cleanHTML = DOMPurify.sanitize(message.htmlContent); diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 4c94e12..266edb6 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -5,7 +5,14 @@ import React, { useRef, useState, } from 'react'; -import { Avatar, Box, Button, ButtonBase, Typography } from '@mui/material'; +import { + Avatar, + Box, + Button, + ButtonBase, + Typography, + useTheme, +} from '@mui/material'; import { ShowMessage } from './ShowMessageWithoutModal'; import { ComposeP, @@ -43,6 +50,7 @@ import { CustomLoader } from '../../../common/CustomLoader'; import { WrapperUserAction } from '../../WrapperUserAction'; import { formatTimestampForum } from '../../../utils/time'; import { useTranslation } from 'react-i18next'; +import { ReturnIcon } from '../../../assets/Icons/ReturnIcon'; const requestQueueSaveToLocal = new RequestQueueWithPromise(1); @@ -116,7 +124,7 @@ export const Thread = ({ const [postReply, setPostReply] = useState(null); const [hasLastPage, setHasLastPage] = useState(false); const { t } = useTranslation(['core']); - + const theme = useTheme(); // Update: Use a new ref for the scrollable container const threadContainerRef = useRef(null); const threadBeginningRef = useRef(null); @@ -606,7 +614,7 @@ export const Thread = ({ closeThread(); }} > - + {t('group:action.return_to_thread', { postProcess: 'capitalize', @@ -619,7 +627,7 @@ export const Thread = ({ {fullMessage?.error} @@ -925,7 +932,6 @@ export const Thread = ({ {t('core:downloading_qdn', { postProcess: 'capitalize' })} @@ -958,9 +964,6 @@ export const Thread = ({ groupInfo?.groupId ); }} - sx={{ - color: 'white', - }} > {t('group:action.refetch_page', { postProcess: 'capitalize', diff --git a/src/components/Group/GroupJoinRequests.tsx b/src/components/Group/GroupJoinRequests.tsx index c7bb32e..2bf0b26 100644 --- a/src/components/Group/GroupJoinRequests.tsx +++ b/src/components/Group/GroupJoinRequests.tsx @@ -7,7 +7,7 @@ import IconButton from '@mui/material/IconButton'; import { RequestQueueWithPromise } from '../../utils/queue/queue'; import GroupAddIcon from '@mui/icons-material/GroupAdd'; import { executeEvent } from '../../utils/events'; -import { Box, ButtonBase, Collapse, Typography } from '@mui/material'; +import { Box, ButtonBase, Collapse, Typography, useTheme } from '@mui/material'; import { CustomLoader } from '../../common/CustomLoader'; import { MyContext, getBaseApiReact } from '../../App'; import { myGroupsWhereIAmAdminAtom } from '../../atoms/global'; @@ -35,7 +35,7 @@ export const GroupJoinRequests = ({ const [loading, setLoading] = React.useState(true); const { txList, setTxList } = React.useContext(MyContext); const setMyGroupsWhereIAmAdmin = useSetRecoilState(myGroupsWhereIAmAdminAtom); - + const theme = useTheme(); const getJoinRequests = async () => { try { setLoading(true); @@ -244,7 +244,7 @@ export const GroupJoinRequests = ({ diff --git a/src/components/Group/GroupMenu.tsx b/src/components/Group/GroupMenu.tsx deleted file mode 100644 index 0d81cd2..0000000 --- a/src/components/Group/GroupMenu.tsx +++ /dev/null @@ -1,254 +0,0 @@ -import { useState } from 'react'; -import { - Button, - Menu, - MenuItem, - ListItemIcon, - ListItemText, - Box, -} from '@mui/material'; -import { ArrowDownIcon } from '../../assets/Icons/ArrowDownIcon'; -import { NotificationIcon2 } from '../../assets/Icons/NotificationIcon2'; -import { ChatIcon } from '../../assets/Icons/ChatIcon'; -import { ThreadsIcon } from '../../assets/Icons/ThreadsIcon'; -import { MembersIcon } from '../../assets/Icons/MembersIcon'; - -export const GroupMenu = ({ - setGroupSection, - groupSection, - setOpenManageMembers, - goToAnnouncements, - goToChat, - hasUnreadChat, - hasUnreadAnnouncements, -}) => { - const [anchorEl, setAnchorEl] = useState(null); - const open = Boolean(anchorEl); - - const handleClick = (event) => { - setAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - return ( - - - - - { - goToChat(); - handleClose(); - }} - > - - - - - - { - goToAnnouncements(); - handleClose(); - }} - > - - - - - - { - setGroupSection('forum'); - handleClose(); - }} - > - - - - - - { - setOpenManageMembers(true); - handleClose(); - }} - > - - - - - - - - ); -}; diff --git a/src/components/Group/HomeDesktop.tsx b/src/components/Group/HomeDesktop.tsx index d0f2459..83f8828 100644 --- a/src/components/Group/HomeDesktop.tsx +++ b/src/components/Group/HomeDesktop.tsx @@ -242,27 +242,6 @@ export const HomeDesktop = ({ )}
    - - - {/* - - */} - ); diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index a684e40..4d402b6 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -21,6 +21,7 @@ import { Select, TextField, Typography, + useTheme, } from '@mui/material'; import { LoadingButton } from '@mui/lab'; import LockIcon from '@mui/icons-material/Lock'; @@ -90,7 +91,7 @@ export const ListOfGroupPromotions = () => { const [isLoadingJoinGroup, setIsLoadingJoinGroup] = useState(false); const [isLoadingPublish, setIsLoadingPublish] = useState(false); const { show, setTxList } = useContext(MyContext); - + const theme = useTheme(); const listRef = useRef(); const rowVirtualizer = useVirtualizer({ count: promotions.length, @@ -673,7 +674,7 @@ export const ListOfGroupPromotions = () => { { } sx={{ fontSize: '12px', - color: 'white', + color: theme.palette.text.primary, }} > Join Group: {` ${promotion?.groupName}`} @@ -845,10 +846,10 @@ export const ListOfGroupPromotions = () => { multiline={true} sx={{ '& .MuiFormLabel-root': { - color: 'white', + color: theme.palette.text.primary, }, '& .MuiFormLabel-root.Mui-focused': { - color: 'white', + color: theme.palette.text.primary, }, }} /> diff --git a/src/components/Group/QMailMessages.tsx b/src/components/Group/QMailMessages.tsx index 8673110..238a2aa 100644 --- a/src/components/Group/QMailMessages.tsx +++ b/src/components/Group/QMailMessages.tsx @@ -153,7 +153,7 @@ export const QMailMessages = ({ userName, userAddress }) => { {isExpanded ? ( @@ -165,7 +165,7 @@ export const QMailMessages = ({ userName, userAddress }) => { ) : ( @@ -268,7 +268,7 @@ export const QMailMessages = ({ userName, userAddress }) => { ) : !lastEnteredTimestamp ? ( ) : lastEnteredTimestamp < mail?.created && @@ -281,7 +281,7 @@ export const QMailMessages = ({ userName, userAddress }) => { ) : ( )} diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index 4af646b..a839bfd 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -1,4 +1,12 @@ -import { forwardRef, Fragment, ReactElement, Ref, useEffect } from 'react'; +import { + ChangeEvent, + forwardRef, + Fragment, + ReactElement, + Ref, + useEffect, + useState, +} from 'react'; import Dialog from '@mui/material/Dialog'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; @@ -54,12 +62,12 @@ const Transition = forwardRef(function Transition( }); export const Settings = ({ address, open, setOpen }) => { - const [checked, setChecked] = React.useState(false); + const [checked, setChecked] = useState(false); const [isEnabledDevMode, setIsEnabledDevMode] = useRecoilState(enabledDevModeAtom); const theme = useTheme(); - const handleChange = (event: React.ChangeEvent) => { + const handleChange = (event: ChangeEvent) => { setChecked(event.target.checked); window .sendMessage('addUserSettings', { diff --git a/src/components/Home/QortPrice.tsx b/src/components/Home/QortPrice.tsx index 2f118c1..ec74b11 100644 --- a/src/components/Home/QortPrice.tsx +++ b/src/components/Home/QortPrice.tsx @@ -116,7 +116,7 @@ export const QortPrice = () => { > + Based on the latest 20 trades } @@ -127,7 +127,7 @@ export const QortPrice = () => { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -156,7 +156,7 @@ export const QortPrice = () => { {!ltcPerQort ? ( - + ) : ( { {!supply ? ( - + ) : ( { + {lastBlock?.timestamp && formatDate(lastBlock?.timestamp)} } @@ -211,7 +211,7 @@ export const QortPrice = () => { tooltip: { sx: { color: theme.palette.text.primary, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, }, }, arrow: { @@ -240,7 +240,7 @@ export const QortPrice = () => { {!lastBlock?.height ? ( - + ) : ( { + const theme = useTheme(); return ( diff --git a/src/components/RegisterName.tsx b/src/components/RegisterName.tsx index 35af458..8afa806 100644 --- a/src/components/RegisterName.tsx +++ b/src/components/RegisterName.tsx @@ -1,28 +1,29 @@ -import React, { useCallback, useContext, useEffect, useState } from 'react' +import React, { useCallback, useContext, useEffect, useState } from 'react'; import { - Avatar, - Box, - Button, - ButtonBase, - Collapse, - Dialog, - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, - Input, - ListItem, - ListItemAvatar, - ListItemButton, - ListItemIcon, - ListItemText, - List, - MenuItem, - Popover, - Select, - TextField, - Typography, - } from "@mui/material"; + Avatar, + Box, + Button, + ButtonBase, + Collapse, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Input, + ListItem, + ListItemAvatar, + ListItemButton, + ListItemIcon, + ListItemText, + List, + MenuItem, + Popover, + Select, + TextField, + Typography, + useTheme, +} from '@mui/material'; import { Label } from './Group/AddGroup'; import { Spacer } from '../common/Spacer'; import { LoadingButton } from '@mui/lab'; @@ -35,278 +36,313 @@ import CheckIcon from '@mui/icons-material/Check'; import ErrorIcon from '@mui/icons-material/Error'; enum Availability { - NULL = 'null', - LOADING = 'loading', - AVAILABLE = 'available', - NOT_AVAILABLE = 'not-available' + NULL = 'null', + LOADING = 'loading', + AVAILABLE = 'available', + NOT_AVAILABLE = 'not-available', } -export const RegisterName = ({setOpenSnack, setInfoSnack, userInfo, show, setTxList, balance}) => { - const [isOpen, setIsOpen] = useState(false) - const [registerNameValue, setRegisterNameValue] = useState('') - const [isLoadingRegisterName, setIsLoadingRegisterName] = useState(false) - const [isNameAvailable, setIsNameAvailable] = useState(Availability.NULL) - const [nameFee, setNameFee] = useState(null) +export const RegisterName = ({ + setOpenSnack, + setInfoSnack, + userInfo, + show, + setTxList, + balance, +}) => { + const [isOpen, setIsOpen] = useState(false); + const [registerNameValue, setRegisterNameValue] = useState(''); + const [isLoadingRegisterName, setIsLoadingRegisterName] = useState(false); + const [isNameAvailable, setIsNameAvailable] = useState( + Availability.NULL + ); + const [nameFee, setNameFee] = useState(null); + const theme = useTheme(); + const checkIfNameExisits = async (name) => { + if (!name?.trim()) { + setIsNameAvailable(Availability.NULL); - const checkIfNameExisits = async (name)=> { - if(!name?.trim()){ - setIsNameAvailable(Availability.NULL) - - return + return; } - setIsNameAvailable(Availability.LOADING) + setIsNameAvailable(Availability.LOADING); try { - const res = await fetch(`${getBaseApiReact()}/names/` + name); - const data = await res.json() - if(data?.message === 'name unknown'){ - setIsNameAvailable(Availability.AVAILABLE) - } else { - setIsNameAvailable(Availability.NOT_AVAILABLE) - } + const res = await fetch(`${getBaseApiReact()}/names/` + name); + const data = await res.json(); + if (data?.message === 'name unknown') { + setIsNameAvailable(Availability.AVAILABLE); + } else { + setIsNameAvailable(Availability.NOT_AVAILABLE); + } } catch (error) { - console.error(error) + console.error(error); } finally { } - } - // Debounce logic - useEffect(() => { - const handler = setTimeout(() => { - checkIfNameExisits(registerNameValue); - }, 500); - - // Cleanup timeout if searchValue changes before the timeout completes - return () => { - clearTimeout(handler); - }; - }, [registerNameValue]); + }; + // Debounce logic + useEffect(() => { + const handler = setTimeout(() => { + checkIfNameExisits(registerNameValue); + }, 500); - const openRegisterNameFunc = useCallback((e) => { - setIsOpen(true) + // Cleanup timeout if searchValue changes before the timeout completes + return () => { + clearTimeout(handler); + }; + }, [registerNameValue]); - }, [ setIsOpen]); - - useEffect(() => { - subscribeToEvent("openRegisterName", openRegisterNameFunc); - - return () => { - unsubscribeFromEvent("openRegisterName", openRegisterNameFunc); - }; - }, [openRegisterNameFunc]); + const openRegisterNameFunc = useCallback( + (e) => { + setIsOpen(true); + }, + [setIsOpen] + ); - useEffect(()=> { - const nameRegistrationFee = async ()=> { - try { - const fee = await getFee("REGISTER_NAME"); - setNameFee(fee?.fee) - } catch (error) { - console.error(error) - } - } - nameRegistrationFee() - }, []) + useEffect(() => { + subscribeToEvent('openRegisterName', openRegisterNameFunc); - const registerName = async () => { + return () => { + unsubscribeFromEvent('openRegisterName', openRegisterNameFunc); + }; + }, [openRegisterNameFunc]); + + useEffect(() => { + const nameRegistrationFee = async () => { try { - if (!userInfo?.address) throw new Error("Your address was not found"); - if(!registerNameValue) throw new Error('Enter a name') - - const fee = await getFee("REGISTER_NAME"); - await show({ - message: "Would you like to register this name?", - publishFee: fee.fee + " QORT", - }); - setIsLoadingRegisterName(true); - new Promise((res, rej) => { - window - .sendMessage("registerName", { - name: registerNameValue, - }) - .then((response) => { - if (!response?.error) { - res(response); - setIsLoadingRegisterName(false); - setInfoSnack({ - type: "success", - message: - "Successfully registered. It may take a couple of minutes for the changes to propagate", - }); - setIsOpen(false); - setRegisterNameValue(""); - setOpenSnack(true); - setTxList((prev) => [ - { - ...response, - type: "register-name", - label: `Registered name: awaiting confirmation. This may take a couple minutes.`, - labelDone: `Registered name: success!`, - done: false, - }, - ...prev.filter((item) => !item.done), - ]); - return; - } - setInfoSnack({ - type: "error", - message: response?.error, - }); - setOpenSnack(true); - rej(response.error); - }) - .catch((error) => { - setInfoSnack({ - type: "error", - message: error.message || "An error occurred", - }); - setOpenSnack(true); - rej(error); - }); - }); + const fee = await getFee('REGISTER_NAME'); + setNameFee(fee?.fee); } catch (error) { - if (error?.message) { - setOpenSnack(true) - setInfoSnack({ - type: "error", - message: error?.message, - }); - } - } finally { - setIsLoadingRegisterName(false); + console.error(error); } }; + nameRegistrationFee(); + }, []); + + const registerName = async () => { + try { + if (!userInfo?.address) throw new Error('Your address was not found'); + if (!registerNameValue) throw new Error('Enter a name'); + + const fee = await getFee('REGISTER_NAME'); + await show({ + message: 'Would you like to register this name?', + publishFee: fee.fee + ' QORT', + }); + setIsLoadingRegisterName(true); + new Promise((res, rej) => { + window + .sendMessage('registerName', { + name: registerNameValue, + }) + .then((response) => { + if (!response?.error) { + res(response); + setIsLoadingRegisterName(false); + setInfoSnack({ + type: 'success', + message: + 'Successfully registered. It may take a couple of minutes for the changes to propagate', + }); + setIsOpen(false); + setRegisterNameValue(''); + setOpenSnack(true); + setTxList((prev) => [ + { + ...response, + type: 'register-name', + label: `Registered name: awaiting confirmation. This may take a couple minutes.`, + labelDone: `Registered name: success!`, + done: false, + }, + ...prev.filter((item) => !item.done), + ]); + return; + } + setInfoSnack({ + type: 'error', + message: response?.error, + }); + setOpenSnack(true); + rej(response.error); + }) + .catch((error) => { + setInfoSnack({ + type: 'error', + message: error.message || 'An error occurred', + }); + setOpenSnack(true); + rej(error); + }); + }); + } catch (error) { + if (error?.message) { + setOpenSnack(true); + setInfoSnack({ + type: 'error', + message: error?.message, + }); + } + } finally { + setIsLoadingRegisterName(false); + } + }; return ( - - {"Register name"} - - - + {'Register name'} + + setRegisterNameValue(e.target.value)} value={registerNameValue} placeholder="Choose a name" /> - {(!balance || (nameFee && balance && balance < nameFee))&& ( + {(!balance || (nameFee && balance && balance < nameFee)) && ( <> - - - - Your balance is {balance ?? 0} QORT. A name registration requires a {nameFee} QORT fee - - - + + + + + Your balance is {balance ?? 0} QORT. A name registration + requires a {nameFee} QORT fee + + + )} {isNameAvailable === Availability.AVAILABLE && ( - - - {registerNameValue} is available + alignItems: 'center', + }} + > + + {registerNameValue} is available )} - {isNameAvailable === Availability.NOT_AVAILABLE && ( - - - {registerNameValue} is unavailable + alignItems: 'center', + }} + > + + {registerNameValue} is unavailable )} - {isNameAvailable === Availability.LOADING && ( - - - Checking if name already existis + alignItems: 'center', + }} + > + + Checking if name already existis )} - Benefits of a name - - - - - - - - - - - - - - - - - - + + Benefits of a name + + + + + + + + + + + + + + + - - - - - - - ) -} + + + + + + + ); +}; From 08f38bad92b958f7660387e53fee651c27cc8cf9 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sun, 27 Apr 2025 22:29:02 +0300 Subject: [PATCH 283/717] more color changes --- src/assets/Icons/StarEmpty.tsx | 7 +++- src/assets/Icons/StarFilled.tsx | 7 +++- src/assets/svgs/Copy.svg | 3 -- src/assets/svgs/Forum.svg | 3 -- src/assets/svgs/Info.svg | 3 -- src/assets/svgs/ModalClose.svg | 3 -- src/assets/svgs/Return.svg | 3 -- src/assets/svgs/Search.svg | 3 -- src/components/Apps/AppInfo.tsx | 2 +- src/components/Apps/AppInfoSnippet.tsx | 2 +- src/components/Apps/Apps-styles.tsx | 6 +-- src/components/Apps/AppsLibraryDesktop.tsx | 8 +++- src/components/Chat/ChatOptions.tsx | 8 +++- src/components/ContextMenuPinnedApps.tsx | 14 +++++-- src/components/Group/Forum/DisplayHtml.tsx | 45 ---------------------- src/components/Group/Forum/GroupMail.tsx | 2 +- src/components/Group/Forum/Thread.tsx | 1 - src/components/Group/WalletsAppWrapper.tsx | 8 ++-- 18 files changed, 44 insertions(+), 84 deletions(-) delete mode 100644 src/assets/svgs/Copy.svg delete mode 100644 src/assets/svgs/Forum.svg delete mode 100644 src/assets/svgs/Info.svg delete mode 100644 src/assets/svgs/ModalClose.svg delete mode 100644 src/assets/svgs/Return.svg delete mode 100644 src/assets/svgs/Search.svg delete mode 100644 src/components/Group/Forum/DisplayHtml.tsx diff --git a/src/assets/Icons/StarEmpty.tsx b/src/assets/Icons/StarEmpty.tsx index d69ad49..fdd0f0c 100644 --- a/src/assets/Icons/StarEmpty.tsx +++ b/src/assets/Icons/StarEmpty.tsx @@ -1,4 +1,9 @@ +import { useTheme } from '@mui/material'; + export const StarEmptyIcon = () => { + const theme = useTheme(); + + const setColor = theme.palette.text.secondary; return ( { > ); diff --git a/src/assets/Icons/StarFilled.tsx b/src/assets/Icons/StarFilled.tsx index 90cab45..b6645cf 100644 --- a/src/assets/Icons/StarFilled.tsx +++ b/src/assets/Icons/StarFilled.tsx @@ -1,4 +1,9 @@ +import { useTheme } from '@mui/material'; + export const StarFilledIcon = () => { + const theme = useTheme(); + + const setColor = theme.palette.text.primary; return ( { > ); diff --git a/src/assets/svgs/Copy.svg b/src/assets/svgs/Copy.svg deleted file mode 100644 index 0348fc9..0000000 --- a/src/assets/svgs/Copy.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/assets/svgs/Forum.svg b/src/assets/svgs/Forum.svg deleted file mode 100644 index 7957b06..0000000 --- a/src/assets/svgs/Forum.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/assets/svgs/Info.svg b/src/assets/svgs/Info.svg deleted file mode 100644 index 63f9bfc..0000000 --- a/src/assets/svgs/Info.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/assets/svgs/ModalClose.svg b/src/assets/svgs/ModalClose.svg deleted file mode 100644 index 1a8b18e..0000000 --- a/src/assets/svgs/ModalClose.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/assets/svgs/Return.svg b/src/assets/svgs/Return.svg deleted file mode 100644 index e4ff9b3..0000000 --- a/src/assets/svgs/Return.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/assets/svgs/Search.svg b/src/assets/svgs/Search.svg deleted file mode 100644 index b6cb06b..0000000 --- a/src/assets/svgs/Search.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index f9e72b3..17f9b7e 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -186,7 +186,7 @@ export const AppInfo = ({ app, myName }) => { }} sx={{ backgroundColor: isInstalled - ? '#0091E1' + ? theme.palette.primary.main : theme.palette.background.paper, height: '29px', maxWidth: '320px', diff --git a/src/components/Apps/AppInfoSnippet.tsx b/src/components/Apps/AppInfoSnippet.tsx index dfe4ea6..8aaf33d 100644 --- a/src/components/Apps/AppInfoSnippet.tsx +++ b/src/components/Apps/AppInfoSnippet.tsx @@ -183,7 +183,7 @@ export const AppInfoSnippet = ({ }} sx={{ backgroundColor: isInstalled - ? '#0091E1' + ? theme.palette.primary.main : theme.palette.background.paper, }} > diff --git a/src/components/Apps/Apps-styles.tsx b/src/components/Apps/Apps-styles.tsx index 40192a3..dd10e36 100644 --- a/src/components/Apps/Apps-styles.tsx +++ b/src/components/Apps/Apps-styles.tsx @@ -75,7 +75,7 @@ export const AppsWidthLimiter = styled(Box)(({ theme }) => ({ export const AppsSearchContainer = styled(Box)(({ theme }) => ({ alignItems: 'center', - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, borderRadius: '8px', color: theme.palette.text.primary, display: 'flex', @@ -87,7 +87,7 @@ export const AppsSearchContainer = styled(Box)(({ theme }) => ({ export const AppsSearchLeft = styled(Box)(({ theme }) => ({ alignItems: 'center', - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, color: theme.palette.text.primary, display: 'flex', flexGrow: 1, @@ -99,7 +99,7 @@ export const AppsSearchLeft = styled(Box)(({ theme }) => ({ export const AppsSearchRight = styled(Box)(({ theme }) => ({ alignItems: 'center', - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, color: theme.palette.text.primary, display: 'flex', flexShrink: 1, diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index f09e7c1..3490e89 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -39,7 +39,8 @@ import { Spacer } from '../../common/Spacer'; import { AppInfoSnippet } from './AppInfoSnippet'; import { Virtuoso } from 'react-virtuoso'; import { executeEvent } from '../../utils/events'; -import { ShowMessageReturnButton } from '../Group/Forum/Mail-styles'; +import { ComposeP, ShowMessageReturnButton } from '../Group/Forum/Mail-styles'; +import { ReturnIcon } from '../../assets/Icons/ReturnIcon.tsx'; const officialAppList = [ 'q-tube', @@ -273,7 +274,10 @@ export const AppsLibraryDesktop = ({ onClick={() => { executeEvent('navigateBack', {}); }} - > + > + + Return to Apps Dashboard + diff --git a/src/components/Chat/ChatOptions.tsx b/src/components/Chat/ChatOptions.tsx index c70dbff..49d5150 100644 --- a/src/components/Chat/ChatOptions.tsx +++ b/src/components/Chat/ChatOptions.tsx @@ -24,7 +24,7 @@ import { AppsSearchLeft, AppsSearchRight, } from '../Apps/Apps-styles'; -import IconSearch from '../../assets/svgs/Search.svg'; + import IconClearInput from '../../assets/svgs/ClearInput.svg'; import { CellMeasurerCache } from 'react-virtualized'; import { getBaseApiReact } from '../../App'; @@ -398,7 +398,11 @@ export const ChatOptions = ({ > - + setSearchValue(e.target.value)} diff --git a/src/components/ContextMenuPinnedApps.tsx b/src/components/ContextMenuPinnedApps.tsx index 7dad268..3e2c2be 100644 --- a/src/components/ContextMenuPinnedApps.tsx +++ b/src/components/ContextMenuPinnedApps.tsx @@ -5,6 +5,7 @@ import { MenuItem, Typography, styled, + useTheme, } from '@mui/material'; import PushPinIcon from '@mui/icons-material/PushPin'; import { saveToLocalStorage } from './Apps/AppsNavBarDesktop'; @@ -13,7 +14,6 @@ import { sortablePinnedAppsAtom } from '../atoms/global'; const CustomStyledMenu = styled(Menu)(({ theme }) => ({ '& .MuiPaper-root': { - backgroundColor: '#f9f9f9', borderRadius: '12px', padding: theme.spacing(1), boxShadow: '0 5px 15px rgba(0, 0, 0, 0.2)', @@ -23,7 +23,7 @@ const CustomStyledMenu = styled(Menu)(({ theme }) => ({ color: '#444', transition: '0.3s background-color', '&:hover': { - backgroundColor: '#f0f0f0', + backgroundColor: theme.palette.action.hover, }, }, })); @@ -37,6 +37,7 @@ export const ContextMenuPinnedApps = ({ children, app, isMine }) => { const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState( sortablePinnedAppsAtom ); + const theme = useTheme(); const handleContextMenu = (event) => { if (isMine) return; @@ -170,9 +171,14 @@ export const ContextMenuPinnedApps = ({ children, app, isMine }) => { }} > - + - + Unpin app diff --git a/src/components/Group/Forum/DisplayHtml.tsx b/src/components/Group/Forum/DisplayHtml.tsx deleted file mode 100644 index 786672a..0000000 --- a/src/components/Group/Forum/DisplayHtml.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useMemo } from 'react'; -import DOMPurify from 'dompurify'; -import 'react-quill/dist/quill.snow.css'; -import 'react-quill/dist/quill.core.css'; -import 'react-quill/dist/quill.bubble.css'; -import { Box, styled } from '@mui/material'; -import { convertQortalLinks } from '../../../utils/qortalLink'; - -const CrowdfundInlineContent = styled(Box)(({ theme }) => ({ - display: 'flex', - fontFamily: 'Mulish', - fontSize: '19px', - fontWeight: 400, - letterSpacing: 0, - color: theme.palette.text.primary, - width: '100%', -})); - -export const DisplayHtml = ({ html, textColor }: any) => { - const cleanContent = useMemo(() => { - if (!html) return null; - - const sanitize: string = DOMPurify.sanitize(html, { - USE_PROFILES: { html: true }, - }); - const anchorQortal = convertQortalLinks(sanitize); - return anchorQortal; - }, [html]); - - if (!cleanContent) return null; - - return ( - -
    - - ); -}; diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index fcc0a09..d6a3700 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -681,8 +681,8 @@ export const GroupMail = ({ diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 266edb6..a086a0b 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -29,7 +29,6 @@ import { } from './Mail-styles'; import { Spacer } from '../../../common/Spacer'; import { threadIdentifier } from './GroupMail'; -import ReturnSVG from '../../../assets/svgs/Return.svg'; import { NewThread } from './NewThread'; import { decryptPublishes, diff --git a/src/components/Group/WalletsAppWrapper.tsx b/src/components/Group/WalletsAppWrapper.tsx index c569425..82e6660 100644 --- a/src/components/Group/WalletsAppWrapper.tsx +++ b/src/components/Group/WalletsAppWrapper.tsx @@ -1,4 +1,4 @@ -import { Box, ButtonBase, Divider, Typography } from '@mui/material'; +import { Box, ButtonBase, Divider, Typography, useTheme } from '@mui/material'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import CloseIcon from '@mui/icons-material/Close'; import AppViewerContainer from '../Apps/AppViewerContainer'; @@ -25,7 +25,7 @@ export const WalletsAppWrapper = () => { service: 'APP', path: 'qortal?authOnMount=true', }); - + const theme = useTheme(); const isDisableBackButton = useMemo(() => { if (selectedTab && navigationController[selectedTab?.tabId]?.hasBack) return false; @@ -62,7 +62,7 @@ export const WalletsAppWrapper = () => { position: 'fixed', height: '100vh', width: '100vw', - backgroundColor: '#27282c', // TODO: set color theme + backgroundColor: theme.palette.background.paper, // TODO: set color theme zIndex: 100, bottom: 0, right: 0, @@ -92,7 +92,7 @@ export const WalletsAppWrapper = () => { From efc83c89aa477b8ccf64a2feaf4f53ff70135ceb Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 28 Apr 2025 10:24:14 +0300 Subject: [PATCH 284/717] added theme to icons --- src/common/BoundedNumericTextField.tsx | 13 +++++++++---- src/components/Apps/AppsDevModeNavBar.tsx | 8 ++++---- src/components/Apps/AppsHomeDesktop.tsx | 13 ++++++------- src/components/GeneralNotifications.tsx | 2 +- .../Group/Forum/ShowMessageWithoutModal.tsx | 2 +- src/components/Group/Group.tsx | 2 +- src/components/Group/ListOfMembers.tsx | 5 +++-- src/components/Group/Settings.tsx | 8 +------- src/components/RegisterName.tsx | 10 +++++----- 9 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/common/BoundedNumericTextField.tsx b/src/common/BoundedNumericTextField.tsx index fa0d4b1..f64c8ad 100644 --- a/src/common/BoundedNumericTextField.tsx +++ b/src/common/BoundedNumericTextField.tsx @@ -1,4 +1,9 @@ -import { IconButton, InputAdornment, TextFieldProps } from '@mui/material'; +import { + IconButton, + InputAdornment, + TextFieldProps, + useTheme, +} from '@mui/material'; import React, { useRef, useState } from 'react'; import AddIcon from '@mui/icons-material/Add'; import RemoveIcon from '@mui/icons-material/Remove'; @@ -39,7 +44,7 @@ export const BoundedNumericTextField = ({ const stringIsEmpty = (value: string) => { return value === ''; }; - + const theme = useTheme(); const isAllZerosNum = /^0*\.?0*$/; const isFloatNum = /^-?[0-9]*\.?[0-9]*$/; const isIntegerNum = /^-?[0-9]+$/; @@ -133,7 +138,7 @@ export const BoundedNumericTextField = ({ > {' '} @@ -143,7 +148,7 @@ export const BoundedNumericTextField = ({ > {' '} diff --git a/src/components/Apps/AppsDevModeNavBar.tsx b/src/components/Apps/AppsDevModeNavBar.tsx index a677d67..bcc8811 100644 --- a/src/components/Apps/AppsDevModeNavBar.tsx +++ b/src/components/Apps/AppsDevModeNavBar.tsx @@ -6,7 +6,7 @@ import { } from './Apps-styles'; import { NavBack } from '../../assets/Icons/NavBack.tsx'; import { NavAdd } from '../../assets/Icons/NavAdd.tsx'; -import { ButtonBase, Tab, Tabs } from '@mui/material'; +import { ButtonBase, Tab, Tabs, useTheme } from '@mui/material'; import { executeEvent, subscribeToEvent, @@ -23,7 +23,7 @@ export const AppsDevModeNavBar = () => { const [navigationController, setNavigationController] = useRecoilState( navigationControllerAtom ); - + const theme = useTheme(); const [isNewTabWindow, setIsNewTabWindow] = useState(false); const tabsRef = useRef(null); const [anchorEl, setAnchorEl] = useState(null); @@ -114,7 +114,7 @@ export const AppsDevModeNavBar = () => { scrollButtons={true} sx={{ '& .MuiTabs-indicator': { - backgroundColor: 'white', + backgroundColor: theme.palette.text.primary, }, maxHeight: `275px`, // Ensure the tabs container fits within the available space overflow: 'hidden', // Prevents overflow on small screens @@ -133,7 +133,7 @@ export const AppsDevModeNavBar = () => { } // Pass custom component sx={{ '&.Mui-selected': { - color: 'white', + color: theme.palette.text.primary, }, padding: '0px', margin: '0px', diff --git a/src/components/Apps/AppsHomeDesktop.tsx b/src/components/Apps/AppsHomeDesktop.tsx index c77865c..8a96015 100644 --- a/src/components/Apps/AppsHomeDesktop.tsx +++ b/src/components/Apps/AppsHomeDesktop.tsx @@ -69,10 +69,7 @@ export const AppsHomeDesktop = ({ display: 'flex', gap: '20px', alignItems: 'center', - backgroundColor: - theme.palette.mode === 'dark' - ? 'rgba(41, 41, 43, 1)' - : 'rgb(209, 209, 209)', + backgroundColor: theme.palette.background.paper, padding: '7px', borderRadius: '20px', width: '100%', @@ -91,9 +88,9 @@ export const AppsHomeDesktop = ({ placeholder="qortal://" sx={{ width: '100%', - color: theme.palette.mode === 'dark' ? 'white' : 'black', + color: theme.palette.text.primary, '& .MuiInput-input::placeholder': { - color: 'rgba(84, 84, 84, 0.70) !important', + color: theme.palette.text.secondary, fontSize: '20px', fontStyle: 'normal', fontWeight: 400, @@ -115,7 +112,9 @@ export const AppsHomeDesktop = ({ openQortalUrl()}> diff --git a/src/components/GeneralNotifications.tsx b/src/components/GeneralNotifications.tsx index 25fbb6b..3e9cd02 100644 --- a/src/components/GeneralNotifications.tsx +++ b/src/components/GeneralNotifications.tsx @@ -149,7 +149,7 @@ export const GeneralNotifications = ({ address }) => { > {' '} {formatDate(latestTx?.timestamp)} diff --git a/src/components/Group/Forum/ShowMessageWithoutModal.tsx b/src/components/Group/Forum/ShowMessageWithoutModal.tsx index 57d920d..914b9c5 100644 --- a/src/components/Group/Forum/ShowMessageWithoutModal.tsx +++ b/src/components/Group/Forum/ShowMessageWithoutModal.tsx @@ -20,7 +20,7 @@ import { WrapperUserAction } from '../../WrapperUserAction'; export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { const [expandAttachments, setExpandAttachments] = useState(false); - console.log('message', message); + let cleanHTML = ''; if (message?.htmlContent) { cleanHTML = DOMPurify.sanitize(message.htmlContent); diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index fdd57e0..4e04b02 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -1780,7 +1780,7 @@ export const Group = ({
    ); }; - console.log('groupsProperties', groupsProperties); + const renderGroups = () => { return (
    { @@ -354,7 +355,7 @@ const ListOfMembers = ({ {member?.isAdmin && ( diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index a839bfd..77032f7 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -131,9 +131,7 @@ export const Settings = ({ address, open, setOpen }) => { onClose={handleClose} TransitionComponent={Transition} > - + General Settings @@ -152,7 +150,6 @@ export const Settings = ({ address, open, setOpen }) => { { /> {window?.electronAPI && ( @@ -248,7 +248,7 @@ export const RegisterName = ({ > {registerNameValue} is available @@ -264,7 +264,7 @@ export const RegisterName = ({ > {registerNameValue} is unavailable @@ -298,7 +298,7 @@ export const RegisterName = ({ @@ -308,7 +308,7 @@ export const RegisterName = ({ From ba9062dbcf4ea909b6004a774251b296bc4f7fed Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 28 Apr 2025 16:13:41 +0300 Subject: [PATCH 285/717] added theme manager --- package-lock.json | 416 ++++++++++++++++++ package.json | 1 + src/Wallets.tsx | 6 +- src/components/BuyQortInformation.tsx | 13 +- .../Group/ListOfGroupPromotions.tsx | 1 + src/components/Group/Settings.tsx | 2 + src/components/Theme/ThemeContext.tsx | 132 ++++-- src/components/Theme/ThemeManager.tsx | 309 +++++++++++++ src/components/Theme/themeManager.css | 39 ++ src/styles/theme-dark.ts | 8 +- src/styles/theme-light.ts | 6 +- 11 files changed, 880 insertions(+), 53 deletions(-) create mode 100644 src/components/Theme/ThemeManager.tsx create mode 100644 src/components/Theme/themeManager.css diff --git a/package-lock.json b/package-lock.json index bd1bebf..dd65c25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "@tiptap/starter-kit": "^2.5.9", "@transistorsoft/capacitor-background-fetch": "^6.0.1", "@types/chrome": "^0.0.263", + "@uiw/react-color": "^2.5.1", "adm-zip": "^0.5.16", "asmcrypto.js": "2.3.2", "axios": "^1.7.7", @@ -5900,6 +5901,399 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@uiw/color-convert": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/color-convert/-/color-convert-2.5.1.tgz", + "integrity": "sha512-p+P8Ho0Z1AbUprES0hcLEDAaXbGH92TmjckkRQZ5S7HcyQ+9ZXlSsDFILjFbYu/okVjx5VG59T57Dx84lv9AWA==", + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0" + } + }, + "node_modules/@uiw/react-color": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color/-/react-color-2.5.1.tgz", + "integrity": "sha512-u6Kj7rdhsMOls2KItpHLkG8WTghDS2jYBucLeOLLJXJDs25TuEBI9d1o939og8cUJtTwBrowWFFU63a1kGsciA==", + "license": "MIT", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1", + "@uiw/react-color-block": "2.5.1", + "@uiw/react-color-chrome": "2.5.1", + "@uiw/react-color-circle": "2.5.1", + "@uiw/react-color-colorful": "2.5.1", + "@uiw/react-color-compact": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1", + "@uiw/react-color-editable-input-hsla": "2.5.1", + "@uiw/react-color-editable-input-rgba": "2.5.1", + "@uiw/react-color-github": "2.5.1", + "@uiw/react-color-hue": "2.5.1", + "@uiw/react-color-material": "2.5.1", + "@uiw/react-color-name": "2.5.1", + "@uiw/react-color-saturation": "2.5.1", + "@uiw/react-color-shade-slider": "2.5.1", + "@uiw/react-color-sketch": "2.5.1", + "@uiw/react-color-slider": "2.5.1", + "@uiw/react-color-swatch": "2.5.1", + "@uiw/react-color-wheel": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-alpha": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-alpha/-/react-color-alpha-2.5.1.tgz", + "integrity": "sha512-hPsIgsnuOQrqinXt3Gt+87fHudbUvvPW+TpvRY0HS9v4ptFu5UsCc/7DPTVKTaL+p+0oaA6eTbziLzPLRLzgsQ==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-drag-event-interactive": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-block": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-block/-/react-color-block-2.5.1.tgz", + "integrity": "sha512-qvubiV0z0P3OxpNt6o1UQ3CVsjVBY1/n/oz6Gzzxx9YPqSClI04AtFjwOQxF7M17SYqXv+88y77gfEfPIqk5+A==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1", + "@uiw/react-color-swatch": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-chrome": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-chrome/-/react-color-chrome-2.5.1.tgz", + "integrity": "sha512-m/CyRaWgmkW5aQTQ8AZwyvopYm+bhvX06uS+ezQjXDYDtjLvq7RbM0JLLNIOyMXke964R58fhoX4G06ZWd8ycA==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1", + "@uiw/react-color-editable-input-hsla": "2.5.1", + "@uiw/react-color-editable-input-rgba": "2.5.1", + "@uiw/react-color-github": "2.5.1", + "@uiw/react-color-hue": "2.5.1", + "@uiw/react-color-saturation": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-circle": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-circle/-/react-color-circle-2.5.1.tgz", + "integrity": "sha512-+8zb/Ork1Q5f2bq0jN+GF7OyqY+2ZDYGrdZovN3EBZLMmERbg6TM2+1gTweeFsdiEM/gpteupJpwKpO1aBCocg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-swatch": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-colorful": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-colorful/-/react-color-colorful-2.5.1.tgz", + "integrity": "sha512-Y/8Y2Kman6IZQpgs4tPTGPuTNr3fJIJxf4f13jll6xuaOsVZeDq9q+DlMErggL+5ICtaBr8gG+w68nCiY+QqKg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1", + "@uiw/react-color-hue": "2.5.1", + "@uiw/react-color-saturation": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-compact": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-compact/-/react-color-compact-2.5.1.tgz", + "integrity": "sha512-5jHJcXEkjMwcghzCgSBU2rPMVjuuaJ7B6IxypNkafRQ4FkW/6bP9WpPkzcNXCZ/gPvSJ1OMQ+Y600mdO78qG5Q==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1", + "@uiw/react-color-editable-input-rgba": "2.5.1", + "@uiw/react-color-swatch": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-editable-input": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input/-/react-color-editable-input-2.5.1.tgz", + "integrity": "sha512-0kr5vQJGPln8LObXwfI2YLiHFz2DW3Atgi51JXlrZUyyaVujXRgMTAc1fz/1RQR6cU2A4bweFaCQljcTsv+Cdg==", + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-editable-input-hsla": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-hsla/-/react-color-editable-input-hsla-2.5.1.tgz", + "integrity": "sha512-gmnXB6JrYFAd8VN/EfNDJaTdkFHAnUxjzcsQjQyOEr046jDjWgEc/5o2uE1LwIvoJNg9Lo6LYsr37LnFWwsiLw==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-editable-input-rgba": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-editable-input-rgba": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-rgba/-/react-color-editable-input-rgba-2.5.1.tgz", + "integrity": "sha512-rk6OxL9lTdRI45aNe3GbUghvaELk4knkEf0gvF/mPHxoeE+nNphSrO5gHm3HhoDOgaplp81VP3q4gUwcdjBzvw==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-github": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-github/-/react-color-github-2.5.1.tgz", + "integrity": "sha512-t05rIy2ifReiVnjv3x+IVlJH7wvwtZugMeouDa/1Y7jIGZswO0zw3zMxz7qfHrzf5NVYWjmEF8QCj85ngv9brg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-swatch": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-hue": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-hue/-/react-color-hue-2.5.1.tgz", + "integrity": "sha512-o7mjZhm+U4gHxaBXFxjPINeE3jWfiZAl7RUFqwn4PDZC8wvhU5hEKgJUvcXzErYro0ZYrE1fC/wUHRpI+vcEBg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-material": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-material/-/react-color-material-2.5.1.tgz", + "integrity": "sha512-iPB4YfKVTNO1lSIQ16DMdDurDKvGTjv6Qwi/nq47yE3nnhB0YbOFwb/IZbWBS1sCTPx1an7dM2IZ+hYoYcjrXg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1", + "@uiw/react-color-editable-input-rgba": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-name": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-name/-/react-color-name-2.5.1.tgz", + "integrity": "sha512-JFb6DFz9kF2jI42MS/vtXZu1XzIrzcSIOqCwVkYWCQnSxOM9h+vd4pv2Yi1oy7IPgaadXUDkrGQSAvEkXU593Q==", + "dependencies": { + "colors-named": "^1.0.1", + "colors-named-hex": "^1.0.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0" + } + }, + "node_modules/@uiw/react-color-saturation": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-saturation/-/react-color-saturation-2.5.1.tgz", + "integrity": "sha512-mQ6eGmn6dUXfScQrb5tP0TBGCpZWzrQuYOAiwK9u31IJaxFwD1NNAzkiienWe4MQkA5zmgz7Ol6FEdLN8K+vGw==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-drag-event-interactive": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-shade-slider": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-shade-slider/-/react-color-shade-slider-2.5.1.tgz", + "integrity": "sha512-hrscAmqmy/Od/usUPETaEuvsNRhUGvNArl73d7HK6e6FjbRFPDBq40LkvjETe8BJMbxrBXTMo6dK7DO08lYq9g==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-sketch": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-sketch/-/react-color-sketch-2.5.1.tgz", + "integrity": "sha512-eQgAnlSZvqoTt6frZa/j+tFdaIBEFneIdxEUfidD8hwvyu5OR/WLHnDy/4fYAxhehDp9Ej8eS3ZsCgPACBMOtA==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1", + "@uiw/react-color-editable-input": "2.5.1", + "@uiw/react-color-editable-input-rgba": "2.5.1", + "@uiw/react-color-hue": "2.5.1", + "@uiw/react-color-saturation": "2.5.1", + "@uiw/react-color-swatch": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-slider": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-slider/-/react-color-slider-2.5.1.tgz", + "integrity": "sha512-2yluI0Akp6UMXTeAJ4CEjL8flhIFpn3xUPsFXbQmBSzMYJygleVFmwhMye8LSA2PCe3UdaqA2cWXxWsTL0FbIg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-color-alpha": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-swatch": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-swatch/-/react-color-swatch-2.5.1.tgz", + "integrity": "sha512-EQ7UEzxdohfsdpXmcEWNmK/uiznZovEKo6+j3OLrSU5pZGO7pxjR9sQMlscikvd8Mu1Mm3U0E6bJseo2acD4Lg==", + "dependencies": { + "@uiw/color-convert": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-wheel": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color-wheel/-/react-color-wheel-2.5.1.tgz", + "integrity": "sha512-e3tDwDoC2T7zTapRRm/QxcOJ7IWJwNCoxZ/f97RL1Ib3gAN/k67H1bkR9TK7euRCUxGy031guxTgdKO9v19XFg==", + "dependencies": { + "@uiw/color-convert": "2.5.1", + "@uiw/react-drag-event-interactive": "2.5.1" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-drag-event-interactive": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@uiw/react-drag-event-interactive/-/react-drag-event-interactive-2.5.1.tgz", + "integrity": "sha512-GNxhxk5L4O5Gpi20A/BG5sO0GNBNwtNWJidJsJu3pgHUBErN4rhqTDXXu3BQTz5C8yOG5D02Y6Zq/6yu6ckImw==", + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -7774,6 +8168,28 @@ "color-support": "bin.js" } }, + "node_modules/colors-named": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/colors-named/-/colors-named-1.0.2.tgz", + "integrity": "sha512-2ANq2r393PV9njYUD66UdfBcxR1slMqRA3QRTWgCx49JoCJ+kOhyfbQYxKJbPZQIhZUcNjVOs5AlyY1WwXec3w==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/colors-named-hex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/colors-named-hex/-/colors-named-hex-1.0.2.tgz", + "integrity": "sha512-k6kq1e1pUCQvSVwIaGFq2l0LrkAPQZWyeuZn1Z8nOiYSEZiKoFj4qx690h2Kd34DFl9Me0gKS6MUwAMBJj8nuA==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", diff --git a/package.json b/package.json index 3c92ad4..3594023 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@tiptap/starter-kit": "^2.5.9", "@transistorsoft/capacitor-background-fetch": "^6.0.1", "@types/chrome": "^0.0.263", + "@uiw/react-color": "^2.5.1", "adm-zip": "^0.5.16", "asmcrypto.js": "2.3.2", "axios": "^1.7.7", diff --git a/src/Wallets.tsx b/src/Wallets.tsx index 3c53c52..cb66936 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -48,7 +48,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { const [password, setPassword] = useState(''); const [isOpenSeedModal, setIsOpenSeedModal] = useState(false); const [isLoadingEncryptSeed, setIsLoadingEncryptSeed] = useState(false); - + const theme = useTheme(); const { isShow, onCancel, onOk, show } = useModal(); const { getRootProps, getInputProps } = useDropzone({ @@ -216,7 +216,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { maxHeight: '60vh', overflowY: 'auto', overflowX: 'hidden', - backgroundColor: 'rgb(30 30 32 / 70%)', + backgroundColor: theme.palette.background.paper, }} > {wallets?.map((wallet, idx) => { @@ -429,7 +429,7 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => { bgcolor: theme.palette.background.default, flexGrow: 1, '&:hover': { - backgroundColor: theme.palette.background.paper, + backgroundColor: theme.palette.action.hover, transform: 'scale(1.01)', }, transition: 'all 0.1s ease-in-out', diff --git a/src/components/BuyQortInformation.tsx b/src/components/BuyQortInformation.tsx index cbcbe39..776eefb 100644 --- a/src/components/BuyQortInformation.tsx +++ b/src/components/BuyQortInformation.tsx @@ -110,7 +110,6 @@ export const BuyQortInformation = ({ balance }) => { { > - + - + diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index 4d402b6..7942dd2 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -48,6 +48,7 @@ import { useVirtualizer } from '@tanstack/react-virtual'; import ErrorBoundary from '../../common/ErrorBoundary'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import { getFee } from '../../background'; export const requestQueuePromos = new RequestQueueWithPromise(20); export function utf8ToBase64(inputString: string): string { diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index 77032f7..3ac9a2e 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -18,6 +18,7 @@ import { TransitionProps } from '@mui/material/transitions'; import { Box, FormControlLabel, Switch, styled, useTheme } from '@mui/material'; import { enabledDevModeAtom } from '../../atoms/global'; import { useRecoilState } from 'recoil'; +import ThemeManager from '../Theme/ThemeManager'; const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ padding: 8, @@ -185,6 +186,7 @@ export const Settings = ({ address, open, setOpen }) => { label="Enable dev mode" /> )} + diff --git a/src/components/Theme/ThemeContext.tsx b/src/components/Theme/ThemeContext.tsx index d2e7048..0f24b4a 100644 --- a/src/components/Theme/ThemeContext.tsx +++ b/src/components/Theme/ThemeContext.tsx @@ -6,57 +6,129 @@ import { useEffect, useCallback, } from 'react'; -import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles'; -import { darkTheme } from '../../styles/theme-dark'; -import { lightTheme } from '../../styles/theme-light'; +import { + ThemeProvider as MuiThemeProvider, + createTheme, +} from '@mui/material/styles'; +import { lightThemeOptions } from '../../styles/theme-light'; +import { darkThemeOptions } from '../../styles/theme-dark'; + +const defaultTheme = { + id: 'default', + name: 'Default Theme', + light: lightThemeOptions.palette, + dark: darkThemeOptions.palette, +}; const ThemeContext = createContext({ themeMode: 'light', toggleTheme: () => {}, + userThemes: [defaultTheme], + addUserTheme: (themes) => {}, + setUserTheme: (theme) => {}, + currentThemeId: 'default', }); -export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { +export const ThemeProvider = ({ children }) => { const [themeMode, setThemeMode] = useState('light'); + const [userThemes, setUserThemes] = useState([defaultTheme]); + const [currentThemeId, setCurrentThemeId] = useState('default'); - const theme = useMemo( - () => (themeMode === 'light' ? lightTheme : darkTheme), - [themeMode] - ); + const currentTheme = + userThemes.find((theme) => theme.id === currentThemeId) || defaultTheme; + + const muiTheme = useMemo(() => { + if (themeMode === 'light') { + return createTheme({ + ...lightThemeOptions, + palette: { + ...currentTheme.light, + }, + }); + } else { + return createTheme({ + ...lightThemeOptions, + palette: { + ...currentTheme.dark, + }, + }); + } + }, [themeMode, currentTheme]); + + const saveSettings = ( + themes = userThemes, + mode = themeMode, + themeId = currentThemeId + ) => { + localStorage.setItem( + 'saved_ui_theme', + JSON.stringify({ + mode, + userThemes: themes, + currentThemeId: themeId, + }) + ); + }; const toggleTheme = () => { - setThemeMode((prevMode) => { - const newMode = prevMode === 'light' ? 'dark' : 'light'; - - const themeProperties = { - mode: newMode, - }; - - localStorage.setItem('saved_ui_theme', JSON.stringify(themeProperties)); - + setThemeMode((prev) => { + const newMode = prev === 'light' ? 'dark' : 'light'; + saveSettings(userThemes, newMode, currentThemeId); return newMode; }); }; - const getSavedTheme = useCallback(async () => { - try { - const themeProperties = JSON.parse( - localStorage.getItem(`saved_ui_theme`) || '{}' - ); + const addUserTheme = (themes) => { + setUserThemes(themes); + saveSettings(themes); + }; - const theme = themeProperties?.mode || 'light'; - setThemeMode(theme); - } catch (error) { - console.log('error', error); + const setUserTheme = (theme) => { + if (theme.id === 'default') { + setCurrentThemeId('default'); + saveSettings(userThemes, themeMode, 'default'); + } else { + setCurrentThemeId(theme.id); + saveSettings(userThemes, themeMode, theme.id); + } + }; + + const loadSettings = useCallback(() => { + const saved = localStorage.getItem('saved_ui_theme'); + if (saved) { + try { + const parsed = JSON.parse(saved); + if (parsed.mode === 'light' || parsed.mode === 'dark') + setThemeMode(parsed.mode); + if (Array.isArray(parsed.userThemes)) { + const filteredThemes = parsed.userThemes.filter( + (theme) => theme.id !== 'default' + ); + setUserThemes([defaultTheme, ...filteredThemes]); + } + if (parsed.currentThemeId) setCurrentThemeId(parsed.currentThemeId); + } catch (error) { + console.error('Failed to parse saved_ui_theme:', error); + } } }, []); useEffect(() => { - getSavedTheme(); - }, [getSavedTheme]); + loadSettings(); + }, [loadSettings]); return ( - - {children} + + {children} ); }; diff --git a/src/components/Theme/ThemeManager.tsx b/src/components/Theme/ThemeManager.tsx new file mode 100644 index 0000000..3b2b711 --- /dev/null +++ b/src/components/Theme/ThemeManager.tsx @@ -0,0 +1,309 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { + Box, + Button, + IconButton, + Typography, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + List, + ListItemText, + ListItemSecondaryAction, + TextField, + Tabs, + Tab, + ListItemButton, +} from '@mui/material'; +import { Sketch } from '@uiw/react-color'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import AddIcon from '@mui/icons-material/Add'; +import CheckIcon from '@mui/icons-material/Check'; +import { useThemeContext } from './ThemeContext'; +import { darkThemeOptions } from '../../styles/theme-dark'; +import { lightThemeOptions } from '../../styles/theme-light'; +import ShortUniqueId from 'short-unique-id'; +import { rgbStringToHsva, rgbaStringToHsva } from '@uiw/color-convert'; + +const uid = new ShortUniqueId({ length: 8 }); + +function detectColorFormat(color) { + if (typeof color !== 'string') return null; + if (color.startsWith('rgba')) return 'rgba'; + if (color.startsWith('rgb')) return 'rgb'; + return null; +} + +export default function ThemeManager() { + const { userThemes, addUserTheme, setUserTheme, currentThemeId } = + useThemeContext(); + const [openEditor, setOpenEditor] = useState(false); + const [themeDraft, setThemeDraft] = useState({ + id: '', + name: '', + light: {}, + dark: {}, + }); + const [currentTab, setCurrentTab] = useState('light'); + const nameInputRef = useRef(null); + + useEffect(() => { + if (openEditor && nameInputRef.current) { + nameInputRef.current.focus(); + } + }, [openEditor]); + + const handleAddTheme = () => { + setThemeDraft({ + id: '', + name: '', + light: structuredClone(lightThemeOptions.palette), + dark: structuredClone(darkThemeOptions.palette), + }); + setOpenEditor(true); + }; + + const handleEditTheme = (themeId) => { + const themeToEdit = userThemes.find((theme) => theme.id === themeId); + if (themeToEdit) { + setThemeDraft({ ...themeToEdit }); + setOpenEditor(true); + } + }; + + const handleSaveTheme = () => { + if (themeDraft.id) { + const updatedThemes = [...userThemes]; + const index = updatedThemes.findIndex( + (theme) => theme.id === themeDraft.id + ); + if (index !== -1) { + updatedThemes[index] = themeDraft; + addUserTheme(updatedThemes); + } + } else { + const newTheme = { ...themeDraft, id: uid.rnd() }; + const updatedThemes = [...userThemes, newTheme]; + addUserTheme(updatedThemes); + setUserTheme(newTheme); + } + setOpenEditor(false); + }; + + const handleDeleteTheme = (id) => { + const updatedThemes = userThemes.filter((theme) => theme.id !== id); + addUserTheme(updatedThemes); + + if (id === currentThemeId) { + // Find the default theme object in the list + const defaultTheme = updatedThemes.find( + (theme) => theme.id === 'default' + ); + + if (defaultTheme) { + setUserTheme(defaultTheme); + } else { + // Emergency fallback + setUserTheme({ + light: lightThemeOptions, + dark: darkThemeOptions, + }); + } + } + }; + + const handleApplyTheme = (theme) => { + setUserTheme(theme); + }; + + const handleColorChange = (mode, fieldPath, color) => { + setThemeDraft((prev) => { + const updated = { ...prev }; + const paths = fieldPath.split('.'); + updated[mode][paths[0]][paths[1]] = color.hex; + return updated; + }); + }; + + const renderColorPicker = (mode, label, fieldPath, currentValue) => { + let color = currentValue || '#ffffff'; + const format = detectColorFormat(currentValue); + if (format === 'rgba') { + color = rgbaStringToHsva(currentValue); + } else if (format === 'rgb') { + color = rgbStringToHsva(currentValue); + } + return ( + + + {label} + + handleColorChange(mode, fieldPath, color)} + /> + + ); + }; + + return ( + + + Theme Manager + + + + + + {userThemes?.map((theme, index) => ( + + + + {theme.id !== 'default' && ( + <> + handleEditTheme(theme.id)}> + + + handleDeleteTheme(theme.id)}> + + + + )} + handleApplyTheme(theme)}> + + + + + ))} + + + setOpenEditor(false)} + fullWidth + maxWidth="md" + > + + {themeDraft.id ? 'Edit Theme' : 'Add New Theme'} + + + + setThemeDraft((prev) => ({ ...prev, name: e.target.value })) + } + /> + + setCurrentTab(newValue)} + sx={{ mt: 2, mb: 2 }} + > + + + + + + {renderColorPicker( + currentTab, + 'Primary Main', + 'primary.main', + themeDraft[currentTab]?.primary?.main + )} + {renderColorPicker( + currentTab, + 'Primary Dark', + 'primary.dark', + themeDraft[currentTab]?.primary?.dark + )} + {renderColorPicker( + currentTab, + 'Primary Light', + 'primary.light', + themeDraft[currentTab]?.primary?.light + )} + {renderColorPicker( + currentTab, + 'Secondary Main', + 'secondary.main', + themeDraft[currentTab]?.secondary?.main + )} + {renderColorPicker( + currentTab, + 'Background Default', + 'background.default', + themeDraft[currentTab]?.background?.default + )} + {renderColorPicker( + currentTab, + 'Background Paper', + 'background.paper', + themeDraft[currentTab]?.background?.paper + )} + {renderColorPicker( + currentTab, + 'Background Surface', + 'background.surface', + themeDraft[currentTab]?.background?.surface + )} + {renderColorPicker( + currentTab, + 'Text Primary', + 'text.primary', + themeDraft[currentTab]?.text?.primary + )} + {renderColorPicker( + currentTab, + 'Text Secondary', + 'text.secondary', + themeDraft[currentTab]?.text?.secondary + )} + {renderColorPicker( + currentTab, + 'Border Main', + 'border.main', + themeDraft[currentTab]?.border?.main + )} + {renderColorPicker( + currentTab, + 'Border Subtle', + 'border.subtle', + themeDraft[currentTab]?.border?.subtle + )} + + + + + + + + + ); +} diff --git a/src/components/Theme/themeManager.css b/src/components/Theme/themeManager.css new file mode 100644 index 0000000..85a4538 --- /dev/null +++ b/src/components/Theme/themeManager.css @@ -0,0 +1,39 @@ +[data-color-mode*='dark'] .w-color-sketch { + --sketch-background: #323232 !important; +} + +[data-color-mode*='dark'] .w-color-swatch { + --sketch-swatch-border-top: 1px solid #525252 !important; +} + +[data-color-mode*='dark'] .w-color-block { + --block-background-color: #323232 !important; + --block-box-shadow: rgb(0 0 0 / 10%) 0 1px !important; +} + +[data-color-mode*='dark'] .w-color-editable-input { + --editable-input-label-color: #757575 !important; + --editable-input-box-shadow: #616161 0px 0px 0px 1px inset !important; + --editable-input-color: #bbb !important; +} + +[data-color-mode*='dark'] .w-color-github { + --github-border: 1px solid rgba(0, 0, 0, 0.2) !important; + --github-background-color: #323232 !important; + --github-box-shadow: rgb(0 0 0 / 15%) 0px 3px 12px !important; + --github-arrow-border-color: rgba(0, 0, 0, 0.15) !important; +} + +[data-color-mode*='dark'] .w-color-compact { + --compact-background-color: #323232 !important; +} + +[data-color-mode*='dark'] .w-color-material { + --material-background-color: #323232 !important; + --material-border-bottom-color: #707070 !important; +} + +[data-color-mode*='dark'] .w-color-alpha { + --alpha-pointer-background-color: #6a6a6a !important; + --alpha-pointer-box-shadow: rgb(0 0 0 / 37%) 0px 1px 4px 0px !important; +} diff --git a/src/styles/theme-dark.ts b/src/styles/theme-dark.ts index 971da81..d54a1c1 100644 --- a/src/styles/theme-dark.ts +++ b/src/styles/theme-dark.ts @@ -1,14 +1,14 @@ import { createTheme, ThemeOptions } from '@mui/material/styles'; import { commonThemeOptions } from './theme-common'; -const darkThemeOptions: ThemeOptions = { +export const darkThemeOptions: ThemeOptions = { ...commonThemeOptions, palette: { mode: 'dark', primary: { - main: 'rgb(46, 61, 96)', - dark: 'rgb(5, 20, 53)', - light: 'rgb(45, 92, 201)', + main: 'rgb(100, 155, 240)', + dark: 'rgb(45, 92, 201)', + light: 'rgb(130, 185, 255)', }, secondary: { main: 'rgb(69, 173, 255)', diff --git a/src/styles/theme-light.ts b/src/styles/theme-light.ts index dbba746..e66788f 100644 --- a/src/styles/theme-light.ts +++ b/src/styles/theme-light.ts @@ -1,7 +1,7 @@ import { createTheme, ThemeOptions } from '@mui/material/styles'; import { commonThemeOptions } from './theme-common'; -const lightThemeOptions: ThemeOptions = { +export const lightThemeOptions: ThemeOptions = { ...commonThemeOptions, palette: { mode: 'light', @@ -26,10 +26,6 @@ const lightThemeOptions: ThemeOptions = { main: 'rgba(0, 0, 0, 0.12)', subtle: 'rgba(0, 0, 0, 0.08)', }, - border: { - main: 'rgba(0, 0, 0, 0.12)', - subtle: 'rgba(0, 0, 0, 0.08)', - }, }, components: { MuiCard: { From ed7b36791a4d5e97569ca6f8734c5b6087fb8628 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 28 Apr 2025 19:55:48 +0300 Subject: [PATCH 286/717] add css vars to theme palette --- src/App.tsx | 14 +++--- src/ExtStates/NotAuthenticated.tsx | 7 +-- src/Wallets.tsx | 6 +-- src/components/Apps/AppsDesktop.tsx | 4 +- src/components/Apps/AppsDevMode.tsx | 4 +- src/components/Apps/AppsNavBarDesktop.tsx | 4 +- src/components/Chat/ChatDirect.tsx | 3 +- src/components/Chat/ChatGroup.tsx | 3 +- src/components/Chat/ChatList.tsx | 2 +- src/components/Chat/ChatOptions.tsx | 2 +- src/components/Chat/GroupAnnouncements.tsx | 2 +- src/components/Desktop/DesktopFooter.tsx | 4 +- src/components/Desktop/DesktopHeader.tsx | 8 ++-- src/components/DesktopSideBar.tsx | 4 +- src/components/Embeds/AttachmentEmbed.tsx | 2 +- src/components/Embeds/ImageEmbed.tsx | 2 +- src/components/Embeds/PollEmbed.tsx | 2 +- src/components/GeneralNotifications.tsx | 2 +- src/components/GlobalActions/JoinGroup.tsx | 4 +- src/components/Group/AddGroupList.tsx | 7 +-- src/components/Group/Forum/GroupMail.tsx | 12 +++-- src/components/Group/Forum/Mail-styles.ts | 44 +++++++------------ src/components/Group/Group.tsx | 26 +++++------ .../Group/ListOfGroupPromotions.tsx | 4 +- src/components/Group/QMailMessages.tsx | 12 +++-- src/components/Group/UserListOfInvites.tsx | 7 +-- src/components/Minting/Minting.tsx | 12 ++--- src/components/QMailStatus.tsx | 2 +- src/components/Save/Save.tsx | 16 +++---- src/components/Theme/ThemeManager.tsx | 18 ++++++++ src/styles/theme-dark.ts | 5 +++ src/styles/theme-light.ts | 5 +++ src/styles/theme.d.ts | 10 +++++ 33 files changed, 149 insertions(+), 110 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index dbdc846..d31f4d7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3121,12 +3121,12 @@ function App() { + + {avatarFile?.name} + + {!myName && ( + + + + A registered name is required to set an avatar + + + )} + + + + Publish avatar + + + + ); +}; From 8bc414356af20c2f49f673163e932b4ef8b048df Mon Sep 17 00:00:00 2001 From: PhilReact Date: Tue, 29 Apr 2025 12:31:48 +0300 Subject: [PATCH 288/717] change fontsize --- src/components/GroupAvatar.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/GroupAvatar.tsx b/src/components/GroupAvatar.tsx index 4d74030..3364b8f 100644 --- a/src/components/GroupAvatar.tsx +++ b/src/components/GroupAvatar.tsx @@ -131,7 +131,7 @@ export const GroupAvatar = ({ @@ -169,7 +169,7 @@ export const GroupAvatar = ({ @@ -197,7 +197,7 @@ export const GroupAvatar = ({ From 49848a7bd095b8381022c476db71604b5db09f35 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Tue, 29 Apr 2025 13:22:45 +0300 Subject: [PATCH 289/717] added import/export for theme --- src/components/Theme/ThemeManager.tsx | 80 ++++++++++++++- src/utils/fileReading/index.ts | 140 ++++++++++++++++---------- 2 files changed, 163 insertions(+), 57 deletions(-) diff --git a/src/components/Theme/ThemeManager.tsx b/src/components/Theme/ThemeManager.tsx index 2043eac..eca0e9f 100644 --- a/src/components/Theme/ThemeManager.tsx +++ b/src/components/Theme/ThemeManager.tsx @@ -26,7 +26,9 @@ import { darkThemeOptions } from '../../styles/theme-dark'; import { lightThemeOptions } from '../../styles/theme-light'; import ShortUniqueId from 'short-unique-id'; import { rgbStringToHsva, rgbaStringToHsva } from '@uiw/color-convert'; - +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import { saveFileToDiskGeneric } from '../../utils/generateWallet/generateWallet'; +import { handleImportClick } from '../../utils/fileReading'; const uid = new ShortUniqueId({ length: 8 }); function detectColorFormat(color) { @@ -36,6 +38,36 @@ function detectColorFormat(color) { return null; } +const validateTheme = (theme) => { + if (typeof theme !== 'object' || !theme) return false; + if (typeof theme.name !== 'string') return false; + if (!theme.light || typeof theme.light !== 'object') return false; + if (!theme.dark || typeof theme.dark !== 'object') return false; + + // Optional: deeper checks on structure + const requiredKeys = [ + 'primary', + 'secondary', + 'background', + 'text', + 'border', + 'other', + ]; + + for (const mode of ['light', 'dark']) { + const modeTheme = theme[mode]; + if (modeTheme.mode !== mode) return false; + + for (const key of requiredKeys) { + if (!modeTheme[key] || typeof modeTheme[key] !== 'object') { + return false; + } + } + } + + return true; +}; + export default function ThemeManager() { const { userThemes, addUserTheme, setUserTheme, currentThemeId } = useThemeContext(); @@ -152,6 +184,38 @@ export default function ThemeManager() { ); }; + const exportTheme = async (theme) => { + try { + const copyTheme = structuredClone(theme); + delete copyTheme.id; + const fileName = `ui_theme_${theme.name}.json`; + + const blob = new Blob([JSON.stringify(copyTheme, null, 2)], { + type: 'application/json', + }); + + await saveFileToDiskGeneric(blob, fileName); + } catch (error) { + console.error(error); + } + }; + + const importTheme = async (theme) => { + try { + const fileContent = await handleImportClick('.json'); + const importedTheme = JSON.parse(fileContent); + if (!validateTheme(importedTheme)) { + throw new Error('Invalid theme format'); + } + const newTheme = { ...importedTheme, id: uid.rnd() }; + const updatedThemes = [...userThemes, newTheme]; + addUserTheme(updatedThemes); + setUserTheme(newTheme); + } catch (error) { + console.error(error); + } + }; + return ( @@ -165,7 +229,16 @@ export default function ThemeManager() { > Add Theme - + {userThemes?.map((theme, index) => ( {theme.id !== 'default' && ( <> + exportTheme(theme)}> + + handleEditTheme(theme.id)}> diff --git a/src/utils/fileReading/index.ts b/src/utils/fileReading/index.ts index a6295c8..a04435c 100644 --- a/src/utils/fileReading/index.ts +++ b/src/utils/fileReading/index.ts @@ -1,63 +1,93 @@ // @ts-nocheck class Semaphore { - constructor(count) { - this.count = count - this.waiting = [] - } - acquire() { - return new Promise(resolve => { - if (this.count > 0) { - this.count-- - resolve() - } else { - this.waiting.push(resolve) - } - }) - } - release() { - if (this.waiting.length > 0) { - const resolve = this.waiting.shift() - resolve() - } else { - this.count++ - } - } + constructor(count) { + this.count = count; + this.waiting = []; + } + acquire() { + return new Promise((resolve) => { + if (this.count > 0) { + this.count--; + resolve(); + } else { + this.waiting.push(resolve); + } + }); + } + release() { + if (this.waiting.length > 0) { + const resolve = this.waiting.shift(); + resolve(); + } else { + this.count++; + } + } } -let semaphore = new Semaphore(1) -let reader = new FileReader() +let semaphore = new Semaphore(1); +let reader = new FileReader(); -export const fileToBase64 = (file) => new Promise(async (resolve, reject) => { - const reader = new FileReader(); // Create a new instance - await semaphore.acquire(); - reader.readAsDataURL(file); - reader.onload = () => { - const dataUrl = reader.result; - semaphore.release(); - if (typeof dataUrl === 'string') { - resolve(dataUrl.split(',')[1]); - } else { - reject(new Error('Invalid data URL')); - } - reader.onload = null; // Clear the handler - reader.onerror = null; // Clear the handle - }; - reader.onerror = (error) => { - semaphore.release(); - reject(error); - reader.onload = null; // Clear the handler - reader.onerror = null; // Clear the handle - }; +export const fileToBase64 = (file) => + new Promise(async (resolve, reject) => { + const reader = new FileReader(); // Create a new instance + await semaphore.acquire(); + reader.readAsDataURL(file); + reader.onload = () => { + const dataUrl = reader.result; + semaphore.release(); + if (typeof dataUrl === 'string') { + resolve(dataUrl.split(',')[1]); + } else { + reject(new Error('Invalid data URL')); + } + reader.onload = null; // Clear the handler + reader.onerror = null; // Clear the handle + }; + reader.onerror = (error) => { + semaphore.release(); + reject(error); + reader.onload = null; // Clear the handler + reader.onerror = null; // Clear the handle + }; }); - -export const base64ToBlobUrl = (base64, mimeType = "image/png") => { - const binary = atob(base64); - const array = []; - for (let i = 0; i < binary.length; i++) { - array.push(binary.charCodeAt(i)); - } - const blob = new Blob([new Uint8Array(array)], { type: mimeType }); - return URL.createObjectURL(blob); - }; \ No newline at end of file +export const base64ToBlobUrl = (base64, mimeType = 'image/png') => { + const binary = atob(base64); + const array = []; + for (let i = 0; i < binary.length; i++) { + array.push(binary.charCodeAt(i)); + } + const blob = new Blob([new Uint8Array(array)], { type: mimeType }); + return URL.createObjectURL(blob); +}; + +export const handleImportClick = async (fileTypes) => { + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = fileTypes; + + // Create a promise to handle file selection and reading synchronously + return await new Promise((resolve, reject) => { + fileInput.onchange = () => { + const file = fileInput.files[0]; + if (!file) { + reject(new Error('No file selected')); + return; + } + + const reader = new FileReader(); + reader.onload = (e) => { + resolve(e.target.result); // Resolve with the file content + }; + reader.onerror = () => { + reject(new Error('Error reading file')); + }; + + reader.readAsText(file); // Read the file as text (Base64 string) + }; + + // Trigger the file input dialog + fileInput.click(); + }); +}; From 6ee01a4ec321bdc4ae41bb14da67c03468ee4b48 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Tue, 29 Apr 2025 17:27:14 +0300 Subject: [PATCH 290/717] group list optimizations --- electron/capacitor.config.ts | 20 +- src/App.tsx | 17 + src/atoms/global.ts | 70 +++ src/components/Chat/MessageItem.tsx | 26 +- src/components/Chat/TipTap.tsx | 540 +++++++++++------------ src/components/ContextMenu.tsx | 10 +- src/components/Group/AddGroup.tsx | 2 + src/components/Group/Group.tsx | 644 +++++++++------------------- src/components/Group/GroupList.tsx | 348 +++++++++++++++ 9 files changed, 937 insertions(+), 740 deletions(-) create mode 100644 src/components/Group/GroupList.tsx diff --git a/electron/capacitor.config.ts b/electron/capacitor.config.ts index c7f641f..87d927b 100644 --- a/electron/capacitor.config.ts +++ b/electron/capacitor.config.ts @@ -1,15 +1,15 @@ -import type { CapacitorConfig } from '@capacitor/cli'; +import type { CapacitorConfig } from "@capacitor/cli"; const config: CapacitorConfig = { - appId: 'org.Qortal.Qortal-Hub', - appName: 'Qortal-Hub', - webDir: 'dist', - "plugins": { - "LocalNotifications": { - "smallIcon": "qort", - "iconColor": "#09b6e8" - } - } + appId: "org.Qortal.Qortal-Hub", + appName: "Qortal-Hub", + webDir: "dist", + plugins: { + LocalNotifications: { + smallIcon: "qort", + iconColor: "#09b6e8", + }, + }, }; export default config; diff --git a/src/App.tsx b/src/App.tsx index dd3c814..b711dc2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -100,6 +100,8 @@ import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil'; import { canSaveSettingToQdnAtom, enabledDevModeAtom, + groupAnnouncementsAtom, + groupChatTimestampsAtom, groupsOwnerNamesAtom, groupsPropertiesAtom, hasSettingsChangedAtom, @@ -107,11 +109,13 @@ import { isUsingImportExportSettingsAtom, lastPaymentSeenTimestampAtom, mailsAtom, + mutedGroupsAtom, oldPinnedAppsAtom, qMailLastEnteredTimestampAtom, settingsLocalLastUpdatedAtom, settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom, + timestampEnterDataAtom, } from './atoms/global'; import { NotAuthenticated } from './ExtStates/NotAuthenticated'; import { handleGetFileFromIndexedDB } from './utils/indexedDB'; @@ -479,6 +483,15 @@ function App() { lastPaymentSeenTimestampAtom ); const resetGroupsOwnerNamesAtom = useResetRecoilState(groupsOwnerNamesAtom); + const resetGroupAnnouncementsAtom = useResetRecoilState( + groupAnnouncementsAtom + ); + const resetMutedGroupsAtom = useResetRecoilState(mutedGroupsAtom); + + const resetGroupChatTimestampsAtom = useResetRecoilState( + groupChatTimestampsAtom + ); + const resetTimestampEnterAtom = useResetRecoilState(timestampEnterDataAtom); const resetAllRecoil = () => { resetAtomSortablePinnedAppsAtom(); @@ -492,6 +505,10 @@ function App() { resetGroupPropertiesAtom(); resetLastPaymentSeenTimestampAtom(); resetGroupsOwnerNamesAtom(); + resetGroupAnnouncementsAtom(); + resetMutedGroupsAtom(); + resetGroupChatTimestampsAtom(); + resetTimestampEnterAtom(); }; const handleSetGlobalApikey = (key) => { diff --git a/src/atoms/global.ts b/src/atoms/global.ts index 2ff3c21..d20b56c 100644 --- a/src/atoms/global.ts +++ b/src/atoms/global.ts @@ -201,3 +201,73 @@ export const isOpenBlockedModalAtom = atom({ key: 'isOpenBlockedModalAtom', default: false, }); + +export const groupsOwnerNamesSelector = selectorFamily({ + key: 'groupsOwnerNamesSelector', + get: + (key) => + ({ get }) => { + const data = get(groupsOwnerNamesAtom); + return data[key] || null; // Return the value for the key or null if not found + }, +}); + +export const groupAnnouncementsAtom = atom({ + key: 'groupAnnouncementsAtom', + default: {}, +}); + +export const groupAnnouncementSelector = selectorFamily({ + key: 'groupAnnouncementSelector', + get: + (key) => + ({ get }) => { + const data = get(groupAnnouncementsAtom); + return data[key] || null; // Return the value for the key or null if not found + }, +}); + +export const groupPropertySelector = selectorFamily({ + key: 'groupPropertySelector', + get: + (key) => + ({ get }) => { + const data = get(groupsPropertiesAtom); + return data[key] || null; // Return the value for the key or null if not found + }, +}); + +export const mutedGroupsAtom = atom({ + key: 'mutedGroupsAtom', + default: [], +}); + +export const groupChatTimestampsAtom = atom({ + key: 'groupChatTimestampsAtom', + default: {}, +}); + +export const groupChatTimestampSelector = selectorFamily({ + key: 'groupChatTimestampSelector', + get: + (key) => + ({ get }) => { + const data = get(groupChatTimestampsAtom); + return data[key] || null; // Return the value for the key or null if not found + }, +}); + +export const timestampEnterDataAtom = atom({ + key: 'timestampEnterDataAtom', + default: {}, +}); + +export const timestampEnterDataSelector = selectorFamily({ + key: 'timestampEnterDataSelector', + get: + (key) => + ({ get }) => { + const data = get(timestampEnterDataAtom); + return data[key] || null; // Return the value for the key or null if not found + }, +}); diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index 0b23e3f..d861dac 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -75,6 +75,21 @@ const getBadgeImg = (level) => { } }; +const UserBadge = React.memo(({ userInfo }) => { + return ( + + + + ); +}); + export const MessageItem = React.memo( ({ message, @@ -210,16 +225,7 @@ export const MessageItem = React.memo( {message?.senderName?.charAt(0)} - - - + )} diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 01155b5..0697798 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useRef } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { EditorProvider, useCurrentEditor } from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import { Color } from '@tiptap/extension-color'; @@ -41,295 +41,297 @@ function textMatcher(doc, from) { return { start, query }; } -const MenuBar = ({ - setEditorRef, - isChat, - isDisabledEditorEnter, - setIsDisabledEditorEnter, -}) => { - const { editor } = useCurrentEditor(); - const fileInputRef = useRef(null); - const theme = useTheme(); +const MenuBar = React.memo( + ({ + setEditorRef, + isChat, + isDisabledEditorEnter, + setIsDisabledEditorEnter, + }) => { + const { editor } = useCurrentEditor(); + const fileInputRef = useRef(null); + const theme = useTheme(); - if (!editor) { - return null; - } + useEffect(() => { + if (editor && setEditorRef) { + setEditorRef(editor); + } + }, [editor, setEditorRef]); - useEffect(() => { - if (editor && setEditorRef) { - setEditorRef(editor); + if (!editor) { + return null; } - }, [editor, setEditorRef]); - const handleImageUpload = async (file) => { - let compressedFile; - await new Promise((resolve) => { - new Compressor(file, { - quality: 0.6, - maxWidth: 1200, - mimeType: 'image/webp', - success(result) { - compressedFile = new File([result], 'image.webp', { - type: 'image/webp', - }); - resolve(); - }, - error(err) { - console.error('Image compression error:', err); - }, + const handleImageUpload = async (file) => { + let compressedFile; + await new Promise((resolve) => { + new Compressor(file, { + quality: 0.6, + maxWidth: 1200, + mimeType: 'image/webp', + success(result) { + compressedFile = new File([result], 'image.webp', { + type: 'image/webp', + }); + resolve(); + }, + error(err) { + console.error('Image compression error:', err); + }, + }); }); - }); - if (compressedFile) { - const reader = new FileReader(); - reader.onload = () => { - const url = reader.result; - editor - .chain() - .focus() - .setImage({ src: url, style: 'width: auto' }) - .run(); - fileInputRef.current.value = ''; - }; - reader.readAsDataURL(compressedFile); - } - }; + if (compressedFile) { + const reader = new FileReader(); + reader.onload = () => { + const url = reader.result; + editor + .chain() + .focus() + .setImage({ src: url, style: 'width: auto' }) + .run(); + fileInputRef.current.value = ''; + }; + reader.readAsDataURL(compressedFile); + } + }; - const triggerImageUpload = () => { - fileInputRef.current.click(); // Trigger the file input click - }; + const triggerImageUpload = () => { + fileInputRef.current.click(); // Trigger the file input click + }; - const handlePaste = (event) => { - const items = event.clipboardData.items; - for (const item of items) { - if (item.type.startsWith('image/')) { - const file = item.getAsFile(); - if (file) { - event.preventDefault(); // Prevent the default paste behavior - handleImageUpload(file); // Call the image upload function + const handlePaste = (event) => { + const items = event.clipboardData.items; + for (const item of items) { + if (item.type.startsWith('image/')) { + const file = item.getAsFile(); + if (file) { + event.preventDefault(); // Prevent the default paste behavior + handleImageUpload(file); // Call the image upload function + } } } - } - }; + }; - useEffect(() => { - if (editor) { - editor.view.dom.addEventListener('paste', handlePaste); - return () => { - editor.view.dom.removeEventListener('paste', handlePaste); - }; - } - }, [editor]); + useEffect(() => { + if (editor) { + editor.view.dom.addEventListener('paste', handlePaste); + return () => { + editor.view.dom.removeEventListener('paste', handlePaste); + }; + } + }, [editor]); - return ( -
    -
    - editor.chain().focus().toggleBold().run()} - disabled={!editor.can().chain().focus().toggleBold().run()} - sx={{ - color: editor.isActive('bold') - ? theme.palette.text.primary - : theme.palette.text.secondary, - padding: 'revert', + return ( +
    +
    - - - editor.chain().focus().toggleItalic().run()} - disabled={!editor.can().chain().focus().toggleItalic().run()} - sx={{ - color: editor.isActive('italic') - ? theme.palette.text.primary - : theme.palette.text.secondary, - padding: 'revert', - }} - > - - - editor.chain().focus().toggleStrike().run()} - disabled={!editor.can().chain().focus().toggleStrike().run()} - sx={{ - color: editor.isActive('strike') - ? theme.palette.text.primary - : theme.palette.text.secondary, - padding: 'revert', - }} - > - - - editor.chain().focus().toggleCode().run()} - disabled={!editor.can().chain().focus().toggleCode().run()} - sx={{ - color: editor.isActive('code') - ? theme.palette.text.primary - : theme.palette.text.secondary, - padding: 'revert', - }} - > - - - editor.chain().focus().unsetAllMarks().run()} - sx={{ - color: - editor.isActive('bold') || - editor.isActive('italic') || - editor.isActive('strike') || - editor.isActive('code') + editor.chain().focus().toggleBold().run()} + disabled={!editor.can().chain().focus().toggleBold().run()} + sx={{ + color: editor.isActive('bold') ? theme.palette.text.primary : theme.palette.text.secondary, - padding: 'revert', - }} - > - - - editor.chain().focus().toggleBulletList().run()} - sx={{ - color: editor.isActive('bulletList') - ? theme.palette.text.primary - : theme.palette.text.secondary, - padding: 'revert', - }} - > - - - editor.chain().focus().toggleOrderedList().run()} - sx={{ - color: editor.isActive('orderedList') - ? theme.palette.text.primary - : theme.palette.text.secondary, - padding: 'revert', - }} - > - - - editor.chain().focus().toggleCodeBlock().run()} - sx={{ - color: editor.isActive('codeBlock') - ? theme.palette.text.primary - : theme.palette.text.secondary, - padding: 'revert', - }} - > - - - editor.chain().focus().toggleBlockquote().run()} - sx={{ - color: editor.isActive('blockquote') - ? theme.palette.text.primary - : theme.palette.text.secondary, - padding: 'revert', - }} - > - - - editor.chain().focus().setHorizontalRule().run()} - disabled={!editor.can().chain().focus().setHorizontalRule().run()} - sx={{ color: 'gray', padding: 'revert' }} - > - - - - editor.chain().focus().toggleHeading({ level: 1 }).run() - } - sx={{ - color: editor.isActive('heading', { level: 1 }) - ? theme.palette.text.primary - : theme.palette.text.secondary, - padding: 'revert', - }} - > - - - editor.chain().focus().undo().run()} - disabled={!editor.can().chain().focus().undo().run()} - sx={{ color: 'gray', padding: 'revert' }} - > - - - editor.chain().focus().redo().run()} - disabled={!editor.can().chain().focus().redo().run()} - sx={{ color: 'gray' }} - > - - - {isChat && ( - { - setIsDisabledEditorEnter(!isDisabledEditorEnter); + padding: 'revert', }} > - + + editor.chain().focus().toggleItalic().run()} + disabled={!editor.can().chain().focus().toggleItalic().run()} + sx={{ + color: editor.isActive('italic') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: 'revert', + }} + > + + + editor.chain().focus().toggleStrike().run()} + disabled={!editor.can().chain().focus().toggleStrike().run()} + sx={{ + color: editor.isActive('strike') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: 'revert', + }} + > + + + editor.chain().focus().toggleCode().run()} + disabled={!editor.can().chain().focus().toggleCode().run()} + sx={{ + color: editor.isActive('code') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: 'revert', + }} + > + + + editor.chain().focus().unsetAllMarks().run()} + sx={{ + color: + editor.isActive('bold') || + editor.isActive('italic') || + editor.isActive('strike') || + editor.isActive('code') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: 'revert', + }} + > + + + editor.chain().focus().toggleBulletList().run()} + sx={{ + color: editor.isActive('bulletList') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: 'revert', + }} + > + + + editor.chain().focus().toggleOrderedList().run()} + sx={{ + color: editor.isActive('orderedList') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: 'revert', + }} + > + + + editor.chain().focus().toggleCodeBlock().run()} + sx={{ + color: editor.isActive('codeBlock') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: 'revert', + }} + > + + + editor.chain().focus().toggleBlockquote().run()} + sx={{ + color: editor.isActive('blockquote') + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: 'revert', + }} + > + + + editor.chain().focus().setHorizontalRule().run()} + disabled={!editor.can().chain().focus().setHorizontalRule().run()} + sx={{ color: 'gray', padding: 'revert' }} + > + + + + editor.chain().focus().toggleHeading({ level: 1 }).run() + } + sx={{ + color: editor.isActive('heading', { level: 1 }) + ? theme.palette.text.primary + : theme.palette.text.secondary, + padding: 'revert', + }} + > + + + editor.chain().focus().undo().run()} + disabled={!editor.can().chain().focus().undo().run()} + sx={{ color: 'gray', padding: 'revert' }} + > + + + editor.chain().focus().redo().run()} + disabled={!editor.can().chain().focus().redo().run()} + sx={{ color: 'gray' }} + > + + + {isChat && ( + - { + setIsDisabledEditorEnter(!isDisabledEditorEnter); }} > - disable enter - - - )} - {!isChat && ( - <> - - - - handleImageUpload(event.target.files[0])} - accept="image/*" - /> - - )} + + + disable enter + + + )} + {!isChat && ( + <> + + + + handleImageUpload(event.target.files[0])} + accept="image/*" + /> + + )} +
    -
    - ); -}; + ); + } +); const extensions = [ Color.configure({ types: [TextStyle.name, ListItem.name] }), @@ -373,10 +375,10 @@ export default ({ ? extensions.filter((item) => item?.name !== 'image') : extensions; const editorRef = useRef(null); - const setEditorRefFunc = (editorInstance) => { + const setEditorRefFunc = useCallback((editorInstance) => { editorRef.current = editorInstance; setEditorRef(editorInstance); - }; + }, []); // const users = [ // { id: 1, label: 'Alice' }, diff --git a/src/components/ContextMenu.tsx b/src/components/ContextMenu.tsx index 84df599..0306e57 100644 --- a/src/components/ContextMenu.tsx +++ b/src/components/ContextMenu.tsx @@ -10,6 +10,8 @@ import { import MailOutlineIcon from '@mui/icons-material/MailOutline'; import NotificationsOffIcon from '@mui/icons-material/NotificationsOff'; import { executeEvent } from '../utils/events'; +import { useRecoilState } from 'recoil'; +import { mutedGroupsAtom } from '../atoms/global'; const CustomStyledMenu = styled(Menu)(({ theme }) => ({ '& .MuiPaper-root': { @@ -28,16 +30,12 @@ const CustomStyledMenu = styled(Menu)(({ theme }) => ({ }, })); -export const ContextMenu = ({ - children, - groupId, - getUserSettings, - mutedGroups, -}) => { +export const ContextMenu = ({ children, groupId, getUserSettings }) => { const [menuPosition, setMenuPosition] = useState(null); const longPressTimeout = useRef(null); const preventClick = useRef(false); // Flag to prevent click after long-press or right-click const theme = useTheme(); + const [mutedGroups] = useRecoilState(mutedGroupsAtom); const isMuted = useMemo(() => { return mutedGroups.includes(groupId); }, [mutedGroups, groupId]); diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 66e3880..278d6a7 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -199,6 +199,8 @@ export const AddGroup = ({ address, open, setOpen }) => { }; }, []); + if (!open) return null; + return ( { const queryString = admins.map((name) => `name=${name}`).join('&'); @@ -117,7 +122,7 @@ interface GroupProps { balance: number; } -const timeDifferenceForNotificationChats = 900000; +export const timeDifferenceForNotificationChats = 900000; export const requestQueueMemberNames = new RequestQueueWithPromise(5); export const requestQueueAdminMemberNames = new RequestQueueWithPromise(5); @@ -410,7 +415,9 @@ export const Group = ({ const { setMemberGroups, rootHeight, isRunningPublicNode } = useContext(MyContext); const lastGroupNotification = useRef(null); - const [timestampEnterData, setTimestampEnterData] = useState({}); + const [timestampEnterData, setTimestampEnterData] = useRecoilState( + timestampEnterDataAtom + ); const [chatMode, setChatMode] = useState('groups'); const [newChat, setNewChat] = useState(false); const [openSnack, setOpenSnack] = React.useState(false); @@ -421,7 +428,10 @@ export const Group = ({ const [firstSecretKeyInCreation, setFirstSecretKeyInCreation] = React.useState(false); const [groupSection, setGroupSection] = React.useState('home'); - const [groupAnnouncements, setGroupAnnouncements] = React.useState({}); + const [groupAnnouncements, setGroupAnnouncements] = useRecoilState( + groupAnnouncementsAtom + ); + const [defaultThread, setDefaultThread] = React.useState(null); const [isOpenDrawer, setIsOpenDrawer] = React.useState(false); const setIsOpenBlockedUserModal = useSetRecoilState(isOpenBlockedModalAtom); @@ -429,7 +439,7 @@ export const Group = ({ const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false); const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState(''); const [drawerMode, setDrawerMode] = React.useState('groups'); - const [mutedGroups, setMutedGroups] = useState([]); + const setMutedGroups = useSetRecoilState(mutedGroupsAtom); const [mobileViewMode, setMobileViewMode] = useState('home'); const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState(''); const isFocusedRef = useRef(true); @@ -443,7 +453,9 @@ export const Group = ({ const settimeoutForRefetchSecretKey = useRef(null); const { clearStatesMessageQueueProvider } = useMessageQueue(); const initiatedGetMembers = useRef(false); - const [groupChatTimestamps, setGroupChatTimestamps] = React.useState({}); + const [groupChatTimestamps, setGroupChatTimestamps] = useRecoilState( + groupChatTimestampsAtom + ); const [appsMode, setAppsMode] = useState('home'); const [appsModeDev, setAppsModeDev] = useState('home'); const [isOpenSideViewDirects, setIsOpenSideViewDirects] = useState(false); @@ -500,7 +512,7 @@ export const Group = ({ selectedDirectRef.current = selectedDirect; }, [selectedDirect]); - const getUserSettings = async () => { + const getUserSettings = useCallback(async () => { try { return new Promise((res, rej) => { window @@ -522,13 +534,13 @@ export const Group = ({ } catch (error) { console.log('error', error); } - }; + }, [setMutedGroups]); useEffect(() => { getUserSettings(); - }, []); + }, [getUserSettings]); - const getTimestampEnterChat = async () => { + const getTimestampEnterChat = useCallback(async () => { try { return new Promise((res, rej) => { window @@ -548,7 +560,7 @@ export const Group = ({ } catch (error) { console.log(error); } - }; + }, []); const refreshHomeDataFunc = () => { setGroupSection('default'); @@ -650,115 +662,139 @@ export const Group = ({ return hasUnread; }, [groupAnnouncements, groups]); - const getSecretKey = async ( - loadingGroupParam?: boolean, - secretKeyToPublish?: boolean - ) => { - try { - setIsLoadingGroupMessage('Locating encryption keys'); - pauseAllQueues(); - let dataFromStorage; - let publishFromStorage; - let adminsFromStorage; - if ( - secretKeyToPublish && - secretKey && - lastFetchedSecretKey.current && - Date.now() - lastFetchedSecretKey.current < 600000 - ) - return secretKey; - if (loadingGroupParam) { - setIsLoadingGroup(true); - } - if (selectedGroup?.groupId !== selectedGroupRef.current.groupId) { - if (settimeoutForRefetchSecretKey.current) { - clearTimeout(settimeoutForRefetchSecretKey.current); - } - return; - } - const prevGroupId = selectedGroupRef.current.groupId; - // const validApi = await findUsableApi(); - const { names, addresses, both } = - adminsFromStorage || (await getGroupAdmins(selectedGroup?.groupId)); - setAdmins(addresses); - setAdminsWithNames(both); - if (!names.length) { - throw new Error('Network error'); - } - const publish = - publishFromStorage || - (await getPublishesFromAdmins(names, selectedGroup?.groupId)); + const getSecretKey = useCallback( + async (loadingGroupParam?: boolean, secretKeyToPublish?: boolean) => { + try { + setIsLoadingGroupMessage('Locating encryption keys'); + pauseAllQueues(); - if (prevGroupId !== selectedGroupRef.current.groupId) { - if (settimeoutForRefetchSecretKey.current) { - clearTimeout(settimeoutForRefetchSecretKey.current); + let dataFromStorage; + let publishFromStorage; + let adminsFromStorage; + + if ( + secretKeyToPublish && + secretKey && + lastFetchedSecretKey.current && + Date.now() - lastFetchedSecretKey.current < 600000 + ) { + return secretKey; } - return; - } - if (publish === false) { - setTriedToFetchSecretKey(true); - settimeoutForRefetchSecretKey.current = setTimeout(() => { - getSecretKey(); - }, 120000); - return false; - } - setSecretKeyPublishDate(publish?.updated || publish?.created); - let data; - if (dataFromStorage) { - data = dataFromStorage; - } else { - // const shouldRebuild = !secretKeyPublishDate || (publish?.update && publish?.updated > secretKeyPublishDate) - setIsLoadingGroupMessage('Downloading encryption keys'); - const res = await fetch( - `${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ - publish.identifier - }?encoding=base64&rebuild=true` - ); - data = await res.text(); - } - const decryptedKey: any = await decryptResource(data); - const dataint8Array = base64ToUint8Array(decryptedKey.data); - const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); - if (!validateSecretKey(decryptedKeyToObject)) - throw new Error('SecretKey is not valid'); - setSecretKeyDetails(publish); - setSecretKey(decryptedKeyToObject); - lastFetchedSecretKey.current = Date.now(); - setMemberCountFromSecretKeyData(decryptedKey.count); - window - .sendMessage('setGroupData', { - groupId: selectedGroup?.groupId, - secretKeyData: data, - secretKeyResource: publish, - admins: { names, addresses, both }, - }) - .catch((error) => { - console.error( - 'Failed to set group data:', - error.message || 'An error occurred' + + if (loadingGroupParam) { + setIsLoadingGroup(true); + } + + if (selectedGroup?.groupId !== selectedGroupRef.current.groupId) { + if (settimeoutForRefetchSecretKey.current) { + clearTimeout(settimeoutForRefetchSecretKey.current); + } + return; + } + + const prevGroupId = selectedGroupRef.current.groupId; + + const { names, addresses, both } = + adminsFromStorage || (await getGroupAdmins(selectedGroup?.groupId)); + setAdmins(addresses); + setAdminsWithNames(both); + + if (!names.length) throw new Error('Network error'); + + const publish = + publishFromStorage || + (await getPublishesFromAdmins(names, selectedGroup?.groupId)); + + if (prevGroupId !== selectedGroupRef.current.groupId) { + if (settimeoutForRefetchSecretKey.current) { + clearTimeout(settimeoutForRefetchSecretKey.current); + } + return; + } + + if (publish === false) { + setTriedToFetchSecretKey(true); + settimeoutForRefetchSecretKey.current = setTimeout(() => { + getSecretKey(); + }, 120000); + return false; + } + + setSecretKeyPublishDate(publish?.updated || publish?.created); + + let data; + if (dataFromStorage) { + data = dataFromStorage; + } else { + setIsLoadingGroupMessage('Downloading encryption keys'); + const res = await fetch( + `${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${publish.identifier}?encoding=base64&rebuild=true` ); - }); + data = await res.text(); + } - if (decryptedKeyToObject) { - setTriedToFetchSecretKey(true); - setFirstSecretKeyInCreation(false); - return decryptedKeyToObject; - } else { - setTriedToFetchSecretKey(true); + const decryptedKey: any = await decryptResource(data); + const dataint8Array = base64ToUint8Array(decryptedKey.data); + const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); + + if (!validateSecretKey(decryptedKeyToObject)) { + throw new Error('SecretKey is not valid'); + } + + setSecretKeyDetails(publish); + setSecretKey(decryptedKeyToObject); + lastFetchedSecretKey.current = Date.now(); + setMemberCountFromSecretKeyData(decryptedKey.count); + + window + .sendMessage('setGroupData', { + groupId: selectedGroup?.groupId, + secretKeyData: data, + secretKeyResource: publish, + admins: { names, addresses, both }, + }) + .catch((error) => { + console.error( + 'Failed to set group data:', + error.message || 'An error occurred' + ); + }); + + if (decryptedKeyToObject) { + setTriedToFetchSecretKey(true); + setFirstSecretKeyInCreation(false); + return decryptedKeyToObject; + } else { + setTriedToFetchSecretKey(true); + } + } catch (error) { + if (error === 'Unable to decrypt data') { + setTriedToFetchSecretKey(true); + settimeoutForRefetchSecretKey.current = setTimeout(() => { + getSecretKey(); + }, 120000); + } + } finally { + setIsLoadingGroup(false); + setIsLoadingGroupMessage(''); + resumeAllQueues(); } - } catch (error) { - if (error === 'Unable to decrypt data') { - setTriedToFetchSecretKey(true); - settimeoutForRefetchSecretKey.current = setTimeout(() => { - getSecretKey(); - }, 120000); - } - } finally { - setIsLoadingGroup(false); - setIsLoadingGroupMessage(''); - resumeAllQueues(); - } - }; + }, + [ + secretKey, + selectedGroup?.groupId, + setIsLoadingGroup, + setIsLoadingGroupMessage, + setSecretKey, + setSecretKeyDetails, + setTriedToFetchSecretKey, + setFirstSecretKeyInCreation, + setMemberCountFromSecretKeyData, + setAdmins, + setAdminsWithNames, + setSecretKeyPublishDate, + ] + ); const getAdminsForPublic = async (selectedGroup) => { try { @@ -1050,8 +1086,6 @@ export const Group = ({ triedToFetchSecretKey, ]); - console.log('groupOwner?.owner', groupOwner); - const notifyAdmin = async (admin) => { try { setIsLoadingNotifyAdmin(true); @@ -1327,8 +1361,6 @@ export const Group = ({ }; }, []); - console.log('selectedGroup', selectedGroup); - const openGroupChatFromNotification = (e) => { if (isLoadingOpenSectionFromNotification.current) return; @@ -1498,9 +1530,9 @@ export const Group = ({ }; }, [groups, selectedGroup]); - const handleSecretKeyCreationInProgress = () => { + const handleSecretKeyCreationInProgress = useCallback(() => { setFirstSecretKeyInCreation(true); - }; + }, []); const goToHome = async () => { setDesktopViewMode('home'); @@ -1811,327 +1843,34 @@ export const Group = ({ ); }; - const renderGroups = () => { - return ( -
    - - { - setDesktopSideView('groups'); - }} - > - - - - - { - setDesktopSideView('directs'); - }} - > - - - - - - -
    - {groups.map((group: any) => ( - - { - setMobileViewMode('group'); - setDesktopSideView('groups'); - initiatedGetMembers.current = false; - clearAllQueues(); - setSelectedDirect(null); - setTriedToFetchSecretKey(false); - setNewChat(false); - setSelectedGroup(null); - setUserInfoForLevels({}); - setSecretKey(null); - lastFetchedSecretKey.current = null; - setSecretKeyPublishDate(null); - setAdmins([]); - setSecretKeyDetails(null); - setAdminsWithNames([]); - setGroupOwner(null); - setMembers([]); - setMemberCountFromSecretKeyData(null); - setHideCommonKeyPopup(false); - setFirstSecretKeyInCreation(false); - setGroupSection('chat'); - setIsOpenDrawer(false); - setIsForceShowCreationKeyPopup(false); - setTimeout(() => { - setSelectedGroup(group); - }, 200); - }} - sx={{ - display: 'flex', - background: - group?.groupId === selectedGroup?.groupId && - theme.palette.action.selected, - borderRadius: '2px', - cursor: 'pointer', - flexDirection: 'column', - padding: '2px', - width: '100%', - '&:hover': { - backgroundColor: 'action.hover', // background on hover - }, - }} - > - - - - {groupsOwnerNames[group?.groupId] ? ( - - {group?.groupName?.charAt(0).toUpperCase()} - - ) : ( - - {' '} - {group?.groupName?.charAt(0).toUpperCase() || 'G'} - - )} - - - {groupAnnouncements[group?.groupId] && - !groupAnnouncements[group?.groupId]?.seentimestamp && ( - - )} - - {group?.data && - groupChatTimestamps[group?.groupId] && - group?.sender !== myAddress && - group?.timestamp && - ((!timestampEnterData[group?.groupId] && - Date.now() - group?.timestamp < - timeDifferenceForNotificationChats) || - timestampEnterData[group?.groupId] < - group?.timestamp) && ( - - )} - {groupsProperties[group?.groupId]?.isOpen === false && ( - - )} - - - - - - ))} -
    -
    - {chatMode === 'groups' && ( - <> - { - setOpenAddGroup(true); - }} - > - - Group - - - {!isRunningPublicNode && ( - { - setIsOpenBlockedUserModal(true); - }} - sx={{ - minWidth: 'unset', - padding: '10px', - }} - > - - - )} - - )} - {chatMode === 'directs' && ( - { - setNewChat(true); - setSelectedDirect(null); - setIsOpenDrawer(false); - }} - > - - New Chat - - )} -
    -
    - ); - }; + const selectGroupFunc = useCallback((group) => { + setMobileViewMode('group'); + setDesktopSideView('groups'); + initiatedGetMembers.current = false; + clearAllQueues(); + setSelectedDirect(null); + setTriedToFetchSecretKey(false); + setNewChat(false); + setSelectedGroup(null); + setUserInfoForLevels({}); + setSecretKey(null); + lastFetchedSecretKey.current = null; + setSecretKeyPublishDate(null); + setAdmins([]); + setSecretKeyDetails(null); + setAdminsWithNames([]); + setGroupOwner(null); + setMembers([]); + setMemberCountFromSecretKeyData(null); + setHideCommonKeyPopup(false); + setFirstSecretKeyInCreation(false); + setGroupSection('chat'); + setIsOpenDrawer(false); + setIsForceShowCreationKeyPopup(false); + setTimeout(() => { + setSelectedGroup(group); + }, 200); + }, []); return ( <> @@ -2176,9 +1915,24 @@ export const Group = ({ /> )} - {desktopViewMode === 'chat' && - desktopSideView !== 'directs' && - renderGroups()} + {desktopViewMode === 'chat' && desktopSideView !== 'directs' && ( + + )} {desktopViewMode === 'chat' && desktopSideView === 'directs' && @@ -2318,7 +2072,7 @@ export const Group = ({ isPrivate={isPrivate} setSecretKey={setSecretKey} handleNewEncryptionNotification={setNewEncryptionNotification} - hide={groupSection !== 'chat' || selectedDirect || newChat} + hide={groupSection !== 'chat' || !!selectedDirect || newChat} hideView={!(desktopViewMode === 'chat' && selectedGroup)} handleSecretKeyCreationInProgress={ handleSecretKeyCreationInProgress diff --git a/src/components/Group/GroupList.tsx b/src/components/Group/GroupList.tsx new file mode 100644 index 0000000..896ed4d --- /dev/null +++ b/src/components/Group/GroupList.tsx @@ -0,0 +1,348 @@ +import { + Avatar, + Box, + ButtonBase, + List, + ListItem, + ListItemAvatar, + ListItemText, + useTheme, +} from '@mui/material'; +import React, { useCallback } from 'react'; +import { IconWrapper } from '../Desktop/DesktopFooter'; +import { HubsIcon } from '../../assets/Icons/HubsIcon'; +import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; +import { ContextMenu } from '../ContextMenu'; +import { getBaseApiReact } from '../../App'; +import { formatEmailDate } from './QMailMessages'; +import CampaignIcon from '@mui/icons-material/Campaign'; +import MarkChatUnreadIcon from '@mui/icons-material/MarkChatUnread'; +import LockIcon from '@mui/icons-material/Lock'; +import { CustomButton } from '../../styles/App-styles'; +import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; +import PersonOffIcon from '@mui/icons-material/PersonOff'; +import { + groupAnnouncementSelector, + groupChatTimestampSelector, + groupPropertySelector, + groupsOwnerNamesSelector, + timestampEnterDataSelector, +} from '../../atoms/global'; +import { useRecoilValue } from 'recoil'; +import { timeDifferenceForNotificationChats } from './Group'; + +export const GroupList = ({ + selectGroupFunc, + setDesktopSideView, + groupChatHasUnread, + groupsAnnHasUnread, + desktopSideView, + directChatHasUnread, + chatMode, + groups, + selectedGroup, + getUserSettings, + setOpenAddGroup, + isRunningPublicNode, + setIsOpenBlockedUserModal, + myAddress, +}) => { + const theme = useTheme(); + return ( +
    + + { + setDesktopSideView('groups'); + }} + > + + + + + { + setDesktopSideView('directs'); + }} + > + + + + + + +
    + + {groups.map((group: any) => ( + + ))} + +
    +
    + <> + { + setOpenAddGroup(true); + }} + > + + Group + + + {!isRunningPublicNode && ( + { + setIsOpenBlockedUserModal(true); + }} + sx={{ + minWidth: 'unset', + padding: '10px', + }} + > + + + )} + +
    +
    + ); +}; + +const GroupItem = React.memo( + ({ selectGroupFunc, group, selectedGroup, getUserSettings, myAddress }) => { + const theme = useTheme(); + const ownerName = useRecoilValue(groupsOwnerNamesSelector(group?.groupId)); + const announcement = useRecoilValue( + groupAnnouncementSelector(group?.groupId) + ); + const groupProperty = useRecoilValue(groupPropertySelector(group?.groupId)); + const groupChatTimestamp = useRecoilValue( + groupChatTimestampSelector(group?.groupId) + ); + const timestampEnterData = useRecoilValue( + timestampEnterDataSelector(group?.groupId) + ); + const selectGroupHandler = useCallback(() => { + selectGroupFunc(group); + }, [group, selectGroupFunc]); + + return ( + + + + + {ownerName ? ( + + {group?.groupName?.charAt(0).toUpperCase()} + + ) : ( + + {' '} + {group?.groupName?.charAt(0).toUpperCase() || 'G'} + + )} + + + {announcement && !announcement?.seentimestamp && ( + + )} + + {group?.data && + groupChatTimestamp && + group?.sender !== myAddress && + group?.timestamp && + ((!timestampEnterData && + Date.now() - group?.timestamp < + timeDifferenceForNotificationChats) || + timestampEnterData < group?.timestamp) && ( + + )} + {groupProperty?.isOpen === false && ( + + )} + + + + + ); + } +); From ea0de88b1f7db26821d6889a208931349de22d7d Mon Sep 17 00:00:00 2001 From: PhilReact Date: Tue, 29 Apr 2025 22:25:20 +0300 Subject: [PATCH 291/717] updated to react 19 --- package-lock.json | 1479 +++++++---------- package.json | 69 +- src/App.tsx | 85 +- src/atoms/global.ts | 333 +--- src/common/useFetchResources.tsx | 275 ++- src/components/Apps/AppInfo.tsx | 10 +- src/components/Apps/AppInfoSnippet.tsx | 9 +- src/components/Apps/AppViewer.tsx | 21 +- src/components/Apps/AppsDesktop.tsx | 6 +- src/components/Apps/AppsDevModeNavBar.tsx | 5 +- src/components/Apps/AppsNavBarDesktop.tsx | 15 +- src/components/Apps/AppsPrivate.tsx | 9 +- src/components/Apps/SortablePinnedApps.tsx | 8 +- src/components/Apps/useHandlePrivateApps.tsx | 149 +- .../Apps/useQortalMessageListener.tsx | 776 ++++----- src/components/Chat/ChatContainer.tsx | 54 - src/components/Chat/ChatDirect.tsx | 1 - src/components/Chat/ChatGroup.tsx | 3 - src/components/Chat/GroupAnnouncements.tsx | 1 - src/components/Chat/TipTap.tsx | 5 +- src/components/ContextMenu.tsx | 5 +- src/components/ContextMenuPinnedApps.tsx | 8 +- src/components/Desktop/DesktopFooter.tsx | 6 +- src/components/DesktopSideBar.tsx | 7 +- src/components/Embeds/AttachmentEmbed.tsx | 3 +- src/components/Embeds/Embed.tsx | 12 +- src/components/Embeds/VideoPlayer.tsx | 7 +- src/components/Group/BlockedUsersModal.tsx | 5 +- src/components/Group/Forum/NewThread.tsx | 7 +- src/components/Group/Forum/TextEditor.tsx | 40 - src/components/Group/Forum/texteditor.css | 70 - src/components/Group/Group.tsx | 27 +- src/components/Group/GroupJoinRequests.tsx | 5 +- src/components/Group/GroupList.tsx | 14 +- .../Group/ListOfGroupPromotions.tsx | 10 +- src/components/Group/QMailMessages.tsx | 8 +- src/components/Group/Settings.tsx | 7 +- src/components/Group/WalletsAppWrapper.tsx | 5 +- src/components/QMailStatus.tsx | 6 +- src/components/Save/Save.tsx | 19 +- src/hooks/useHandlePaymentNotification.tsx | 7 +- src/main.tsx | 9 +- src/useQortalGetSaveSettings.tsx | 20 +- src/useRetrieveDataLocalStorage.tsx | 20 +- 44 files changed, 1513 insertions(+), 2127 deletions(-) delete mode 100644 src/components/Chat/ChatContainer.tsx delete mode 100644 src/components/Group/Forum/TextEditor.tsx delete mode 100644 src/components/Group/Forum/texteditor.css diff --git a/package-lock.json b/package-lock.json index dd65c25..fbba254 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,30 +15,28 @@ "@capacitor/core": "^6.1.2", "@capacitor/filesystem": "^6.0.1", "@capacitor/local-notifications": "^6.1.0", - "@chatscope/chat-ui-kit-react": "^2.0.3", - "@dnd-kit/core": "^6.1.0", - "@dnd-kit/sortable": "^8.0.0", + "@dnd-kit/core": "^6.3.0", + "@dnd-kit/sortable": "^10.0.0", "@electron/packager": "^18.3.6", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.0", "@evva/capacitor-secure-storage-plugin": "^3.0.1", - "@mui/icons-material": "^5.16.4", - "@mui/lab": "^5.0.0-alpha.173", - "@mui/material": "^5.16.7", - "@reduxjs/toolkit": "^2.2.7", - "@tanstack/react-virtual": "^3.10.8", + "@mui/icons-material": "^7.0.1", + "@mui/lab": "^7.0.0-beta.11", + "@mui/material": "^7.0.1", + "@tanstack/react-virtual": "^3.13.6", "@testing-library/jest-dom": "^6.4.6", - "@testing-library/user-event": "^14.5.2", - "@tiptap/extension-color": "^2.5.9", - "@tiptap/extension-highlight": "^2.6.6", - "@tiptap/extension-image": "^2.6.6", - "@tiptap/extension-mention": "^2.9.1", - "@tiptap/extension-placeholder": "^2.6.2", - "@tiptap/extension-text-style": "^2.5.9", - "@tiptap/extension-underline": "^2.6.6", - "@tiptap/pm": "^2.5.9", - "@tiptap/react": "^2.5.9", - "@tiptap/starter-kit": "^2.5.9", + "@testing-library/user-event": "^14.6.1", + "@tiptap/extension-color": "^2.11.7", + "@tiptap/extension-highlight": "^2.11.7", + "@tiptap/extension-image": "^2.11.7", + "@tiptap/extension-mention": "^2.11.7", + "@tiptap/extension-placeholder": "^2.11.7", + "@tiptap/extension-text-style": "^2.11.7", + "@tiptap/extension-underline": "^2.11.7", + "@tiptap/pm": "^2.11.7", + "@tiptap/react": "^2.11.7", + "@tiptap/starter-kit": "^2.11.7", "@transistorsoft/capacitor-background-fetch": "^6.0.1", "@types/chrome": "^0.0.263", "@uiw/react-color": "^2.5.1", @@ -61,30 +59,25 @@ "i18next-browser-languagedetector": "^8.0.5", "i18next-http-backend": "^3.0.2", "i18next-localstorage-backend": "^4.2.0", + "jotai": "^2.12.3", "jssha": "3.3.1", "lit": "^3.2.1", "lodash": "^4.17.21", "mime": "^4.0.4", "moment": "^2.30.1", "npm": "^10.8.3", - "quill-image-resize-module-react": "^3.0.0", - "react": "^18.2.0", - "react-copy-to-clipboard": "^5.1.0", + "react": "^19.1.0", "react-countdown-circle-timer": "^3.2.1", - "react-dom": "^18.2.0", + "react-dom": "^19.1.0", "react-dropzone": "^14.2.3", "react-frame-component": "^5.2.7", "react-i18next": "^15.4.1", - "react-infinite-scroller": "^1.2.6", - "react-intersection-observer": "^9.13.0", - "react-json-view-lite": "^2.0.1", + "react-intersection-observer": "^9.16.0", + "react-json-view-lite": "^2.4.1", "react-loader-spinner": "^6.1.6", "react-qr-code": "^2.0.15", - "react-quill": "^2.0.0", - "react-redux": "^9.1.2", - "react-virtualized": "^9.22.5", + "react-virtualized": "^9.22.6", "react-virtuoso": "^4.10.4", - "recoil": "^0.7.7", "short-unique-id": "^5.2.0", "slate": "^0.103.0", "slate-react": "^0.109.0", @@ -96,14 +89,12 @@ "vite-plugin-wasm": "^3.3.0" }, "devDependencies": { - "@testing-library/dom": "^10.3.0", - "@testing-library/react": "^16.0.0", + "@testing-library/dom": "^10.4.0", + "@testing-library/react": "^16.3.0", "@types/dompurify": "^3.0.5", "@types/lodash": "^4.17.7", - "@types/react": "^18.2.64", - "@types/react-copy-to-clipboard": "^5.0.7", - "@types/react-dom": "^18.2.21", - "@types/react-infinite-scroller": "^1.2.5", + "@types/react": "^19.1.0", + "@types/react-dom": "^19.1.0", "@types/react-virtualized": "^9.21.30", "@typescript-eslint/eslint-plugin": "^7.1.1", "@typescript-eslint/parser": "^7.1.1", @@ -1779,30 +1770,6 @@ "@capacitor/core": "^6.0.0" } }, - "node_modules/@chatscope/chat-ui-kit-react": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@chatscope/chat-ui-kit-react/-/chat-ui-kit-react-2.0.3.tgz", - "integrity": "sha512-0IkjFskRec7SHrFivOQPiZMie5GLQL+ZnROiIbj4yptbC3aMEMFdHRAZrfqlid3uQx9kYhdtn34wMLh1vVNMLA==", - "dependencies": { - "@chatscope/chat-ui-kit-styles": "^1.2.0", - "@fortawesome/fontawesome-free": "^5.12.1", - "@fortawesome/fontawesome-svg-core": "^1.2.26", - "@fortawesome/free-solid-svg-icons": "^5.12.0", - "@fortawesome/react-fontawesome": "^0.1.8", - "classnames": "^2.2.6", - "prop-types": "^15.7.2" - }, - "peerDependencies": { - "prop-types": "^15.7.2", - "react": "^16.12.0 || ^17.0.0 || ^18.2.0", - "react-dom": "^16.12.0 || ^17.0.0 || ^18.2.0" - } - }, - "node_modules/@chatscope/chat-ui-kit-styles": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@chatscope/chat-ui-kit-styles/-/chat-ui-kit-styles-1.4.0.tgz", - "integrity": "sha512-016mBJD3DESw7Nh+lkKcPd22xG92ghA0VpIXIbjQtmXhC7Ve6wRazTy8z1Ahut+Tbv179+JxrftuMngsj/yV8Q==" - }, "node_modules/@develar/schema-utils": { "version": "2.6.5", "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", @@ -1820,9 +1787,10 @@ } }, "node_modules/@dnd-kit/accessibility": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.0.tgz", - "integrity": "sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", "dependencies": { "tslib": "^2.0.0" }, @@ -1831,11 +1799,12 @@ } }, "node_modules/@dnd-kit/core": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.1.0.tgz", - "integrity": "sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", "dependencies": { - "@dnd-kit/accessibility": "^3.1.0", + "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", "tslib": "^2.0.0" }, @@ -1845,15 +1814,16 @@ } }, "node_modules/@dnd-kit/sortable": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-8.0.0.tgz", - "integrity": "sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "license": "MIT", "dependencies": { "@dnd-kit/utilities": "^3.2.2", "tslib": "^2.0.0" }, "peerDependencies": { - "@dnd-kit/core": "^6.1.0", + "@dnd-kit/core": "^6.3.0", "react": ">=16.8.0" } }, @@ -2433,21 +2403,35 @@ } }, "node_modules/@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", "dependencies": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, + "node_modules/@emotion/cache/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/cache/node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, "node_modules/@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" }, "node_modules/@emotion/is-prop-valid": { "version": "1.2.2", @@ -2486,21 +2470,35 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz", - "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", "dependencies": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, + "node_modules/@emotion/serialize/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/serialize/node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, "node_modules/@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" }, "node_modules/@emotion/styled": { "version": "11.11.0", @@ -2527,7 +2525,8 @@ "node_modules/@emotion/unitless": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "license": "MIT" }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { "version": "1.0.1", @@ -2538,9 +2537,10 @@ } }, "node_modules/@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" }, "node_modules/@emotion/weak-memoize": { "version": "0.3.1", @@ -2993,94 +2993,6 @@ "@capacitor/core": "^6.1.2" } }, - "node_modules/@floating-ui/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", - "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", - "dependencies": { - "@floating-ui/utils": "^0.2.1" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", - "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", - "dependencies": { - "@floating-ui/core": "^1.0.0", - "@floating-ui/utils": "^0.2.0" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", - "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", - "dependencies": { - "@floating-ui/dom": "^1.6.1" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", - "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" - }, - "node_modules/@fortawesome/fontawesome-common-types": { - "version": "0.2.36", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", - "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==", - "hasInstallScript": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/fontawesome-free": { - "version": "5.15.4", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", - "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==", - "hasInstallScript": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "1.2.36", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", - "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", - "hasInstallScript": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "^0.2.36" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "5.15.4", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", - "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", - "hasInstallScript": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "^0.2.36" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/react-fontawesome": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz", - "integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==", - "dependencies": { - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "~1 || ~6", - "react": ">=16.x" - } - }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -3732,64 +3644,35 @@ "node": ">= 10.0.0" } }, - "node_modules/@mui/base": { - "version": "5.0.0-beta.40", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", - "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@floating-ui/react-dom": "^2.0.8", - "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", - "@popperjs/core": "^2.11.8", - "clsx": "^2.1.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.16.7", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.7.tgz", - "integrity": "sha512-RtsCt4Geed2/v74sbihWzzRs+HsIQCfclHeORh5Ynu2fS4icIKozcSubwuG7vtzq2uW3fOR1zITSP84TNt2GoQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.0.2.tgz", + "integrity": "sha512-TfeFU9TgN1N06hyb/pV/63FfO34nijZRMqgHk0TJ3gkl4Fbd+wZ73+ZtOd7jag6hMmzO9HSrBc6Vdn591nhkAg==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.16.4.tgz", - "integrity": "sha512-j9/CWctv6TH6Dou2uR2EH7UOgu79CW/YcozxCYVLJ7l03pCsiOlJ5sBArnWJxJ+nGkFwyL/1d1k8JEPMDR125A==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.0.2.tgz", + "integrity": "sha512-Bo57PFLOqXOqPNrXjd8AhzH5s6TCsNUQbvnQ0VKZ8D+lIlteqKnrk/O1luMJUc/BXONK7BfIdTdc7qOnXYbMdw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.9" + "@babel/runtime": "^7.27.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^5.0.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@mui/material": "^7.0.2", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -3798,20 +3681,20 @@ } }, "node_modules/@mui/lab": { - "version": "5.0.0-alpha.173", - "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.173.tgz", - "integrity": "sha512-Gt5zopIWwxDgGy/MXcp6GueD84xFFugFai4hYiXY0zowJpTVnIrTQCQXV004Q7rejJ7aaCntX9hpPJqCrioshA==", + "version": "7.0.0-beta.11", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-7.0.0-beta.11.tgz", + "integrity": "sha512-VJDEUgiRsjo8V2xDvBEE9Cfs1cSlwzp9UFmD3KzIg6emFMqz9BfYh+9cFI4iQWaoz3Agm3q6bVKlhlX6xw6sVQ==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/base": "5.0.0-beta.40", - "@mui/system": "^5.16.5", - "@mui/types": "^7.2.15", - "@mui/utils": "^5.16.5", - "clsx": "^2.1.0", + "@babel/runtime": "^7.27.0", + "@mui/system": "^7.0.2", + "@mui/types": "^7.4.1", + "@mui/utils": "^7.0.2", + "clsx": "^2.1.1", "prop-types": "^15.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", @@ -3820,10 +3703,11 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material": ">=5.15.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material": "^7.0.2", + "@mui/material-pigment-css": "^7.0.2", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -3832,31 +3716,35 @@ "@emotion/styled": { "optional": true }, + "@mui/material-pigment-css": { + "optional": true + }, "@types/react": { "optional": true } } }, "node_modules/@mui/material": { - "version": "5.16.7", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.7.tgz", - "integrity": "sha512-cwwVQxBhK60OIOqZOVLFt55t01zmarKJiJUWbk0+8s/Ix5IaUzAShqlJchxsIQ4mSrWqgcKCCXKtIlG5H+/Jmg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.0.2.tgz", + "integrity": "sha512-rjJlJ13+3LdLfobRplkXbjIFEIkn6LgpetgU/Cs3Xd8qINCCQK9qXQIjjQ6P0FXFTPFzEVMj0VgBR1mN+FhOcA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/core-downloads-tracker": "^5.16.7", - "@mui/system": "^5.16.7", - "@mui/types": "^7.2.15", - "@mui/utils": "^5.16.6", + "@babel/runtime": "^7.27.0", + "@mui/core-downloads-tracker": "^7.0.2", + "@mui/system": "^7.0.2", + "@mui/types": "^7.4.1", + "@mui/utils": "^7.0.2", "@popperjs/core": "^2.11.8", - "@types/react-transition-group": "^4.4.10", - "clsx": "^2.1.0", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1", - "react-is": "^18.3.1", + "react-is": "^19.1.0", "react-transition-group": "^4.4.5" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", @@ -3865,9 +3753,10 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material-pigment-css": "^7.0.2", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -3876,35 +3765,40 @@ "@emotion/styled": { "optional": true }, + "@mui/material-pigment-css": { + "optional": true + }, "@types/react": { "optional": true } } }, "node_modules/@mui/material/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", + "license": "MIT" }, "node_modules/@mui/private-theming": { - "version": "5.16.6", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.6.tgz", - "integrity": "sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.0.2.tgz", + "integrity": "sha512-6lt8heDC9wN8YaRqEdhqnm0cFCv08AMf4IlttFvOVn7ZdKd81PNpD/rEtPGLLwQAFyyKSxBG4/2XCgpbcdNKiA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.16.6", + "@babel/runtime": "^7.27.0", + "@mui/utils": "^7.0.2", "prop-types": "^15.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -3913,17 +3807,20 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.16.6", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.6.tgz", - "integrity": "sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.0.2.tgz", + "integrity": "sha512-11Bt4YdHGlh7sB8P75S9mRCUxTlgv7HGbr0UKz6m6Z9KLeiw1Bm9y/t3iqLLVMvSHYB6zL8X8X+LmfTE++gyBw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.9", - "@emotion/cache": "^11.11.0", + "@babel/runtime": "^7.27.0", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", @@ -3932,7 +3829,7 @@ "peerDependencies": { "@emotion/react": "^11.4.1", "@emotion/styled": "^11.3.0", - "react": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -3944,21 +3841,22 @@ } }, "node_modules/@mui/system": { - "version": "5.16.7", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.7.tgz", - "integrity": "sha512-Jncvs/r/d/itkxh7O7opOunTqbbSSzMTHzZkNLM+FjAOg+cYAZHrPDlYe1ZGKUYORwwb2XexlWnpZp0kZ4AHuA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.0.2.tgz", + "integrity": "sha512-yFUraAWYWuKIISPPEVPSQ1NLeqmTT4qiQ+ktmyS8LO/KwHxB+NNVOacEZaIofh5x1NxY8rzphvU5X2heRZ/RDA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.16.6", - "@mui/styled-engine": "^5.16.6", - "@mui/types": "^7.2.15", - "@mui/utils": "^5.16.6", - "clsx": "^2.1.0", + "@babel/runtime": "^7.27.0", + "@mui/private-theming": "^7.0.2", + "@mui/styled-engine": "^7.0.2", + "@mui/types": "^7.4.1", + "@mui/utils": "^7.0.2", + "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", @@ -3967,8 +3865,8 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -3983,11 +3881,15 @@ } }, "node_modules/@mui/types": { - "version": "7.2.15", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz", - "integrity": "sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.1.tgz", + "integrity": "sha512-gUL8IIAI52CRXP/MixT1tJKt3SI6tVv4U/9soFsTtAsHzaJQptZ42ffdHZV3niX1ei0aUgMvOxBBN0KYqdG39g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.0" + }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -3996,27 +3898,28 @@ } }, "node_modules/@mui/utils": { - "version": "5.16.6", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.6.tgz", - "integrity": "sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.0.2.tgz", + "integrity": "sha512-72gcuQjPzhj/MLmPHLCgZjy2VjOH4KniR/4qRtXTTXIEwbkgcN+Y5W/rC90rWtMmZbjt9svZev/z+QHUI4j74w==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/types": "^7.2.15", - "@types/prop-types": "^15.7.12", + "@babel/runtime": "^7.27.0", + "@mui/types": "^7.4.1", + "@types/prop-types": "^15.7.14", "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^18.3.1" + "react-is": "^19.1.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -4025,9 +3928,10 @@ } }, "node_modules/@mui/utils/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", + "license": "MIT" }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -4107,29 +4011,6 @@ "url": "https://opencollective.com/popperjs" } }, - "node_modules/@reduxjs/toolkit": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.7.tgz", - "integrity": "sha512-faI3cZbSdFb8yv9dhDTmGwclW0vk0z5o1cia+kf7gCbaCwHI5e+7tP57mJUv22pNcNbeA62GSrPpfrUfdXcQ6g==", - "dependencies": { - "immer": "^10.0.3", - "redux": "^5.0.1", - "redux-thunk": "^3.1.0", - "reselect": "^5.1.0" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18", - "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-redux": { - "optional": true - } - } - }, "node_modules/@remirror/core-constants": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz", @@ -4649,34 +4530,36 @@ } }, "node_modules/@tanstack/react-virtual": { - "version": "3.10.8", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.8.tgz", - "integrity": "sha512-VbzbVGSsZlQktyLrP5nxE+vE1ZR+U0NFAWPbJLoG2+DKPwd2D7dVICTVIIaYlJqX1ZCEnYDbaOpmMwbsyhBoIA==", + "version": "3.13.6", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.6.tgz", + "integrity": "sha512-WT7nWs8ximoQ0CDx/ngoFP7HbQF9Q2wQe4nh2NB+u2486eX3nZRE40P9g6ccCVq7ZfTSH5gFOuCoVH5DLNS/aA==", + "license": "MIT", "dependencies": { - "@tanstack/virtual-core": "3.10.8" + "@tanstack/virtual-core": "3.13.6" }, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/@tanstack/virtual-core": { - "version": "3.10.8", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.8.tgz", - "integrity": "sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA==", + "version": "3.13.6", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.6.tgz", + "integrity": "sha512-cnQUeWnhNP8tJ4WsGcYiX24Gjkc9ALstLbHcBj1t3E7EimN6n6kHH+DPV4PpDnuw00NApQp+ViojMj1GRdwYQg==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" } }, "node_modules/@testing-library/dom": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.3.0.tgz", - "integrity": "sha512-pT/TYB2+IyMYkkB6lqpkzD7VFbsR0JBJtflK3cS68sCNWxmOhWwRm1XvVHlseNEorsNcxkYsb4sRDV3aNIpttg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.10.4", @@ -4913,9 +4796,9 @@ } }, "node_modules/@testing-library/react": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.0.tgz", - "integrity": "sha512-guuxUKRWQ+FgNX0h0NS0FIq3Q3uLtWVpBzcLOggmfMoUpgBnzBzvLLd4fbm6yS8ydJd94cIfY4yP9qUQjM2KwQ==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", + "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", "dev": true, "license": "MIT", "dependencies": { @@ -4926,10 +4809,10 @@ }, "peerDependencies": { "@testing-library/dom": "^10.0.0", - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -4941,9 +4824,9 @@ } }, "node_modules/@testing-library/user-event": { - "version": "14.5.2", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", - "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", "license": "MIT", "engines": { "node": ">=12", @@ -4954,9 +4837,10 @@ } }, "node_modules/@tiptap/core": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.9.1.tgz", - "integrity": "sha512-tifnLL/ARzQ6/FGEJjVwj9UT3v+pENdWHdk9x6F3X0mB1y0SeCjV21wpFLYESzwNdBPAj8NMp8Behv7dBnhIfw==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.11.7.tgz", + "integrity": "sha512-zN+NFFxLsxNEL8Qioc+DL6b8+Tt2bmRbXH22Gk6F6nD30x83eaUSFlSv3wqvgyCq3I1i1NO394So+Agmayx6rQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -4966,33 +4850,36 @@ } }, "node_modules/@tiptap/extension-blockquote": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.5.9.tgz", - "integrity": "sha512-LhGyigmd/v1OjYPeoVK8UvFHbH6ffh175ZuNvseZY4PsBd7kZhrSUiuMG8xYdNX8FxamsxAzr2YpsYnOzu3W7A==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.11.7.tgz", + "integrity": "sha512-liD8kWowl3CcYCG9JQlVx1eSNc/aHlt6JpVsuWvzq6J8APWX693i3+zFqyK2eCDn0k+vW62muhSBe3u09hA3Zw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-bold": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.5.9.tgz", - "integrity": "sha512-XUJdzFb31t0+bwiRquJf0btBpqOB3axQNHTKM9XADuL4S+Z6OBPj0I5rYINeElw/Q7muvdWrHWHh/ovNJA1/5A==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.11.7.tgz", + "integrity": "sha512-VTR3JlldBixXbjpLTFme/Bxf1xeUgZZY3LTlt5JDlCW3CxO7k05CIa+kEZ8LXpog5annytZDUVtWqxrNjmsuHQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-bubble-menu": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.9.tgz", - "integrity": "sha512-NddZ8Qn5dgPPa1W4yk0jdhF4tDBh0FwzBpbnDu2Xz/0TUHrA36ugB2CvR5xS1we4zUKckgpVqOqgdelrmqqFVg==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.11.7.tgz", + "integrity": "sha512-0vYqSUSSap3kk3/VT4tFE1/6StX70I3/NKQ4J68ZSFgkgyB3ZVlYv7/dY3AkEukjsEp3yN7m8Gw8ei2eEwyzwg==", + "license": "MIT", "dependencies": { "tippy.js": "^6.3.7" }, @@ -5001,89 +4888,96 @@ "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9", - "@tiptap/pm": "^2.5.9" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-bullet-list": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.5.9.tgz", - "integrity": "sha512-hJTv1x4omFgaID4LMRT5tOZb/VKmi8Kc6jsf4JNq4Grxd2sANmr9qpmKtBZvviK+XD5PpTXHvL+1c8C1SQtuHQ==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.11.7.tgz", + "integrity": "sha512-WbPogE2/Q3e3/QYgbT1Sj4KQUfGAJNc5pvb7GrUbvRQsAh7HhtuO8hqdDwH8dEdD/cNUehgt17TO7u8qV6qeBw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-code": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.5.9.tgz", - "integrity": "sha512-Q1PL3DUXiEe5eYUwOug1haRjSaB0doAKwx7KFVI+kSGbDwCV6BdkKAeYf3us/O2pMP9D0im8RWX4dbSnatgwBA==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.11.7.tgz", + "integrity": "sha512-VpPO1Uy/eF4hYOpohS/yMOcE1C07xmMj0/D989D9aS1x95jWwUVrSkwC+PlWMUBx9PbY2NRsg1ZDwVvlNKZ6yQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-code-block": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.5.9.tgz", - "integrity": "sha512-+MUwp0VFFv2aFiZ/qN6q10vfIc6VhLoFFpfuETX10eIRks0Xuj2nGiqCDj7ca0/M44bRg2VvW8+tg/ZEHFNl9g==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.11.7.tgz", + "integrity": "sha512-To/y/2H04VWqiANy53aXjV7S6fA86c2759RsH1hTIe57jA1KyE7I5tlAofljOLZK/covkGmPeBddSPHGJbz++Q==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9", - "@tiptap/pm": "^2.5.9" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-color": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-color/-/extension-color-2.5.9.tgz", - "integrity": "sha512-VUGCT9iqD/Ni9arLIxkCbykAElRMFyew7uk2kbbNvttzdwzmZkbslEgCiaEZQTqKr8w4wjuQL14YOtXc6iwEww==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-color/-/extension-color-2.11.7.tgz", + "integrity": "sha512-2CWb0Qnh8Crf9OwnnWB+M1QJtWrbn6IMSwuOzk+tSzdWSazjN8h6XAZVemr0qMdAA/SyUigzorStiPxN6o3/vQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9", - "@tiptap/extension-text-style": "^2.5.9" + "@tiptap/core": "^2.7.0", + "@tiptap/extension-text-style": "^2.7.0" } }, "node_modules/@tiptap/extension-document": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.5.9.tgz", - "integrity": "sha512-VdNZYDyCzC3W430UdeRXR9IZzPeODSbi5Xz/JEdV93THVp8AC9CrZR7/qjqdBTgbTB54VP8Yr6bKfCoIAF0BeQ==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.11.7.tgz", + "integrity": "sha512-95ouJXPjdAm9+VBRgFo4lhDoMcHovyl/awORDI8gyEn0Rdglt+ZRZYoySFzbVzer9h0cre+QdIwr9AIzFFbfdA==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-dropcursor": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.5.9.tgz", - "integrity": "sha512-nEOb37UryG6bsU9JAs/HojE6Jg43LupNTAMISbnuB1CPAeAqNsFMwORd9eEPkyEwnQT7MkhsMOSJM44GoPGIFA==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.11.7.tgz", + "integrity": "sha512-63mL+nxQILizsr5NbmgDeOjFEWi34BLt7evwL6UUZEVM15K8V1G8pD9Y0kCXrZYpHWz0tqFRXdrhDz0Ppu8oVw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9", - "@tiptap/pm": "^2.5.9" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-floating-menu": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.9.tgz", - "integrity": "sha512-MWJIQQT6e5MgqHny8neeH2Dx926nVPF7sv4p84nX4E0dnkRbEYUP8mCsWYhSUvxxIif6e+yY+4654f2Q9qTx1w==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.11.7.tgz", + "integrity": "sha512-DG54WoUu2vxHRVzKZiR5I5RMOYj45IlxQMkBAx1wjS0ch41W8DUYEeipvMMjCeKtEI+emz03xYUcOAP9LRmg+w==", + "license": "MIT", "dependencies": { "tippy.js": "^6.3.7" }, @@ -5092,125 +4986,135 @@ "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9", - "@tiptap/pm": "^2.5.9" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-gapcursor": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.5.9.tgz", - "integrity": "sha512-yW7V2ebezsa7mWEDWCg4A1ZGsmSV5bEHKse9wzHCDkb7TutSVhLZxGo72U6hNN9PnAksv+FJQk03NuZNYvNyRQ==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.11.7.tgz", + "integrity": "sha512-EceesmPG7FyjXZ8EgeJPUov9G1mAf2AwdypxBNH275g6xd5dmU/KvjoFZjmQ0X1ve7mS+wNupVlGxAEUYoveew==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9", - "@tiptap/pm": "^2.5.9" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-hard-break": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.5.9.tgz", - "integrity": "sha512-8hQ63SgZRG4BqHOeSfeaowG2eMr2beced018pOGbpHbE3XSYoISkMVuFz4Z8UEVR3W9dTbKo4wxNufSTducocQ==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.11.7.tgz", + "integrity": "sha512-zTkZSA6q+F5sLOdCkiC2+RqJQN0zdsJqvFIOVFL/IDVOnq6PZO5THzwRRLvOSnJJl3edRQCl/hUgS0L5sTInGQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-heading": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.5.9.tgz", - "integrity": "sha512-HHowAlGUbFn1qvmY02ydM7qiPPMTGhAJn2A46enDRjNHW5UoqeMfkMpTEYaioOexyguRFSfDT3gpK68IHkQORQ==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.11.7.tgz", + "integrity": "sha512-8kWh7y4Rd2fwxfWOhFFWncHdkDkMC1Z60yzIZWjIu72+6yQxvo8w3yeb7LI7jER4kffbMmadgcfhCHC/fkObBA==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-highlight": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.6.6.tgz", - "integrity": "sha512-Z02AYWm1AJAfhmfT4fGCI3YitijF4uNu+eiuq7OxhCiVf9IYaq8xlH2YMxa09QvMUo70ovklxk97+vQUUHeqfQ==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.11.7.tgz", + "integrity": "sha512-c/NH4kIpNOWCUQv8RkFNDyOcgt+2pYFpDf0QBJmzhAuv4BIeS2bDmDtuNS7VgoWRZH+xxCNXfvm2BG+kjtipEg==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.6" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-history": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.5.9.tgz", - "integrity": "sha512-hGPtJgoZSwnVVqi/xipC2ET/9X2G2UI/Y+M3IYV1ZlM0tCYsv4spNi3uXlZqnXRwYcBXLk5u6e/dmsy5QFbL8g==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.11.7.tgz", + "integrity": "sha512-Cu5x3aS13I040QSRoLdd+w09G4OCVfU+azpUqxufZxeNs9BIJC+0jowPLeOxKDh6D5GGT2A8sQtxc6a/ssbs8g==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9", - "@tiptap/pm": "^2.5.9" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-horizontal-rule": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.5.9.tgz", - "integrity": "sha512-/ES5NdxCndBmZAgIXSpCJH8YzENcpxR0S8w34coSWyv+iW0Sq7rW/mksQw8ZIVsj8a7ntpoY5OoRFpSlqcvyGw==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.11.7.tgz", + "integrity": "sha512-uVmQwD2dzZ5xwmvUlciy0ItxOdOfQjH6VLmu80zyJf8Yu7mvwP8JyxoXUX0vd1xHpwAhgQ9/ozjIWYGIw79DPQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9", - "@tiptap/pm": "^2.5.9" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-image": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.6.6.tgz", - "integrity": "sha512-dwJKvoqsr72B4tcTH8hXhfBJzUMs/jXUEE9MnfzYnSXf+CYALLjF8r/IkGYbxce62GP/bMDoj8BgpF8saeHtqA==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.11.7.tgz", + "integrity": "sha512-YvCmTDB7Oo+A56tR4S/gcNaYpqU4DDlSQcRp5IQvmQV5EekSe0lnEazGDoqOCwsit9qQhj4MPQJhKrnaWrJUrg==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.6" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-italic": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.5.9.tgz", - "integrity": "sha512-Bw+P139L4cy+B56zpUiRjP8BZSaAUl3JFMnr/FO+FG55QhCxFMXIc6XrC3vslNy5ef3B3zv4gCttS3ee8ByMiw==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.11.7.tgz", + "integrity": "sha512-r985bkQfG0HMpmCU0X0p/Xe7U1qgRm2mxvcp6iPCuts2FqxaCoyfNZ8YnMsgVK1mRhM7+CQ5SEg2NOmQNtHvPw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-list-item": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.5.9.tgz", - "integrity": "sha512-d9Eo+vBz74SMxP0r25aqiErV256C+lGz+VWMjOoqJa6xWLM1keYy12JtGQWJi8UDVZrDskJKCHq81A0uLt27WA==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.11.7.tgz", + "integrity": "sha512-6ikh7Y+qAbkSuIHXPIINqfzmWs5uIGrylihdZ9adaIyvrN1KSnWIqrZIk/NcZTg5YFIJlXrnGSRSjb/QM3WUhw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-mention": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tiptap/extension-mention/-/extension-mention-2.9.1.tgz", - "integrity": "sha512-2IzunpivdNtDNdtAXwRiQbNhTm87zrbkhz1cCE+2y9pWiX1QLXyx0HQq/DIAjxp6v7y4sIh+5UTUTFlH7vD9wQ==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-mention/-/extension-mention-2.11.7.tgz", + "integrity": "sha512-Q/fkceDOug4VjiqrCRLzBnOL9Oj+XugWwDgwfucJJMBOJxZ3++3eZGZ54dri/xK39A4ZD+xuMBF7PrJIy+Z5dw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -5222,113 +5126,121 @@ } }, "node_modules/@tiptap/extension-ordered-list": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.5.9.tgz", - "integrity": "sha512-9MsWpvVvzILuEOd/GdroF7RI7uDuE1M6at9rzsaVGvCPVHZBvu1XR3MSVK5OdiJbbJuPGttlzEFLaN/rQdCGFg==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.11.7.tgz", + "integrity": "sha512-bLGCHDMB0vbJk7uu8bRg8vES3GsvxkX7Cgjgm/6xysHFbK98y0asDtNxkW1VvuRreNGz4tyB6vkcVCfrxl4jKw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-paragraph": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.5.9.tgz", - "integrity": "sha512-HDXGiHTJ/V85dbDMjcFj4XfqyTQZqry6V21ucMzgBZYX60X3gIn7VpQTQnnRjvULSgtfOASSJP6BELc5TyiK0w==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.11.7.tgz", + "integrity": "sha512-Pl3B4q6DJqTvvAdraqZaNP9Hh0UWEHL5nNdxhaRNuhKaUo7lq8wbDSIxIW3lvV0lyCs0NfyunkUvSm1CXb6d4Q==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-placeholder": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.6.2.tgz", - "integrity": "sha512-Aou6lH456j5mpry36jyAdZzINxFx6fjqvmapmmORJKV+9J889P7RN7laRRsosWHez0Oxg4KuWL3FuDexx6ZJOQ==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.11.7.tgz", + "integrity": "sha512-/06zXV4HIjYoiaUq1fVJo/RcU8pHbzx21evOpeG/foCfNpMI4xLU/vnxdUi6/SQqpZMY0eFutDqod1InkSOqsg==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.2", - "@tiptap/pm": "^2.6.2" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-strike": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.5.9.tgz", - "integrity": "sha512-QezkOZpczpl09S8lp5JL7sRkwREoPY16Y/lTvBcFKm3TZbVzYZZ/KwS0zpwK9HXTfXr8os4L9AGjQf0tHonX+w==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.11.7.tgz", + "integrity": "sha512-D6GYiW9F24bvAY7XMOARNZbC8YGPzdzWdXd8VOOJABhf4ynMi/oW4NNiko+kZ67jn3EGaKoz32VMJzNQgYi1HA==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-text": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.5.9.tgz", - "integrity": "sha512-W0pfiQUPsMkwaV5Y/wKW4cFsyXAIkyOFt7uN5u6LrZ/iW9KZ/IsDODPJDikWp0aeQnXzT9NNQULTpCjbHzzS6g==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.11.7.tgz", + "integrity": "sha512-wObCn8qZkIFnXTLvBP+X8KgaEvTap/FJ/i4hBMfHBCKPGDx99KiJU6VIbDXG8d5ZcFZE0tOetK1pP5oI7qgMlQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-text-style": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.5.9.tgz", - "integrity": "sha512-1pNnl/a5EdY7g3IeFomm0B6eiTvAFOBeJGswoYxogzHmkWbLFhXFdgZ6qz7+k985w4qscsG1GpvtOW3IrJ9J6g==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.11.7.tgz", + "integrity": "sha512-LHO6DBg/9SkCQFdWlVfw9nolUmw+Cid94WkTY+7IwrpyG2+ZGQxnKpCJCKyeaFNbDoYAtvu0vuTsSXeCkgShcA==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-underline": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.6.6.tgz", - "integrity": "sha512-3A4HqsDM/AFb2VaeWACpGexjgI257kz0yU4jNV8uyydDR2KhqeinuEnoSoOmx9T3pL006TWfPg4vaQYPO3qvrQ==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.11.7.tgz", + "integrity": "sha512-NtoQw6PGijOAtXC6G+0Aq0/Z5wwEjPhNHs8nsjXogfWIgaj/aI4/zfBnA06eI3WT+emMYQTl0fTc4CUPnLVU8g==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.6" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/pm": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.9.1.tgz", - "integrity": "sha512-mvV86fr7kEuDYEApQ2uMPCKL2uagUE0BsXiyyz3KOkY1zifyVm1fzdkscb24Qy1GmLzWAIIihA+3UHNRgYdOlQ==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.11.7.tgz", + "integrity": "sha512-7gEEfz2Q6bYKXM07vzLUD0vqXFhC5geWRA6LCozTiLdVFDdHWiBrvb2rtkL5T7mfLq03zc1QhH7rI3F6VntOEA==", + "license": "MIT", "dependencies": { "prosemirror-changeset": "^2.2.1", "prosemirror-collab": "^1.3.1", - "prosemirror-commands": "^1.6.0", + "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", - "prosemirror-markdown": "^1.13.0", + "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", - "prosemirror-model": "^1.22.3", + "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", - "prosemirror-tables": "^1.4.0", + "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", - "prosemirror-transform": "^1.10.0", - "prosemirror-view": "^1.34.3" + "prosemirror-transform": "^1.10.2", + "prosemirror-view": "^1.37.0" }, "funding": { "type": "github", @@ -5336,50 +5248,55 @@ } }, "node_modules/@tiptap/react": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.5.9.tgz", - "integrity": "sha512-NZYAslIb79oxIOFHx9T9ey5oX0aJ1uRbtT2vvrvvyRaO6fKWgAwMYN92bOu5/f2oUVGUp6l7wkYZGdjz/XP5bA==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.11.7.tgz", + "integrity": "sha512-gQZEUkAoPsBptnB4T2gAtiUxswjVGhfsM9vOElQco+b11DYmy110T2Zuhg+2YGvB/CG3RoWJx34808P0FX1ijA==", + "license": "MIT", "dependencies": { - "@tiptap/extension-bubble-menu": "^2.5.9", - "@tiptap/extension-floating-menu": "^2.5.9", + "@tiptap/extension-bubble-menu": "^2.11.7", + "@tiptap/extension-floating-menu": "^2.11.7", "@types/use-sync-external-store": "^0.0.6", - "use-sync-external-store": "^1.2.2" + "fast-deep-equal": "^3", + "use-sync-external-store": "^1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.5.9", - "@tiptap/pm": "^2.5.9", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/@tiptap/starter-kit": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.5.9.tgz", - "integrity": "sha512-nZ4V+vRayomjxUsajFMHv1iJ5SiSaEA65LAXze/CzyZXGMXfL2OLzY7wJoaVJ4BgwINuO0dOSAtpNDN6jI+6mQ==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.11.7.tgz", + "integrity": "sha512-K+q51KwNU/l0kqRuV5e1824yOLVftj6kGplGQLvJG56P7Rb2dPbM/JeaDbxQhnHT/KDGamG0s0Po0M3pPY163A==", + "license": "MIT", "dependencies": { - "@tiptap/core": "^2.5.9", - "@tiptap/extension-blockquote": "^2.5.9", - "@tiptap/extension-bold": "^2.5.9", - "@tiptap/extension-bullet-list": "^2.5.9", - "@tiptap/extension-code": "^2.5.9", - "@tiptap/extension-code-block": "^2.5.9", - "@tiptap/extension-document": "^2.5.9", - "@tiptap/extension-dropcursor": "^2.5.9", - "@tiptap/extension-gapcursor": "^2.5.9", - "@tiptap/extension-hard-break": "^2.5.9", - "@tiptap/extension-heading": "^2.5.9", - "@tiptap/extension-history": "^2.5.9", - "@tiptap/extension-horizontal-rule": "^2.5.9", - "@tiptap/extension-italic": "^2.5.9", - "@tiptap/extension-list-item": "^2.5.9", - "@tiptap/extension-ordered-list": "^2.5.9", - "@tiptap/extension-paragraph": "^2.5.9", - "@tiptap/extension-strike": "^2.5.9", - "@tiptap/extension-text": "^2.5.9" + "@tiptap/core": "^2.11.7", + "@tiptap/extension-blockquote": "^2.11.7", + "@tiptap/extension-bold": "^2.11.7", + "@tiptap/extension-bullet-list": "^2.11.7", + "@tiptap/extension-code": "^2.11.7", + "@tiptap/extension-code-block": "^2.11.7", + "@tiptap/extension-document": "^2.11.7", + "@tiptap/extension-dropcursor": "^2.11.7", + "@tiptap/extension-gapcursor": "^2.11.7", + "@tiptap/extension-hard-break": "^2.11.7", + "@tiptap/extension-heading": "^2.11.7", + "@tiptap/extension-history": "^2.11.7", + "@tiptap/extension-horizontal-rule": "^2.11.7", + "@tiptap/extension-italic": "^2.11.7", + "@tiptap/extension-list-item": "^2.11.7", + "@tiptap/extension-ordered-list": "^2.11.7", + "@tiptap/extension-paragraph": "^2.11.7", + "@tiptap/extension-strike": "^2.11.7", + "@tiptap/extension-text": "^2.11.7", + "@tiptap/extension-text-style": "^2.11.7", + "@tiptap/pm": "^2.11.7" }, "funding": { "type": "github", @@ -5550,12 +5467,34 @@ "@types/node": "*" } }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "license": "MIT" + }, "node_modules/@types/lodash": { "version": "4.17.7", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", "dev": true }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "license": "MIT" + }, "node_modules/@types/ms": { "version": "0.7.34", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", @@ -5585,60 +5524,36 @@ } }, "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" - }, - "node_modules/@types/quill": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz", - "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==", - "dependencies": { - "parchment": "^1.1.2" - } + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT" }, "node_modules/@types/react": { - "version": "18.2.67", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.67.tgz", - "integrity": "sha512-vkIE2vTIMHQ/xL0rgmuoECBCkZFZeHr49HeWSc24AptMbNRo7pwSBvj73rlJJs9fGKj0koS+V7kQB1jHS0uCgw==", + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", + "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", + "license": "MIT", "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, - "node_modules/@types/react-copy-to-clipboard": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.7.tgz", - "integrity": "sha512-Gft19D+as4M+9Whq1oglhmK49vqPhcLzk8WfvfLvaYMIPYanyfLy0+CwFucMJfdKoSFyySPmkkWn8/E6voQXjQ==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-dom": { - "version": "18.2.22", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.22.tgz", - "integrity": "sha512-fHkBXPeNtfvri6gdsMYyW+dW7RXFo6Ad09nLFK0VQWR7yGLai/Cyvyj696gbwYvBnhGtevUG9cET0pmUbMtoPQ==", + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.2.tgz", + "integrity": "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==", "dev": true, - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/react-infinite-scroller": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@types/react-infinite-scroller/-/react-infinite-scroller-1.2.5.tgz", - "integrity": "sha512-fJU1jhMgoL6NJFrqTM0Ob7tnd2sQWGxe2ESwiU6FZWbJK/VO/Er5+AOhc+e2zbT0dk5pLygqctsulOLJ8xnSzw==", - "dev": true, - "dependencies": { - "@types/react": "*" + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" } }, "node_modules/@types/react-transition-group": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", - "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", - "dependencies": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { "@types/react": "*" } }, @@ -5665,11 +5580,6 @@ "@types/node": "*" } }, - "node_modules/@types/scheduler": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" - }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", @@ -5684,7 +5594,8 @@ "node_modules/@types/stylis": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", - "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==" + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==", + "license": "MIT" }, "node_modules/@types/trusted-types": { "version": "2.0.7", @@ -5952,6 +5863,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-alpha/-/react-color-alpha-2.5.1.tgz", "integrity": "sha512-hPsIgsnuOQrqinXt3Gt+87fHudbUvvPW+TpvRY0HS9v4ptFu5UsCc/7DPTVKTaL+p+0oaA6eTbziLzPLRLzgsQ==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-drag-event-interactive": "2.5.1" @@ -5969,6 +5881,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-block/-/react-color-block-2.5.1.tgz", "integrity": "sha512-qvubiV0z0P3OxpNt6o1UQ3CVsjVBY1/n/oz6Gzzxx9YPqSClI04AtFjwOQxF7M17SYqXv+88y77gfEfPIqk5+A==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-color-editable-input": "2.5.1", @@ -5987,6 +5900,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-chrome/-/react-color-chrome-2.5.1.tgz", "integrity": "sha512-m/CyRaWgmkW5aQTQ8AZwyvopYm+bhvX06uS+ezQjXDYDtjLvq7RbM0JLLNIOyMXke964R58fhoX4G06ZWd8ycA==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-color-alpha": "2.5.1", @@ -6010,6 +5924,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-circle/-/react-color-circle-2.5.1.tgz", "integrity": "sha512-+8zb/Ork1Q5f2bq0jN+GF7OyqY+2ZDYGrdZovN3EBZLMmERbg6TM2+1gTweeFsdiEM/gpteupJpwKpO1aBCocg==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-color-swatch": "2.5.1" @@ -6027,6 +5942,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-colorful/-/react-color-colorful-2.5.1.tgz", "integrity": "sha512-Y/8Y2Kman6IZQpgs4tPTGPuTNr3fJIJxf4f13jll6xuaOsVZeDq9q+DlMErggL+5ICtaBr8gG+w68nCiY+QqKg==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-color-alpha": "2.5.1", @@ -6046,6 +5962,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-compact/-/react-color-compact-2.5.1.tgz", "integrity": "sha512-5jHJcXEkjMwcghzCgSBU2rPMVjuuaJ7B6IxypNkafRQ4FkW/6bP9WpPkzcNXCZ/gPvSJ1OMQ+Y600mdO78qG5Q==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-color-editable-input": "2.5.1", @@ -6065,6 +5982,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input/-/react-color-editable-input-2.5.1.tgz", "integrity": "sha512-0kr5vQJGPln8LObXwfI2YLiHFz2DW3Atgi51JXlrZUyyaVujXRgMTAc1fz/1RQR6cU2A4bweFaCQljcTsv+Cdg==", + "license": "MIT", "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" }, @@ -6078,6 +5996,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-hsla/-/react-color-editable-input-hsla-2.5.1.tgz", "integrity": "sha512-gmnXB6JrYFAd8VN/EfNDJaTdkFHAnUxjzcsQjQyOEr046jDjWgEc/5o2uE1LwIvoJNg9Lo6LYsr37LnFWwsiLw==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-color-editable-input-rgba": "2.5.1" @@ -6095,6 +6014,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-rgba/-/react-color-editable-input-rgba-2.5.1.tgz", "integrity": "sha512-rk6OxL9lTdRI45aNe3GbUghvaELk4knkEf0gvF/mPHxoeE+nNphSrO5gHm3HhoDOgaplp81VP3q4gUwcdjBzvw==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-color-editable-input": "2.5.1" @@ -6112,6 +6032,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-github/-/react-color-github-2.5.1.tgz", "integrity": "sha512-t05rIy2ifReiVnjv3x+IVlJH7wvwtZugMeouDa/1Y7jIGZswO0zw3zMxz7qfHrzf5NVYWjmEF8QCj85ngv9brg==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-color-swatch": "2.5.1" @@ -6129,6 +6050,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-hue/-/react-color-hue-2.5.1.tgz", "integrity": "sha512-o7mjZhm+U4gHxaBXFxjPINeE3jWfiZAl7RUFqwn4PDZC8wvhU5hEKgJUvcXzErYro0ZYrE1fC/wUHRpI+vcEBg==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-color-alpha": "2.5.1" @@ -6146,6 +6068,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-material/-/react-color-material-2.5.1.tgz", "integrity": "sha512-iPB4YfKVTNO1lSIQ16DMdDurDKvGTjv6Qwi/nq47yE3nnhB0YbOFwb/IZbWBS1sCTPx1an7dM2IZ+hYoYcjrXg==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-color-editable-input": "2.5.1", @@ -6179,6 +6102,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-saturation/-/react-color-saturation-2.5.1.tgz", "integrity": "sha512-mQ6eGmn6dUXfScQrb5tP0TBGCpZWzrQuYOAiwK9u31IJaxFwD1NNAzkiienWe4MQkA5zmgz7Ol6FEdLN8K+vGw==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-drag-event-interactive": "2.5.1" @@ -6196,6 +6120,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-shade-slider/-/react-color-shade-slider-2.5.1.tgz", "integrity": "sha512-hrscAmqmy/Od/usUPETaEuvsNRhUGvNArl73d7HK6e6FjbRFPDBq40LkvjETe8BJMbxrBXTMo6dK7DO08lYq9g==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-color-alpha": "2.5.1" @@ -6213,6 +6138,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-sketch/-/react-color-sketch-2.5.1.tgz", "integrity": "sha512-eQgAnlSZvqoTt6frZa/j+tFdaIBEFneIdxEUfidD8hwvyu5OR/WLHnDy/4fYAxhehDp9Ej8eS3ZsCgPACBMOtA==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-color-alpha": "2.5.1", @@ -6235,6 +6161,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-slider/-/react-color-slider-2.5.1.tgz", "integrity": "sha512-2yluI0Akp6UMXTeAJ4CEjL8flhIFpn3xUPsFXbQmBSzMYJygleVFmwhMye8LSA2PCe3UdaqA2cWXxWsTL0FbIg==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-color-alpha": "2.5.1" @@ -6252,6 +6179,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-swatch/-/react-color-swatch-2.5.1.tgz", "integrity": "sha512-EQ7UEzxdohfsdpXmcEWNmK/uiznZovEKo6+j3OLrSU5pZGO7pxjR9sQMlscikvd8Mu1Mm3U0E6bJseo2acD4Lg==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1" }, @@ -6268,6 +6196,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-color-wheel/-/react-color-wheel-2.5.1.tgz", "integrity": "sha512-e3tDwDoC2T7zTapRRm/QxcOJ7IWJwNCoxZ/f97RL1Ib3gAN/k67H1bkR9TK7euRCUxGy031guxTgdKO9v19XFg==", + "license": "MIT", "dependencies": { "@uiw/color-convert": "2.5.1", "@uiw/react-drag-event-interactive": "2.5.1" @@ -6285,6 +6214,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@uiw/react-drag-event-interactive/-/react-drag-event-interactive-2.5.1.tgz", "integrity": "sha512-GNxhxk5L4O5Gpi20A/BG5sO0GNBNwtNWJidJsJu3pgHUBErN4rhqTDXXu3BQTz5C8yOG5D02Y6Zq/6yu6ckImw==", + "license": "MIT", "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" }, @@ -7657,6 +7587,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7843,11 +7774,6 @@ "node": ">=8" } }, - "node_modules/classnames": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -8108,14 +8034,6 @@ "wrap-ansi": "^6.2.0" } }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "engines": { - "node": ">=0.8" - } - }, "node_modules/clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -8327,14 +8245,6 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, - "node_modules/copy-to-clipboard": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", - "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", - "dependencies": { - "toggle-selection": "^1.0.6" - } - }, "node_modules/cordova-plugin-android-permissions": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/cordova-plugin-android-permissions/-/cordova-plugin-android-permissions-1.1.5.tgz", @@ -8501,6 +8411,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "license": "ISC", "engines": { "node": ">=4" } @@ -8509,6 +8420,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "license": "MIT", "dependencies": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", @@ -8695,25 +8607,6 @@ "node": ">=6" } }, - "node_modules/deep-equal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", - "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", - "dependencies": { - "is-arguments": "^1.1.1", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.5.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -10093,11 +9986,6 @@ "node": ">=0.10.0" } }, - "node_modules/eventemitter3": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", - "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==" - }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -10138,7 +10026,8 @@ "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true }, "node_modules/external-editor": { "version": "3.1.0", @@ -10213,11 +10102,6 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, - "node_modules/fast-diff": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", - "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==" - }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -10993,11 +10877,6 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/hamt_plus": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", - "integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==" - }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -11554,21 +11433,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -12248,6 +12112,27 @@ "node": ">=8" } }, + "node_modules/jotai": { + "version": "2.12.3", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.12.3.tgz", + "integrity": "sha512-DpoddSkmPGXMFtdfnoIHfueFeGP643nqYUWC6REjUcME+PG2UkAtYnLbffRDw3OURI9ZUTcRWkRGLsOvxuWMCg==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=17.0.0", + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/jpeg-exif": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz", @@ -12505,6 +12390,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", "dependencies": { "uc.micro": "^2.0.0" } @@ -12895,6 +12781,7 @@ "version": "14.1.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -12961,7 +12848,8 @@ "node_modules/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==" + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" }, "node_modules/merge-stream": { "version": "2.0.0", @@ -15924,21 +15812,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -16234,11 +16107,6 @@ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, - "node_modules/parchment": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", - "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -16478,9 +16346,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "funding": [ { "type": "opencollective", @@ -16495,10 +16363,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -16507,7 +16376,8 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" }, "node_modules/postject": { "version": "1.0.0-alpha.6", @@ -16702,13 +16572,14 @@ } }, "node_modules/prosemirror-commands": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.6.0.tgz", - "integrity": "sha512-xn1U/g36OqXn2tn5nGmvnnimAj/g1pUx2ypJJIe8WkVX83WyJVC5LTARaxZa2AtQRwntu9Jc5zXs9gL9svp/mg==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz", + "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==", + "license": "MIT", "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.0.0" + "prosemirror-transform": "^1.10.2" } }, "node_modules/prosemirror-dropcursor": { @@ -16762,12 +16633,14 @@ } }, "node_modules/prosemirror-markdown": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.0.tgz", - "integrity": "sha512-UziddX3ZYSYibgx8042hfGKmukq5Aljp2qoBiJRejD/8MH70siQNz5RB1TrdTPheqLMy4aCe4GYNF10/3lQS5g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.2.tgz", + "integrity": "sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==", + "license": "MIT", "dependencies": { + "@types/markdown-it": "^14.0.0", "markdown-it": "^14.0.0", - "prosemirror-model": "^1.20.0" + "prosemirror-model": "^1.25.0" } }, "node_modules/prosemirror-menu": { @@ -16782,9 +16655,10 @@ } }, "node_modules/prosemirror-model": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.22.3.tgz", - "integrity": "sha512-V4XCysitErI+i0rKFILGt/xClnFJaohe/wrrlT2NSZ+zk8ggQfDH4x2wNK7Gm0Hp4CIoWizvXFP7L9KMaCuI0Q==", + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.1.tgz", + "integrity": "sha512-AUvbm7qqmpZa5d9fPKMvH1Q5bqYQvAZWOGRvxsB6iFLyycvC9MwNemNVjHVrWgjaoxAfY8XVg7DbvQ/qxvI9Eg==", + "license": "MIT", "dependencies": { "orderedmap": "^2.0.0" } @@ -16818,15 +16692,16 @@ } }, "node_modules/prosemirror-tables": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.4.0.tgz", - "integrity": "sha512-fxryZZkQG12fSCNuZDrYx6Xvo2rLYZTbKLRd8rglOPgNJGMKIS8uvTt6gGC38m7UCu/ENnXIP9pEz5uDaPc+cA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.7.1.tgz", + "integrity": "sha512-eRQ97Bf+i9Eby99QbyAiyov43iOKgWa7QCGly+lrDt7efZ1v8NWolhXiB43hSDGIXT1UXgbs4KJN3a06FGpr1Q==", + "license": "MIT", "dependencies": { - "prosemirror-keymap": "^1.1.2", - "prosemirror-model": "^1.8.1", - "prosemirror-state": "^1.3.1", - "prosemirror-transform": "^1.2.1", - "prosemirror-view": "^1.13.3" + "prosemirror-keymap": "^1.2.2", + "prosemirror-model": "^1.25.0", + "prosemirror-state": "^1.4.3", + "prosemirror-transform": "^1.10.3", + "prosemirror-view": "^1.39.1" } }, "node_modules/prosemirror-trailing-node": { @@ -16855,17 +16730,19 @@ } }, "node_modules/prosemirror-transform": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.2.tgz", - "integrity": "sha512-2iUq0wv2iRoJO/zj5mv8uDUriOHWzXRnOTVgCzSXnktS/2iQRa3UUQwVlkBlYZFtygw6Nh1+X4mGqoYBINn5KQ==", + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.4.tgz", + "integrity": "sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==", + "license": "MIT", "dependencies": { "prosemirror-model": "^1.21.0" } }, "node_modules/prosemirror-view": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.36.0.tgz", - "integrity": "sha512-U0GQd5yFvV5qUtT41X1zCQfbw14vkbbKwLlQXhdylEmgpYVHkefXYcC4HHwWOfZa3x6Y8wxDLUBv7dxN5XQ3nA==", + "version": "1.39.2", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.39.2.tgz", + "integrity": "sha512-BmOkml0QWNob165gyUxXi5K5CVUgVPpqMEAAml/qzgKn9boLUWVPzQ6LtzXw8Cn1GtRQX4ELumPxqtLTDaAKtg==", + "license": "MIT", "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", @@ -16907,6 +16784,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -16956,42 +16834,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/quill": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", - "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", - "dependencies": { - "clone": "^2.1.1", - "deep-equal": "^1.0.1", - "eventemitter3": "^2.0.3", - "extend": "^3.0.2", - "parchment": "^1.1.4", - "quill-delta": "^3.6.2" - } - }, - "node_modules/quill-delta": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", - "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", - "dependencies": { - "deep-equal": "^1.0.1", - "extend": "^3.0.2", - "fast-diff": "1.1.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/quill-image-resize-module-react": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/quill-image-resize-module-react/-/quill-image-resize-module-react-3.0.0.tgz", - "integrity": "sha512-3jVChLoXh+fwEELx3OswOEEuF+1KU3r/B9RAqZ//s+d+UMduVZzUepU1g/XoxjKoBJvWD2lJwBIFBRUNb8ebCw==", - "dependencies": { - "lodash": "^4.17.4", - "quill": "^1.2.2", - "raw-loader": "^0.5.1" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -17000,34 +16842,15 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/raw-loader": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", - "integrity": "sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q==" - }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/react-copy-to-clipboard": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", - "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==", - "dependencies": { - "copy-to-clipboard": "^3.3.1", - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "react": "^15.3.0 || 16 || 17 || 18" - } - }, "node_modules/react-countdown-circle-timer": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/react-countdown-circle-timer/-/react-countdown-circle-timer-3.2.1.tgz", @@ -17037,15 +16860,15 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.26.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^19.1.0" } }, "node_modules/react-dropzone": { @@ -17096,24 +16919,14 @@ } } }, - "node_modules/react-infinite-scroller": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/react-infinite-scroller/-/react-infinite-scroller-1.2.6.tgz", - "integrity": "sha512-mGdMyOD00YArJ1S1F3TVU9y4fGSfVVl6p5gh/Vt4u99CJOptfVu/q5V/Wlle72TMgYlBwIhbxK5wF0C/R33PXQ==", - "dependencies": { - "prop-types": "^15.5.8" - }, - "peerDependencies": { - "react": "^0.14.9 || ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-intersection-observer": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.13.0.tgz", - "integrity": "sha512-y0UvBfjDiXqC8h0EWccyaj4dVBWMxgEx0t5RGNzQsvkfvZwugnKwxpu70StY4ivzYuMajavwUDjH4LJyIki9Lw==", + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.16.0.tgz", + "integrity": "sha512-w9nJSEp+DrW9KmQmeWHQyfaP6b03v+TdXynaoA964Wxt7mdR3An11z4NNCQgL4gKSK7y1ver2Fq+JKH6CWEzUA==", + "license": "MIT", "peerDependencies": { - "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "react-dom": { @@ -17127,14 +16940,15 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-json-view-lite": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-2.0.1.tgz", - "integrity": "sha512-yElNMSzL7UJ9rMDQIbTiBemXbvfAoqpxM/0IQd3nr52CLLBC0HxOSKcta/bayct2QCq7ZVzLzI8CGfuf387hHw==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-2.4.1.tgz", + "integrity": "sha512-fwFYknRIBxjbFm0kBDrzgBy1xa5tDg2LyXXBepC5f1b+MY3BUClMCsvanMPn089JbV1Eg3nZcrp0VCuH43aXnA==", + "license": "MIT", "engines": { "node": ">=18" }, "peerDependencies": { - "react": "^18.0.0" + "react": "^18.0.0 || ^19.0.0" } }, "node_modules/react-lifecycles-compat": { @@ -17175,47 +16989,6 @@ "react": "*" } }, - "node_modules/react-quill": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz", - "integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==", - "dependencies": { - "@types/quill": "^1.3.10", - "lodash": "^4.17.4", - "quill": "^1.3.7" - }, - "peerDependencies": { - "react": "^16 || ^17 || ^18", - "react-dom": "^16 || ^17 || ^18" - } - }, - "node_modules/react-redux": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", - "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", - "dependencies": { - "@types/use-sync-external-store": "^0.0.3", - "use-sync-external-store": "^1.0.0" - }, - "peerDependencies": { - "@types/react": "^18.2.25", - "react": "^18.0", - "redux": "^5.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, - "node_modules/react-redux/node_modules/@types/use-sync-external-store": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" - }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -17229,6 +17002,7 @@ "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -17241,9 +17015,10 @@ } }, "node_modules/react-virtualized": { - "version": "9.22.5", - "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.5.tgz", - "integrity": "sha512-YqQMRzlVANBv1L/7r63OHa2b0ZsAaDp1UhVNEdUaXI8A5u6hTpA5NYtUueLH2rFuY/27mTGIBl7ZhqFKzw18YQ==", + "version": "9.22.6", + "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.6.tgz", + "integrity": "sha512-U5j7KuUQt3AaMatlMJ0UJddqSiX+Km0YJxSqbAzIiGw5EmNz0khMyqP2hzgu4+QUtm+QPIrxzUX4raJxmVJnHg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.7.2", "clsx": "^1.0.4", @@ -17253,8 +17028,8 @@ "react-lifecycles-compat": "^3.0.4" }, "peerDependencies": { - "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0", - "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0" + "react": "^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/react-virtualized/node_modules/clsx": { @@ -17430,25 +17205,6 @@ "node": ">=8.10.0" } }, - "node_modules/recoil": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz", - "integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==", - "dependencies": { - "hamt_plus": "1.0.2" - }, - "peerDependencies": { - "react": ">=16.13.1" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -17462,19 +17218,6 @@ "node": ">=8" } }, - "node_modules/redux": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", - "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" - }, - "node_modules/redux-thunk": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", - "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", - "peerDependencies": { - "redux": "^5.0.0" - } - }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -17734,11 +17477,6 @@ "url": "https://github.com/sponsors/jet2jet" } }, - "node_modules/reselect": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", - "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" - }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -18075,12 +17813,10 @@ } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dependencies": { - "loose-envify": "^1.1.0" - } + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" }, "node_modules/scroll-into-view-if-needed": { "version": "3.1.0", @@ -18223,7 +17959,8 @@ "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" }, "node_modules/shebang-command": { "version": "2.0.0", @@ -18500,9 +18237,10 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -18886,16 +18624,17 @@ } }, "node_modules/styled-components": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.13.tgz", - "integrity": "sha512-M0+N2xSnAtwcVAQeFEsGWFFxXDftHUD7XrKla06QbpUMmbmtFBMMTcKWvFXtWxuD5qQkB8iU5gk6QASlx2ZRMw==", + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.17.tgz", + "integrity": "sha512-97D7DwWanI7nN24v0D4SvbfjLE9656umNSJZkBkDIWL37aZqG/wRQ+Y9pWtXyBIM/NSfcBzHLErEsqHmJNSVUg==", + "license": "MIT", "dependencies": { "@emotion/is-prop-valid": "1.2.2", "@emotion/unitless": "0.8.1", "@types/stylis": "4.2.5", "css-to-react-native": "3.2.0", "csstype": "3.1.3", - "postcss": "8.4.38", + "postcss": "8.4.49", "shallowequal": "1.1.0", "stylis": "4.3.2", "tslib": "2.6.2" @@ -18915,7 +18654,8 @@ "node_modules/styled-components/node_modules/stylis": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", - "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", + "license": "MIT" }, "node_modules/stylis": { "version": "4.2.0", @@ -19265,11 +19005,6 @@ "node": ">=8.0" } }, - "node_modules/toggle-selection": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", - "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" - }, "node_modules/tough-cookie": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", @@ -19498,7 +19233,8 @@ "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" }, "node_modules/ufo": { "version": "1.5.3", @@ -19749,11 +19485,12 @@ } }, "node_modules/use-sync-external-store": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", - "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/utf8-byte-length": { diff --git a/package.json b/package.json index 3594023..2869cb9 100644 --- a/package.json +++ b/package.json @@ -20,30 +20,28 @@ "@capacitor/core": "^6.1.2", "@capacitor/filesystem": "^6.0.1", "@capacitor/local-notifications": "^6.1.0", - "@chatscope/chat-ui-kit-react": "^2.0.3", - "@dnd-kit/core": "^6.1.0", - "@dnd-kit/sortable": "^8.0.0", + "@dnd-kit/core": "^6.3.0", + "@dnd-kit/sortable": "^10.0.0", "@electron/packager": "^18.3.6", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.0", "@evva/capacitor-secure-storage-plugin": "^3.0.1", - "@mui/icons-material": "^5.16.4", - "@mui/lab": "^5.0.0-alpha.173", - "@mui/material": "^5.16.7", - "@reduxjs/toolkit": "^2.2.7", - "@tanstack/react-virtual": "^3.10.8", + "@mui/icons-material": "^7.0.1", + "@mui/lab": "^7.0.0-beta.11", + "@mui/material": "^7.0.1", + "@tanstack/react-virtual": "^3.13.6", "@testing-library/jest-dom": "^6.4.6", - "@testing-library/user-event": "^14.5.2", - "@tiptap/extension-color": "^2.5.9", - "@tiptap/extension-highlight": "^2.6.6", - "@tiptap/extension-image": "^2.6.6", - "@tiptap/extension-mention": "^2.9.1", - "@tiptap/extension-placeholder": "^2.6.2", - "@tiptap/extension-text-style": "^2.5.9", - "@tiptap/extension-underline": "^2.6.6", - "@tiptap/pm": "^2.5.9", - "@tiptap/react": "^2.5.9", - "@tiptap/starter-kit": "^2.5.9", + "@testing-library/user-event": "^14.6.1", + "@tiptap/extension-color": "^2.11.7", + "@tiptap/extension-highlight": "^2.11.7", + "@tiptap/extension-image": "^2.11.7", + "@tiptap/extension-mention": "^2.11.7", + "@tiptap/extension-placeholder": "^2.11.7", + "@tiptap/extension-text-style": "^2.11.7", + "@tiptap/extension-underline": "^2.11.7", + "@tiptap/pm": "^2.11.7", + "@tiptap/react": "^2.11.7", + "@tiptap/starter-kit": "^2.11.7", "@transistorsoft/capacitor-background-fetch": "^6.0.1", "@types/chrome": "^0.0.263", "@uiw/react-color": "^2.5.1", @@ -66,30 +64,25 @@ "i18next-browser-languagedetector": "^8.0.5", "i18next-http-backend": "^3.0.2", "i18next-localstorage-backend": "^4.2.0", + "jotai": "^2.12.3", "jssha": "3.3.1", "lit": "^3.2.1", "lodash": "^4.17.21", "mime": "^4.0.4", "moment": "^2.30.1", "npm": "^10.8.3", - "quill-image-resize-module-react": "^3.0.0", - "react": "^18.2.0", - "react-copy-to-clipboard": "^5.1.0", + "react": "^19.1.0", "react-countdown-circle-timer": "^3.2.1", - "react-dom": "^18.2.0", + "react-dom": "^19.1.0", "react-dropzone": "^14.2.3", "react-frame-component": "^5.2.7", "react-i18next": "^15.4.1", - "react-infinite-scroller": "^1.2.6", - "react-intersection-observer": "^9.13.0", - "react-json-view-lite": "^2.0.1", + "react-intersection-observer": "^9.16.0", + "react-json-view-lite": "^2.4.1", "react-loader-spinner": "^6.1.6", "react-qr-code": "^2.0.15", - "react-quill": "^2.0.0", - "react-redux": "^9.1.2", - "react-virtualized": "^9.22.5", + "react-virtualized": "^9.22.6", "react-virtuoso": "^4.10.4", - "recoil": "^0.7.7", "short-unique-id": "^5.2.0", "slate": "^0.103.0", "slate-react": "^0.109.0", @@ -101,14 +94,12 @@ "vite-plugin-wasm": "^3.3.0" }, "devDependencies": { - "@testing-library/dom": "^10.3.0", - "@testing-library/react": "^16.0.0", + "@testing-library/dom": "^10.4.0", + "@testing-library/react": "^16.3.0", "@types/dompurify": "^3.0.5", "@types/lodash": "^4.17.7", - "@types/react": "^18.2.64", - "@types/react-copy-to-clipboard": "^5.0.7", - "@types/react-dom": "^18.2.21", - "@types/react-infinite-scroller": "^1.2.5", + "@types/react": "^19.1.0", + "@types/react-dom": "^19.1.0", "@types/react-virtualized": "^9.21.30", "@typescript-eslint/eslint-plugin": "^7.1.1", "@typescript-eslint/parser": "^7.1.1", @@ -122,5 +113,11 @@ "typescript": "^5.2.2", "vite": "^5.1.6", "vitest": "^1.6.1" + }, + "overrides": { + "react-loader-spinner": { + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + } } } diff --git a/src/App.tsx b/src/App.tsx index b711dc2..74f5dee 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -33,7 +33,6 @@ import DownloadIcon from '@mui/icons-material/Download'; import ltcLogo from './assets/ltc.png'; import PersonSearchIcon from '@mui/icons-material/PersonSearch'; import qortLogo from './assets/qort.png'; -import { CopyToClipboard } from 'react-copy-to-clipboard'; import { Return } from './assets/Icons/Return.tsx'; import WarningIcon from '@mui/icons-material/Warning'; import './utils/seedPhrase/RandomSentenceGenerator'; @@ -96,7 +95,6 @@ import { Settings } from './components/Group/Settings'; import { MainAvatar } from './components/MainAvatar'; import { useRetrieveDataLocalStorage } from './useRetrieveDataLocalStorage'; import { useQortalGetSaveSettings } from './useQortalGetSaveSettings'; -import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil'; import { canSaveSettingToQdnAtom, enabledDevModeAtom, @@ -142,6 +140,8 @@ import LanguageSelector from './components/Language/LanguageSelector.tsx'; import { DownloadWallet } from './components/Auth/DownloadWallet.tsx'; import { CopyIcon } from './assets/Icons/CopyIcon.tsx'; import { SuccessIcon } from './assets/Icons/SuccessIcon.tsx'; +import { useAtom, useSetAtom } from 'jotai'; +import { useResetAtom } from 'jotai/utils'; type extStates = | 'not-authenticated' @@ -330,9 +330,10 @@ function App() { const [txList, setTxList] = useState([]); const [memberGroups, setMemberGroups] = useState([]); const [isFocused, setIsFocused] = useState(true); - const [hasSettingsChanged, setHasSettingsChanged] = useRecoilState( + const [hasSettingsChanged, setHasSettingsChanged] = useAtom( hasSettingsChangedAtom ); + const balanceSetIntervalRef = useRef(null); const { downloadResource } = useFetchResources(); const holdRefExtState = useRef('not-authenticated'); @@ -405,9 +406,10 @@ function App() { const qortalRequestCheckbox1Ref = useRef(null); useRetrieveDataLocalStorage(userInfo?.address); useQortalGetSaveSettings(userInfo?.name, extState === 'authenticated'); - const [isEnabledDevMode, setIsEnabledDevMode] = - useRecoilState(enabledDevModeAtom); - const setIsDisabledEditorEnter = useSetRecoilState(isDisabledEditorEnterAtom); + const setIsEnabledDevMode = useSetAtom(enabledDevModeAtom); + + const setIsDisabledEditorEnter = useSetAtom(isDisabledEditorEnterAtom); + const [isOpenMinting, setIsOpenMinting] = useState(false); const generatorRef = useRef(null); @@ -453,45 +455,33 @@ function App() { }, [extState, walletToBeDownloaded, shownTutorialsInitiated]); //resets for recoil - const resetAtomSortablePinnedAppsAtom = useResetRecoilState( - sortablePinnedAppsAtom - ); - - const resetAtomIsUsingImportExportSettingsAtom = useResetRecoilState( + const resetAtomSortablePinnedAppsAtom = useResetAtom(sortablePinnedAppsAtom); + const resetAtomIsUsingImportExportSettingsAtom = useResetAtom( isUsingImportExportSettingsAtom ); - const resetAtomCanSaveSettingToQdnAtom = useResetRecoilState( + const resetAtomCanSaveSettingToQdnAtom = useResetAtom( canSaveSettingToQdnAtom ); - - const resetAtomSettingsQDNLastUpdatedAtom = useResetRecoilState( + const resetAtomSettingsQDNLastUpdatedAtom = useResetAtom( settingsQDNLastUpdatedAtom ); - - const resetAtomSettingsLocalLastUpdatedAtom = useResetRecoilState( + const resetAtomSettingsLocalLastUpdatedAtom = useResetAtom( settingsLocalLastUpdatedAtom ); - - const resetAtomOldPinnedAppsAtom = useResetRecoilState(oldPinnedAppsAtom); - const resetAtomQMailLastEnteredTimestampAtom = useResetRecoilState( + const resetAtomOldPinnedAppsAtom = useResetAtom(oldPinnedAppsAtom); + const resetAtomQMailLastEnteredTimestampAtom = useResetAtom( qMailLastEnteredTimestampAtom ); - - const resetAtomMailsAtom = useResetRecoilState(mailsAtom); - const resetGroupPropertiesAtom = useResetRecoilState(groupsPropertiesAtom); - const resetLastPaymentSeenTimestampAtom = useResetRecoilState( + const resetAtomMailsAtom = useResetAtom(mailsAtom); + const resetGroupPropertiesAtom = useResetAtom(groupsPropertiesAtom); + const resetLastPaymentSeenTimestampAtom = useResetAtom( lastPaymentSeenTimestampAtom ); - const resetGroupsOwnerNamesAtom = useResetRecoilState(groupsOwnerNamesAtom); - const resetGroupAnnouncementsAtom = useResetRecoilState( - groupAnnouncementsAtom - ); - const resetMutedGroupsAtom = useResetRecoilState(mutedGroupsAtom); - - const resetGroupChatTimestampsAtom = useResetRecoilState( - groupChatTimestampsAtom - ); - const resetTimestampEnterAtom = useResetRecoilState(timestampEnterDataAtom); + const resetGroupsOwnerNamesAtom = useResetAtom(groupsOwnerNamesAtom); + const resetGroupAnnouncementsAtom = useResetAtom(groupAnnouncementsAtom); + const resetMutedGroupsAtom = useResetAtom(mutedGroupsAtom); + const resetGroupChatTimestampsAtom = useResetAtom(groupChatTimestampsAtom); + const resetTimestampEnterAtom = useResetAtom(timestampEnterDataAtom); const resetAllRecoil = () => { resetAtomSortablePinnedAppsAtom(); @@ -1314,13 +1304,23 @@ function App() { - + { + if (rawWallet?.ltcAddress) { + navigator.clipboard + .writeText(rawWallet.ltcAddress) + .catch((err) => { + console.error('Failed to copy LTC address:', err); + }); + } + }} + > {rawWallet?.ltcAddress?.slice(0, 6)}... {rawWallet?.ltcAddress?.slice(-4)}{' '} - + @@ -1380,13 +1380,24 @@ function App() { - + { + if (rawWallet?.address0) { + navigator.clipboard + .writeText(rawWallet.address0) + .catch((err) => { + console.error('Failed to copy address:', err); + }); + } + }} + > {rawWallet?.address0?.slice(0, 6)}... {rawWallet?.address0?.slice(-4)}{' '} - + + {qortBalanceLoading && ( diff --git a/src/atoms/global.ts b/src/atoms/global.ts index d20b56c..8f4c6d5 100644 --- a/src/atoms/global.ts +++ b/src/atoms/global.ts @@ -1,273 +1,80 @@ -import { atom, selectorFamily } from 'recoil'; +import { atom } from 'jotai'; +import { atomWithReset, atomFamily } from 'jotai/utils'; -export const sortablePinnedAppsAtom = atom({ - key: 'sortablePinnedAppsFromAtom', - default: [ - { - name: 'Q-Tube', - service: 'APP', - }, - { - name: 'Q-Mail', - service: 'APP', - }, - { - name: 'Q-Share', - service: 'APP', - }, - { - name: 'Q-Fund', - service: 'APP', - }, - { - name: 'Q-Shop', - service: 'APP', - }, - { - name: 'Q-Trade', - service: 'APP', - }, - { - name: 'Q-Support', - service: 'APP', - }, - { - name: 'Q-Manager', - service: 'APP', - }, - { - name: 'Q-Blog', - service: 'APP', - }, - { - name: 'Q-Mintership', - service: 'APP', - }, - { - name: 'Q-Wallets', - service: 'APP', - }, - { - name: 'Q-Search', - service: 'APP', - }, - { - name: 'Q-Nodecontrol', - service: 'APP', - }, - ], -}); +// Atoms (resettable) +export const sortablePinnedAppsAtom = atomWithReset([ + { name: 'Q-Tube', service: 'APP' }, + { name: 'Q-Mail', service: 'APP' }, + { name: 'Q-Share', service: 'APP' }, + { name: 'Q-Fund', service: 'APP' }, + { name: 'Q-Shop', service: 'APP' }, + { name: 'Q-Trade', service: 'APP' }, + { name: 'Q-Support', service: 'APP' }, + { name: 'Q-Manager', service: 'APP' }, + { name: 'Q-Blog', service: 'APP' }, + { name: 'Q-Mintership', service: 'APP' }, + { name: 'Q-Wallets', service: 'APP' }, + { name: 'Q-Search', service: 'APP' }, + { name: 'Q-Nodecontrol', service: 'APP' }, +]); -export const canSaveSettingToQdnAtom = atom({ - key: 'canSaveSettingToQdnAtom', - default: false, -}); +export const canSaveSettingToQdnAtom = atomWithReset(false); +export const settingsQDNLastUpdatedAtom = atomWithReset(-100); +export const settingsLocalLastUpdatedAtom = atomWithReset(0); +export const oldPinnedAppsAtom = atomWithReset([]); +export const isUsingImportExportSettingsAtom = atomWithReset(null); +export const fullScreenAtom = atomWithReset(false); +export const hasSettingsChangedAtom = atomWithReset(false); +export const navigationControllerAtom = atomWithReset({}); +export const enabledDevModeAtom = atomWithReset(false); +export const myGroupsWhereIAmAdminAtom = atomWithReset([]); +export const promotionTimeIntervalAtom = atomWithReset(0); +export const promotionsAtom = atomWithReset([]); +export const resourceDownloadControllerAtom = atomWithReset({}); +export const blobControllerAtom = atomWithReset({}); +export const selectedGroupIdAtom = atomWithReset(null); +export const addressInfoControllerAtom = atomWithReset({}); +export const isDisabledEditorEnterAtom = atomWithReset(false); +export const qMailLastEnteredTimestampAtom = atomWithReset(null); +export const lastPaymentSeenTimestampAtom = atomWithReset(null); +export const mailsAtom = atomWithReset([]); +export const groupsPropertiesAtom = atomWithReset({}); +export const groupsOwnerNamesAtom = atomWithReset({}); +export const isOpenBlockedModalAtom = atomWithReset(false); +export const groupAnnouncementsAtom = atomWithReset({}); +export const mutedGroupsAtom = atomWithReset([]); +export const groupChatTimestampsAtom = atomWithReset({}); +export const timestampEnterDataAtom = atomWithReset({}); -export const settingsQDNLastUpdatedAtom = atom({ - key: 'settingsQDNLastUpdatedAtom', - default: -100, -}); +// Atom Families (replacing selectorFamily) +export const resourceKeySelector = atomFamily((key) => + atom((get) => get(resourceDownloadControllerAtom)[key] || null) +); -export const settingsLocalLastUpdatedAtom = atom({ - key: 'settingsLocalLastUpdatedAtom', - default: 0, -}); +export const blobKeySelector = atomFamily((key) => + atom((get) => get(blobControllerAtom)[key] || null) +); -export const oldPinnedAppsAtom = atom({ - key: 'oldPinnedAppsAtom', - default: [], -}); +export const addressInfoKeySelector = atomFamily((key) => + atom((get) => get(addressInfoControllerAtom)[key] || null) +); -export const isUsingImportExportSettingsAtom = atom({ - key: 'isUsingImportExportSettingsAtom', - default: null, -}); +export const groupsOwnerNamesSelector = atomFamily((key) => + atom((get) => get(groupsOwnerNamesAtom)[key] || null) +); -export const fullScreenAtom = atom({ - key: 'fullScreenAtom', - default: false, -}); +export const groupAnnouncementSelector = atomFamily((key) => + atom((get) => get(groupAnnouncementsAtom)[key] || null) +); -export const hasSettingsChangedAtom = atom({ - key: 'hasSettingsChangedAtom', - default: false, -}); +export const groupPropertySelector = atomFamily((key) => + atom((get) => get(groupsPropertiesAtom)[key] || null) +); -export const navigationControllerAtom = atom({ - key: 'navigationControllerAtom', - default: {}, -}); +export const groupChatTimestampSelector = atomFamily((key) => + atom((get) => get(groupChatTimestampsAtom)[key] || null) +); -export const enabledDevModeAtom = atom({ - key: 'enabledDevModeAtom', - default: false, -}); - -export const myGroupsWhereIAmAdminAtom = atom({ - key: 'myGroupsWhereIAmAdminAtom', - default: [], -}); - -export const promotionTimeIntervalAtom = atom({ - key: 'promotionTimeIntervalAtom', - default: 0, -}); - -export const promotionsAtom = atom({ - key: 'promotionsAtom', - default: [], -}); - -export const resourceDownloadControllerAtom = atom({ - key: 'resourceDownloadControllerAtom', - default: {}, -}); - -export const resourceKeySelector = selectorFamily({ - key: 'resourceKeySelector', - get: - (key) => - ({ get }) => { - const resources = get(resourceDownloadControllerAtom); - return resources[key] || null; // Return the value for the key or null if not found - }, -}); - -export const blobControllerAtom = atom({ - key: 'blobControllerAtom', - default: {}, -}); - -export const blobKeySelector = selectorFamily({ - key: 'blobKeySelector', - get: - (key) => - ({ get }) => { - const blobs = get(blobControllerAtom); - return blobs[key] || null; // Return the value for the key or null if not found - }, -}); - -export const selectedGroupIdAtom = atom({ - key: 'selectedGroupIdAtom', - default: null, -}); - -export const addressInfoControllerAtom = atom({ - key: 'addressInfoControllerAtom', - default: {}, -}); - -export const addressInfoKeySelector = selectorFamily({ - key: 'addressInfoKeySelector', - get: - (key) => - ({ get }) => { - const userInfo = get(addressInfoControllerAtom); - return userInfo[key] || null; // Return the value for the key or null if not found - }, -}); - -export const isDisabledEditorEnterAtom = atom({ - key: 'isDisabledEditorEnterAtom', - default: false, -}); - -export const qMailLastEnteredTimestampAtom = atom({ - key: 'qMailLastEnteredTimestampAtom', - default: null, -}); - -export const lastPaymentSeenTimestampAtom = atom({ - key: 'lastPaymentSeenTimestampAtom', - default: null, -}); - -export const mailsAtom = atom({ - key: 'mailsAtom', - default: [], -}); - -export const groupsPropertiesAtom = atom({ - key: 'groupsPropertiesAtom', - default: {}, -}); -export const groupsOwnerNamesAtom = atom({ - key: 'groupsOwnerNamesAtom', - default: {}, -}); - -export const isOpenBlockedModalAtom = atom({ - key: 'isOpenBlockedModalAtom', - default: false, -}); - -export const groupsOwnerNamesSelector = selectorFamily({ - key: 'groupsOwnerNamesSelector', - get: - (key) => - ({ get }) => { - const data = get(groupsOwnerNamesAtom); - return data[key] || null; // Return the value for the key or null if not found - }, -}); - -export const groupAnnouncementsAtom = atom({ - key: 'groupAnnouncementsAtom', - default: {}, -}); - -export const groupAnnouncementSelector = selectorFamily({ - key: 'groupAnnouncementSelector', - get: - (key) => - ({ get }) => { - const data = get(groupAnnouncementsAtom); - return data[key] || null; // Return the value for the key or null if not found - }, -}); - -export const groupPropertySelector = selectorFamily({ - key: 'groupPropertySelector', - get: - (key) => - ({ get }) => { - const data = get(groupsPropertiesAtom); - return data[key] || null; // Return the value for the key or null if not found - }, -}); - -export const mutedGroupsAtom = atom({ - key: 'mutedGroupsAtom', - default: [], -}); - -export const groupChatTimestampsAtom = atom({ - key: 'groupChatTimestampsAtom', - default: {}, -}); - -export const groupChatTimestampSelector = selectorFamily({ - key: 'groupChatTimestampSelector', - get: - (key) => - ({ get }) => { - const data = get(groupChatTimestampsAtom); - return data[key] || null; // Return the value for the key or null if not found - }, -}); - -export const timestampEnterDataAtom = atom({ - key: 'timestampEnterDataAtom', - default: {}, -}); - -export const timestampEnterDataSelector = selectorFamily({ - key: 'timestampEnterDataSelector', - get: - (key) => - ({ get }) => { - const data = get(timestampEnterDataAtom); - return data[key] || null; // Return the value for the key or null if not found - }, -}); +export const timestampEnterDataSelector = atomFamily((key) => + atom((get) => get(timestampEnterDataAtom)[key] || null) +); diff --git a/src/common/useFetchResources.tsx b/src/common/useFetchResources.tsx index 1a4cbd8..cc2ab41 100644 --- a/src/common/useFetchResources.tsx +++ b/src/common/useFetchResources.tsx @@ -1,168 +1,167 @@ import React, { useCallback, useRef } from 'react'; -import { useRecoilState } from 'recoil'; import { resourceDownloadControllerAtom } from '../atoms/global'; import { getBaseApiReact } from '../App'; +import { useSetAtom } from 'jotai'; export const useFetchResources = () => { - const [resources, setResources] = useRecoilState(resourceDownloadControllerAtom); + const setResources = useSetAtom(resourceDownloadControllerAtom); - const downloadResource = useCallback(({ service, name, identifier }, build) => { - setResources((prev) => ({ - ...prev, - [`${service}-${name}-${identifier}`]: { - ...(prev[`${service}-${name}-${identifier}`] || {}), - service, - name, - identifier, - }, - })); + const downloadResource = useCallback( + ({ service, name, identifier }, build) => { + setResources((prev) => ({ + ...prev, + [`${service}-${name}-${identifier}`]: { + ...(prev[`${service}-${name}-${identifier}`] || {}), + service, + name, + identifier, + }, + })); - try { - let isCalling = false; - let percentLoaded = 0; - let timer = 24; - let tries = 0; - let calledFirstTime = false - let intervalId - let timeoutId - const callFunction = async ()=> { - if (isCalling) return; - isCalling = true; + try { + let isCalling = false; + let percentLoaded = 0; + let timer = 24; + let tries = 0; + let calledFirstTime = false; + let intervalId; + let timeoutId; + const callFunction = async () => { + if (isCalling) return; + isCalling = true; - - - let res - - if(!build){ + let res; + + if (!build) { const urlFirstTime = `${getBaseApiReact()}/arbitrary/resource/status/${service}/${name}/${identifier}`; const resCall = await fetch(urlFirstTime, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - res = await resCall.json() - if(tries > 18 ){ - if(intervalId){ - clearInterval(intervalId) - } - if(timeoutId){ - clearTimeout(timeoutId) - } - setResources((prev) => ({ - ...prev, - [`${service}-${name}-${identifier}`]: { - ...(prev[`${service}-${name}-${identifier}`] || {}), - status: { - ...res, - status: 'FAILED_TO_DOWNLOAD', - }, - }, - })); - return - } - tries = tries + 1 - - } - - - if(build || (calledFirstTime === false && res?.status !== 'READY')){ - const url = `${getBaseApiReact()}/arbitrary/resource/properties/${service}/${name}/${identifier}?build=true`; - const resCall = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - res = await resCall.json(); - - } - calledFirstTime = true - isCalling = false; - - if (res.localChunkCount) { - if (res.percentLoaded) { - if (res.percentLoaded === percentLoaded && res.percentLoaded !== 100) { - timer = timer - 5; - } else { - timer = 24; - } - - if (timer < 0) { - timer = 24; - isCalling = true; - - // Update Recoil state for refetching + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + res = await resCall.json(); + if (tries > 18) { + if (intervalId) { + clearInterval(intervalId); + } + if (timeoutId) { + clearTimeout(timeoutId); + } setResources((prev) => ({ ...prev, [`${service}-${name}-${identifier}`]: { ...(prev[`${service}-${name}-${identifier}`] || {}), status: { ...res, - status: 'REFETCHING', + status: 'FAILED_TO_DOWNLOAD', }, }, })); - - timeoutId = setTimeout(() => { - isCalling = false; - downloadResource({ name, service, identifier }, true); - }, 25000); - return; } - - percentLoaded = res.percentLoaded; + tries = tries + 1; } - // Update Recoil state for progress - setResources((prev) => ({ - ...prev, - [`${service}-${name}-${identifier}`]: { - ...(prev[`${service}-${name}-${identifier}`] || {}), - status: res, - }, - })); - } - - // Check if progress is 100% and clear interval if true - if (res?.status === 'READY') { - if(intervalId){ - clearInterval(intervalId); - - } - if(timeoutId){ - clearTimeout(timeoutId) - } - // Update Recoil state for completion - setResources((prev) => ({ - ...prev, - [`${service}-${name}-${identifier}`]: { - ...(prev[`${service}-${name}-${identifier}`] || {}), - status: res, - }, - })); - } - if(res?.status === 'DOWNLOADED'){ - const url = `${getBaseApiReact()}/arbitrary/resource/status/${service}/${name}/${identifier}?build=true`; - const resCall = await fetch(url, { - method: "GET", + if (build || (calledFirstTime === false && res?.status !== 'READY')) { + const url = `${getBaseApiReact()}/arbitrary/resource/properties/${service}/${name}/${identifier}?build=true`; + const resCall = await fetch(url, { + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); - res = await resCall.json(); - } + res = await resCall.json(); + } + calledFirstTime = true; + isCalling = false; + + if (res.localChunkCount) { + if (res.percentLoaded) { + if ( + res.percentLoaded === percentLoaded && + res.percentLoaded !== 100 + ) { + timer = timer - 5; + } else { + timer = 24; + } + + if (timer < 0) { + timer = 24; + isCalling = true; + + // Update Recoil state for refetching + setResources((prev) => ({ + ...prev, + [`${service}-${name}-${identifier}`]: { + ...(prev[`${service}-${name}-${identifier}`] || {}), + status: { + ...res, + status: 'REFETCHING', + }, + }, + })); + + timeoutId = setTimeout(() => { + isCalling = false; + downloadResource({ name, service, identifier }, true); + }, 25000); + + return; + } + + percentLoaded = res.percentLoaded; + } + + // Update Recoil state for progress + setResources((prev) => ({ + ...prev, + [`${service}-${name}-${identifier}`]: { + ...(prev[`${service}-${name}-${identifier}`] || {}), + status: res, + }, + })); + } + + // Check if progress is 100% and clear interval if true + if (res?.status === 'READY') { + if (intervalId) { + clearInterval(intervalId); + } + if (timeoutId) { + clearTimeout(timeoutId); + } + // Update Recoil state for completion + setResources((prev) => ({ + ...prev, + [`${service}-${name}-${identifier}`]: { + ...(prev[`${service}-${name}-${identifier}`] || {}), + status: res, + }, + })); + } + if (res?.status === 'DOWNLOADED') { + const url = `${getBaseApiReact()}/arbitrary/resource/status/${service}/${name}/${identifier}?build=true`; + const resCall = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + res = await resCall.json(); + } + }; + callFunction(); + intervalId = setInterval(async () => { + callFunction(); + }, 5000); + } catch (error) { + console.error('Error during resource fetch:', error); } - callFunction() - intervalId = setInterval(async () => { - callFunction() - }, 5000); - - } catch (error) { - console.error('Error during resource fetch:', error); - } - }, [setResources]); + }, + [setResources] + ); return { downloadResource }; }; diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index 17f9b7e..6dc8c37 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -28,21 +28,21 @@ import { sortablePinnedAppsAtom, } from '../../atoms/global'; import { saveToLocalStorage } from './AppsNavBarDesktop'; -import { useRecoilState, useSetRecoilState } from 'recoil'; + +import { useAtom, useSetAtom } from 'jotai'; export const AppInfo = ({ app, myName }) => { const isInstalled = app?.status?.status === 'READY'; - const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState( + const [sortablePinnedApps, setSortablePinnedApps] = useAtom( sortablePinnedAppsAtom ); + const theme = useTheme(); const isSelectedAppPinned = !!sortablePinnedApps?.find( (item) => item?.name === app?.name && item?.service === app?.service ); - const setSettingsLocalLastUpdated = useSetRecoilState( - settingsLocalLastUpdatedAtom - ); + const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom); return ( { const isInstalled = app?.status?.status === 'READY'; - const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState( + const [sortablePinnedApps, setSortablePinnedApps] = useAtom( sortablePinnedAppsAtom ); + const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom); const isSelectedAppPinned = !!sortablePinnedApps?.find( (item) => item?.name === app?.name && item?.service === app?.service ); - const setSettingsLocalLastUpdated = useSetRecoilState( - settingsLocalLastUpdatedAtom - ); + const theme = useTheme(); return ( diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 723db86..e76467b 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -74,15 +74,18 @@ export const AppViewer = React.forwardRef( }, [app, path, isDevMode]); useEffect(() => { - if (!iframeRef?.current) return; - const targetOrigin = iframeRef.current - ? new URL(iframeRef.current.src).origin - : '*'; - // Send the navigation command after setting up the listener and timeout - iframeRef.current.contentWindow.postMessage( - { action: 'THEME_CHANGED', theme: themeMode, requestedHandler: 'UI' }, - targetOrigin - ); + const iframe = iframeRef?.current; + if (!iframe) return; + + try { + const targetOrigin = new URL(iframe.src).origin; + iframe.contentWindow?.postMessage( + { action: 'THEME_CHANGED', theme: themeMode, requestedHandler: 'UI' }, + targetOrigin + ); + } catch (err) { + console.error('Failed to send theme change to iframe:', err); + } }, [themeMode]); const removeTrailingSlash = (str) => str.replace(/\/$/, ''); diff --git a/src/components/Apps/AppsDesktop.tsx b/src/components/Apps/AppsDesktop.tsx index 1fc8f65..13bbf7d 100644 --- a/src/components/Apps/AppsDesktop.tsx +++ b/src/components/Apps/AppsDesktop.tsx @@ -20,11 +20,11 @@ import { HomeIcon } from '../../assets/Icons/HomeIcon'; import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; import { Save } from '../Save/Save'; import { IconWrapper } from '../Desktop/DesktopFooter'; -import { useRecoilState } from 'recoil'; import { enabledDevModeAtom } from '../../atoms/global'; import { AppsIcon } from '../../assets/Icons/AppsIcon'; import { CoreSyncStatus } from '../CoreSyncStatus'; import { MessagingIconFilled } from '../../assets/Icons/MessagingIconFilled'; +import { useAtom } from 'jotai'; const uid = new ShortUniqueId({ length: 8 }); @@ -47,8 +47,8 @@ export const AppsDesktop = ({ const [isNewTabWindow, setIsNewTabWindow] = useState(false); const [categories, setCategories] = useState([]); const iframeRefs = useRef({}); - const [isEnabledDevMode, setIsEnabledDevMode] = - useRecoilState(enabledDevModeAtom); + const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); + const { showTutorial } = useContext(GlobalContext); const theme = useTheme(); diff --git a/src/components/Apps/AppsDevModeNavBar.tsx b/src/components/Apps/AppsDevModeNavBar.tsx index bcc8811..1c0422a 100644 --- a/src/components/Apps/AppsDevModeNavBar.tsx +++ b/src/components/Apps/AppsDevModeNavBar.tsx @@ -13,16 +13,17 @@ import { unsubscribeFromEvent, } from '../../utils/events'; import RefreshIcon from '@mui/icons-material/Refresh'; -import { useRecoilState } from 'recoil'; import { navigationControllerAtom } from '../../atoms/global'; import { AppsDevModeTabComponent } from './AppsDevModeTabComponent'; +import { useAtom } from 'jotai'; export const AppsDevModeNavBar = () => { const [tabs, setTabs] = useState([]); const [selectedTab, setSelectedTab] = useState(null); - const [navigationController, setNavigationController] = useRecoilState( + const [navigationController, setNavigationController] = useAtom( navigationControllerAtom ); + const theme = useTheme(); const [isNewTabWindow, setIsNewTabWindow] = useState(false); const tabsRef = useRef(null); diff --git a/src/components/Apps/AppsNavBarDesktop.tsx b/src/components/Apps/AppsNavBarDesktop.tsx index a274abf..8d45604 100644 --- a/src/components/Apps/AppsNavBarDesktop.tsx +++ b/src/components/Apps/AppsNavBarDesktop.tsx @@ -26,12 +26,12 @@ import { import TabComponent from './TabComponent'; import PushPinIcon from '@mui/icons-material/PushPin'; import RefreshIcon from '@mui/icons-material/Refresh'; -import { useRecoilState, useSetRecoilState } from 'recoil'; import { navigationControllerAtom, settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom, } from '../../atoms/global'; +import { useAtom, useSetAtom } from 'jotai'; export function saveToLocalStorage(key, subKey, newValue) { try { @@ -67,22 +67,21 @@ export function saveToLocalStorage(key, subKey, newValue) { export const AppsNavBarDesktop = ({ disableBack }) => { const [tabs, setTabs] = useState([]); const [selectedTab, setSelectedTab] = useState(null); - const [navigationController, setNavigationController] = useRecoilState( + const [navigationController, setNavigationController] = useAtom( navigationControllerAtom ); + const [sortablePinnedApps, setSortablePinnedApps] = useAtom( + sortablePinnedAppsAtom + ); + const theme = useTheme(); const [isNewTabWindow, setIsNewTabWindow] = useState(false); const tabsRef = useRef(null); const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); - const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState( - sortablePinnedAppsAtom - ); - const setSettingsLocalLastUpdated = useSetRecoilState( - settingsLocalLastUpdatedAtom - ); + const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom); const handleClick = (event) => { setAnchorEl(event.currentTarget); diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index 4a39b63..78db4ad 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -15,7 +15,6 @@ import { } from '@mui/material'; import { useDropzone } from 'react-dropzone'; import { useHandlePrivateApps } from './useHandlePrivateApps'; -import { useRecoilState } from 'recoil'; import { groupsPropertiesAtom, myGroupsWhereIAmAdminAtom, @@ -35,6 +34,7 @@ import { MyContext } from '../../App'; import { fileToBase64 } from '../../utils/fileReading'; import { objectToBase64 } from '../../qdn/encryption/group-encryption'; import { getFee } from '../../background'; +import { useAtom } from 'jotai'; const maxFileSize = 50 * 1024 * 1024; // 50MB @@ -44,11 +44,10 @@ export const AppsPrivate = ({ myName }) => { const [logo, setLogo] = useState(null); const [qortalUrl, setQortalUrl] = useState(''); const [selectedGroup, setSelectedGroup] = useState(0); - const [groupsProperties] = useRecoilState(groupsPropertiesAtom); + const [valueTabPrivateApp, setValueTabPrivateApp] = useState(0); - const [myGroupsWhereIAmAdminFromGlobal] = useRecoilState( - myGroupsWhereIAmAdminAtom - ); + const [groupsProperties] = useAtom(groupsPropertiesAtom); + const [myGroupsWhereIAmAdminFromGlobal] = useAtom(myGroupsWhereIAmAdminAtom); const myGroupsWhereIAmAdmin = useMemo(() => { return myGroupsWhereIAmAdminFromGlobal?.filter( diff --git a/src/components/Apps/SortablePinnedApps.tsx b/src/components/Apps/SortablePinnedApps.tsx index f9a24a7..72e42df 100644 --- a/src/components/Apps/SortablePinnedApps.tsx +++ b/src/components/Apps/SortablePinnedApps.tsx @@ -22,11 +22,11 @@ import { settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom, } from '../../atoms/global'; -import { useRecoilState, useSetRecoilState } from 'recoil'; import { saveToLocalStorage } from './AppsNavBarDesktop'; import { ContextMenuPinnedApps } from '../ContextMenuPinnedApps'; import LockIcon from '@mui/icons-material/Lock'; import { useHandlePrivateApps } from './useHandlePrivateApps'; +import { useAtom, useSetAtom } from 'jotai'; const SortableItem = ({ id, name, app, isDesktop }) => { const { openApp } = useHandlePrivateApps(); @@ -137,10 +137,8 @@ export const SortablePinnedApps = ({ myApp, availableQapps = [], }) => { - const [pinnedApps, setPinnedApps] = useRecoilState(sortablePinnedAppsAtom); - const setSettingsLocalLastUpdated = useSetRecoilState( - settingsLocalLastUpdatedAtom - ); + const [pinnedApps, setPinnedApps] = useAtom(sortablePinnedAppsAtom); + const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom); const transformPinnedApps = useMemo(() => { // Clone the existing pinned apps list diff --git a/src/components/Apps/useHandlePrivateApps.tsx b/src/components/Apps/useHandlePrivateApps.tsx index 2eaa5f9..9a324bb 100644 --- a/src/components/Apps/useHandlePrivateApps.tsx +++ b/src/components/Apps/useHandlePrivateApps.tsx @@ -1,49 +1,44 @@ -import React, { useContext, useState } from "react"; -import { executeEvent } from "../../utils/events"; -import { getBaseApiReact, MyContext } from "../../App"; -import { createEndpoint } from "../../background"; -import { useRecoilState, useSetRecoilState } from "recoil"; +import React, { useContext, useState } from 'react'; +import { executeEvent } from '../../utils/events'; +import { getBaseApiReact, MyContext } from '../../App'; +import { createEndpoint } from '../../background'; import { settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom, -} from "../../atoms/global"; -import { saveToLocalStorage } from "./AppsNavBarDesktop"; -import { base64ToBlobUrl } from "../../utils/fileReading"; -import { base64ToUint8Array } from "../../qdn/encryption/group-encryption"; -import { uint8ArrayToObject } from "../../backgroundFunctions/encryption"; +} from '../../atoms/global'; +import { saveToLocalStorage } from './AppsNavBarDesktop'; +import { base64ToBlobUrl } from '../../utils/fileReading'; +import { base64ToUint8Array } from '../../qdn/encryption/group-encryption'; +import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; +import { useAtom, useSetAtom } from 'jotai'; export const useHandlePrivateApps = () => { - const [status, setStatus] = useState(""); + const [status, setStatus] = useState(''); const { openSnackGlobal, setOpenSnackGlobal, infoSnackCustom, setInfoSnackCustom, } = useContext(MyContext); - const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState( - sortablePinnedAppsAtom - ); - const setSettingsLocalLastUpdated = useSetRecoilState( - settingsLocalLastUpdatedAtom - ); + const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom); + + const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom); + const openApp = async ( privateAppProperties, addToPinnedApps, setLoadingStatePrivateApp ) => { try { - - - if(setLoadingStatePrivateApp){ + if (setLoadingStatePrivateApp) { setLoadingStatePrivateApp(`Downloading and decrypting private app.`); - } setOpenSnackGlobal(true); setInfoSnackCustom({ - type: "info", - message: "Fetching app data", - duration: null + type: 'info', + message: 'Fetching app data', + duration: null, }); const urlData = `${getBaseApiReact()}/arbitrary/${ privateAppProperties?.service @@ -53,32 +48,30 @@ export const useHandlePrivateApps = () => { let data; try { const responseData = await fetch(urlData, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); - if(!responseData?.ok){ - if(setLoadingStatePrivateApp){ - setLoadingStatePrivateApp("Error! Unable to download private app."); + if (!responseData?.ok) { + if (setLoadingStatePrivateApp) { + setLoadingStatePrivateApp('Error! Unable to download private app.'); } - - throw new Error("Unable to fetch app"); - } + + throw new Error('Unable to fetch app'); + } data = await responseData.text(); if (data?.error) { - if(setLoadingStatePrivateApp){ - - setLoadingStatePrivateApp("Error! Unable to download private app."); + if (setLoadingStatePrivateApp) { + setLoadingStatePrivateApp('Error! Unable to download private app.'); } - throw new Error("Unable to fetch app"); + throw new Error('Unable to fetch app'); } } catch (error) { - if(setLoadingStatePrivateApp){ - - setLoadingStatePrivateApp("Error! Unable to download private app."); + if (setLoadingStatePrivateApp) { + setLoadingStatePrivateApp('Error! Unable to download private app.'); } throw error; } @@ -87,7 +80,7 @@ export const useHandlePrivateApps = () => { // eslint-disable-next-line no-useless-catch try { decryptedData = await window.sendMessage( - "DECRYPT_QORTAL_GROUP_DATA", + 'DECRYPT_QORTAL_GROUP_DATA', { base64: data, @@ -95,16 +88,14 @@ export const useHandlePrivateApps = () => { } ); if (decryptedData?.error) { - if(setLoadingStatePrivateApp){ - - setLoadingStatePrivateApp("Error! Unable to decrypt private app."); + if (setLoadingStatePrivateApp) { + setLoadingStatePrivateApp('Error! Unable to decrypt private app.'); } throw new Error(decryptedData?.error); } } catch (error) { - if(setLoadingStatePrivateApp){ - - setLoadingStatePrivateApp("Error! Unable to decrypt private app."); + if (setLoadingStatePrivateApp) { + setLoadingStatePrivateApp('Error! Unable to decrypt private app.'); } throw error; } @@ -112,19 +103,19 @@ export const useHandlePrivateApps = () => { try { const convertToUint = base64ToUint8Array(decryptedData); const UintToObject = uint8ArrayToObject(convertToUint); - + if (decryptedData) { setInfoSnackCustom({ - type: "info", - message: "Building app", + type: 'info', + message: 'Building app', }); const endpoint = await createEndpoint( `/arbitrary/APP/${privateAppProperties?.name}/zip?preview=true` ); const response = await fetch(endpoint, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "text/plain", + 'Content-Type': 'text/plain', }, body: UintToObject?.app, }); @@ -135,7 +126,7 @@ export const useHandlePrivateApps = () => { ); const res = await fetch(checkIfPreviewLinkStillWorksUrl); if (res.ok) { - executeEvent("refreshApp", { + executeEvent('refreshApp', { tabId: tabId, }); } else { @@ -143,51 +134,50 @@ export const useHandlePrivateApps = () => { `/arbitrary/APP/${privateAppProperties?.name}/zip?preview=true` ); const response = await fetch(endpoint, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "text/plain", + 'Content-Type': 'text/plain', }, body: UintToObject?.app, }); const previewPath = await response.text(); - executeEvent("updateAppUrl", { + executeEvent('updateAppUrl', { tabId: tabId, url: await createEndpoint(previewPath), }); - + setTimeout(() => { - executeEvent("refreshApp", { + executeEvent('refreshApp', { tabId: tabId, }); }, 300); } }; - + const appName = UintToObject?.name; const logo = UintToObject?.logo ? `data:image/png;base64,${UintToObject?.logo}` : null; - + const dataBody = { url: await createEndpoint(previewPath), isPreview: true, isPrivate: true, privateAppProperties: { ...privateAppProperties, logo, appName }, - filePath: "", + filePath: '', refreshFunc: (tabId) => { refreshfunc(tabId, privateAppProperties); }, }; - executeEvent("addTab", { + executeEvent('addTab', { data: dataBody, }); setInfoSnackCustom({ - type: "success", - message: "Opened", + type: 'success', + message: 'Opened', }); - if(setLoadingStatePrivateApp){ - - setLoadingStatePrivateApp(``); + if (setLoadingStatePrivateApp) { + setLoadingStatePrivateApp(``); } if (addToPinnedApps) { setSortablePinnedApps((prev) => { @@ -203,10 +193,10 @@ export const useHandlePrivateApps = () => { }, }, ]; - + saveToLocalStorage( - "ext_saved_settings", - "sortablePinnedApps", + 'ext_saved_settings', + 'sortablePinnedApps', updatedApps ); return updatedApps; @@ -215,20 +205,19 @@ export const useHandlePrivateApps = () => { } } } catch (error) { - if(setLoadingStatePrivateApp){ - - setLoadingStatePrivateApp(`Error! ${error?.message || 'Unable to build private app.'}`); + if (setLoadingStatePrivateApp) { + setLoadingStatePrivateApp( + `Error! ${error?.message || 'Unable to build private app.'}` + ); } - throw error + throw error; } + } catch (error) { + setInfoSnackCustom({ + type: 'error', + message: error?.message || 'Unable to fetch app', + }); } - catch (error) { - setInfoSnackCustom({ - type: "error", - message: error?.message || "Unable to fetch app", - }); - } - }; return { openApp, diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index bef9a99..8770807 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -1,15 +1,12 @@ import { useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { executeEvent } from '../../utils/events'; -import { useSetRecoilState } from 'recoil'; import { navigationControllerAtom } from '../../atoms/global'; import { Filesystem, Directory, Encoding } from '@capacitor/filesystem'; import { saveFile } from '../../qortalRequests/get'; import { mimeToExtensionMap } from '../../utils/memeTypes'; import { MyContext } from '../../App'; import FileSaver from 'file-saver'; - - - +import { useSetAtom } from 'jotai'; export const saveFileInChunks = async ( blob: Blob, @@ -45,7 +42,6 @@ export const saveFileInChunks = async ( // Map MIME type to file extension const mimeTypeToExtension = (mimeType: string): string => { - return mimeToExtensionMap[mimeType] || existingExtension || ''; // Use existing extension if MIME type not found }; @@ -76,21 +72,18 @@ export const saveFileInChunks = async ( offset += chunkSize; isFirstChunk = false; } - } catch (error) { console.error('Error saving file in chunks:', error); } }; - - // Helper function to convert a Blob to a Base64 string const blobToBase64 = (blob: Blob): Promise => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => { - const base64data = reader.result?.toString().split(",")[1]; - resolve(base64data || ""); + const base64data = reader.result?.toString().split(',')[1]; + resolve(base64data || ''); }; reader.onerror = reject; reader.readAsDataURL(blob); @@ -98,81 +91,81 @@ const blobToBase64 = (blob: Blob): Promise => { }; class Semaphore { - constructor(count) { - this.count = count - this.waiting = [] - } - acquire() { - return new Promise(resolve => { - if (this.count > 0) { - this.count-- - resolve() - } else { - this.waiting.push(resolve) - } - }) - } - release() { - if (this.waiting.length > 0) { - const resolve = this.waiting.shift() - resolve() - } else { - this.count++ - } - } -} -let semaphore = new Semaphore(1) -let reader = new FileReader() - -const fileToBase64 = (file) => new Promise(async (resolve, reject) => { - if (!reader) { - reader = new FileReader() - } - await semaphore.acquire() - reader.readAsDataURL(file) - reader.onload = () => { - const dataUrl = reader.result - if (typeof dataUrl === "string") { - const base64String = dataUrl.split(',')[1] - reader.onload = null - reader.onerror = null - resolve(base64String) - } else { - reader.onload = null - reader.onerror = null - reject(new Error('Invalid data URL')) - } - semaphore.release() - } - reader.onerror = (error) => { - reader.onload = null - reader.onerror = null - reject(error) - semaphore.release() - } -}) - -export function openIndexedDB() { - return new Promise((resolve, reject) => { - const request = indexedDB.open("fileStorageDB", 1); - - request.onupgradeneeded = function (event) { - const db = event.target.result; - if (!db.objectStoreNames.contains("files")) { - db.createObjectStore("files", { keyPath: "id" }); - } - }; - - request.onsuccess = function (event) { - resolve(event.target.result); - }; - - request.onerror = function () { - reject("Error opening IndexedDB"); - }; + constructor(count) { + this.count = count; + this.waiting = []; + } + acquire() { + return new Promise((resolve) => { + if (this.count > 0) { + this.count--; + resolve(); + } else { + this.waiting.push(resolve); + } }); } + release() { + if (this.waiting.length > 0) { + const resolve = this.waiting.shift(); + resolve(); + } else { + this.count++; + } + } +} +let semaphore = new Semaphore(1); +let reader = new FileReader(); +const fileToBase64 = (file) => + new Promise(async (resolve, reject) => { + if (!reader) { + reader = new FileReader(); + } + await semaphore.acquire(); + reader.readAsDataURL(file); + reader.onload = () => { + const dataUrl = reader.result; + if (typeof dataUrl === 'string') { + const base64String = dataUrl.split(',')[1]; + reader.onload = null; + reader.onerror = null; + resolve(base64String); + } else { + reader.onload = null; + reader.onerror = null; + reject(new Error('Invalid data URL')); + } + semaphore.release(); + }; + reader.onerror = (error) => { + reader.onload = null; + reader.onerror = null; + reject(error); + semaphore.release(); + }; + }); + +export function openIndexedDB() { + return new Promise((resolve, reject) => { + const request = indexedDB.open('fileStorageDB', 1); + + request.onupgradeneeded = function (event) { + const db = event.target.result; + if (!db.objectStoreNames.contains('files')) { + db.createObjectStore('files', { keyPath: 'id' }); + } + }; + + request.onsuccess = function (event) { + resolve(event.target.result); + }; + + request.onerror = function () { + reject('Error opening IndexedDB'); + }; + }); +} export const listOfAllQortalRequests = [ 'GET_USER_ACCOUNT', @@ -209,7 +202,7 @@ export const listOfAllQortalRequests = [ 'DECRYPT_QORTAL_GROUP_DATA', 'DECRYPT_DATA_WITH_SHARING_KEY', 'DELETE_HOSTED_DATA', - 'GET_HOSTED_DATA', + 'GET_HOSTED_DATA', 'PUBLISH_MULTIPLE_QDN_RESOURCES', 'PUBLISH_QDN_RESOURCE', 'ENCRYPT_DATA', @@ -259,8 +252,8 @@ export const listOfAllQortalRequests = [ 'UPDATE_GROUP', 'SELL_NAME', 'CANCEL_SELL_NAME', - 'BUY_NAME' -] + 'BUY_NAME', +]; export const UIQortalRequests = [ 'GET_USER_ACCOUNT', @@ -319,316 +312,327 @@ export const UIQortalRequests = [ 'UPDATE_GROUP', 'SELL_NAME', 'CANCEL_SELL_NAME', - 'BUY_NAME' + 'BUY_NAME', ]; +async function retrieveFileFromIndexedDB(fileId) { + const db = await openIndexedDB(); + const transaction = db.transaction(['files'], 'readwrite'); + const objectStore = transaction.objectStore('files'); + return new Promise((resolve, reject) => { + const getRequest = objectStore.get(fileId); - - - async function retrieveFileFromIndexedDB(fileId) { + getRequest.onsuccess = function (event) { + if (getRequest.result) { + // File found, resolve it and delete from IndexedDB + const file = getRequest.result.data; + objectStore.delete(fileId); + resolve(file); + } else { + reject('File not found in IndexedDB'); + } + }; + + getRequest.onerror = function () { + reject('Error retrieving file from IndexedDB'); + }; + }); +} + +async function deleteQortalFilesFromIndexedDB() { + try { const db = await openIndexedDB(); - const transaction = db.transaction(["files"], "readwrite"); - const objectStore = transaction.objectStore("files"); - - return new Promise((resolve, reject) => { - const getRequest = objectStore.get(fileId); - - getRequest.onsuccess = function (event) { - if (getRequest.result) { - // File found, resolve it and delete from IndexedDB - const file = getRequest.result.data; - objectStore.delete(fileId); - resolve(file); - } else { - reject("File not found in IndexedDB"); + const transaction = db.transaction(['files'], 'readwrite'); + const objectStore = transaction.objectStore('files'); + + // Create a request to get all keys + const getAllKeysRequest = objectStore.getAllKeys(); + + getAllKeysRequest.onsuccess = function (event) { + const keys = event.target.result; + + // Iterate through keys to find and delete those containing '_qortalfile' + for (let key of keys) { + if (key.includes('_qortalfile')) { + const deleteRequest = objectStore.delete(key); + + deleteRequest.onsuccess = function () { + console.log( + `File with key '${key}' has been deleted from IndexedDB` + ); + }; + + deleteRequest.onerror = function () { + console.error( + `Failed to delete file with key '${key}' from IndexedDB` + ); + }; } - }; - - getRequest.onerror = function () { - reject("Error retrieving file from IndexedDB"); - }; - }); - } - - async function deleteQortalFilesFromIndexedDB() { - try { - const db = await openIndexedDB(); - const transaction = db.transaction(["files"], "readwrite"); - const objectStore = transaction.objectStore("files"); - - // Create a request to get all keys - const getAllKeysRequest = objectStore.getAllKeys(); - - getAllKeysRequest.onsuccess = function (event) { - const keys = event.target.result; - - // Iterate through keys to find and delete those containing '_qortalfile' - for (let key of keys) { - if (key.includes("_qortalfile")) { - const deleteRequest = objectStore.delete(key); - - deleteRequest.onsuccess = function () { - console.log(`File with key '${key}' has been deleted from IndexedDB`); - }; - - deleteRequest.onerror = function () { - console.error(`Failed to delete file with key '${key}' from IndexedDB`); - }; - } - } - }; - - getAllKeysRequest.onerror = function () { - console.error("Failed to retrieve keys from IndexedDB"); - }; - - transaction.oncomplete = function () { - console.log("Transaction complete for deleting files from IndexedDB"); - }; - - transaction.onerror = function () { - console.error("Error occurred during transaction for deleting files"); - }; - } catch (error) { - console.error("Error opening IndexedDB:", error); - } + } + }; + + getAllKeysRequest.onerror = function () { + console.error('Failed to retrieve keys from IndexedDB'); + }; + + transaction.oncomplete = function () { + console.log('Transaction complete for deleting files from IndexedDB'); + }; + + transaction.onerror = function () { + console.error('Error occurred during transaction for deleting files'); + }; + } catch (error) { + console.error('Error opening IndexedDB:', error); } +} +export const showSaveFilePicker = async ( + data, + { openSnackGlobal, setOpenSnackGlobal, infoSnackCustom, setInfoSnackCustom } +) => { + try { + const { filename, mimeType, blob, fileHandleOptions } = data; - - - export const showSaveFilePicker = async (data, {openSnackGlobal, - setOpenSnackGlobal, - infoSnackCustom, - setInfoSnackCustom}) => { - - - try { - const { filename, mimeType, blob, fileHandleOptions } = data; - - setInfoSnackCustom({ - type: "info", - message: - "Saving file...", - }); - - - setOpenSnackGlobal(true); - - - - FileSaver.saveAs(blob, filename) - - setInfoSnackCustom({ - type: "success", - message: - "Saving file success!", + setInfoSnackCustom({ + type: 'info', + message: 'Saving file...', }); - setOpenSnackGlobal(true); - } catch (error) { - setInfoSnackCustom({ - type: "error", - message: - error?.message ? `Error saving file: ${error?.message}` : 'Error saving file', - }); - - - setOpenSnackGlobal(true); - console.error("Error saving file:", error); - + + FileSaver.saveAs(blob, filename); + + setInfoSnackCustom({ + type: 'success', + message: 'Saving file success!', + }); + + setOpenSnackGlobal(true); + } catch (error) { + setInfoSnackCustom({ + type: 'error', + message: error?.message + ? `Error saving file: ${error?.message}` + : 'Error saving file', + }); + + setOpenSnackGlobal(true); + console.error('Error saving file:', error); + } +}; + +declare var cordova: any; + +async function storeFilesInIndexedDB(obj) { + // First delete any existing files in IndexedDB with '_qortalfile' in their ID + await deleteQortalFilesFromIndexedDB(); + + // Open the IndexedDB + const db = await openIndexedDB(); + const transaction = db.transaction(['files'], 'readwrite'); + const objectStore = transaction.objectStore('files'); + + // Handle the obj.file if it exists and is a File instance + if (obj.file) { + const fileId = Date.now() + 'objFile_qortalfile'; + + // Store the file in IndexedDB + const fileData = { + id: fileId, + data: obj.file, + }; + objectStore.put(fileData); + + // Replace the file object with the file ID in the original object + obj.fileId = fileId; + delete obj.file; + } + if (obj.blob) { + const fileId = Date.now() + 'objFile_qortalfile'; + + // Store the file in IndexedDB + const fileData = { + id: fileId, + data: obj.blob, + }; + objectStore.put(fileData); + + // Replace the file object with the file ID in the original object + let blobObj = { + type: obj.blob?.type, + }; + obj.fileId = fileId; + delete obj.blob; + obj.blob = blobObj; + } + + // Iterate through resources to find files and save them to IndexedDB + for (let resource of obj?.resources || []) { + if (resource.file) { + const fileId = resource.identifier + Date.now() + '_qortalfile'; + + // Store the file in IndexedDB + const fileData = { + id: fileId, + data: resource.file, + }; + objectStore.put(fileData); + + // Replace the file object with the file ID in the original object + resource.fileId = fileId; + delete resource.file; } + } + + // Set transaction completion handlers + transaction.oncomplete = function () { + console.log('Files saved successfully to IndexedDB'); }; - declare var cordova: any; + transaction.onerror = function () { + console.error('Error saving files to IndexedDB'); + }; + return obj; // Updated object with references to stored files +} - - async function storeFilesInIndexedDB(obj) { - // First delete any existing files in IndexedDB with '_qortalfile' in their ID - await deleteQortalFilesFromIndexedDB(); - - // Open the IndexedDB - const db = await openIndexedDB(); - const transaction = db.transaction(["files"], "readwrite"); - const objectStore = transaction.objectStore("files"); - - // Handle the obj.file if it exists and is a File instance - if (obj.file) { - const fileId = Date.now() + "objFile_qortalfile"; - - // Store the file in IndexedDB - const fileData = { - id: fileId, - data: obj.file, - }; - objectStore.put(fileData); - - // Replace the file object with the file ID in the original object - obj.fileId = fileId; - delete obj.file; - } - if (obj.blob) { - const fileId = Date.now() + "objFile_qortalfile"; - - // Store the file in IndexedDB - const fileData = { - id: fileId, - data: obj.blob, - }; - objectStore.put(fileData); - - // Replace the file object with the file ID in the original object - let blobObj = { - type: obj.blob?.type - } - obj.fileId = fileId; - delete obj.blob; - obj.blob = blobObj - } - - // Iterate through resources to find files and save them to IndexedDB - for (let resource of (obj?.resources || [])) { - if (resource.file) { - const fileId = resource.identifier + Date.now() + "_qortalfile"; - - // Store the file in IndexedDB - const fileData = { - id: fileId, - data: resource.file, - }; - objectStore.put(fileData); - - // Replace the file object with the file ID in the original object - resource.fileId = fileId; - delete resource.file; - } - } - - // Set transaction completion handlers - transaction.oncomplete = function () { - console.log("Files saved successfully to IndexedDB"); - }; - - transaction.onerror = function () { - console.error("Error saving files to IndexedDB"); - }; - - return obj; // Updated object with references to stored files - } - -export const useQortalMessageListener = (frameWindow, iframeRef, tabId, isDevMode, appName, appService, skipAuth) => { - const [path, setPath] = useState('') +export const useQortalMessageListener = ( + frameWindow, + iframeRef, + tabId, + isDevMode, + appName, + appService, + skipAuth +) => { + const [path, setPath] = useState(''); const [history, setHistory] = useState({ customQDNHistoryPaths: [], -currentIndex: -1, -isDOMContentLoaded: false - }) - const setHasSettingsChangedAtom = useSetRecoilState(navigationControllerAtom); - const { openSnackGlobal, + currentIndex: -1, + isDOMContentLoaded: false, + }); + const setHasSettingsChangedAtom = useSetAtom(navigationControllerAtom); + + const { + openSnackGlobal, setOpenSnackGlobal, infoSnackCustom, - setInfoSnackCustom } = useContext(MyContext); + setInfoSnackCustom, + } = useContext(MyContext); - - - useEffect(()=> { - if(tabId && !isNaN(history?.currentIndex)){ - setHasSettingsChangedAtom((prev)=> { + useEffect(() => { + if (tabId && !isNaN(history?.currentIndex)) { + setHasSettingsChangedAtom((prev) => { return { ...prev, [tabId]: { hasBack: history?.currentIndex > 0, - } - } - }) + }, + }; + }); } - }, [history?.currentIndex, tabId]) + }, [history?.currentIndex, tabId]); - - const changeCurrentIndex = useCallback((value)=> { - setHistory((prev)=> { + const changeCurrentIndex = useCallback((value) => { + setHistory((prev) => { return { ...prev, - currentIndex: value - } - }) - }, []) + currentIndex: value, + }; + }); + }, []); - const resetHistory = useCallback(()=> { + const resetHistory = useCallback(() => { setHistory({ customQDNHistoryPaths: [], - currentIndex: -1, - isManualNavigation: true, - isDOMContentLoaded: false - }) - }, []) + currentIndex: -1, + isManualNavigation: true, + isDOMContentLoaded: false, + }); + }, []); useEffect(() => { - const listener = async (event) => { - - if (event?.data?.requestedHandler !== 'UI') return; const sendMessageToRuntime = (message, eventPort) => { - window.sendMessage(message.action, message.payload, 300000, message.isExtension, { - name: appName, service: appService - }, skipAuth) - .then((response) => { - if (response.error) { - eventPort.postMessage({ - result: null, - error: { - error: response?.error, - message: typeof response?.error === 'string' ? response?.error : typeof response?.message === 'string' ? response?.message : 'An error has occurred' - }, - }); - } else { - eventPort.postMessage({ - result: response, - error: null, - }); - } - }) - .catch((error) => { - console.error("Failed qortalRequest", error); - }); - + window + .sendMessage( + message.action, + message.payload, + 300000, + message.isExtension, + { + name: appName, + service: appService, + }, + skipAuth + ) + .then((response) => { + if (response.error) { + eventPort.postMessage({ + result: null, + error: { + error: response?.error, + message: + typeof response?.error === 'string' + ? response?.error + : typeof response?.message === 'string' + ? response?.message + : 'An error has occurred', + }, + }); + } else { + eventPort.postMessage({ + result: response, + error: null, + }); + } + }) + .catch((error) => { + console.error('Failed qortalRequest', error); + }); }; // Check if action is included in the predefined list of UI requests if (UIQortalRequests.includes(event.data.action)) { sendMessageToRuntime( - { action: event.data.action, type: 'qortalRequest', payload: event.data, isExtension: true }, + { + action: event.data.action, + type: 'qortalRequest', + payload: event.data, + isExtension: true, + }, event.ports[0] ); - } else if(event?.data?.action === 'SAVE_FILE' - ){ + } else if (event?.data?.action === 'SAVE_FILE') { try { - const res = await saveFile( event.data, null, true, { - openSnackGlobal, - setOpenSnackGlobal, - infoSnackCustom, - setInfoSnackCustom + const res = await saveFile(event.data, null, true, { + openSnackGlobal, + setOpenSnackGlobal, + infoSnackCustom, + setInfoSnackCustom, }); - - } catch (error) { - - } + } catch (error) {} } else if ( event?.data?.action === 'PUBLISH_MULTIPLE_QDN_RESOURCES' || event?.data?.action === 'PUBLISH_QDN_RESOURCE' || - event?.data?.action === 'ENCRYPT_DATA' || event?.data?.action === 'ENCRYPT_DATA_WITH_SHARING_KEY' || event?.data?.action === 'ENCRYPT_QORTAL_GROUP_DATA' - + event?.data?.action === 'ENCRYPT_DATA' || + event?.data?.action === 'ENCRYPT_DATA_WITH_SHARING_KEY' || + event?.data?.action === 'ENCRYPT_QORTAL_GROUP_DATA' ) { const data = event.data; if (data) { sendMessageToRuntime( - { action: event.data.action, type: 'qortalRequest', payload: data, isExtension: true }, + { + action: event.data.action, + type: 'qortalRequest', + payload: data, + isExtension: true, + }, event.ports[0] ); } else { @@ -637,52 +641,73 @@ isDOMContentLoaded: false error: 'Failed to prepare data for publishing', }); } - } else if(event?.data?.action === 'LINK_TO_QDN_RESOURCE' || - event?.data?.action === 'QDN_RESOURCE_DISPLAYED'){ - const pathUrl = event?.data?.path != null ? (event?.data?.path.startsWith('/') ? '' : '/') + event?.data?.path : null - setPath(pathUrl) - if(appName?.toLowerCase() === 'q-mail'){ - window.sendMessage("addEnteredQmailTimestamp").catch((error) => { + } else if ( + event?.data?.action === 'LINK_TO_QDN_RESOURCE' || + event?.data?.action === 'QDN_RESOURCE_DISPLAYED' + ) { + const pathUrl = + event?.data?.path != null + ? (event?.data?.path.startsWith('/') ? '' : '/') + event?.data?.path + : null; + setPath(pathUrl); + if (appName?.toLowerCase() === 'q-mail') { + window.sendMessage('addEnteredQmailTimestamp').catch((error) => { // error }); - } else if(appName?.toLowerCase() === 'q-wallets'){ - executeEvent('setLastEnteredTimestampPaymentEvent', {}) + } else if (appName?.toLowerCase() === 'q-wallets') { + executeEvent('setLastEnteredTimestampPaymentEvent', {}); } - } else if(event?.data?.action === 'NAVIGATION_HISTORY'){ - if(event?.data?.payload?.isDOMContentLoaded){ - setHistory((prev)=> { - const copyPrev = {...prev} - if((copyPrev?.customQDNHistoryPaths || []).at(-1) === (event?.data?.payload?.customQDNHistoryPaths || []).at(-1)) { + } else if (event?.data?.action === 'NAVIGATION_HISTORY') { + if (event?.data?.payload?.isDOMContentLoaded) { + setHistory((prev) => { + const copyPrev = { ...prev }; + if ( + (copyPrev?.customQDNHistoryPaths || []).at(-1) === + (event?.data?.payload?.customQDNHistoryPaths || []).at(-1) + ) { return { ...prev, - currentIndex: prev.customQDNHistoryPaths.length - 1 === -1 ? 0 : prev.customQDNHistoryPaths.length - 1 - } + currentIndex: + prev.customQDNHistoryPaths.length - 1 === -1 + ? 0 + : prev.customQDNHistoryPaths.length - 1, + }; } - const copyHistory = {...prev} - const paths = [...(copyHistory?.customQDNHistoryPaths.slice(0, copyHistory.currentIndex + 1) || []), ...(event?.data?.payload?.customQDNHistoryPaths || [])] + const copyHistory = { ...prev }; + const paths = [ + ...(copyHistory?.customQDNHistoryPaths.slice( + 0, + copyHistory.currentIndex + 1 + ) || []), + ...(event?.data?.payload?.customQDNHistoryPaths || []), + ]; return { ...prev, customQDNHistoryPaths: paths, - currentIndex: paths.length - 1 - } - }) + currentIndex: paths.length - 1, + }; + }); } else { - setHistory(event?.data?.payload) - + setHistory(event?.data?.payload); } - } else if(event?.data?.action === 'SET_TAB' && !isDevMode){ - executeEvent("addTab", { - data: event?.data?.payload - }) - const targetOrigin = iframeRef.current ? new URL(iframeRef.current.src).origin : "*"; + } else if (event?.data?.action === 'SET_TAB' && !isDevMode) { + executeEvent('addTab', { + data: event?.data?.payload, + }); + const targetOrigin = iframeRef.current + ? new URL(iframeRef.current.src).origin + : '*'; iframeRef.current.contentWindow.postMessage( - { action: 'SET_TAB_SUCCESS', requestedHandler: 'UI',payload: { - name: event?.data?.payload?.name - } }, targetOrigin + { + action: 'SET_TAB_SUCCESS', + requestedHandler: 'UI', + payload: { + name: event?.data?.payload?.name, + }, + }, + targetOrigin ); - } - - + } }; // Add the listener for messages coming from the frameWindow @@ -692,12 +717,7 @@ isDOMContentLoaded: false return () => { frameWindow.removeEventListener('message', listener); }; - - }, [isDevMode, appName, appService]); // Empty dependency array to run once when the component mounts - - - return {path, history, resetHistory, changeCurrentIndex} + return { path, history, resetHistory, changeCurrentIndex }; }; - diff --git a/src/components/Chat/ChatContainer.tsx b/src/components/Chat/ChatContainer.tsx deleted file mode 100644 index 2823b83..0000000 --- a/src/components/Chat/ChatContainer.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { - MainContainer, - ChatContainer, - MessageList, - Message, - MessageInput, - Avatar, -} from '@chatscope/chat-ui-kit-react'; -import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css'; - -export const ChatContainerComp = ({ messages }) => { - // const [messages, setMessages] = useState([ - // { id: 1, text: "Hello! How are you?", sender: "Joe"}, - // { id: 2, text: "I'm good, thank you!", sender: "Me" } - // ]); - - // const loadMoreMessages = () => { - // // Simulate loading more messages (you could fetch these from an API) - // const moreMessages = [ - // { id: 3, text: "What about you?", sender: "Joe", direction: "incoming" }, - // { id: 4, text: "I'm great, thanks!", sender: "Me", direction: "outgoing" } - // ]; - // setMessages((prevMessages) => [...moreMessages, ...prevMessages]); - // }; - - return ( -
    - - - - {messages.map((msg) => ( - - {msg.direction === 'incoming' && ( - - )} - - ))} - - - - - -
    - ); -}; diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index a3973e1..1b6b256 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -8,7 +8,6 @@ import React, { } from 'react'; import { ChatList } from './ChatList'; -import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css'; import Tiptap from './TipTap'; import { CustomButton } from '../../styles/App-styles'; import CircularProgress from '@mui/material/CircularProgress'; diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 154427d..0dd626e 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -12,7 +12,6 @@ import { objectToBase64, } from '../../qdn/encryption/group-encryption'; import { ChatList } from './ChatList'; -import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css'; import Tiptap from './TipTap'; import { CustomButton } from '../../styles/App-styles'; import CircularProgress from '@mui/material/CircularProgress'; @@ -1238,8 +1237,6 @@ export const ChatGroup = ({ )} - {/* */} - { const theme = useTheme(); - const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useRecoilState( + const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useAtom( isDisabledEditorEnterAtom ); + const extensionsFiltered = isChat ? extensions.filter((item) => item?.name !== 'image') : extensions; diff --git a/src/components/ContextMenu.tsx b/src/components/ContextMenu.tsx index 0306e57..3d1ede6 100644 --- a/src/components/ContextMenu.tsx +++ b/src/components/ContextMenu.tsx @@ -10,8 +10,8 @@ import { import MailOutlineIcon from '@mui/icons-material/MailOutline'; import NotificationsOffIcon from '@mui/icons-material/NotificationsOff'; import { executeEvent } from '../utils/events'; -import { useRecoilState } from 'recoil'; import { mutedGroupsAtom } from '../atoms/global'; +import { useAtom } from 'jotai'; const CustomStyledMenu = styled(Menu)(({ theme }) => ({ '& .MuiPaper-root': { @@ -35,7 +35,8 @@ export const ContextMenu = ({ children, groupId, getUserSettings }) => { const longPressTimeout = useRef(null); const preventClick = useRef(false); // Flag to prevent click after long-press or right-click const theme = useTheme(); - const [mutedGroups] = useRecoilState(mutedGroupsAtom); + const [mutedGroups] = useAtom(mutedGroupsAtom); + const isMuted = useMemo(() => { return mutedGroups.includes(groupId); }, [mutedGroups, groupId]); diff --git a/src/components/ContextMenuPinnedApps.tsx b/src/components/ContextMenuPinnedApps.tsx index 3e2c2be..b7cea32 100644 --- a/src/components/ContextMenuPinnedApps.tsx +++ b/src/components/ContextMenuPinnedApps.tsx @@ -9,8 +9,8 @@ import { } from '@mui/material'; import PushPinIcon from '@mui/icons-material/PushPin'; import { saveToLocalStorage } from './Apps/AppsNavBarDesktop'; -import { useRecoilState } from 'recoil'; import { sortablePinnedAppsAtom } from '../atoms/global'; +import { useSetAtom } from 'jotai'; const CustomStyledMenu = styled(Menu)(({ theme }) => ({ '& .MuiPaper-root': { @@ -34,9 +34,9 @@ export const ContextMenuPinnedApps = ({ children, app, isMine }) => { const maxHoldTimeout = useRef(null); const preventClick = useRef(false); const startTouchPosition = useRef({ x: 0, y: 0 }); // Track initial touch position - const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState( - sortablePinnedAppsAtom - ); + + const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom); + const theme = useTheme(); const handleContextMenu = (event) => { diff --git a/src/components/Desktop/DesktopFooter.tsx b/src/components/Desktop/DesktopFooter.tsx index 3bef9cc..d85c8a0 100644 --- a/src/components/Desktop/DesktopFooter.tsx +++ b/src/components/Desktop/DesktopFooter.tsx @@ -6,8 +6,9 @@ import AppIcon from '../../assets/svgs/AppIcon.svg'; import { HomeIcon } from '../../assets/Icons/HomeIcon'; import { Save } from '../Save/Save'; -import { useRecoilState } from 'recoil'; + import { enabledDevModeAtom } from '../../atoms/global'; +import { useAtom } from 'jotai'; export const IconWrapper = ({ children, @@ -65,8 +66,7 @@ export const DesktopFooter = ({ setIsOpenSideViewDirects, setIsOpenSideViewGroups, }) => { - const [isEnabledDevMode, setIsEnabledDevMode] = - useRecoilState(enabledDevModeAtom); + const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); const theme = useTheme(); diff --git a/src/components/DesktopSideBar.tsx b/src/components/DesktopSideBar.tsx index 616a128..e2774a7 100644 --- a/src/components/DesktopSideBar.tsx +++ b/src/components/DesktopSideBar.tsx @@ -2,13 +2,13 @@ import { Box, ButtonBase, useTheme } from '@mui/material'; import { HomeIcon } from '../assets/Icons/HomeIcon'; import { Save } from './Save/Save'; import { IconWrapper } from './Desktop/DesktopFooter'; -import { useRecoilState } from 'recoil'; import { enabledDevModeAtom } from '../atoms/global'; import { AppsIcon } from '../assets/Icons/AppsIcon'; import ThemeSelector from './Theme/ThemeSelector'; import { CoreSyncStatus } from './CoreSyncStatus'; import LanguageSelector from './Language/LanguageSelector'; import { MessagingIconFilled } from '../assets/Icons/MessagingIconFilled'; +import { useAtom } from 'jotai'; export const DesktopSideBar = ({ goToHome, @@ -24,13 +24,10 @@ export const DesktopSideBar = ({ desktopViewMode, myName, }) => { - const [isEnabledDevMode, setIsEnabledDevMode] = - useRecoilState(enabledDevModeAtom); + const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); const theme = useTheme(); - console.log('test', desktopViewMode === 'home'); - return ( { const pollName = name; @@ -63,8 +63,9 @@ export const Embed = ({ embedLink }) => { const [external, setExternal] = useState(null); const [imageUrl, setImageUrl] = useState(''); const [parsedData, setParsedData] = useState(null); - const setBlobs = useSetRecoilState(blobControllerAtom); - const [selectedGroupId] = useRecoilState(selectedGroupIdAtom); + const setBlobs = useSetAtom(blobControllerAtom); + const [selectedGroupId] = useAtom(selectedGroupIdAtom); + const resourceData = useMemo(() => { const parsedDataOnTheFly = parseQortalLink(embedLink); if ( @@ -98,7 +99,8 @@ export const Embed = ({ embedLink }) => { return undefined; } }, [resourceData]); - const blobUrl = useRecoilValue(blobKeySelector(keyIdentifier)); + + const blobUrl = useAtomValue(blobKeySelector(keyIdentifier)); const handlePoll = async (parsedData) => { try { @@ -312,7 +314,7 @@ export const Embed = ({ embedLink }) => { hasFetched.current = true; }, [embedLink]); - const resourceDetails = useRecoilValue(resourceKeySelector(keyIdentifier)); + const resourceDetails = useAtomValue(resourceKeySelector(keyIdentifier)); const { parsedType, encryptionType } = useMemo(() => { let parsedType; diff --git a/src/components/Embeds/VideoPlayer.tsx b/src/components/Embeds/VideoPlayer.tsx index 59bfc76..26e3e08 100644 --- a/src/components/Embeds/VideoPlayer.tsx +++ b/src/components/Embeds/VideoPlayer.tsx @@ -19,7 +19,8 @@ import { Menu, MenuItem } from '@mui/material'; import { MoreVert as MoreIcon } from '@mui/icons-material'; import { GlobalContext, getBaseApiReact } from '../../App'; import { resourceKeySelector } from '../../atoms/global'; -import { useRecoilValue } from 'recoil'; + +import { useAtomValue } from 'jotai'; const VideoContainer = styled(Box)` position: relative; display: flex; @@ -80,7 +81,9 @@ export const VideoPlayer: React.FC = ({ return undefined; } }, [service, name, identifier]); - const download = useRecoilValue(resourceKeySelector(keyIdentifier)); + + const download = useAtomValue(resourceKeySelector(keyIdentifier)); + const { downloadResource } = useContext(GlobalContext); const videoRef = useRef(null); diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index b2d58e7..17837fc 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -21,15 +21,16 @@ import { import { validateAddress } from '../../utils/validateAddress'; import { getNameInfo, requestQueueMemberNames } from './Group'; import { useModal } from '../../common/useModal'; -import { useRecoilState } from 'recoil'; import { isOpenBlockedModalAtom } from '../../atoms/global'; import InfoIcon from '@mui/icons-material/Info'; +import { useAtom } from 'jotai'; export const BlockedUsersModal = () => { const theme = useTheme(); - const [isOpenBlockedModal, setIsOpenBlockedModal] = useRecoilState( + const [isOpenBlockedModal, setIsOpenBlockedModal] = useAtom( isOpenBlockedModalAtom ); + const [hasChanged, setHasChanged] = useState(false); const [value, setValue] = useState(''); const [addressesWithNames, setAddressesWithNames] = useState({}); diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index 74ce7f4..94be106 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -516,12 +516,7 @@ export const NewThread = ({ overrideMobile customEditorHeight="240px" /> - {/* { - setValue(val); - }} - /> */} + diff --git a/src/components/Group/Forum/TextEditor.tsx b/src/components/Group/Forum/TextEditor.tsx deleted file mode 100644 index ac26275..0000000 --- a/src/components/Group/Forum/TextEditor.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import ReactQuill, { Quill } from 'react-quill'; -import 'react-quill/dist/quill.snow.css'; -import ImageResize from 'quill-image-resize-module-react'; -import './texteditor.css'; - -Quill.register('modules/imageResize', ImageResize); - -const modules = { - imageResize: { - parchment: Quill.import('parchment'), - modules: ['Resize', 'DisplaySize'], - }, - toolbar: [ - ['bold', 'italic', 'underline', 'strike'], // styled text - ['blockquote', 'code-block'], // blocks - [{ header: 1 }, { header: 2 }], // custom button values - [{ list: 'ordered' }, { list: 'bullet' }], // lists - [{ script: 'sub' }, { script: 'super' }], // superscript/subscript - [{ indent: '-1' }, { indent: '+1' }], // outdent/indent - [{ direction: 'rtl' }], // text direction - [{ size: ['small', false, 'large', 'huge'] }], // custom dropdown - [{ header: [1, 2, 3, 4, 5, 6, false] }], // custom button values - [{ color: [] }, { background: [] }], // dropdown with defaults - [{ font: [] }], // font family - [{ align: [] }], // text align - ['clean'], // remove formatting - // ["image"], // image - ], -}; - -export const TextEditor = ({ inlineContent, setInlineContent }: any) => { - return ( - - ); -}; diff --git a/src/components/Group/Forum/texteditor.css b/src/components/Group/Forum/texteditor.css deleted file mode 100644 index ac6d2ed..0000000 --- a/src/components/Group/Forum/texteditor.css +++ /dev/null @@ -1,70 +0,0 @@ -.ql-editor { - min-height: 200px; - width: 100%; - color: black; - font-size: 16px; - font-family: Roboto; - max-height: 225px; - overflow-y: scroll; - padding: 0px !important; -} - -.ql-editor::-webkit-scrollbar-track { - background-color: transparent; - cursor: default; -} -.ql-editor::-webkit-scrollbar-track:hover { - background-color: transparent; -} - -.ql-editor::-webkit-scrollbar { - width: 16px; - height: 10px; - background-color: rgba(229, 229, 229, 0.7); -} - -.ql-editor::-webkit-scrollbar-thumb { - background-color: #b0b0b0; - border-radius: 8px; - background-clip: content-box; - border: 4px solid transparent; -} - -.ql-editor img { - cursor: default; -} - -.ql-editor-display { - min-height: 20px; - width: 100%; - color: black; - font-size: 16px; - font-family: Roboto; - padding: 0px !important; -} - -.ql-editor-display img { - cursor: default; -} - -.ql-container { - font-size: 16px; -} - -.ql-toolbar .ql-stroke { - fill: none !important; - stroke: black !important; -} - -.ql-toolbar .ql-fill { - fill: black !important; - stroke: none !important; -} - -.ql-toolbar .ql-picker { - color: black !important; -} - -.ql-toolbar .ql-picker-options { - background-color: white !important; -} diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index f92c3a1..e16404a 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -65,7 +65,7 @@ import { HubsIcon } from '../../assets/Icons/HubsIcon'; import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; import { formatEmailDate } from './QMailMessages'; import { AdminSpace } from '../Chat/AdminSpace'; -import { useRecoilState, useSetRecoilState } from 'recoil'; + import { addressInfoControllerAtom, groupAnnouncementsAtom, @@ -85,6 +85,7 @@ import { BlockedUsersModal } from './BlockedUsersModal'; import { WalletsAppWrapper } from './WalletsAppWrapper'; import { useTranslation } from 'react-i18next'; import { GroupList } from './GroupList'; +import { useAtom, useSetAtom } from 'jotai'; export const getPublishesFromAdmins = async (admins: string[], groupId) => { const queryString = admins.map((name) => `name=${name}`).join('&'); @@ -415,9 +416,10 @@ export const Group = ({ const { setMemberGroups, rootHeight, isRunningPublicNode } = useContext(MyContext); const lastGroupNotification = useRef(null); - const [timestampEnterData, setTimestampEnterData] = useRecoilState( + const [timestampEnterData, setTimestampEnterData] = useAtom( timestampEnterDataAtom ); + const [chatMode, setChatMode] = useState('groups'); const [newChat, setNewChat] = useState(false); const [openSnack, setOpenSnack] = React.useState(false); @@ -428,18 +430,19 @@ export const Group = ({ const [firstSecretKeyInCreation, setFirstSecretKeyInCreation] = React.useState(false); const [groupSection, setGroupSection] = React.useState('home'); - const [groupAnnouncements, setGroupAnnouncements] = useRecoilState( + const [groupAnnouncements, setGroupAnnouncements] = useAtom( groupAnnouncementsAtom ); const [defaultThread, setDefaultThread] = React.useState(null); const [isOpenDrawer, setIsOpenDrawer] = React.useState(false); - const setIsOpenBlockedUserModal = useSetRecoilState(isOpenBlockedModalAtom); + const setIsOpenBlockedUserModal = useSetAtom(isOpenBlockedModalAtom); const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false); const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState(''); const [drawerMode, setDrawerMode] = React.useState('groups'); - const setMutedGroups = useSetRecoilState(mutedGroupsAtom); + const setMutedGroups = useSetAtom(mutedGroupsAtom); + const [mobileViewMode, setMobileViewMode] = useState('home'); const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState(''); const isFocusedRef = useRef(true); @@ -453,9 +456,10 @@ export const Group = ({ const settimeoutForRefetchSecretKey = useRef(null); const { clearStatesMessageQueueProvider } = useMessageQueue(); const initiatedGetMembers = useRef(false); - const [groupChatTimestamps, setGroupChatTimestamps] = useRecoilState( + const [groupChatTimestamps, setGroupChatTimestamps] = useAtom( groupChatTimestampsAtom ); + const [appsMode, setAppsMode] = useState('home'); const [appsModeDev, setAppsModeDev] = useState('home'); const [isOpenSideViewDirects, setIsOpenSideViewDirects] = useState(false); @@ -465,12 +469,10 @@ export const Group = ({ const groupsOwnerNamesRef = useRef({}); const { t } = useTranslation(['core', 'group']); - const [groupsProperties, setGroupsProperties] = - useRecoilState(groupsPropertiesAtom); - const [groupsOwnerNames, setGroupsOwnerNames] = - useRecoilState(groupsOwnerNamesAtom); + const [groupsProperties, setGroupsProperties] = useAtom(groupsPropertiesAtom); + const setGroupsOwnerNames = useSetAtom(groupsOwnerNamesAtom); - const setUserInfoForLevels = useSetRecoilState(addressInfoControllerAtom); + const setUserInfoForLevels = useSetAtom(addressInfoControllerAtom); const isPrivate = useMemo(() => { if (selectedGroup?.groupId === '0') return false; @@ -481,7 +483,8 @@ export const Group = ({ return null; }, [selectedGroup]); - const setSelectedGroupId = useSetRecoilState(selectedGroupIdAtom); + const setSelectedGroupId = useSetAtom(selectedGroupIdAtom); + const toggleSideViewDirects = () => { if (isOpenSideViewGroups) { setIsOpenSideViewGroups(false); diff --git a/src/components/Group/GroupJoinRequests.tsx b/src/components/Group/GroupJoinRequests.tsx index 2bf0b26..32252da 100644 --- a/src/components/Group/GroupJoinRequests.tsx +++ b/src/components/Group/GroupJoinRequests.tsx @@ -11,10 +11,10 @@ import { Box, ButtonBase, Collapse, Typography, useTheme } from '@mui/material'; import { CustomLoader } from '../../common/CustomLoader'; import { MyContext, getBaseApiReact } from '../../App'; import { myGroupsWhereIAmAdminAtom } from '../../atoms/global'; -import { useSetRecoilState } from 'recoil'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import { useTranslation } from 'react-i18next'; +import { useSetAtom } from 'jotai'; export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2); export const GroupJoinRequests = ({ @@ -34,7 +34,8 @@ export const GroupJoinRequests = ({ ); const [loading, setLoading] = React.useState(true); const { txList, setTxList } = React.useContext(MyContext); - const setMyGroupsWhereIAmAdmin = useSetRecoilState(myGroupsWhereIAmAdminAtom); + const setMyGroupsWhereIAmAdmin = useSetAtom(myGroupsWhereIAmAdminAtom); + const theme = useTheme(); const getJoinRequests = async () => { try { diff --git a/src/components/Group/GroupList.tsx b/src/components/Group/GroupList.tsx index 896ed4d..47f464c 100644 --- a/src/components/Group/GroupList.tsx +++ b/src/components/Group/GroupList.tsx @@ -28,8 +28,9 @@ import { groupsOwnerNamesSelector, timestampEnterDataSelector, } from '../../atoms/global'; -import { useRecoilValue } from 'recoil'; + import { timeDifferenceForNotificationChats } from './Group'; +import { useAtomValue } from 'jotai'; export const GroupList = ({ selectGroupFunc, @@ -211,17 +212,18 @@ export const GroupList = ({ const GroupItem = React.memo( ({ selectGroupFunc, group, selectedGroup, getUserSettings, myAddress }) => { const theme = useTheme(); - const ownerName = useRecoilValue(groupsOwnerNamesSelector(group?.groupId)); - const announcement = useRecoilValue( + const ownerName = useAtomValue(groupsOwnerNamesSelector(group?.groupId)); + const announcement = useAtomValue( groupAnnouncementSelector(group?.groupId) ); - const groupProperty = useRecoilValue(groupPropertySelector(group?.groupId)); - const groupChatTimestamp = useRecoilValue( + const groupProperty = useAtomValue(groupPropertySelector(group?.groupId)); + const groupChatTimestamp = useAtomValue( groupChatTimestampSelector(group?.groupId) ); - const timestampEnterData = useRecoilValue( + const timestampEnterData = useAtomValue( timestampEnterDataSelector(group?.groupId) ); + const selectGroupHandler = useCallback(() => { selectGroupFunc(group); }, [group, selectGroupFunc]); diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index 4c0ef85..ae275b9 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -34,7 +34,7 @@ import { import { Spacer } from '../../common/Spacer'; import { CustomLoader } from '../../common/CustomLoader'; import { RequestQueueWithPromise } from '../../utils/queue/queue'; -import { useRecoilState } from 'recoil'; + import { myGroupsWhereIAmAdminAtom, promotionTimeIntervalAtom, @@ -49,6 +49,7 @@ import ErrorBoundary from '../../common/ErrorBoundary'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import { getFee } from '../../background'; +import { useAtom } from 'jotai'; export const requestQueuePromos = new RequestQueueWithPromise(3); export function utf8ToBase64(inputString: string): string { @@ -77,13 +78,14 @@ export const ListOfGroupPromotions = () => { const [loading, setLoading] = useState(false); const [isShowModal, setIsShowModal] = useState(false); const [text, setText] = useState(''); - const [myGroupsWhereIAmAdmin, setMyGroupsWhereIAmAdmin] = useRecoilState( + const [myGroupsWhereIAmAdmin, setMyGroupsWhereIAmAdmin] = useAtom( myGroupsWhereIAmAdminAtom ); - const [promotions, setPromotions] = useRecoilState(promotionsAtom); - const [promotionTimeInterval, setPromotionTimeInterval] = useRecoilState( + const [promotions, setPromotions] = useAtom(promotionsAtom); + const [promotionTimeInterval, setPromotionTimeInterval] = useAtom( promotionTimeIntervalAtom ); + const [isExpanded, setIsExpanded] = React.useState(false); const [openSnack, setOpenSnack] = useState(false); diff --git a/src/components/Group/QMailMessages.tsx b/src/components/Group/QMailMessages.tsx index 624d371..72a23d5 100644 --- a/src/components/Group/QMailMessages.tsx +++ b/src/components/Group/QMailMessages.tsx @@ -11,11 +11,12 @@ import MailIcon from '@mui/icons-material/Mail'; import MailOutlineIcon from '@mui/icons-material/MailOutline'; import { executeEvent } from '../../utils/events'; import { CustomLoader } from '../../common/CustomLoader'; -import { useRecoilState } from 'recoil'; + import { mailsAtom, qMailLastEnteredTimestampAtom } from '../../atoms/global'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import MarkEmailUnreadIcon from '@mui/icons-material/MarkEmailUnread'; +import { useAtom } from 'jotai'; export const isLessThanOneWeekOld = (timestamp) => { // Current time in milliseconds @@ -46,10 +47,11 @@ export function formatEmailDate(timestamp: number) { export const QMailMessages = ({ userName, userAddress }) => { const [isExpanded, setIsExpanded] = useState(false); - const [mails, setMails] = useRecoilState(mailsAtom); - const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState( + const [mails, setMails] = useAtom(mailsAtom); + const [lastEnteredTimestamp, setLastEnteredTimestamp] = useAtom( qMailLastEnteredTimestampAtom ); + const [loading, setLoading] = useState(true); const theme = useTheme(); diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index 3ac9a2e..0279d2b 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -17,8 +17,9 @@ import Slide from '@mui/material/Slide'; import { TransitionProps } from '@mui/material/transitions'; import { Box, FormControlLabel, Switch, styled, useTheme } from '@mui/material'; import { enabledDevModeAtom } from '../../atoms/global'; -import { useRecoilState } from 'recoil'; + import ThemeManager from '../Theme/ThemeManager'; +import { useAtom } from 'jotai'; const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ padding: 8, @@ -64,8 +65,8 @@ const Transition = forwardRef(function Transition( export const Settings = ({ address, open, setOpen }) => { const [checked, setChecked] = useState(false); - const [isEnabledDevMode, setIsEnabledDevMode] = - useRecoilState(enabledDevModeAtom); + const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); + const theme = useTheme(); const handleChange = (event: ChangeEvent) => { diff --git a/src/components/Group/WalletsAppWrapper.tsx b/src/components/Group/WalletsAppWrapper.tsx index 82e6660..7e44eb6 100644 --- a/src/components/Group/WalletsAppWrapper.tsx +++ b/src/components/Group/WalletsAppWrapper.tsx @@ -7,18 +7,19 @@ import { subscribeToEvent, unsubscribeFromEvent, } from '../../utils/events'; -import { useRecoilState } from 'recoil'; import { navigationControllerAtom } from '../../atoms/global'; import { AppsNavBarLeft, AppsNavBarParent } from '../Apps/Apps-styles'; import { NavBack } from '../../assets/Icons/NavBack.tsx'; import RefreshIcon from '@mui/icons-material/Refresh'; +import { useAtom } from 'jotai'; export const WalletsAppWrapper = () => { const iframeRef = useRef(null); const [isOpen, setIsOpen] = useState(false); - const [navigationController, setNavigationController] = useRecoilState( + const [navigationController, setNavigationController] = useAtom( navigationControllerAtom ); + const [selectedTab, setSelectedTab] = useState({ tabId: '5558589', name: 'Q-Wallets', diff --git a/src/components/QMailStatus.tsx b/src/components/QMailStatus.tsx index c665145..deac6db 100644 --- a/src/components/QMailStatus.tsx +++ b/src/components/QMailStatus.tsx @@ -1,20 +1,20 @@ import { useMemo } from 'react'; -import { useRecoilState } from 'recoil'; import { mailsAtom, qMailLastEnteredTimestampAtom } from '../atoms/global'; import { isLessThanOneWeekOld } from './Group/QMailMessages'; import { ButtonBase, Tooltip, useTheme } from '@mui/material'; import { executeEvent } from '../utils/events'; import { Mail } from '@mui/icons-material'; import { useTranslation } from 'react-i18next'; +import { useAtom } from 'jotai'; export const QMailStatus = () => { const { t } = useTranslation(['core']); const theme = useTheme(); - const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState( + const [lastEnteredTimestamp, setLastEnteredTimestamp] = useAtom( qMailLastEnteredTimestampAtom ); - const [mails, setMails] = useRecoilState(mailsAtom); + const [mails, setMails] = useAtom(mailsAtom); const hasNewMail = useMemo(() => { if (mails?.length === 0) return false; diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index a19efd2..d67780a 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -1,5 +1,4 @@ import { useContext, useEffect, useMemo, useState } from 'react'; -import { useRecoilState, useSetRecoilState } from 'recoil'; import isEqual from 'lodash/isEqual'; // TODO Import deep comparison utility import { canSaveSettingToQdnAtom, @@ -34,6 +33,7 @@ import { uint8ArrayToObject, } from '../../backgroundFunctions/encryption'; import { useTranslation } from 'react-i18next'; +import { useAtom, useSetAtom } from 'jotai'; export const handleImportClick = async () => { const fileInput = document.createElement('input'); @@ -66,22 +66,21 @@ export const handleImportClick = async () => { }; export const Save = ({ isDesktop, disableWidth, myName }) => { - const [pinnedApps, setPinnedApps] = useRecoilState(sortablePinnedAppsAtom); - const [settingsQdnLastUpdated, setSettingsQdnLastUpdated] = useRecoilState( + const [pinnedApps, setPinnedApps] = useAtom(sortablePinnedAppsAtom); + const [settingsQdnLastUpdated, setSettingsQdnLastUpdated] = useAtom( settingsQDNLastUpdatedAtom ); - const [settingsLocalLastUpdated] = useRecoilState( - settingsLocalLastUpdatedAtom + const [settingsLocalLastUpdated] = useAtom(settingsLocalLastUpdatedAtom); + const setHasSettingsChangedAtom = useSetAtom(hasSettingsChangedAtom); + const [isUsingImportExportSettings, setIsUsingImportExportSettings] = useAtom( + isUsingImportExportSettingsAtom ); - const setHasSettingsChangedAtom = useSetRecoilState(hasSettingsChangedAtom); - const [isUsingImportExportSettings, setIsUsingImportExportSettings] = - useRecoilState(isUsingImportExportSettingsAtom); - const [canSave] = useRecoilState(canSaveSettingToQdnAtom); const [openSnack, setOpenSnack] = useState(false); const [isLoading, setIsLoading] = useState(false); const [infoSnack, setInfoSnack] = useState(null); - const [oldPinnedApps, setOldPinnedApps] = useRecoilState(oldPinnedAppsAtom); + const [oldPinnedApps, setOldPinnedApps] = useAtom(oldPinnedAppsAtom); + const [anchorEl, setAnchorEl] = useState(null); const { show } = useContext(MyContext); const theme = useTheme(); diff --git a/src/hooks/useHandlePaymentNotification.tsx b/src/hooks/useHandlePaymentNotification.tsx index cc6e4e5..9ca9ff5 100644 --- a/src/hooks/useHandlePaymentNotification.tsx +++ b/src/hooks/useHandlePaymentNotification.tsx @@ -2,9 +2,9 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { getBaseApiReact } from '../App'; import { getData, storeData } from '../utils/chromeStorage'; import { checkDifference, getNameInfoForOthers } from '../background'; -import { useRecoilState } from 'recoil'; import { lastPaymentSeenTimestampAtom } from '../atoms/global'; import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events'; +import { useAtom } from 'jotai'; export const useHandlePaymentNotification = (address) => { const [latestTx, setLatestTx] = useState(null); @@ -12,8 +12,9 @@ export const useHandlePaymentNotification = (address) => { const nameAddressOfSender = useRef({}); const isFetchingName = useRef({}); - const [lastEnteredTimestampPayment, setLastEnteredTimestampPayment] = - useRecoilState(lastPaymentSeenTimestampAtom); + const [lastEnteredTimestampPayment, setLastEnteredTimestampPayment] = useAtom( + lastPaymentSeenTimestampAtom + ); useEffect(() => { if (lastEnteredTimestampPayment && address) { diff --git a/src/main.tsx b/src/main.tsx index 3d64137..0a35316 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,21 +1,18 @@ -import ReactDOM from 'react-dom/client'; +import { createRoot } from 'react-dom/client'; import App from './App.tsx'; import '../src/styles/index.css'; import './messaging/messagesToBackground'; import { MessageQueueProvider } from './MessageQueueContext.tsx'; -import { RecoilRoot } from 'recoil'; import { ThemeProvider } from './components/Theme/ThemeContext.tsx'; import { CssBaseline } from '@mui/material'; import '../i18n'; -ReactDOM.createRoot(document.getElementById('root')!).render( +createRoot(document.getElementById('root')!).render( <> - - - + diff --git a/src/useQortalGetSaveSettings.tsx b/src/useQortalGetSaveSettings.tsx index c74f1a2..66691b7 100644 --- a/src/useQortalGetSaveSettings.tsx +++ b/src/useQortalGetSaveSettings.tsx @@ -1,5 +1,4 @@ import { useCallback, useEffect } from 'react'; -import { useRecoilState, useSetRecoilState } from 'recoil'; import { canSaveSettingToQdnAtom, isUsingImportExportSettingsAtom, @@ -14,6 +13,7 @@ import { base64ToUint8Array, uint8ArrayToObject, } from './backgroundFunctions/encryption'; +import { useAtom, useSetAtom } from 'jotai'; function fetchFromLocalStorage(key) { try { @@ -67,19 +67,15 @@ const getPublish = async (myName) => { }; export const useQortalGetSaveSettings = (myName, isAuthenticated) => { - const setSortablePinnedApps = useSetRecoilState(sortablePinnedAppsAtom); - const setCanSave = useSetRecoilState(canSaveSettingToQdnAtom); - const setSettingsQDNLastUpdated = useSetRecoilState( - settingsQDNLastUpdatedAtom - ); - const [settingsLocalLastUpdated] = useRecoilState( - settingsLocalLastUpdatedAtom - ); - const [isUsingImportExportSettings] = useRecoilState( + const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom); + const setCanSave = useSetAtom(canSaveSettingToQdnAtom); + const setSettingsQDNLastUpdated = useSetAtom(settingsQDNLastUpdatedAtom); + + const [settingsLocalLastUpdated] = useAtom(settingsLocalLastUpdatedAtom); + const [isUsingImportExportSettings] = useAtom( isUsingImportExportSettingsAtom ); - - const [oldPinnedApps, setOldPinnedApps] = useRecoilState(oldPinnedAppsAtom); + const setOldPinnedApps = useSetAtom(oldPinnedAppsAtom); const getSavedSettings = useCallback( async (myName, settingsLocalLastUpdated) => { diff --git a/src/useRetrieveDataLocalStorage.tsx b/src/useRetrieveDataLocalStorage.tsx index 41c2b8e..6ddc673 100644 --- a/src/useRetrieveDataLocalStorage.tsx +++ b/src/useRetrieveDataLocalStorage.tsx @@ -1,5 +1,4 @@ import { useCallback, useEffect } from 'react'; -import { useSetRecoilState } from 'recoil'; import { isUsingImportExportSettingsAtom, oldPinnedAppsAtom, @@ -7,6 +6,7 @@ import { settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom, } from './atoms/global'; +import { useSetAtom } from 'jotai'; function fetchFromLocalStorage(key) { try { @@ -22,21 +22,13 @@ function fetchFromLocalStorage(key) { } export const useRetrieveDataLocalStorage = (address) => { - const setSortablePinnedApps = useSetRecoilState(sortablePinnedAppsAtom); - - const setSettingsLocalLastUpdated = useSetRecoilState( - settingsLocalLastUpdatedAtom - ); - - const setIsUsingImportExportSettings = useSetRecoilState( + const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom); + const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom); + const setIsUsingImportExportSettings = useSetAtom( isUsingImportExportSettingsAtom ); - - const setSettingsQDNLastUpdated = useSetRecoilState( - settingsQDNLastUpdatedAtom - ); - - const setOldPinnedApps = useSetRecoilState(oldPinnedAppsAtom); + const setSettingsQDNLastUpdated = useSetAtom(settingsQDNLastUpdatedAtom); + const setOldPinnedApps = useSetAtom(oldPinnedAppsAtom); const getSortablePinnedApps = useCallback(() => { const pinnedAppsLocal = fetchFromLocalStorage('ext_saved_settings'); From f591d523e94f14c5430b018183a8ab1cb41430df Mon Sep 17 00:00:00 2001 From: PhilReact Date: Tue, 29 Apr 2025 22:44:45 +0300 Subject: [PATCH 292/717] fix styling --- src/App.tsx | 4 ++++ src/assets/Icons/Return.tsx | 2 +- src/components/Auth/DownloadWallet.tsx | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 74f5dee..6c55ff4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2063,6 +2063,7 @@ function App() { style={{ cursor: 'pointer', height: '24px', + width: 'auto', }} onClick={returnToMain} /> @@ -2560,6 +2561,7 @@ function App() { style={{ cursor: 'pointer', height: '24px', + width: 'auto', }} onClick={() => { setRawWallet(null); @@ -2593,6 +2595,7 @@ function App() { style={{ cursor: 'pointer', height: '24px', + width: 'auto', }} onClick={() => { setRawWallet(null); @@ -2722,6 +2725,7 @@ function App() { style={{ cursor: 'pointer', height: '24px', + width: 'auto', }} onClick={() => { if (creationStep === 2) { diff --git a/src/assets/Icons/Return.tsx b/src/assets/Icons/Return.tsx index 81a6690..5506a57 100644 --- a/src/assets/Icons/Return.tsx +++ b/src/assets/Icons/Return.tsx @@ -8,9 +8,9 @@ export const Return: React.FC = ({ color, opacity, ...children }) => { return ( From 2604f52aa136e7444dbae37cbfa3f1088b92908e Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 30 Apr 2025 03:04:21 +0300 Subject: [PATCH 293/717] optimize global states --- src/App.tsx | 199 ++++++++---------- src/ExtStates/NotAuthenticated.tsx | 4 +- src/Wallets.tsx | 4 +- src/atoms/global.ts | 4 + src/background.ts | 6 +- src/common/useFetchResources.tsx | 2 +- src/common/useModal.tsx | 98 ++++----- src/components/Apps/AppViewer.tsx | 5 +- src/components/Apps/AppViewerContainer.tsx | 2 - src/components/Apps/AppsCategoryDesktop.tsx | 4 +- src/components/Apps/AppsDesktop.tsx | 4 +- src/components/Apps/AppsPrivate.tsx | 5 +- src/components/Chat/AdminSpace.tsx | 1 - src/components/Chat/AdminSpaceInner.tsx | 2 +- src/components/Chat/CreateCommonSecret.tsx | 5 +- src/components/Chat/GroupAnnouncements.tsx | 2 +- src/components/Chat/GroupForum.tsx | 4 +- src/components/Embeds/VideoPlayer.tsx | 4 +- .../GlobalActions/GlobalActions.tsx | 12 +- src/components/GlobalActions/JoinGroup.tsx | 8 +- src/components/Group/AddGroup.tsx | 6 +- src/components/Group/AddGroupList.tsx | 8 +- src/components/Group/Group.tsx | 10 +- src/components/Group/GroupJoinRequests.tsx | 9 +- src/components/Group/GroupList.tsx | 6 +- .../Group/ListOfGroupPromotions.tsx | 7 +- src/components/Group/ListOfJoinRequests.tsx | 8 +- src/components/Group/ManageMembers.tsx | 5 +- src/components/Group/UserListOfInvites.tsx | 6 +- src/components/Group/useBlockUsers.tsx | 17 +- src/components/Group/useHandleUserInfo.tsx | 4 +- src/components/Minting/Minting.tsx | 14 +- src/components/RegisterName.tsx | 5 +- src/components/TaskManager/TaskManager.tsx | 11 +- src/components/Tutorials/Tutorials.tsx | 4 +- .../Tutorials/useHandleTutorials.tsx | 25 ++- src/components/WrapperUserAction.tsx | 5 +- src/utils/mobile/mobileUtils.ts | 5 - 38 files changed, 263 insertions(+), 267 deletions(-) delete mode 100644 src/utils/mobile/mobileUtils.ts diff --git a/src/App.tsx b/src/App.tsx index 6c55ff4..558cd01 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -104,9 +104,11 @@ import { groupsPropertiesAtom, hasSettingsChangedAtom, isDisabledEditorEnterAtom, + isRunningPublicNodeAtom, isUsingImportExportSettingsAtom, lastPaymentSeenTimestampAtom, mailsAtom, + memberGroupsAtom, mutedGroupsAtom, oldPinnedAppsAtom, qMailLastEnteredTimestampAtom, @@ -114,6 +116,7 @@ import { settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom, timestampEnterDataAtom, + txListAtom, } from './atoms/global'; import { NotAuthenticated } from './ExtStates/NotAuthenticated'; import { handleGetFileFromIndexedDB } from './utils/indexedDB'; @@ -161,10 +164,6 @@ type extStates = | 'group'; interface MyContextInterface { - txList: any[]; - memberGroups: any[]; - setTxList: (val) => void; - setMemberGroups: (val) => void; isShow: boolean; onCancel: () => void; onOk: () => void; @@ -173,10 +172,6 @@ interface MyContextInterface { } const defaultValues: MyContextInterface = { - txList: [], - memberGroups: [], - setTxList: () => {}, - setMemberGroups: () => {}, isShow: false, onCancel: () => {}, onOk: () => {}, @@ -245,9 +240,6 @@ const defaultValuesGlobal = { export const MyContext = createContext(defaultValues); -export const GlobalContext = - createContext(defaultValuesGlobal); - export let globalApiKey: string | null = null; export const getBaseApiReact = (customApi?: string) => { @@ -327,15 +319,13 @@ function App() { useState(''); const [walletToBeDecryptedError, setWalletToBeDecryptedError] = useState(''); - const [txList, setTxList] = useState([]); - const [memberGroups, setMemberGroups] = useState([]); const [isFocused, setIsFocused] = useState(true); const [hasSettingsChanged, setHasSettingsChanged] = useAtom( hasSettingsChangedAtom ); const balanceSetIntervalRef = useRef(null); - const { downloadResource } = useFetchResources(); + const downloadResource = useFetchResources(); const holdRefExtState = useRef('not-authenticated'); const isFocusedRef = useRef(true); const { @@ -375,8 +365,7 @@ function App() { isShow: isShowQortalRequestExtension, message: messageQortalRequestExtension, } = useModal(); - - const [isRunningPublicNode, setIsRunningPublicNode] = useState(false); + const setIsRunningPublicNode = useSetAtom(isRunningPublicNodeAtom); const [infoSnack, setInfoSnack] = useState(null); const [openSnack, setOpenSnack] = useState(false); @@ -386,7 +375,6 @@ function App() { const [apiKey, setApiKey] = useState(''); const [isOpenSendQort, setIsOpenSendQort] = useState(false); const [isOpenSendQortSuccess, setIsOpenSendQortSuccess] = useState(false); - const [rootHeight, setRootHeight] = useState('100%'); const { isUserBlocked, addToBlockList, @@ -402,7 +390,7 @@ function App() { const [isSettingsOpen, setIsSettingsOpen] = useState(false); const [showSeed, setShowSeed] = useState(false); const [creationStep, setCreationStep] = useState(1); - const { getIndividualUserInfo } = useHandleUserInfo(); + const getIndividualUserInfo = useHandleUserInfo(); const qortalRequestCheckbox1Ref = useRef(null); useRetrieveDataLocalStorage(userInfo?.address); useQortalGetSaveSettings(userInfo?.name, extState === 'authenticated'); @@ -482,6 +470,8 @@ function App() { const resetMutedGroupsAtom = useResetAtom(mutedGroupsAtom); const resetGroupChatTimestampsAtom = useResetAtom(groupChatTimestampsAtom); const resetTimestampEnterAtom = useResetAtom(timestampEnterDataAtom); + const resettxListAtomAtom = useResetAtom(txListAtom); + const resetmemberGroupsAtomAtom = useResetAtom(memberGroupsAtom); const resetAllRecoil = () => { resetAtomSortablePinnedAppsAtom(); @@ -499,8 +489,59 @@ function App() { resetMutedGroupsAtom(); resetGroupChatTimestampsAtom(); resetTimestampEnterAtom(); + resettxListAtomAtom(); + resetmemberGroupsAtomAtom(); }; + const contextValue = useMemo( + () => ({ + isShow, + onCancel, + onOk, + show, + userInfo, + message, + showInfo, + openSnackGlobal: openSnack, + setOpenSnackGlobal: setOpenSnack, + infoSnackCustom: infoSnack, + setInfoSnackCustom: setInfoSnack, + downloadResource, + getIndividualUserInfo, + isUserBlocked, + addToBlockList, + removeBlockFromList, + getAllBlockedUsers, + showTutorial, + openTutorialModal, + setOpenTutorialModal, + hasSeenGettingStarted, + }), + [ + isShow, + onCancel, + onOk, + show, + userInfo, + message, + showInfo, + openSnack, + setOpenSnack, + infoSnack, + setInfoSnack, + downloadResource, + getIndividualUserInfo, + isUserBlocked, + addToBlockList, + removeBlockFromList, + getAllBlockedUsers, + showTutorial, + openTutorialModal, + setOpenTutorialModal, + hasSeenGettingStarted, + ] + ); + const handleSetGlobalApikey = (key) => { globalApiKey = key; }; @@ -985,7 +1026,7 @@ function App() { } }; - const logoutFunc = async () => { + const logoutFunc = useCallback(async () => { try { if (hasSettingsChanged) { await showUnsavedChanges({ @@ -1014,7 +1055,7 @@ function App() { } catch (error) { console.log(error); } - }; + }, [hasSettingsChanged, extState]); const returnToMain = () => { setPaymentTo(''); @@ -1059,8 +1100,6 @@ function App() { setWalletToBeDownloadedError(''); setSendqortState(null); setHasLocalNode(false); - setTxList([]); - setMemberGroups([]); resetAllRecoil(); if (balanceSetIntervalRef?.current) { clearInterval(balanceSetIntervalRef?.current); @@ -1758,36 +1797,10 @@ function App() { }} > {extState === 'authenticated' && isMainWindow && ( - + <> - - + + )} @@ -1955,15 +1968,7 @@ function App() { }} > - + {extState === 'not-authenticated' && ( logout )} */} {extState === 'authenticated' && isMainWindow && ( - - - - {renderProfile()} - - + + {renderProfile()} + )} {isOpenSendQort && isMainWindow && ( - + {extState === 'create-wallet' && walletToBeDownloaded && ( { @@ -3636,11 +3612,8 @@ function App() { {isOpenMinting && ( )} diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 04f068e..6d580fe 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -27,11 +27,11 @@ import Logo1Dark from '../assets/svgs/Logo1Dark.svg'; import HelpIcon from '@mui/icons-material/Help'; import { CustomizedSnackbars } from '../components/Snackbar/Snackbar'; import { cleanUrl, gateways } from '../background'; -import { GlobalContext } from '../App'; import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip'; import ThemeSelector from '../components/Theme/ThemeSelector'; import { useTranslation } from 'react-i18next'; import LanguageSelector from '../components/Language/LanguageSelector'; +import { MyContext } from '../App'; const manifestData = { version: '0.5.3', @@ -85,7 +85,7 @@ export const NotAuthenticated = ({ const [enteredApiKey, setEnteredApiKey] = useState(''); const [customNodeToSaveIndex, setCustomNodeToSaveIndex] = React.useState(null); - const { showTutorial, hasSeenGettingStarted } = useContext(GlobalContext); + const { showTutorial, hasSeenGettingStarted } = useContext(MyContext); const theme = useTheme(); const { t } = useTranslation(['auth', 'core']); diff --git a/src/Wallets.tsx b/src/Wallets.tsx index 1f218da..281e07e 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -31,7 +31,7 @@ import { crypto } from './constants/decryptWallet'; import { LoadingButton } from '@mui/lab'; import { PasswordField } from './components'; import { HtmlTooltip } from './ExtStates/NotAuthenticated'; -import { GlobalContext } from './App'; +import { MyContext } from './App'; const parsefilenameQortal = (filename) => { return filename.startsWith('qortal_backup_') ? filename.slice(14) : filename; @@ -43,7 +43,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { const [seedValue, setSeedValue] = useState(''); const [seedName, setSeedName] = useState(''); const [seedError, setSeedError] = useState(''); - const { hasSeenGettingStarted } = useContext(GlobalContext); + const { hasSeenGettingStarted } = useContext(MyContext); const [password, setPassword] = useState(''); const [isOpenSeedModal, setIsOpenSeedModal] = useState(false); diff --git a/src/atoms/global.ts b/src/atoms/global.ts index 8f4c6d5..e0b4156 100644 --- a/src/atoms/global.ts +++ b/src/atoms/global.ts @@ -46,6 +46,10 @@ export const mutedGroupsAtom = atomWithReset([]); export const groupChatTimestampsAtom = atomWithReset({}); export const timestampEnterDataAtom = atomWithReset({}); +export const txListAtom = atomWithReset([]); +export const memberGroupsAtom = atomWithReset([]); +export const isRunningPublicNodeAtom = atomWithReset(false); + // Atom Families (replacing selectorFamily) export const resourceKeySelector = atomFamily((key) => atom((get) => get(resourceDownloadControllerAtom)[key] || null) diff --git a/src/background.ts b/src/background.ts index 3811a42..93a5e28 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1742,7 +1742,7 @@ export async function decryptSingleFunc({ const responseData = uint8ArrayToObject(decryptToUnit8Array); holdMessages.push({ ...message, decryptedData: responseData }); } catch (error) { - console.log(error); + console.error(error); } } return holdMessages; @@ -1766,7 +1766,7 @@ export async function decryptSingleForPublishes({ const responseData = uint8ArrayToObject(decryptToUnit8Array); holdMessages.push({ ...message, decryptedData: responseData }); } catch (error) { - console.log(error); + console.error(error); } } return holdMessages; @@ -1795,7 +1795,7 @@ export async function decryptDirectFunc({ messages, involvingAddress }) { const parsedMessage = JSON.parse(decodedMessage); holdMessages.push({ ...message, ...parsedMessage }); } catch (error) { - console.log(error); + console.error(error); } } return holdMessages; diff --git a/src/common/useFetchResources.tsx b/src/common/useFetchResources.tsx index cc2ab41..242f418 100644 --- a/src/common/useFetchResources.tsx +++ b/src/common/useFetchResources.tsx @@ -163,5 +163,5 @@ export const useFetchResources = () => { [setResources] ); - return { downloadResource }; + return downloadResource; }; diff --git a/src/common/useModal.tsx b/src/common/useModal.tsx index b9ab713..e92dc99 100644 --- a/src/common/useModal.tsx +++ b/src/common/useModal.tsx @@ -1,64 +1,50 @@ -import { useRef, useState } from 'react'; +import { useRef, useState, useCallback, useMemo } from 'react'; interface State { - isShow: boolean; + isShow: boolean; } + export const useModal = () => { - const [state, setState] = useState({ - isShow: false, + const [state, setState] = useState({ isShow: false }); + const [message, setMessage] = useState({ publishFee: '', message: '' }); + const promiseConfig = useRef(null); + + const show = useCallback((data) => { + setMessage(data); + return new Promise((resolve, reject) => { + promiseConfig.current = { resolve, reject }; + setState({ isShow: true }); }); - const [message, setMessage] = useState({ - publishFee: "", - message: "" - }); - const promiseConfig = useRef(null); - const show = async (data) => { - setMessage(data) - return new Promise((resolve, reject) => { - promiseConfig.current = { - resolve, - reject, - }; - setState({ - isShow: true, - }); - }); - }; + }, []); - const hide = () => { - setState({ - isShow: false, - }); - setMessage({ - publishFee: "", - message: "" - }) - }; + const hide = useCallback(() => { + setState({ isShow: false }); + setMessage({ publishFee: '', message: '' }); + }, []); - const onOk = (payload:any) => { - const { resolve } = promiseConfig.current; - setMessage({ - publishFee: "", - message: "" - }) - hide(); - resolve(payload); - }; + const onOk = useCallback( + (payload: any) => { + const { resolve } = promiseConfig.current || {}; + hide(); + resolve?.(payload); + }, + [hide] + ); - const onCancel = () => { - const { reject } = promiseConfig.current; - hide(); - reject(); - setMessage({ - publishFee: "", - message: "" - }) - }; - return { - show, - onOk, - onCancel, - isShow: state.isShow, - message - }; -}; \ No newline at end of file + const onCancel = useCallback(() => { + const { reject } = promiseConfig.current || {}; + hide(); + reject?.(); + }, [hide]); + + return useMemo( + () => ({ + show, + onOk, + onCancel, + isShow: state.isShow, + message, + }), + [show, onOk, onCancel, state.isShow, message] + ); +}; diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index e76467b..06a64c5 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -1,6 +1,6 @@ -import React, { useContext, useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { Box } from '@mui/material'; -import { MyContext, getBaseApiReact } from '../../App'; +import { getBaseApiReact } from '../../App'; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useFrame } from 'react-frame-component'; import { useQortalMessageListener } from './useQortalMessageListener'; @@ -8,7 +8,6 @@ import { useThemeContext } from '../Theme/ThemeContext'; export const AppViewer = React.forwardRef( ({ app, hide, isDevMode, skipAuth }, iframeRef) => { - const { rootHeight } = useContext(MyContext); // const iframeRef = useRef(null); const { window: frameWindow } = useFrame(); const { path, history, changeCurrentIndex, resetHistory } = diff --git a/src/components/Apps/AppViewerContainer.tsx b/src/components/Apps/AppViewerContainer.tsx index 43c661f..b159aa7 100644 --- a/src/components/Apps/AppViewerContainer.tsx +++ b/src/components/Apps/AppViewerContainer.tsx @@ -5,8 +5,6 @@ import { MyContext } from '../../App'; const AppViewerContainer = React.forwardRef( ({ app, isSelected, hide, isDevMode, customHeight, skipAuth }, ref) => { - const { rootHeight } = useContext(MyContext); - return ( { if (category?.id === 'all') return availableQapps; diff --git a/src/components/Apps/AppsDesktop.tsx b/src/components/Apps/AppsDesktop.tsx index 13bbf7d..b5a5cba 100644 --- a/src/components/Apps/AppsDesktop.tsx +++ b/src/components/Apps/AppsDesktop.tsx @@ -1,7 +1,7 @@ import React, { useContext, useEffect, useMemo, useRef, useState } from 'react'; import { AppsHomeDesktop } from './AppsHomeDesktop'; import { Spacer } from '../../common/Spacer'; -import { GlobalContext, getBaseApiReact } from '../../App'; +import { MyContext, getBaseApiReact } from '../../App'; import { AppInfo } from './AppInfo'; import { executeEvent, @@ -49,7 +49,7 @@ export const AppsDesktop = ({ const iframeRefs = useRef({}); const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); - const { showTutorial } = useContext(GlobalContext); + const { showTutorial } = useContext(MyContext); const theme = useTheme(); const myApp = useMemo(() => { diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index 78db4ad..86ec6d3 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -17,6 +17,7 @@ import { useDropzone } from 'react-dropzone'; import { useHandlePrivateApps } from './useHandlePrivateApps'; import { groupsPropertiesAtom, + memberGroupsAtom, myGroupsWhereIAmAdminAtom, } from '../../atoms/global'; import { Label } from '../Group/AddGroup'; @@ -56,8 +57,10 @@ export const AppsPrivate = ({ myName }) => { }, [myGroupsWhereIAmAdminFromGlobal, groupsProperties]); const [isOpenPrivateModal, setIsOpenPrivateModal] = useState(false); - const { show, setInfoSnackCustom, setOpenSnackGlobal, memberGroups } = + const { show, setInfoSnackCustom, setOpenSnackGlobal } = useContext(MyContext); + const [memberGroups] = useAtom(memberGroupsAtom); + const theme = useTheme(); const myGroupsPrivate = useMemo(() => { diff --git a/src/components/Chat/AdminSpace.tsx b/src/components/Chat/AdminSpace.tsx index a57b6e4..49109aa 100644 --- a/src/components/Chat/AdminSpace.tsx +++ b/src/components/Chat/AdminSpace.tsx @@ -18,7 +18,6 @@ export const AdminSpace = ({ balance, isOwner, }) => { - const { rootHeight } = useContext(MyContext); const [isMoved, setIsMoved] = useState(false); useEffect(() => { if (hide) { diff --git a/src/components/Chat/AdminSpaceInner.tsx b/src/components/Chat/AdminSpaceInner.tsx index de68ed6..9a1cffd 100644 --- a/src/components/Chat/AdminSpaceInner.tsx +++ b/src/components/Chat/AdminSpaceInner.tsx @@ -70,7 +70,7 @@ export const AdminSpaceInner = ({ const [groupSecretKeyPublishDetails, setGroupSecretKeyPublishDetails] = useState(null); const [isLoadingPublishKey, setIsLoadingPublishKey] = useState(false); - const { show, setTxList, setInfoSnackCustom, setOpenSnackGlobal } = + const { show, setInfoSnackCustom, setOpenSnackGlobal } = useContext(MyContext); const getAdminGroupSecretKey = useCallback(async () => { diff --git a/src/components/Chat/CreateCommonSecret.tsx b/src/components/Chat/CreateCommonSecret.tsx index 6c4fcfa..9cf3b79 100644 --- a/src/components/Chat/CreateCommonSecret.tsx +++ b/src/components/Chat/CreateCommonSecret.tsx @@ -16,6 +16,8 @@ import { } from '../Group/Group'; import { base64ToUint8Array } from '../../qdn/encryption/group-encryption'; import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; +import { useSetAtom } from 'jotai'; +import { txListAtom } from '../../atoms/global'; export const CreateCommonSecret = ({ groupId, @@ -29,7 +31,8 @@ export const CreateCommonSecret = ({ setIsForceShowCreationKeyPopup, isForceShowCreationKeyPopup, }) => { - const { show, setTxList } = useContext(MyContext); + const { show } = useContext(MyContext); + const setTxList = useSetAtom(txListAtom); const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index a597e21..9ac3ded 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -138,7 +138,7 @@ export const GroupAnnouncements = ({ const [selectedAnnouncement, setSelectedAnnouncement] = useState(null); const [isFocusedParent, setIsFocusedParent] = useState(false); - const { show, rootHeight } = React.useContext(MyContext); + const { show } = React.useContext(MyContext); const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); const hasInitialized = useRef(false); diff --git a/src/components/Chat/GroupForum.tsx b/src/components/Chat/GroupForum.tsx index 78dd02a..551a2db 100644 --- a/src/components/Chat/GroupForum.tsx +++ b/src/components/Chat/GroupForum.tsx @@ -1,6 +1,5 @@ -import { useContext, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { GroupMail } from '../Group/Forum/GroupMail'; -import { MyContext } from '../../App'; export const GroupForum = ({ selectedGroup, @@ -14,7 +13,6 @@ export const GroupForum = ({ setDefaultThread, isPrivate, }) => { - const { rootHeight } = useContext(MyContext); const [isMoved, setIsMoved] = useState(false); useEffect(() => { diff --git a/src/components/Embeds/VideoPlayer.tsx b/src/components/Embeds/VideoPlayer.tsx index 26e3e08..30991ff 100644 --- a/src/components/Embeds/VideoPlayer.tsx +++ b/src/components/Embeds/VideoPlayer.tsx @@ -17,7 +17,7 @@ import { Refresh } from '@mui/icons-material'; import { Menu, MenuItem } from '@mui/material'; import { MoreVert as MoreIcon } from '@mui/icons-material'; -import { GlobalContext, getBaseApiReact } from '../../App'; +import { MyContext, getBaseApiReact } from '../../App'; import { resourceKeySelector } from '../../atoms/global'; import { useAtomValue } from 'jotai'; @@ -84,7 +84,7 @@ export const VideoPlayer: React.FC = ({ const download = useAtomValue(resourceKeySelector(keyIdentifier)); - const { downloadResource } = useContext(GlobalContext); + const { downloadResource } = useContext(MyContext); const videoRef = useRef(null); const [playing, setPlaying] = useState(false); diff --git a/src/components/GlobalActions/GlobalActions.tsx b/src/components/GlobalActions/GlobalActions.tsx index 6dd845c..e181df4 100644 --- a/src/components/GlobalActions/GlobalActions.tsx +++ b/src/components/GlobalActions/GlobalActions.tsx @@ -1,10 +1,10 @@ -import React from 'react' -import { JoinGroup } from './JoinGroup' +import React from 'react'; +import { JoinGroup } from './JoinGroup'; -export const GlobalActions = ({memberGroups}) => { +export const GlobalActions = () => { return ( <> - + - ) -} + ); +}; diff --git a/src/components/GlobalActions/JoinGroup.tsx b/src/components/GlobalActions/JoinGroup.tsx index 58ff5ed..6503de4 100644 --- a/src/components/GlobalActions/JoinGroup.tsx +++ b/src/components/GlobalActions/JoinGroup.tsx @@ -16,9 +16,13 @@ import { getBaseApiReact, MyContext } from '../../App'; import { getFee } from '../../background'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { FidgetSpinner } from 'react-loader-spinner'; +import { useAtom, useSetAtom } from 'jotai'; +import { memberGroupsAtom, txListAtom } from '../../atoms/global'; -export const JoinGroup = ({ memberGroups }) => { - const { show, setTxList } = useContext(MyContext); +export const JoinGroup = () => { + const { show } = useContext(MyContext); + const setTxList = useSetAtom(txListAtom); + const [memberGroups] = useAtom(memberGroupsAtom); const [openSnack, setOpenSnack] = useState(false); const [infoSnack, setInfoSnack] = useState(null); const [groupInfo, setGroupInfo] = useState(null); diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 278d6a7..9628c55 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -38,6 +38,8 @@ import { getFee } from '../../background'; import { MyContext } from '../../App'; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useTranslation } from 'react-i18next'; +import { useSetAtom } from 'jotai'; +import { txListAtom } from '../../atoms/global'; export const Label = styled('label')` display: block; @@ -57,7 +59,9 @@ const Transition = forwardRef(function Transition( }); export const AddGroup = ({ address, open, setOpen }) => { - const { show, setTxList } = useContext(MyContext); + const { show } = useContext(MyContext); + const setTxList = useSetAtom(txListAtom); + const [openAdvance, setOpenAdvance] = useState(false); const [name, setName] = useState(''); const [description, setDescription] = useState(''); diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index 3030c55..971f6e0 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -30,6 +30,8 @@ import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { Spacer } from '../../common/Spacer'; import { useTranslation } from 'react-i18next'; +import { useAtom, useSetAtom } from 'jotai'; +import { memberGroupsAtom, txListAtom } from '../../atoms/global'; const cache = new CellMeasurerCache({ fixedWidth: true, @@ -37,7 +39,11 @@ const cache = new CellMeasurerCache({ }); export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { - const { memberGroups, show, setTxList } = useContext(MyContext); + const { show } = useContext(MyContext); + const [memberGroups] = useAtom(memberGroupsAtom); + + const setTxList = useSetAtom(txListAtom); + const { t } = useTranslation(['core', 'group']); const [groups, setGroups] = useState([]); const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index e16404a..2f2a314 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -11,7 +11,6 @@ import { } from '@mui/material'; import React, { useCallback, - useContext, useEffect, useMemo, useRef, @@ -33,7 +32,6 @@ import { Spacer } from '../../common/Spacer'; import { ManageMembers } from './ManageMembers'; import MarkChatUnreadIcon from '@mui/icons-material/MarkChatUnread'; import { - MyContext, clearAllQueues, getArbitraryEndpointReact, getBaseApiReact, @@ -73,6 +71,7 @@ import { groupsOwnerNamesAtom, groupsPropertiesAtom, isOpenBlockedModalAtom, + memberGroupsAtom, mutedGroupsAtom, selectedGroupIdAtom, timestampEnterDataAtom, @@ -413,8 +412,9 @@ export const Group = ({ const [openAddGroup, setOpenAddGroup] = useState(false); const [isInitialGroups, setIsInitialGroups] = useState(false); const [openManageMembers, setOpenManageMembers] = useState(false); - const { setMemberGroups, rootHeight, isRunningPublicNode } = - useContext(MyContext); + + const setMemberGroups = useSetAtom(memberGroupsAtom); + const lastGroupNotification = useRef(null); const [timestampEnterData, setTimestampEnterData] = useAtom( timestampEnterDataAtom @@ -1309,7 +1309,6 @@ export const Group = ({ setOpenAddGroup(false); setIsInitialGroups(false); setOpenManageMembers(false); - setMemberGroups([]); // Assuming you're clearing the context here as well setTimestampEnterData({}); setChatMode('groups'); setNewChat(false); @@ -1931,7 +1930,6 @@ export const Group = ({ selectedGroup={selectedGroup} getUserSettings={getUserSettings} setOpenAddGroup={setOpenAddGroup} - isRunningPublicNode={isRunningPublicNode} setIsOpenBlockedUserModal={setIsOpenBlockedUserModal} myAddress={myAddress} /> diff --git a/src/components/Group/GroupJoinRequests.tsx b/src/components/Group/GroupJoinRequests.tsx index 32252da..15304f5 100644 --- a/src/components/Group/GroupJoinRequests.tsx +++ b/src/components/Group/GroupJoinRequests.tsx @@ -9,12 +9,12 @@ import GroupAddIcon from '@mui/icons-material/GroupAdd'; import { executeEvent } from '../../utils/events'; import { Box, ButtonBase, Collapse, Typography, useTheme } from '@mui/material'; import { CustomLoader } from '../../common/CustomLoader'; -import { MyContext, getBaseApiReact } from '../../App'; -import { myGroupsWhereIAmAdminAtom } from '../../atoms/global'; +import { getBaseApiReact } from '../../App'; +import { myGroupsWhereIAmAdminAtom, txListAtom } from '../../atoms/global'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import { useTranslation } from 'react-i18next'; -import { useSetAtom } from 'jotai'; +import { useAtom, useSetAtom } from 'jotai'; export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2); export const GroupJoinRequests = ({ @@ -33,7 +33,8 @@ export const GroupJoinRequests = ({ [] ); const [loading, setLoading] = React.useState(true); - const { txList, setTxList } = React.useContext(MyContext); + const [txList] = useAtom(txListAtom); + const setMyGroupsWhereIAmAdmin = useSetAtom(myGroupsWhereIAmAdminAtom); const theme = useTheme(); diff --git a/src/components/Group/GroupList.tsx b/src/components/Group/GroupList.tsx index 47f464c..1057281 100644 --- a/src/components/Group/GroupList.tsx +++ b/src/components/Group/GroupList.tsx @@ -26,11 +26,12 @@ import { groupChatTimestampSelector, groupPropertySelector, groupsOwnerNamesSelector, + isRunningPublicNodeAtom, timestampEnterDataSelector, } from '../../atoms/global'; import { timeDifferenceForNotificationChats } from './Group'; -import { useAtomValue } from 'jotai'; +import { useAtom, useAtomValue } from 'jotai'; export const GroupList = ({ selectGroupFunc, @@ -44,11 +45,12 @@ export const GroupList = ({ selectedGroup, getUserSettings, setOpenAddGroup, - isRunningPublicNode, setIsOpenBlockedUserModal, myAddress, }) => { const theme = useTheme(); + const [isRunningPublicNode] = useAtom(isRunningPublicNodeAtom); + return (
    { const [fee, setFee] = useState(null); const [isLoadingJoinGroup, setIsLoadingJoinGroup] = useState(false); const [isLoadingPublish, setIsLoadingPublish] = useState(false); - const { show, setTxList } = useContext(MyContext); + const { show } = useContext(MyContext); + const setTxList = useSetAtom(txListAtom); + const theme = useTheme(); const listRef = useRef(); const rowVirtualizer = useVirtualizer({ diff --git a/src/components/Group/ListOfJoinRequests.tsx b/src/components/Group/ListOfJoinRequests.tsx index d2bd5ad..8c51ee2 100644 --- a/src/components/Group/ListOfJoinRequests.tsx +++ b/src/components/Group/ListOfJoinRequests.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { Avatar, Box, @@ -17,7 +17,9 @@ import { import { getNameInfo } from './Group'; import { getBaseApi, getFee } from '../../background'; import { LoadingButton } from '@mui/lab'; -import { MyContext, getBaseApiReact } from '../../App'; +import { getBaseApiReact } from '../../App'; +import { txListAtom } from '../../atoms/global'; +import { useAtom } from 'jotai'; export const getMemberInvites = async (groupNumber) => { const response = await fetch( @@ -56,7 +58,7 @@ export const ListOfJoinRequests = ({ show, }) => { const [invites, setInvites] = useState([]); - const { txList, setTxList } = useContext(MyContext); + const [txList, setTxList] = useAtom(txListAtom); const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index eba8de3..6956693 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -23,6 +23,8 @@ import { LoadingButton } from '@mui/lab'; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { Spacer } from '../../common/Spacer'; import InsertLinkIcon from '@mui/icons-material/InsertLink'; +import { useSetAtom } from 'jotai'; +import { txListAtom } from '../../atoms/global'; function a11yProps(index: number) { return { @@ -61,7 +63,8 @@ export const ManageMembers = ({ setValue(newValue); }; const theme = useTheme(); - const { show, setTxList } = React.useContext(MyContext); + const { show } = React.useContext(MyContext); + const setTxList = useSetAtom(txListAtom); const handleClose = () => { setOpen(false); diff --git a/src/components/Group/UserListOfInvites.tsx b/src/components/Group/UserListOfInvites.tsx index c6bb8d3..a0362be 100644 --- a/src/components/Group/UserListOfInvites.tsx +++ b/src/components/Group/UserListOfInvites.tsx @@ -20,6 +20,8 @@ import { getFee } from '../../background'; import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { Spacer } from '../../common/Spacer'; +import { useSetAtom } from 'jotai'; +import { txListAtom } from '../../atoms/global'; const cache = new CellMeasurerCache({ fixedWidth: true, @@ -52,7 +54,9 @@ export const UserListOfInvites = ({ setInfoSnack, setOpenSnack, }) => { - const { txList, setTxList, show } = useContext(MyContext); + const { show } = useContext(MyContext); + const setTxList = useSetAtom(txListAtom); + const [invites, setInvites] = useState([]); const [isLoading, setIsLoading] = useState(false); const theme = useTheme(); diff --git a/src/components/Group/useBlockUsers.tsx b/src/components/Group/useBlockUsers.tsx index 4165260..e82d0c6 100644 --- a/src/components/Group/useBlockUsers.tsx +++ b/src/components/Group/useBlockUsers.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef } from 'react'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; export const useBlockedAddresses = () => { const userBlockedRef = useRef({}); @@ -191,10 +191,13 @@ export const useBlockedAddresses = () => { } }, []); - return { - isUserBlocked, - addToBlockList, - removeBlockFromList, - getAllBlockedUsers, - }; + return useMemo( + () => ({ + isUserBlocked, + addToBlockList, + removeBlockFromList, + getAllBlockedUsers, + }), + [isUserBlocked, addToBlockList, removeBlockFromList, getAllBlockedUsers] + ); }; diff --git a/src/components/Group/useHandleUserInfo.tsx b/src/components/Group/useHandleUserInfo.tsx index da7ee13..8b8796f 100644 --- a/src/components/Group/useHandleUserInfo.tsx +++ b/src/components/Group/useHandleUserInfo.tsx @@ -26,7 +26,5 @@ export const useHandleUserInfo = () => { } }, []); - return { - getIndividualUserInfo, - }; + return getIndividualUserInfo; }; diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index 552d39c..fe05c5b 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -25,15 +25,13 @@ import { getFee, getNameOrAddress } from '../../background'; import { Spacer } from '../../common/Spacer'; import { FidgetSpinner } from 'react-loader-spinner'; import { useModal } from '../../common/useModal'; +import { useAtom, useSetAtom } from 'jotai'; +import { memberGroupsAtom, txListAtom } from '../../atoms/global'; + +export const Minting = ({ setIsOpenMinting, myAddress, show }) => { + const setTxList = useSetAtom(txListAtom); + const [groups] = useAtom(memberGroupsAtom); -export const Minting = ({ - setIsOpenMinting, - myAddress, - groups, - show, - setTxList, - txList, -}) => { const [mintingAccounts, setMintingAccounts] = useState([]); const [accountInfo, setAccountInfo] = useState(null); const [rewardSharePublicKey, setRewardSharePublicKey] = useState(''); diff --git a/src/components/RegisterName.tsx b/src/components/RegisterName.tsx index 4cee6ec..4b52a45 100644 --- a/src/components/RegisterName.tsx +++ b/src/components/RegisterName.tsx @@ -34,6 +34,8 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events'; import { BarSpinner } from '../common/Spinners/BarSpinner/BarSpinner'; import CheckIcon from '@mui/icons-material/Check'; import ErrorIcon from '@mui/icons-material/Error'; +import { useSetAtom } from 'jotai'; +import { txListAtom } from '../atoms/global'; enum Availability { NULL = 'null', @@ -46,9 +48,10 @@ export const RegisterName = ({ setInfoSnack, userInfo, show, - setTxList, balance, }) => { + const setTxList = useSetAtom(txListAtom); + const [isOpen, setIsOpen] = useState(false); const [registerNameValue, setRegisterNameValue] = useState(''); const [isLoadingRegisterName, setIsLoadingRegisterName] = useState(false); diff --git a/src/components/TaskManager/TaskManager.tsx b/src/components/TaskManager/TaskManager.tsx index 673c385..d49077e 100644 --- a/src/components/TaskManager/TaskManager.tsx +++ b/src/components/TaskManager/TaskManager.tsx @@ -7,16 +7,21 @@ import { IconButton, useTheme, } from '@mui/material'; -import React, { useContext, useEffect, useRef } from 'react'; +import React, { useEffect, useRef } from 'react'; import PendingIcon from '@mui/icons-material/Pending'; import TaskAltIcon from '@mui/icons-material/TaskAlt'; import ExpandLess from '@mui/icons-material/ExpandLess'; import ExpandMore from '@mui/icons-material/ExpandMore'; -import { MyContext, getBaseApiReact } from '../../App'; +import { getBaseApiReact } from '../../App'; import { executeEvent } from '../../utils/events'; +import { useAtom } from 'jotai'; +import { memberGroupsAtom, txListAtom } from '../../atoms/global'; export const TaskManager = ({ getUserInfo }) => { - const { txList, setTxList, memberGroups } = useContext(MyContext); + const [memberGroups] = useAtom(memberGroupsAtom); + + const [txList, setTxList] = useAtom(txListAtom); + const [open, setOpen] = React.useState(false); const intervals = useRef({}); const theme = useTheme(); diff --git a/src/components/Tutorials/Tutorials.tsx b/src/components/Tutorials/Tutorials.tsx index 522324f..f1a5fe5 100644 --- a/src/components/Tutorials/Tutorials.tsx +++ b/src/components/Tutorials/Tutorials.tsx @@ -1,5 +1,5 @@ import { useContext, useState } from 'react'; -import { GlobalContext } from '../../App'; +import { MyContext } from '../../App'; import { Button, Dialog, @@ -16,7 +16,7 @@ import { VideoPlayer } from '../Embeds/VideoPlayer'; import { useTranslation } from 'react-i18next'; export const Tutorials = () => { - const { openTutorialModal, setOpenTutorialModal } = useContext(GlobalContext); + const { openTutorialModal, setOpenTutorialModal } = useContext(MyContext); const [multiNumber, setMultiNumber] = useState(0); const theme = useTheme(); const { t } = useTranslation(['core', 'tutorial']); diff --git a/src/components/Tutorials/useHandleTutorials.tsx b/src/components/Tutorials/useHandleTutorials.tsx index 5fde392..597199e 100644 --- a/src/components/Tutorials/useHandleTutorials.tsx +++ b/src/components/Tutorials/useHandleTutorials.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { saveToLocalStorage } from '../Apps/AppsNavBarDesktop'; import creationImg from './img/creation.webp'; import dashboardImg from './img/dashboard.webp'; @@ -196,14 +196,17 @@ export const useHandleTutorials = () => { }, [shownTutorials] ); - return { - showTutorial, - hasSeenGettingStarted: - shownTutorials === null - ? null - : !!(shownTutorials || {})['getting-started'], - openTutorialModal, - setOpenTutorialModal, - shownTutorialsInitiated: !!shownTutorials, - }; + return useMemo( + () => ({ + showTutorial, + hasSeenGettingStarted: + shownTutorials === null + ? null + : !!(shownTutorials || {})['getting-started'], + openTutorialModal, + setOpenTutorialModal, + shownTutorialsInitiated: !!shownTutorials, + }), + [showTutorial, openTutorialModal, setOpenTutorialModal, shownTutorials] + ); }; diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx index 44d7c12..07b5d45 100644 --- a/src/components/WrapperUserAction.tsx +++ b/src/components/WrapperUserAction.tsx @@ -8,10 +8,13 @@ import { } from '@mui/material'; import { executeEvent } from '../utils/events'; import { MyContext } from '../App'; +import { useAtom } from 'jotai'; +import { isRunningPublicNodeAtom } from '../atoms/global'; export const WrapperUserAction = ({ children, address, name, disabled }) => { const theme = useTheme(); - const { isRunningPublicNode } = useContext(MyContext); + const [isRunningPublicNode] = useAtom(isRunningPublicNodeAtom); + const [anchorEl, setAnchorEl] = useState(null); // Handle child element click to open Popover diff --git a/src/utils/mobile/mobileUtils.ts b/src/utils/mobile/mobileUtils.ts deleted file mode 100644 index 144c8c1..0000000 --- a/src/utils/mobile/mobileUtils.ts +++ /dev/null @@ -1,5 +0,0 @@ - -export const getRootHeight = ()=> { - - return document?.getElementById('root')?.style?.height || '100%' -} \ No newline at end of file From 6b2d92d73df6d089f3cd1c00fd568b9c202bad2c Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 30 Apr 2025 15:13:06 +0300 Subject: [PATCH 294/717] added autocomplete for user lookup. scrollbar theme --- electron/package.json | 4 +- src/ExtStates/NotAuthenticated.tsx | 2 +- src/components/Theme/ThemeContext.tsx | 4 +- src/components/UserLookup.tsx/UserLookup.tsx | 58 ++++++++++++-------- src/hooks/useNameSearch.tsx | 55 +++++++++++++++++++ src/styles/index.css | 27 +-------- src/styles/theme-dark.ts | 39 +++++++++---- src/styles/theme-light.ts | 47 +++++++++++----- 8 files changed, 159 insertions(+), 77 deletions(-) create mode 100644 src/hooks/useNameSearch.tsx diff --git a/electron/package.json b/electron/package.json index 31f5372..05f8865 100644 --- a/electron/package.json +++ b/electron/package.json @@ -1,6 +1,6 @@ { "name": "qortal-hub", - "version": "0.5.3", + "version": "0.5.4-pre", "description": "A desktop app that gives you access to the Qortal network", "author": { "name": "", @@ -57,4 +57,4 @@ "capacitor", "electron" ] -} \ No newline at end of file +} diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 6d580fe..789f0fe 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -34,7 +34,7 @@ import LanguageSelector from '../components/Language/LanguageSelector'; import { MyContext } from '../App'; const manifestData = { - version: '0.5.3', + version: '0.5.4', }; export const HtmlTooltip = styled(({ className, ...props }: TooltipProps) => ( diff --git a/src/components/Theme/ThemeContext.tsx b/src/components/Theme/ThemeContext.tsx index 0f24b4a..72d4f3b 100644 --- a/src/components/Theme/ThemeContext.tsx +++ b/src/components/Theme/ThemeContext.tsx @@ -21,7 +21,7 @@ const defaultTheme = { }; const ThemeContext = createContext({ - themeMode: 'light', + themeMode: 'dark', toggleTheme: () => {}, userThemes: [defaultTheme], addUserTheme: (themes) => {}, @@ -30,7 +30,7 @@ const ThemeContext = createContext({ }); export const ThemeProvider = ({ children }) => { - const [themeMode, setThemeMode] = useState('light'); + const [themeMode, setThemeMode] = useState('dark'); const [userThemes, setUserThemes] = useState([defaultTheme]); const [currentThemeId, setCurrentThemeId] = useState('default'); diff --git a/src/components/UserLookup.tsx/UserLookup.tsx b/src/components/UserLookup.tsx/UserLookup.tsx index 0b19ba1..ee0b8c9 100644 --- a/src/components/UserLookup.tsx/UserLookup.tsx +++ b/src/components/UserLookup.tsx/UserLookup.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { DrawerUserLookup } from '../Drawer/DrawerUserLookup'; import { Avatar, @@ -17,6 +17,7 @@ import { Table, CircularProgress, useTheme, + Autocomplete, } from '@mui/material'; import { getAddressInfo, getNameOrAddress } from '../../background'; import { getBaseApiReact } from '../../App'; @@ -31,6 +32,7 @@ import { subscribeToEvent, unsubscribeFromEvent, } from '../../utils/events'; +import { useNameSearch } from '../../hooks/useNameSearch'; function formatAddress(str) { if (str.length <= 12) return str; @@ -44,6 +46,9 @@ function formatAddress(str) { export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => { const theme = useTheme(); const [nameOrAddress, setNameOrAddress] = useState(''); + const [inputValue, setInputValue] = useState(''); + const { results, isLoading } = useNameSearch(inputValue); + const options = useMemo(() => results?.map((item) => item.name), [results]); const [errorMessage, setErrorMessage] = useState(''); const [addressInfo, setAddressInfo] = useState(null); const [isLoadingUser, setIsLoadingUser] = useState(false); @@ -120,6 +125,7 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => { const onClose = () => { setIsOpenDrawerLookup(false); setNameOrAddress(''); + setInputValue(''); setErrorMessage(''); setPayments([]); setIsLoadingUser(false); @@ -146,31 +152,39 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => { gap: '5px', }} > - setNameOrAddress(e.target.value)} - size="small" - placeholder="Address or Name" - autoComplete="off" - onKeyDown={(e) => { - if (e.key === 'Enter' && nameOrAddress) { - lookupFunc(); + onChange={(event: any, newValue: string | null) => { + if (!newValue) { + setNameOrAddress(''); + return; } + setNameOrAddress(newValue); + lookupFunc(newValue); }} + inputValue={inputValue} + onInputChange={(event, newInputValue) => { + setInputValue(newInputValue); + }} + id="controllable-states-demo" + loading={isLoading} + options={options} + sx={{ width: 300 }} + renderInput={(params) => ( + { + if (e.key === 'Enter' && nameOrAddress) { + lookupFunc(inputValue); + } + }} + /> + )} /> - { - lookupFunc(); - }} - > - - + { + const [nameList, setNameList] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const checkIfNameExisits = useCallback( + async (name: string, listLimit: number) => { + try { + if (!name) { + setNameList([]); + return; + } + + const res = await fetch( + `${getBaseApiReact()}/names/search?query=${name}&prefix=true&limit=${listLimit}` + ); + const data = await res.json(); + setNameList( + data?.map((item: any) => { + return { + name: item.name, + address: item.owner, + }; + }) + ); + } catch (error) { + console.error(error); + } finally { + setIsLoading(false); + } + }, + [] + ); + // Debounce logic + useEffect(() => { + setIsLoading(true); + const handler = setTimeout(() => { + checkIfNameExisits(value, limit); + }, 500); + + // Cleanup timeout if searchValue changes before the timeout completes + return () => { + clearTimeout(handler); + }; + }, [value, limit, checkIfNameExisits]); + return { + isLoading, + results: nameList, + }; +}; diff --git a/src/styles/index.css b/src/styles/index.css index 02c897f..ce338ba 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -42,31 +42,6 @@ opacity: 0.6; } -::-webkit-scrollbar-track { - background-color: transparent; -} - -::-webkit-scrollbar-track:hover { - background-color: transparent; -} - -::-webkit-scrollbar { - width: 16px; - height: 10px; -} - -::-webkit-scrollbar-thumb { - background-color: #444444; - border-radius: 8px; - background-clip: content-box; - border: 4px solid transparent; - transition: 0.3s background-color; -} - -::-webkit-scrollbar-thumb:hover { - background-color: #363636; -} - @property --var1 { syntax: ''; inherits: true; @@ -78,7 +53,7 @@ } .scrollable-container:hover { - --var1: #444444; + --var1: var(--primary-main); } .scrollable-container::-webkit-scrollbar-thumb { diff --git a/src/styles/theme-dark.ts b/src/styles/theme-dark.ts index 8929561..142ee54 100644 --- a/src/styles/theme-dark.ts +++ b/src/styles/theme-dark.ts @@ -32,6 +32,7 @@ export const darkThemeOptions: ThemeOptions = { unread: 'rgb(66, 151, 226)', }, }, + components: { MuiCard: { styleOverrides: { @@ -48,19 +49,12 @@ export const darkThemeOptions: ThemeOptions = { }, }, MuiCssBaseline: { - styleOverrides: { + styleOverrides: (theme) => ({ ':root': { - '--color-instance': 'rgb(30, 30, 32)', - '--color-instance-popover-bg': 'rgb(34, 34, 34)', '--Mail-Background': 'rgb(43, 43, 43)', - '--new-message-text': 'rgb(0, 0, 0)', '--bg-primary': 'rgba(31, 32, 35, 1)', '--bg-2': 'rgb(39, 40, 44)', - '--bg-3': 'rgba(0, 0, 0, 0.1)', - '--unread': 'rgb(66, 151, 226)', - '--danger': 'rgb(177, 70, 70)', - '--apps-circle': 'rgb(31, 32, 35)', - '--green': 'rgb(94, 176, 73)', + '--primary-main': theme.palette.primary.main, }, '*, *::before, *::after': { boxSizing: 'border-box', @@ -74,9 +68,32 @@ export const darkThemeOptions: ThemeOptions = { margin: 0, wordBreak: 'break-word', backgroundColor: 'var(--bg-primary)', - color: 'var(--new-message-text)', }, - }, + '::-webkit-scrollbar-track': { + backgroundColor: 'transparent', + }, + + '::-webkit-scrollbar-track:hover': { + backgroundColor: 'transparent', + }, + + '::-webkit-scrollbar': { + width: '16px', + height: '10px', + }, + + '::-webkit-scrollbar-thumb': { + backgroundColor: theme.palette.primary.main, + borderRadius: '8px', + backgroundClip: 'content-box', + border: '4px solid transparent', + transition: '0.3s background-color', + }, + + '::-webkit-scrollbar-thumb:hover': { + backgroundColor: theme.palette.primary.light, + }, + }), }, MuiIcon: { defaultProps: { diff --git a/src/styles/theme-light.ts b/src/styles/theme-light.ts index 15b1f8b..26ca782 100644 --- a/src/styles/theme-light.ts +++ b/src/styles/theme-light.ts @@ -6,9 +6,9 @@ export const lightThemeOptions: ThemeOptions = { palette: { mode: 'light', primary: { - main: 'rgb(162, 162, 221)', // old light becomes main + main: 'rgb(162, 162, 221)', dark: 'rgb(113, 198, 212)', - light: 'rgba(244, 244, 251, 1)', // former main becomes light + light: 'rgb(180, 200, 235)', }, secondary: { main: 'rgba(194, 222, 236, 1)', @@ -49,36 +49,57 @@ export const lightThemeOptions: ThemeOptions = { }, }, MuiCssBaseline: { - styleOverrides: { + styleOverrides: (theme) => ({ ':root': { - '--color-instance': 'rgba(30, 30, 32, 1)', - '--color-instance-popover-bg': 'rgba(34, 34, 34, 1)', '--Mail-Background': 'rgba(49, 51, 56, 1)', - '--new-message-text': 'rgba(0, 0, 0, 1)', '--bg-primary': 'rgba(31, 32, 35, 1)', '--bg-2': 'rgba(39, 40, 44, 1)', - '--bg-3': 'rgba(0, 0, 0, 0.1)', - '--unread': 'rgba(66, 151, 226, 1)', - '--danger': 'rgba(177, 70, 70, 1)', - '--apps-circle': 'rgba(31, 32, 35, 1)', - '--green': 'rgba(94, 176, 73, 1)', + '--primary-main': theme.palette.primary.main, }, + '*, *::before, *::after': { boxSizing: 'border-box', }, + html: { padding: 0, margin: 0, }, + body: { padding: 0, margin: 0, wordBreak: 'break-word', backgroundColor: 'var(--bg-primary)', - color: 'var(--new-message-text)', }, - }, + + '::-webkit-scrollbar-track': { + backgroundColor: 'transparent', + }, + + '::-webkit-scrollbar-track:hover': { + backgroundColor: 'transparent', + }, + + '::-webkit-scrollbar': { + width: '16px', + height: '10px', + }, + + '::-webkit-scrollbar-thumb': { + backgroundColor: theme.palette.primary.main, + borderRadius: '8px', + backgroundClip: 'content-box', + border: '4px solid transparent', + transition: '0.3s background-color', + }, + + '::-webkit-scrollbar-thumb:hover': { + backgroundColor: theme.palette.primary.light, + }, + }), }, + MuiIcon: { defaultProps: { style: { From 5d1ef926c80301bac54c628de2c5cb215ee7a8a9 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 1 May 2025 01:05:28 +0300 Subject: [PATCH 295/717] started --- src/App.tsx | 2 +- src/components/Chat/ChatGroup.tsx | 66 ++++++++++++++++++++++++++++++- src/components/Chat/TipTap.tsx | 55 +++++++++++++++++++++++++- src/qortalRequests/get.ts | 1 + 4 files changed, 121 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 558cd01..ad1f496 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3219,7 +3219,7 @@ function App() { onClick={onOkUnsavedChanges} autoFocus > - {t('core:action.decline', { + {t('core:action.continue_logout', { postProcess: 'capitalize', })} diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 0dd626e..2af7c2d 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -42,7 +42,7 @@ import CloseIcon from '@mui/icons-material/Close'; import { throttle } from 'lodash'; const uid = new ShortUniqueId({ length: 5 }); - +const uidImages = new ShortUniqueId({ length: 12 }); export const ChatGroup = ({ selectedGroup, secretKey, @@ -74,6 +74,7 @@ export const ChatGroup = ({ const [isOpenQManager, setIsOpenQManager] = useState(null); const [messageSize, setMessageSize] = useState(0); + const [chatImagesToSave, setChatImagesToSave] = useState([]); const hasInitializedWebsocket = useRef(false); const socketRef = useRef(null); // WebSocket reference const timeoutIdRef = useRef(null); // Timeout ID reference @@ -778,11 +779,45 @@ export const ChatGroup = ({ : { isEdited: chatReference ? true : false, }; + const imagesToPublish = []; + if (!chatReference && chatImagesToSave?.length > 0) { + chatImagesToSave.forEach((base64Img) => { + const identifier = `qchat_1_group_${selectedGroup}_${uidImages.rnd()}`; + imagesToPublish.push({ + service: 'IMAGE', + identifier, + name: myName, + base64: base64Img, + }); + }); + + const res = await window.sendMessage( + 'PUBLISH_MULTIPLE_QDN_RESOURCES', + + { + resources: imagesToPublish, + }, + 240000, + true + ); + console.log('res', res); + if (res !== true) throw new Error('Unable to publish images'); + } + const otherData = { repliedTo, ...(onEditMessage?.decryptedData || {}), type: chatReference ? 'edit' : '', specialId: uid.rnd(), + images: + onEditMessage?.images || + imagesToPublish.map((item) => { + return { + name: item.name, + identifier: item.identifier, + service: item.service, + }; + }), ...publicData, }; const objectMessage = { @@ -824,6 +859,7 @@ export const ChatGroup = ({ clearEditorContent(); setReplyMessage(null); setOnEditMessage(null); + setChatImagesToSave([]); } // send chat message } catch (error) { @@ -986,6 +1022,10 @@ export const ChatGroup = ({ const theme = useTheme(); + const insertImage = useCallback((img) => { + setChatImagesToSave((prev) => [...prev, img]); + }, []); + return (
    + + {chatImagesToSave?.map((imgBase64) => { + return ( + + ); + })} + {replyMessage && ( {messageSize > 750 && ( { - if (editor) { + if (editor && !isChat) { editor.view.dom.addEventListener('paste', handlePaste); return () => { editor.view.dom.removeEventListener('paste', handlePaste); @@ -366,12 +367,46 @@ export default ({ customEditorHeight, membersWithNames, enableMentions, + insertImage, }) => { const theme = useTheme(); const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useAtom( isDisabledEditorEnterAtom ); + const handleImageUpload = async (file) => { + try { + if (!file.type.includes('image')) return; + let compressedFile = file; + if (file.type !== 'image/gif') { + await new Promise((resolve) => { + new Compressor(file, { + quality: 0.6, + maxWidth: 1200, + mimeType: 'image/webp', + success(result) { + compressedFile = new File([result], 'image.webp', { + type: 'image/webp', + }); + resolve(); + }, + error(err) { + console.error('Image compression error:', err); + }, + }); + }); + } + + if (compressedFile) { + const toBase64 = await fileToBase64(compressedFile); + insertImage(toBase64); + console.log('toBase64', toBase64); + } + } catch (error) { + console.error(error); + } + }; + const extensionsFiltered = isChat ? extensions.filter((item) => item?.name !== 'image') : extensions; @@ -543,6 +578,24 @@ export default ({ } return false; }, + handlePaste(view, event) { + if (!isChat) return; + const items = event.clipboardData?.items; + if (!items) return false; + + for (const item of items) { + if (item.type.startsWith('image/')) { + const file = item.getAsFile(); + if (file) { + event.preventDefault(); // Block the default paste + handleImageUpload(file); // Custom handler + return true; // Let ProseMirror know we handled it + } + } + } + + return false; // fallback to default behavior otherwise + }, }} /> diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index a160305..37ef736 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -1218,6 +1218,7 @@ export const publishMultipleQDNResources = async ( sender, isFromExtension ) => { + console.log('data', data); const requiredFields = ['resources']; const missingFields: string[] = []; let feeAmount = null; From 170e99b4d0cde3044c8e57e03b2e7ea1e4cf32bf Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 1 May 2025 12:05:07 +0300 Subject: [PATCH 296/717] add qr for sign_fee --- .../Apps/useQortalMessageListener.tsx | 2 + src/qortalRequests.ts | 27 ++++++++ src/qortalRequests/get.ts | 65 +++++++++++++++++++ 3 files changed, 94 insertions(+) diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index 8770807..17ce580 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -253,6 +253,7 @@ export const listOfAllQortalRequests = [ 'SELL_NAME', 'CANCEL_SELL_NAME', 'BUY_NAME', + 'SIGN_FOREIGN_FEES', ]; export const UIQortalRequests = [ @@ -313,6 +314,7 @@ export const UIQortalRequests = [ 'SELL_NAME', 'CANCEL_SELL_NAME', 'BUY_NAME', + 'SIGN_FOREIGN_FEES', ]; async function retrieveFileFromIndexedDB(fileId) { diff --git a/src/qortalRequests.ts b/src/qortalRequests.ts index 701c433..4951a0d 100644 --- a/src/qortalRequests.ts +++ b/src/qortalRequests.ts @@ -61,6 +61,7 @@ import { buyNameRequest, sellNameRequest, cancelSellNameRequest, + signForeignFees, } from './qortalRequests/get'; import { getData, storeData } from './utils/chromeStorage'; import { executeEvent } from './utils/events'; @@ -1833,6 +1834,32 @@ function setupMessageListenerQortalRequest() { } break; } + case 'SIGN_FOREIGN_FEES': { + try { + const res = await signForeignFees(request.payload, isFromExtension); + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + default: break; } diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index a160305..5f71707 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -4923,3 +4923,68 @@ export const buyNameRequest = async (data, isFromExtension) => { throw new Error('User declined request'); } }; + +export const signForeignFees = async (data, isFromExtension) => { + const resPermission = await getUserPermission( + { + text1: `Do you give this application permission to sign the required fees for all your trade offers?`, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + const unsignedFeesUrl = await createEndpoint( + `/crosschain/unsignedfees/${address}` + ); + + const unsignedFeesResponse = await fetch(unsignedFeesUrl); + + const unsignedFees = await unsignedFeesResponse.json(); + + const signedFees = []; + + unsignedFees.forEach((unsignedFee) => { + const unsignedDataDecoded = Base58.decode(unsignedFee.data); + + const signature = nacl.sign.detached( + unsignedDataDecoded, + keyPair.privateKey + ); + + const signedFee = { + timestamp: unsignedFee.timestamp, + data: `${Base58.encode(signature)}`, + atAddress: unsignedFee.atAddress, + fee: unsignedFee.fee, + }; + + signedFees.push(signedFee); + }); + + const signedFeesUrl = await createEndpoint(`/crosschain/signedfees`); + + await fetch(signedFeesUrl, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: `${JSON.stringify(signedFees)}`, + }); + + return true; + } else { + throw new Error('User declined request'); + } +}; From ab1eaeb338a602064af8a85aa19e90cb614a1c7e Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 3 May 2025 00:12:39 +0300 Subject: [PATCH 297/717] fixes --- src/App.tsx | 34 +++++++------ src/components/Apps/AppsDesktop.tsx | 19 ++++++- src/components/QortPayment.tsx | 6 +++ src/components/ReactionPicker.tsx | 29 +++++++---- src/components/Theme/ThemeContext.tsx | 8 +-- src/components/Theme/ThemeManager.tsx | 20 +++++--- src/qortalRequests/get.ts | 73 +++++++++++++++++++++++++-- 7 files changed, 147 insertions(+), 42 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 8a36a7d..b57bc95 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1028,12 +1028,7 @@ function App() { const logoutFunc = useCallback(async () => { try { - if (hasSettingsChanged) { - await showUnsavedChanges({ - message: - 'Your settings have changed. If you logout you will lose your changes. Click on the save button in the header to keep your changed settings.', - }); // TODO translate - } else if (extState === 'authenticated') { + if (extState === 'authenticated') { await showUnsavedChanges({ message: 'Are you sure you would like to logout?', }); @@ -3014,13 +3009,16 @@ function App() { })} - { returnToMain(); }} > - {t('core:action.continue', { postProcess: 'capitalize' })} - + + {t('core:action.continue', { postProcess: 'capitalize' })} + + )} {extState === 'transfer-success-request' && ( @@ -3221,7 +3219,7 @@ function App() { onClick={onOkUnsavedChanges} autoFocus > - {t('core:action.decline', { + {t('core:action.continue_logout', { postProcess: 'capitalize', })} @@ -3270,6 +3268,8 @@ function App() { lineHeight: 1.2, maxWidth: '90%', textAlign: 'center', + fontSize: '16px', + marginBottom: '10px', }} > {messageQortalRequestExtension?.text1} @@ -3342,11 +3342,15 @@ function App() { )} {messageQortalRequestExtension?.html && ( -
    + <> + + +
    + )} diff --git a/src/components/Apps/AppsDesktop.tsx b/src/components/Apps/AppsDesktop.tsx index b5a5cba..80f57b9 100644 --- a/src/components/Apps/AppsDesktop.tsx +++ b/src/components/Apps/AppsDesktop.tsx @@ -414,8 +414,23 @@ export const AppsDesktop = ({ setDesktopViewMode('dev'); }} > - - + + )} diff --git a/src/components/QortPayment.tsx b/src/components/QortPayment.tsx index d9baa65..3b48590 100644 --- a/src/components/QortPayment.tsx +++ b/src/components/QortPayment.tsx @@ -158,6 +158,12 @@ export const QortPayment = ({ balance, show, onSuccess, defaultPaymentTo }) => { value={paymentPassword} onChange={(e) => setPaymentPassword(e.target.value)} autoComplete="off" + onKeyDown={(e) => { + if (e.key === 'Enter') { + if (isLoadingSendCoin) return; + sendCoinFunc(); + } + }} /> diff --git a/src/components/ReactionPicker.tsx b/src/components/ReactionPicker.tsx index c95adcf..911a396 100644 --- a/src/components/ReactionPicker.tsx +++ b/src/components/ReactionPicker.tsx @@ -27,15 +27,25 @@ export const ReactionPicker = ({ onReaction }) => { if (showPicker) { setShowPicker(false); } else { - // Get the button's position const buttonRect = buttonRef.current.getBoundingClientRect(); const pickerWidth = 350; + const pickerHeight = 400; // Match Picker height prop - // Calculate position to align the right edge of the picker with the button's right edge - setPickerPosition({ - top: buttonRect.bottom + window.scrollY, // Position below the button - left: buttonRect.right + window.scrollX - pickerWidth, // Align right edges - }); + // Initial position (below the button) + let top = buttonRect.bottom + window.scrollY; + let left = buttonRect.right + window.scrollX - pickerWidth; + + // If picker would overflow bottom, show it above the button + const overflowBottom = + top + pickerHeight > window.innerHeight + window.scrollY; + if (overflowBottom) { + top = buttonRect.top + window.scrollY - pickerHeight; + } + + // Optional: prevent overflow on the left too + if (left < 0) left = 0; + + setPickerPosition({ top, left }); setShowPicker(true); } }; @@ -92,12 +102,13 @@ export const ReactionPicker = ({ onReaction }) => { allowExpandReactions={true} autoFocusSearch={false} emojiStyle={EmojiStyle.NATIVE} - height="450" + height={400} onEmojiClick={handlePicker} onReactionClick={handleReaction} - reactionsDefaultOpen={true} + // reactionsDefaultOpen={true} + // open={true} theme={Theme.DARK} - width="350" + width={350} />
    , document.body diff --git a/src/components/Theme/ThemeContext.tsx b/src/components/Theme/ThemeContext.tsx index 72d4f3b..3ebace0 100644 --- a/src/components/Theme/ThemeContext.tsx +++ b/src/components/Theme/ThemeContext.tsx @@ -25,7 +25,7 @@ const ThemeContext = createContext({ toggleTheme: () => {}, userThemes: [defaultTheme], addUserTheme: (themes) => {}, - setUserTheme: (theme) => {}, + setUserTheme: (theme, themes) => {}, currentThemeId: 'default', }); @@ -83,13 +83,13 @@ export const ThemeProvider = ({ children }) => { saveSettings(themes); }; - const setUserTheme = (theme) => { + const setUserTheme = (theme, themes) => { if (theme.id === 'default') { setCurrentThemeId('default'); - saveSettings(userThemes, themeMode, 'default'); + saveSettings(themes || userThemes, themeMode, 'default'); } else { setCurrentThemeId(theme.id); - saveSettings(userThemes, themeMode, theme.id); + saveSettings(themes || userThemes, themeMode, theme.id); } }; diff --git a/src/components/Theme/ThemeManager.tsx b/src/components/Theme/ThemeManager.tsx index eca0e9f..badcb42 100644 --- a/src/components/Theme/ThemeManager.tsx +++ b/src/components/Theme/ThemeManager.tsx @@ -119,7 +119,7 @@ export default function ThemeManager() { const newTheme = { ...themeDraft, id: uid.rnd() }; const updatedThemes = [...userThemes, newTheme]; addUserTheme(updatedThemes); - setUserTheme(newTheme); + setUserTheme(newTheme, updatedThemes); } setOpenEditor(false); }; @@ -135,19 +135,22 @@ export default function ThemeManager() { ); if (defaultTheme) { - setUserTheme(defaultTheme); + setUserTheme(defaultTheme, updatedThemes); } else { // Emergency fallback - setUserTheme({ - light: lightThemeOptions, - dark: darkThemeOptions, - }); + setUserTheme( + { + light: lightThemeOptions, + dark: darkThemeOptions, + }, + updatedThemes + ); } } }; const handleApplyTheme = (theme) => { - setUserTheme(theme); + setUserTheme(theme, null); }; const handleColorChange = (mode, fieldPath, color) => { @@ -210,7 +213,8 @@ export default function ThemeManager() { const newTheme = { ...importedTheme, id: uid.rnd() }; const updatedThemes = [...userThemes, newTheme]; addUserTheme(updatedThemes); - setUserTheme(newTheme); + + setUserTheme(newTheme, updatedThemes); } catch (error) { console.error(error); } diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index d7b040e..0a0221a 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -2680,7 +2680,7 @@ export const updateForeignFee = async (data) => { const { coin, type, value } = data; const url = `/crosschain/${coin.toLowerCase()}/update${type}`; - + const valueStringified = JSON.stringify(+value); try { const endpoint = await createEndpoint(url); const response = await fetch(endpoint, { @@ -2689,7 +2689,7 @@ export const updateForeignFee = async (data) => { Accept: '*/*', 'Content-Type': 'application/json', }, - body: JSON.stringify({ value }), + body: valueStringified, }); if (!response.ok) throw new Error('Failed to update foreign fee'); @@ -3493,6 +3493,35 @@ export const sendCoin = async (data, isFromExtension) => { } }; +function calculateFeeFromRate(feePerKb, sizeInBytes) { + return (feePerKb / 1000) * sizeInBytes; +} + +const getBuyingFees = async (foreignBlockchain) => { + const ticker = sellerForeignFee[foreignBlockchain].ticker; + if (!ticker) throw new Error('invalid foreign blockchain'); + const unlockFee = await getForeignFee({ + coin: ticker, + type: 'feerequired', + }); + const lockFee = await getForeignFee({ + coin: ticker, + type: 'feekb', + }); + return { + ticker: ticker, + lock: { + sats: lockFee, + fee: lockFee / QORT_DECIMALS, + }, + unlock: { + sats: unlockFee, + fee: unlockFee / QORT_DECIMALS, + byteFee300: calculateFeeFromRate(+unlockFee, 300) / QORT_DECIMALS, + }, + }; +}; + export const createBuyOrder = async (data, isFromExtension) => { const requiredFields = ['crosschainAtInfo', 'foreignBlockchain']; const missingFields: string[] = []; @@ -3528,6 +3557,7 @@ export const createBuyOrder = async (data, isFromExtension) => { const crosschainAtInfo = await Promise.all(atPromises); try { + const buyingFees = await getBuyingFees(foreignBlockchain); const resPermission = await getUserPermission( { text1: @@ -3541,10 +3571,45 @@ export const createBuyOrder = async (data, isFromExtension) => { return latest + +cur?.expectedForeignAmount; }, 0) )} - ${` ${crosschainAtInfo?.[0]?.foreignBlockchain}`}`, + ${` ${buyingFees.ticker}`}`, highlightedText: `Is using public node: ${isGateway}`, fee: '', - foreignFee: `${sellerForeignFee[foreignBlockchain].value} ${sellerForeignFee[foreignBlockchain].ticker}`, + html: ` +
    + + +
    +
    Total Unlocking Fee:
    +
    ${(+buyingFees?.unlock?.byteFee300 * atAddresses?.length)?.toFixed(8)} ${buyingFees.ticker}
    +
    + This fee is an estimate based on ${atAddresses?.length} ${atAddresses?.length > 1 ? 'orders' : 'order'} at a 300 byte cost of ${buyingFees?.unlock?.byteFee300?.toFixed(8)} +
    + +
    Total Locking Fee:
    +
    ${+buyingFees?.unlock.fee.toFixed(8)} ${buyingFees.ticker} per kb
    + +
    +
    +`, }, isFromExtension ); From b1af797ad32243c94f4d724af9849ce5868be4bd Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 3 May 2025 01:58:37 +0300 Subject: [PATCH 298/717] fix colors --- src/background.ts | 2 +- src/components/Apps/AppsDevModeNavBar.tsx | 2 +- src/qortalRequests/get.ts | 31 +++++++++-------------- src/styles/theme-dark.ts | 5 ++++ src/styles/theme-light.ts | 5 ++++ 5 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/background.ts b/src/background.ts index dca7e62..b34a01b 100644 --- a/src/background.ts +++ b/src/background.ts @@ -3689,7 +3689,7 @@ export const checkThreads = async (bringBack) => { dataToBringBack.push(thread); } } catch (error) { - conosle.log({ error }); + console.log({ error }); } } diff --git a/src/components/Apps/AppsDevModeNavBar.tsx b/src/components/Apps/AppsDevModeNavBar.tsx index 1c0422a..1bfcf42 100644 --- a/src/components/Apps/AppsDevModeNavBar.tsx +++ b/src/components/Apps/AppsDevModeNavBar.tsx @@ -180,7 +180,7 @@ export const AppsDevModeNavBar = () => { > @@ -3605,7 +3602,7 @@ export const createBuyOrder = async (data, isFromExtension) => {
    Total Locking Fee:
    -
    ${+buyingFees?.unlock.fee.toFixed(8)} ${buyingFees.ticker} per kb
    +
    ${+buyingFees?.lock.fee.toFixed(8)} ${buyingFees.ticker} per kb
    @@ -5204,19 +5201,15 @@ export const multiPaymentWithPrivateData = async (data, isFromExtension) => { html: `
    -
    Total Unlocking Fee:
    +
    ${i18n.t('question:total_unlocking_fee', { + postProcess: 'capitalizeFirstChar', + })}
    ${(+buyingFees?.unlock?.fee * atAddresses?.length)?.toFixed(8)} ${buyingFees.ticker}
    - This fee is an estimate based on ${atAddresses?.length} ${atAddresses?.length > 1 ? 'orders' : 'order'}, assuming a 300-byte size at a rate of ${buyingFees?.unlock?.feePerKb?.toFixed(8)} ${buyingFees.ticker} per KB. -
    - -
    Total Locking Fee:
    -
    ${+buyingFees?.lock.fee.toFixed(8)} ${buyingFees.ticker} per kb
    - + ${i18n.t('question:permission_buy_order_fee_estimation', { + quantity: atAddresses?.length, + fee: buyingFees?.unlock?.feePerKb?.toFixed(8), + ticker: buyingFees.ticker, + postProcess: 'capitalizeFirstChar', + })} +
    +
    ${i18n.t('question:total_locking_fee', { + postProcess: 'capitalizeFirstChar', + })}
    +
    ${i18n.t('question:permission_buy_order_per_kb', { + fee: +buyingFees?.lock.fee.toFixed(8), + ticker: buyingFees.ticker, + postProcess: 'capitalizeFirstChar', + })} +
    `, From a89de90ee01b64226dcc0f434da1b84723fcef34 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 23 May 2025 15:02:16 +0200 Subject: [PATCH 433/717] Remove notificationCase function --- src/background-cases.ts | 38 -------------------------------------- src/background.ts | 3 --- 2 files changed, 41 deletions(-) diff --git a/src/background-cases.ts b/src/background-cases.ts index f10dbdd..56697a7 100644 --- a/src/background-cases.ts +++ b/src/background-cases.ts @@ -900,44 +900,6 @@ export async function removeAdminCase(request, event) { } } -export async function notificationCase(request, event) { - try { - const notificationId = 'chat_notification_' + Date.now(); // Create a unique ID - - // chrome.notifications.create(notificationId, { - // type: "basic", - // iconUrl: "qort.png", // Add an appropriate icon for chat notifications - // title: "New Group Message!", - // message: "You have received a new message from one of your groups", - // priority: 2, // Use the maximum priority to ensure it's - // }); - // Set a timeout to clear the notification after 'timeout' milliseconds - // setTimeout(() => { - // chrome.notifications.clear(notificationId); - // }, 3000); - - // event.source.postMessage( - // { - // requestId: request.requestId, - // action: "notification", - // payload: true, - // type: "backgroundMessageResponse", - // }, - // event.origin - // ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: 'notification', - error: 'Error displaying notifaction', - type: 'backgroundMessageResponse', - }, - event.origin - ); - } -} - export async function addTimestampEnterChatCase(request, event) { try { const { groupId, timestamp } = request.payload; diff --git a/src/background.ts b/src/background.ts index 2d81e2e..cbc96b3 100644 --- a/src/background.ts +++ b/src/background.ts @@ -3209,9 +3209,6 @@ function setupMessageListener() { case 'removeAdmin': removeAdminCase(request, event); break; - case 'notification': - notificationCase(request, event); - break; case 'addTimestampEnterChat': addTimestampEnterChatCase(request, event); break; From 421996b572a58fe729cf0d5d92b811266e189035 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 23 May 2025 15:02:50 +0200 Subject: [PATCH 434/717] Add transaltions --- src/i18n/locales/en/question.json | 9 +++++-- src/qortalRequests/get.ts | 40 ++++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/i18n/locales/en/question.json b/src/i18n/locales/en/question.json index 28000a5..ac96ab3 100644 --- a/src/i18n/locales/en/question.json +++ b/src/i18n/locales/en/question.json @@ -11,6 +11,8 @@ "message": { "error": { "add_to_list": "failed to add to list", + "at_info": "cannot find AT info.", + "buy_order": "failed to submit trade order", "cancel_sell_order": "failed to Cancel Sell Order. Try again!", "copy_clipboard": "failed to copy to clipboard", "create_sell_order": "failed to Create Sell Order. Try again!", @@ -20,13 +22,14 @@ "encrypt": "unable to encrypt", "encryption_failed": "encryption failed", "encryption_requires_public_key": "encrypting data requires public keys", - "fetch_balance": "unable to fetch balance", "fetch_balance_token": "failed to fetch {{ token }} Balance. Try again!", + "fetch_balance": "unable to fetch balance", "fetch_generic": "unable to fetch", "fetch_group": "failed to fetch the group", "fetch_list": "failed to fetch the list", "fetch_poll": "failed to fetch poll", "fetch_recipient_public_key": "failed to fetch recipient's public key", + "get_foreign_fee": "error in get foreign fee", "insufficient_balance_qort": "your QORT balance is insufficient", "insufficient_balance": "your asset balance is insufficient", "insufficient_funds": "insufficient funds", @@ -51,8 +54,8 @@ "send": "failed to send", "submit_sell_order": "failed to submit sell order", "timeout_request": "request timed out", - "get_foreign_fee": "error in get foreign fee", "update_foreign_fee": "failed to update foreign fee", + "update_tradebot": "unable to update tradebot", "upload_encryption": "upload failed due to failed encryption", "user_qortal_name": "user has no Qortal name" }, @@ -92,6 +95,8 @@ "permission_pay_publish": "do you give this application permission to make the following payments and publishes?", "permission_publish_qdn": "do you give this application permission to publish to QDN?", "permission_remove_from_list": "do you give this application permission to remove the following from the list {{ name }}:", + "permission_sell_order": "do you give this application permission to perform a sell order?", + "permission_sell_order_detail": "{{ qort_amount }} QORT for {{ foreign_amount }} {{ ticker }}", "permission_send_coins": "do you give this application permission to send coins?", "permission_transfer_asset": "do you give this application permission to transfer the following asset?", "poll": "poll: {{ name }}", diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index 90218e7..c098450 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -4335,7 +4335,12 @@ export const createBuyOrder = async (data, isFromExtension) => { ); } } catch (error) { - throw new Error(error?.message || 'Failed to submit trade order.'); + throw new Error( + error?.message || + i18n.t('question:message.error.buy_order', { + postProcess: 'capitalizeFirstChar', + }) + ); } }; @@ -4352,7 +4357,14 @@ const cancelTradeOfferTradeBot = async (body, keyPair) => { body: bodyToString, }); - if (!deleteTradeBotResponse.ok) throw new Error('Unable to update tradebot'); + if (!deleteTradeBotResponse.ok) { + throw new Error( + i18n.t('question:message.error.update_tradebot', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const unsignedTxn = await deleteTradeBotResponse.text(); const signedTxnBytes = await signTradeBotTransaction(unsignedTxn, keyPair); const signedBytes = Base58.encode(signedTxnBytes); @@ -4505,11 +4517,15 @@ export const createSellOrder = async (data, isFromExtension) => { try { const resPermission = await getUserPermission( { - text1: - 'Do you give this application permission to perform a sell order?', - text2: `${data.qortAmount}${' '} - ${`QORT`}`, - text3: `FOR ${parsedForeignAmount} ${data.foreignBlockchain}`, + text1: i18n.t('question:permission_sell_order', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:permission_sell_order_detail', { + qort_amount: data.qortAmount, + foreign_amount: parsedForeignAmount, + ticker: data.foreignBlockchain, + postProcess: 'capitalizeFirstChar', + }), fee: '0.02', }, isFromExtension @@ -4559,6 +4575,7 @@ export const createSellOrder = async (data, isFromExtension) => { export const cancelSellOrder = async (data, isFromExtension) => { const requiredFields = ['atAddress']; const missingFields: string[] = []; + requiredFields.forEach((field) => { if (!data[field]) { missingFields.push(field); @@ -4576,7 +4593,14 @@ export const cancelSellOrder = async (data, isFromExtension) => { const url = await createEndpoint(`/crosschain/trade/${data.atAddress}`); const resAddress = await fetch(url); const resData = await resAddress.json(); - if (!resData?.qortalAtAddress) throw new Error('Cannot find AT info.'); + + if (!resData?.qortalAtAddress) + throw new Error( + i18n.t('question:message.error.at_info', { + postProcess: 'capitalizeFirstChar', + }) + ); + try { const fee = await getFee('MESSAGE'); From 0539995a5dff98fc0949a1b5202b3630c66a9f2b Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 23 May 2025 16:10:54 +0200 Subject: [PATCH 435/717] Add translations --- src/i18n/locales/en/question.json | 29 ++++ src/qortalRequests/get.ts | 230 ++++++++++++++++++++++++------ 2 files changed, 213 insertions(+), 46 deletions(-) diff --git a/src/i18n/locales/en/question.json b/src/i18n/locales/en/question.json index ac96ab3..fc0d9f3 100644 --- a/src/i18n/locales/en/question.json +++ b/src/i18n/locales/en/question.json @@ -1,7 +1,10 @@ { + "accept_app_fee": "accept app fee", "always_authenticate": "always authenticate automatically", + "always_chat_messages": "always allow chat messages from this app", "always_retrieve_balance": "always allow balance to be retrieved automatically", "always_retrieve_list": "always allow lists to be retrieved automatically", + "always_retrieve_wallet": "always allow wallet to be retrieved automatically", "amount_qty": "amount: {{ quantity }}", "asset_name": "asset: {{ asset }}", "assets_used_pay": "asset used in payments: {{ asset }}", @@ -29,11 +32,20 @@ "fetch_list": "failed to fetch the list", "fetch_poll": "failed to fetch poll", "fetch_recipient_public_key": "failed to fetch recipient's public key", + "fetch_wallet": "fetch Wallet Failed. Please try again", + "file_extension": "a file extension could not be derived", + "gateway_balance_local_node": "cannot view {{ token }} balance through the gateway. Please use your local node.", + "gateway_non_qort_local_node": "cannot send a non-QORT coin through the gateway. Please use your local node.", + "gateway_retrieve_balance": "retrieving {{ token }} balance is not allowed through a gateway", + "gateway_wallet_local_node": "cannot view {{ token }} wallet through the gateway. Please use your local node.", "get_foreign_fee": "error in get foreign fee", "insufficient_balance_qort": "your QORT balance is insufficient", "insufficient_balance": "your asset balance is insufficient", "insufficient_funds": "insufficient funds", + "invalid_fullcontent": "field fullContent is in an invalid format. Either use a string, base64 or an object", "invalid_receiver": "invalid receiver address or name", + "invalid_type": "invalid type", + "mime_type": "a mimeType could not be derived", "missing_fields": "missing fields: {{ fields }}", "name_already_for_sale": "this name is already for sale", "name_not_for_sale": "this name is not for sale", @@ -43,23 +55,35 @@ "no_group_found": "group not found", "no_group_key": "no group key found", "no_poll": "poll not found", + "no_resources_publish": "no resources to publish", "node_info": "failed to retrieve node info", "node_status": "failed to retrieve node status", + "only_encrypted_data": "only encrypted data can go into private services", "perform_request": "failed to perform request", + "poll_create": "failed to create poll", + "poll_vote": "failed to vote on the poll", "process_transaction": "unable to process transaction", "registered_name": "a registered name is needed to publish", + "resources_publish": "some resources have failed to publish", "retrieve_file": "failed to retrieve file", "retrieve_keys": "unable to retrieve keys", "same_foreign_blockchain": "all requested ATs need to be of the same foreign Blockchain.", "send": "failed to send", + "server_info": "error in retrieving server info", "submit_sell_order": "failed to submit sell order", + "synchronization_attempts": "failed to synchronize after {{ quantity }} attempts", "timeout_request": "request timed out", + "token_not_supported": "{{ token }} is not supported for this call", + "unknown_error": "unknown error", "update_foreign_fee": "failed to update foreign fee", "update_tradebot": "unable to update tradebot", "upload_encryption": "upload failed due to failed encryption", + "upload": "upload failed", + "use_private_service": "for an encrypted publish please use a service that ends with _PRIVATE", "user_qortal_name": "user has no Qortal name" }, "generic": { + "confirm_join_group": "confirm joining the group:", "include_data_decrypt": "please include data to decrypt", "include_data_encrypt": "please include data to encrypt", "max_retry_transaction": "max retries reached. Skipping transaction.", @@ -72,6 +96,7 @@ "user_declined_join": "user declined to join group", "user_declined_list": "user declined to get list of hosted resources", "user_declined_request": "user declined request", + "user_declined_save_file": "user declined to save file", "user_declined_share_list": "user declined to share list", "user_declined_send_message": "user declined to send message" } @@ -99,9 +124,13 @@ "permission_sell_order_detail": "{{ qort_amount }} QORT for {{ foreign_amount }} {{ ticker }}", "permission_send_coins": "do you give this application permission to send coins?", "permission_transfer_asset": "do you give this application permission to transfer the following asset?", + "permission_get_wallet_info": "do you give this application permission to get your wallet information?", + "permission_send_chat_message": "do you give this application permission to send this chat message?", "poll": "poll: {{ name }}", + "provide_recipient_group_id": "please provide a recipient or groupId", "request_create_poll": "you are requesting to create the poll below:", "request_vote_poll": "you are being requested to vote on the poll below:", + "to_group": "to: group {{ group_id }}", "to_recipient": "to: {{ recipient }}", "total_locking_fee": "total Locking Fee:", "total_unlocking_fee": "total Unlocking Fee:" diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index c098450..e137770 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -1398,7 +1398,9 @@ export const publishQDNResource = async ( (handleDynamicValues['appFee'] = +appFee + +feePayment.fee), (handleDynamicValues['checkbox1'] = { value: true, - label: 'accept app fee', + label: i18n.t('question:accept_app_fee', { + postProcess: 'capitalizeFirstChar', + }), }); } if (!!data?.encrypt) { @@ -1490,8 +1492,13 @@ export const checkArrrSyncStatus = async (seed) => { } } - // If we exceed 6 tries, throw an error - throw new Error('Failed to synchronize after 36 attempts'); + // If we exceed N tries, throw an error + throw new Error( + i18n.t('question:message.error.synchronization_attempts', { + quantity: 36, + postProcess: 'capitalizeFirstChar', + }) + ); }; export const publishMultipleQDNResources = async ( @@ -1501,12 +1508,13 @@ export const publishMultipleQDNResources = async ( ) => { const requiredFields = ['resources']; const missingFields: string[] = []; - let feeAmount = null; + requiredFields.forEach((field) => { if (!data[field]) { missingFields.push(field); } }); + if (missingFields.length > 0) { const missingFieldsString = missingFields.join(', '); const errorMsg = i18n.t('question:message.error.missing_fields', { @@ -1515,24 +1523,38 @@ export const publishMultipleQDNResources = async ( }); throw new Error(errorMsg); } + const resources = data.resources; if (!Array.isArray(resources)) { - throw new Error('Invalid data'); + throw new Error( + i18n.t('group:message.generic.invalid_data', { + postProcess: 'capitalizeFirstChar', + }) + ); } + if (resources.length === 0) { - throw new Error('No resources to publish'); + throw new Error( + i18n.t('question:message.error.no_resources_publish', { + postProcess: 'capitalizeFirstChar', + }) + ); } const encrypt = data?.encrypt; for (const resource of resources) { const resourceEncrypt = encrypt && resource?.disableEncrypt !== true; + if (!resourceEncrypt && resource?.service.endsWith('_PRIVATE')) { - const errorMsg = 'Only encrypted data can go into private services'; + const errorMsg = i18n.t('question:message.error.only_encrypted_data', { + postProcess: 'capitalizeFirstChar', + }); throw new Error(errorMsg); } else if (resourceEncrypt && !resource?.service.endsWith('_PRIVATE')) { - const errorMsg = - 'For an encrypted publish please use a service that ends with _PRIVATE'; + const errorMsg = i18n.t('question:message.error.use_private_service', { + postProcess: 'capitalizeFirstChar', + }); throw new Error(errorMsg); } } @@ -1540,6 +1562,7 @@ export const publishMultipleQDNResources = async ( const fee = await getFee('ARBITRARY'); const registeredName = await getNameInfo(); const name = registeredName; + if (!name) { throw new Error( i18n.t('question:message.error.registered_name', { @@ -1547,9 +1570,11 @@ export const publishMultipleQDNResources = async ( }) ); } + const appFee = data?.appFee ? +data.appFee : undefined; const appFeeRecipient = data?.appFeeRecipient; let hasAppFee = false; + if (appFee && appFee > 0 && appFeeRecipient) { hasAppFee = true; } @@ -1561,7 +1586,9 @@ export const publishMultipleQDNResources = async ( (handleDynamicValues['appFee'] = +appFee + +feePayment.fee), (handleDynamicValues['checkbox1'] = { value: true, - label: 'accept app fee', + label: i18n.t('question:accept_app_fee', { + postProcess: 'capitalizeFirstChar', + }), }); } if (data?.encrypt) { @@ -1575,8 +1602,6 @@ export const publishMultipleQDNResources = async ( html: `
    + + ${data.resources + .map( + (resource) => ` +
    +
    Service: ${ + resource.service + }
    +
    Name: ${name}
    +
    Identifier: ${ + resource.identifier + }
    + ${ + resource.filename + ? `
    Filename: ${resource.filename}
    ` + : '' + } +
    ` + ) + .join('')} +
    + + `, + fee: +fee.fee * resources.length, + ...handleDynamicValues, + }, + isFromExtension + ); + + const { accepted, checkbox1 = false } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + type FailedPublish = { + reason: string; + identifier: any; + service: any; + }; + + const failedPublishesIdentifiers: FailedPublish[] = []; + + for (const resource of resources) { + try { + const requiredFields = ['service']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!resource[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + failedPublishesIdentifiers.push({ + reason: errorMsg, + identifier: resource.identifier, + service: resource.service, + }); + continue; + } + if (!resource.file && !resource.data64 && !resource?.base64) { + const errorMsg = i18n.t( + 'question:message.error.no_data_file_submitted', + { + postProcess: 'capitalizeFirstChar', + } + ); + failedPublishesIdentifiers.push({ + reason: errorMsg, + identifier: resource.identifier, + service: resource.service, + }); + continue; + } + const service = resource.service; + let identifier = resource.identifier; + let data64 = resource?.data64 || resource?.base64; + const filename = resource.filename; + const title = resource.title; + const description = resource.description; + const category = resource.category; + const tags = resource?.tags || []; + const result = {}; + + // Fill tags dynamically while maintaining backward compatibility + for (let i = 0; i < 5; i++) { + result[`tag${i + 1}`] = tags[i] || resource[`tag${i + 1}`] || undefined; + } + + // Access tag1 to tag5 from result + const { tag1, tag2, tag3, tag4, tag5 } = result; + const resourceEncrypt = encrypt && resource?.disableEncrypt !== true; + if (resource.identifier == null) { + identifier = 'default'; + } + if (!resourceEncrypt && service.endsWith('_PRIVATE')) { + const errorMsg = i18n.t('question:message.error.only_encrypted_data', { + postProcess: 'capitalizeFirstChar', + }); + failedPublishesIdentifiers.push({ + reason: errorMsg, + identifier: resource.identifier, + service: resource.service, + }); + continue; + } + if (resource.file) { + data64 = await fileToBase64(resource.file); + } + if (resourceEncrypt) { + try { + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const privateKey = parsedData.privateKey; + const userPublicKey = parsedData.publicKey; + const encryptDataResponse = encryptDataGroup({ + data64, + publicKeys: data.publicKeys, + privateKey, + userPublicKey, + }); + if (encryptDataResponse) { + data64 = encryptDataResponse; + } + } catch (error) { + const errorMsg = + error?.message || + i18n.t('question:message.error.upload_encryption', { + postProcess: 'capitalizeFirstChar', + }); + failedPublishesIdentifiers.push({ + reason: errorMsg, + identifier: resource.identifier, + service: resource.service, + }); + continue; + } + } + + try { + await retryTransaction( + publishData, + [ + { + registeredName: encodeURIComponent(name), + file: data64, + service: service, + identifier: encodeURIComponent(identifier), + uploadType: 'file', + isBase64: true, + filename: filename, + title, + description, + category, + tag1, + tag2, + tag3, + tag4, + tag5, + apiVersion: 2, + withFee: true, + }, + ], + true + ); + await new Promise((res) => { + setTimeout(() => { + res(); + }, 1000); + }); + } catch (error) { + const errorMsg = + error.message || + i18n.t('question:message.error.upload', { + postProcess: 'capitalizeFirstChar', + }); + failedPublishesIdentifiers.push({ + reason: errorMsg, + identifier: resource.identifier, + service: resource.service, + }); + } + } catch (error) { + failedPublishesIdentifiers.push({ + reason: + error?.message || + i18n.t('question:message.error.unknown_error', { + postProcess: 'capitalizeFirstChar', + }), + identifier: resource.identifier, + service: resource.service, + }); + } + } + if (failedPublishesIdentifiers.length > 0) { + const obj = { + message: i18n.t('question:message.error.resources_publish', { + postProcess: 'capitalizeFirstChar', + }), + }; + obj['error'] = { + unsuccessfulPublishes: failedPublishesIdentifiers, + }; + return obj; + } + if (hasAppFee && checkbox1) { + sendCoinFunc( + { + amount: appFee, + receiver: appFeeRecipient, + }, + true + ); + } + return true; +}; + +export const voteOnPoll = async (data, isFromExtension) => { + const requiredFields = ['pollName', 'optionIndex']; + const missingFields: string[] = []; + + requiredFields.forEach((field) => { + if (!data[field] && data[field] !== 0) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const pollName = data.pollName; + const optionIndex = data.optionIndex; + let pollInfo = null; + try { + const url = await createEndpoint(`/polls/${encodeURIComponent(pollName)}`); + const response = await fetch(url); + if (!response.ok) { + const errorMessage = await parseErrorResponse( + response, + i18n.t('question:message.error.fetch_poll', { + postProcess: 'capitalizeFirstChar', + }) + ); + throw new Error(errorMessage); + } + + pollInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_poll', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + if (!pollInfo || pollInfo.error) { + const errorMsg = + (pollInfo && pollInfo.message) || + i18n.t('question:message.error.no_poll', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + try { + const optionName = pollInfo.pollOptions[optionIndex].optionName; + const resVoteOnPoll = await _voteOnPoll( + { pollName, optionIndex, optionName }, + isFromExtension + ); + return resVoteOnPoll; + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.poll_vote', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const createPoll = async (data, isFromExtension) => { + const requiredFields = [ + 'pollName', + 'pollDescription', + 'pollOptions', + 'pollOwnerAddress', + ]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const pollName = data.pollName; + const pollDescription = data.pollDescription; + const pollOptions = data.pollOptions; + try { + const resCreatePoll = await _createPoll( + { + pollName, + pollDescription, + options: pollOptions, + }, + isFromExtension + ); + return resCreatePoll; + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.poll_create', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +function isBase64(str) { + const base64Regex = + /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; + return base64Regex.test(str) && str.length % 4 === 0; +} + +function checkValue(value) { + if (typeof value === 'string') { + if (isBase64(value)) { + return 'string'; + } else { + return 'string'; + } + } else if (typeof value === 'object' && value !== null) { + return 'object'; + } else { + throw new Error( + i18n.t('question:message.error.invalid_fullcontent', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +} + +export const sendChatMessage = async (data, isFromExtension, appInfo) => { + const message = data?.message; + const fullMessageObject = data?.fullMessageObject || data?.fullContent; + const recipient = data?.destinationAddress || data.recipient; + const groupId = data.groupId; + const isRecipient = groupId === undefined; + const chatReference = data?.chatReference; + if (groupId === undefined && recipient === undefined) { + throw new Error( + i18n.t('question:provide_recipient_group_id', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + let fullMessageObjectType; + if (fullMessageObject) { + fullMessageObjectType = checkValue(fullMessageObject); + } + const value = + (await getPermission(`qAPPSendChatMessage-${appInfo?.name}`)) || false; + let skip = false; + if (value) { + skip = true; + } + let resPermission; + if (!skip) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_chat_message', { + postProcess: 'capitalizeFirstChar', + }), + text2: isRecipient + ? i18n.t('question:to_recipient', { + recipient: recipient, + postProcess: 'capitalizeFirstChar', + }) + : i18n.t('question:to_group', { + group_id: groupId, + postProcess: 'capitalizeFirstChar', + }), + text3: fullMessageObject + ? fullMessageObjectType === 'string' + ? `${fullMessageObject?.slice(0, 25)}${fullMessageObject?.length > 25 ? '...' : ''}` + : `${JSON.stringify(fullMessageObject)?.slice(0, 25)}${JSON.stringify(fullMessageObject)?.length > 25 ? '...' : ''}` + : `${message?.slice(0, 25)}${message?.length > 25 ? '...' : ''}`, + checkbox1: { + value: false, + label: i18n.t('question:always_chat_messages', { + postProcess: 'capitalizeFirstChar', + }), + }, + }, + isFromExtension + ); + } + const { accepted = false, checkbox1 = false } = resPermission || {}; + if (resPermission && accepted) { + setPermission(`qAPPSendChatMessage-${appInfo?.name}`, checkbox1); + } + if (accepted || skip) { + const tiptapJson = { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: message, + }, + ], + }, + ], + }; + const messageObject = fullMessageObject + ? fullMessageObject + : { + messageText: tiptapJson, + images: [], + repliedTo: '', + version: 3, + }; + + let stringifyMessageObject = JSON.stringify(messageObject); + if (fullMessageObjectType === 'string') { + stringifyMessageObject = messageObject; + } + + const balance = await getBalanceInfo(); + const hasEnoughBalance = +balance < 4 ? false : true; + if (!hasEnoughBalance) { + throw new Error( + i18n.t('group:message.error.qortals_required', { + quantity: 4, + postProcess: 'capitalizeFirstChar', + }) + ); + } + if (isRecipient && recipient) { + const url = await createEndpoint(`/addresses/publickey/${recipient}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_recipient_public_key', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let key; + let hasPublicKey; + let res; + const contentType = response.headers.get('content-type'); + + // If the response is JSON, parse it as JSON + if (contentType && contentType.includes('application/json')) { + res = await response.json(); + } else { + // Otherwise, treat it as plain text + res = await response.text(); + } + if (res?.error === 102) { + key = ''; + hasPublicKey = false; + } else if (res !== false) { + key = res; + hasPublicKey = true; + } else { + key = ''; + hasPublicKey = false; + } + + if (!hasPublicKey && isRecipient) { + throw new Error( + 'Cannot send an encrypted message to this user since they do not have their publickey on chain.' + ); + } + let _reference = new Uint8Array(64); + self.crypto.getRandomValues(_reference); + + let sendTimestamp = Date.now(); + + let reference = Base58.encode(_reference); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + let handleDynamicValues = {}; + if (chatReference) { + handleDynamicValues['chatReference'] = chatReference; + } + + const tx = await createTransaction(18, keyPair, { + timestamp: sendTimestamp, + recipient: recipient, + recipientPublicKey: key, + hasChatReference: chatReference ? 1 : 0, + message: stringifyMessageObject, + lastReference: reference, + proofOfWorkNonce: 0, + isEncrypted: 1, + isText: 1, + ...handleDynamicValues, + }); + + const chatBytes = tx.chatBytes; + const difficulty = 8; + const { nonce, chatBytesArray } = await performPowTask( + chatBytes, + difficulty + ); + + let _response = await signChatFunc(chatBytesArray, nonce, null, keyPair); + if (_response?.error) { + throw new Error(_response?.message); + } + return _response; + } else if (!isRecipient && groupId) { + let _reference = new Uint8Array(64); + self.crypto.getRandomValues(_reference); + + let reference = Base58.encode(_reference); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + let handleDynamicValues = {}; + if (chatReference) { + handleDynamicValues['chatReference'] = chatReference; + } + + const txBody = { + timestamp: Date.now(), + groupID: Number(groupId), + hasReceipient: 0, + hasChatReference: chatReference ? 1 : 0, + message: stringifyMessageObject, + lastReference: reference, + proofOfWorkNonce: 0, + isEncrypted: 0, // Set default to not encrypted for groups + isText: 1, + ...handleDynamicValues, + }; + + const tx = await createTransaction(181, keyPair, txBody); + + // if (!hasEnoughBalance) { + // throw new Error("Must have at least 4 QORT to send a chat message"); + // } + + const chatBytes = tx.chatBytes; + const difficulty = 8; + const { nonce, chatBytesArray } = await performPowTask( + chatBytes, + difficulty + ); + + let _response = await signChatFunc(chatBytesArray, nonce, null, keyPair); + if (_response?.error) { + throw new Error(_response?.message); + } + return _response; + } else { + throw new Error( + i18n.t('question:provide_recipient_group_id', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_send_message', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const joinGroup = async (data, isFromExtension) => { + const requiredFields = ['groupId']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${data.groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const fee = await getFee('JOIN_GROUP'); + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:message.generic.confirm_join_group', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${groupInfo.groupName}`, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const groupId = data.groupId; + + if (!groupInfo || groupInfo.error) { + const errorMsg = + (groupInfo && groupInfo.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + try { + const resJoinGroup = await joinGroupFunc({ groupId }); + return resJoinGroup; + } catch (error) { + throw new Error( + error?.message || + i18n.t('group:message.error.group_join', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_join', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const saveFile = async (data, sender, isFromExtension, snackMethods) => { + try { + const requiredFields = ['filename', 'blob']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const filename = data.filename; + const blob = data.blob; + const resPermission = await getUserPermission( + { + text1: i18n.t('question:download_file', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${filename}`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const mimeType = blob.type || data.mimeType; + let backupExention = filename.split('.').pop(); + if (backupExention) { + backupExention = '.' + backupExention; + } + const fileExtension = mimeToExtensionMap[mimeType] || backupExention; + let fileHandleOptions = {}; + if (!mimeType) { + throw new Error( + i18n.t('question:message.error.mime_type', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + if (!fileExtension) { + throw new Error( + i18n.t('question:message.error.file_extension', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + if (fileExtension && mimeType) { + fileHandleOptions = { + accept: { + [mimeType]: [fileExtension], + }, + }; + } + + showSaveFilePicker( + { + filename, + mimeType, + blob, + }, + snackMethods + ); + return true; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_save_file', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } catch (error) { + throw new Error( + error?.message || + i18n.t('core:message.error.initiate_download', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const deployAt = async (data, isFromExtension) => { + const requiredFields = [ + 'name', + 'description', + 'tags', + 'creationBytes', + 'amount', + 'assetId', + 'type', + ]; + + const missingFields: string[] = []; + + requiredFields.forEach((field) => { + if (!data[field] && data[field] !== 0) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + try { + const resDeployAt = await _deployAt( + { + name: data.name, + description: data.description, + tags: data.tags, + creationBytes: data.creationBytes, + amount: data.amount, + assetId: data.assetId, + atType: data.type, + }, + isFromExtension + ); + return resDeployAt; + } catch (error) { + throw new Error( + error?.message || + i18n.t('group:message.error.group_join', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getUserWallet = async (data, isFromExtension, appInfo) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const isGateway = await isRunningGateway(); + + if (data?.coin === 'ARRR' && isGateway) + throw new Error( + i18n.t('question:message.error.gateway_wallet_local_node', { + token: 'ARRR', + postProcess: 'capitalizeFirstChar', + }) + ); + + const value = + (await getPermission( + `qAPPAutoGetUserWallet-${appInfo?.name}-${data.coin}` + )) || false; + let skip = false; + if (value) { + skip = true; + } + + let resPermission; + + if (!skip) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.get_wallet_info', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `coin: ${data.coin}`, + checkbox1: { + value: true, + label: i18n.t('question:always_retrieve_wallet', { + postProcess: 'capitalizeFirstChar', + }), + }, + }, + isFromExtension + ); + } + const { accepted = false, checkbox1 = false } = resPermission || {}; + + if (resPermission) { + setPermission( + `qAPPAutoGetUserWallet-${appInfo?.name}-${data.coin}`, + checkbox1 + ); + } + + if (accepted || skip) { + let coin = data.coin; + let userWallet = {}; + let arrrAddress = ''; + const wallet = await getSaveWallet(); + const address = wallet.address0; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const arrrSeed58 = parsedData.arrrSeed58; + if (coin === 'ARRR') { + const bodyToString = arrrSeed58; + const url = await createEndpoint(`/crosschain/arrr/walletaddress`); + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: bodyToString, + }); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + arrrAddress = res; + } + switch (coin) { + case 'QORT': + userWallet['address'] = address; + userWallet['publickey'] = parsedData.publicKey; + break; + case 'BTC': + userWallet['address'] = parsedData.btcAddress; + userWallet['publickey'] = parsedData.btcPublicKey; + break; + case 'LTC': + userWallet['address'] = parsedData.ltcAddress; + userWallet['publickey'] = parsedData.ltcPublicKey; + break; + case 'DOGE': + userWallet['address'] = parsedData.dogeAddress; + userWallet['publickey'] = parsedData.dogePublicKey; + break; + case 'DGB': + userWallet['address'] = parsedData.dgbAddress; + userWallet['publickey'] = parsedData.dgbPublicKey; + break; + case 'RVN': + userWallet['address'] = parsedData.rvnAddress; + userWallet['publickey'] = parsedData.rvnPublicKey; + break; + case 'ARRR': + await checkArrrSyncStatus(parsedData.arrrSeed58); + userWallet['address'] = arrrAddress; + break; + default: + break; + } + return userWallet; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getWalletBalance = async ( + data, + bypassPermission?: boolean, + isFromExtension?: boolean, + appInfo?: any +) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const isGateway = await isRunningGateway(); + + if (data?.coin === 'ARRR' && isGateway) + throw new Error( + i18n.t('question:message.error.gateway_balance_local_node', { + token: 'ARRR', + postProcess: 'capitalizeFirstChar', + }) + ); + + const value = + (await getPermission( + `qAPPAutoWalletBalance-${appInfo?.name}-${data.coin}` + )) || false; + let skip = false; + if (value) { + skip = true; + } + let resPermission; + + if (!bypassPermission && !skip) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.fetch_balance', { + coin: data.coin, // TODO highlight coin in the modal + postProcess: 'capitalizeFirstChar', + }), + checkbox1: { + value: true, + label: i18n.t('question:always_retrieve_balance', { + postProcess: 'capitalizeFirstChar', + }), + }, + }, + isFromExtension + ); + } + const { accepted = false, checkbox1 = false } = resPermission || {}; + if (resPermission) { + setPermission( + `qAPPAutoWalletBalance-${appInfo?.name}-${data.coin}`, + checkbox1 + ); + } + if (accepted || bypassPermission || skip) { + let coin = data.coin; + const wallet = await getSaveWallet(); + const address = wallet.address0; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + if (coin === 'QORT') { + let qortAddress = address; + try { + const url = await createEndpoint(`/addresses/balance/${qortAddress}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_balance', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.fetch_wallet', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else { + let _url = ``; + let _body = null; + switch (coin) { + case 'BTC': + _url = await createEndpoint(`/crosschain/btc/walletbalance`); + + _body = parsedData.btcPublicKey; + break; + case 'LTC': + _url = await createEndpoint(`/crosschain/ltc/walletbalance`); + _body = parsedData.ltcPublicKey; + break; + case 'DOGE': + _url = await createEndpoint(`/crosschain/doge/walletbalance`); + _body = parsedData.dogePublicKey; + break; + case 'DGB': + _url = await createEndpoint(`/crosschain/dgb/walletbalance`); + _body = parsedData.dgbPublicKey; + break; + case 'RVN': + _url = await createEndpoint(`/crosschain/rvn/walletbalance`); + _body = parsedData.rvnPublicKey; + break; + case 'ARRR': + await checkArrrSyncStatus(parsedData.arrrSeed58); + _url = await createEndpoint(`/crosschain/arrr/walletbalance`); + _body = parsedData.arrrSeed58; + break; + default: + break; + } + try { + const response = await fetch(_url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: _body, + }); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + if (isNaN(Number(res))) { + throw new Error( + i18n.t('question:message.error.fetch_balance', { + postProcess: 'capitalizeFirstChar', + }) + ); + } else { + return (Number(res) / 1e8).toFixed(8); + } + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.fetch_balance', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +const getPirateWallet = async (arrrSeed58) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.error.gateway_retrieve_balance', { + token: 'PIRATECHAIN', + postProcess: 'capitalizeFirstChar', + }) + ); + } + const bodyToString = arrrSeed58; + await checkArrrSyncStatus(bodyToString); + const url = await createEndpoint(`/crosschain/arrr/walletaddress`); + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: bodyToString, + }); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + return res; +}; + +export const getUserWalletFunc = async (coin) => { + let userWallet = {}; + const wallet = await getSaveWallet(); + const address = wallet.address0; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + switch (coin) { + case 'QORT': + userWallet['address'] = address; + userWallet['publickey'] = parsedData.publicKey; + break; + case 'BTC': + case 'BITCOIN': + userWallet['address'] = parsedData.btcAddress; + userWallet['publickey'] = parsedData.btcPublicKey; + break; + case 'LTC': + case 'LITECOIN': + userWallet['address'] = parsedData.ltcAddress; + userWallet['publickey'] = parsedData.ltcPublicKey; + break; + case 'DOGE': + case 'DOGECOIN': + userWallet['address'] = parsedData.dogeAddress; + userWallet['publickey'] = parsedData.dogePublicKey; + break; + case 'DGB': + case 'DIGIBYTE': + userWallet['address'] = parsedData.dgbAddress; + userWallet['publickey'] = parsedData.dgbPublicKey; + break; + case 'RVN': + case 'RAVENCOIN': + userWallet['address'] = parsedData.rvnAddress; + userWallet['publickey'] = parsedData.rvnPublicKey; + break; + case 'ARRR': + case 'PIRATECHAIN': + const arrrAddress = await getPirateWallet(parsedData.arrrSeed58); + userWallet['address'] = arrrAddress; + break; + default: + break; + } + return userWallet; +}; + +export const getUserWalletInfo = async (data, isFromExtension, appInfo) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + if (data?.coin === 'ARRR') { + throw new Error( + i18n.t('question:message.error.token_not_supported', { + token: 'ARRR', + postProcess: 'capitalizeFirstChar', + }) + ); + } + const value = + (await getPermission(`getUserWalletInfo-${appInfo?.name}-${data.coin}`)) || + false; + let skip = false; + if (value) { + skip = true; + } + let resPermission; + + if (!skip) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.get_wallet_info', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `coin: ${data.coin}`, + checkbox1: { + value: true, + label: i18n.t('question:always_retrieve_wallet', { + postProcess: 'capitalizeFirstChar', + }), + }, + }, + isFromExtension + ); + } + const { accepted = false, checkbox1 = false } = resPermission || {}; + + if (resPermission) { + setPermission(`getUserWalletInfo-${appInfo?.name}-${data.coin}`, checkbox1); + } + + if (accepted || skip) { + let coin = data.coin; + let walletKeys = await getUserWalletFunc(coin); + + const _url = await createEndpoint( + `/crosschain/` + data.coin.toLowerCase() + `/addressinfos` + ); + let _body = { xpub58: walletKeys['publickey'] }; + try { + const response = await fetch(_url, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(_body), + }); + if (!response?.ok) + throw new Error( + i18n.t('question:message.error.fetch_wallet_info', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.fetch_wallet', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getUserWalletTransactions = async ( + data, + isFromExtension, + appInfo +) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const value = + (await getPermission( + `getUserWalletTransactions-${appInfo?.name}-${data.coin}` + )) || false; + let skip = false; + if (value) { + skip = true; + } + let resPermission; + + if (!skip) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.get_wallet_transactions', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `coin: ${data.coin}`, + checkbox1: { + value: true, + label: i18n.t('question:always_retrieve_wallet_transactions', { + postProcess: 'capitalizeFirstChar', + }), + }, + }, + isFromExtension + ); + } + const { accepted = false, checkbox1 = false } = resPermission || {}; + + if (resPermission) { + setPermission( + `getUserWalletTransactions-${appInfo?.name}-${data.coin}`, + checkbox1 + ); + } + + if (accepted || skip) { + const coin = data.coin; + const walletKeys = await getUserWalletFunc(coin); + let publicKey; + if (data?.coin === 'ARRR') { + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + publicKey = parsedData.arrrSeed58; + } else { + publicKey = walletKeys['publickey']; + } + + const _url = await createEndpoint( + `/crosschain/` + data.coin.toLowerCase() + `/wallettransactions` + ); + const _body = publicKey; + try { + const response = await fetch(_url, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: _body, + }); + if (!response?.ok) + throw new Error( + i18n.t('question:message.error.fetch_wallet_transactions', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.fetch_wallet_transactions', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getCrossChainServerInfo = async (data) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const _url = `/crosschain/` + data.coin.toLowerCase() + `/serverinfos`; + try { + const url = await createEndpoint(_url); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_generic', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + return res.servers; + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.server_info', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getTxActivitySummary = async (data) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const coin = data.coin; + const url = `/crosschain/txactivity?foreignBlockchain=${coin}`; // No apiKey here + + try { + const endpoint = await createEndpoint(url); + const response = await fetch(endpoint, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_generic', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + return res; // Return full response here + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.transaction_activity_summary', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getForeignFee = async (data) => { + const requiredFields = ['coin', 'type']; + const missingFields: string[] = []; + + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const { coin, type } = data; + const url = `/crosschain/${coin.toLowerCase()}/${type}`; + + try { + const endpoint = await createEndpoint(url); + const response = await fetch(endpoint, { + method: 'GET', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_generic', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + return res; // Return full response here + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.get_foreign_fee', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +function calculateRateFromFee(totalFee, sizeInBytes) { + const fee = (totalFee / sizeInBytes) * 1000; + return fee.toFixed(0); +} + +export const updateForeignFee = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const requiredFields = ['coin', 'type', 'value']; + const missingFields: string[] = []; + + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const { coin, type, value } = data; + + const text3 = + type === 'feerequired' + ? i18n.t('question:sats', { + amount: value, + postProcess: 'capitalizeFirstChar', + }) + : i18n.t('question:sats_per_kb', { + amount: value, + postProcess: 'capitalizeFirstChar', + }); + const text4 = + type === 'feerequired' + ? i18n.t('question:message.generic.calculate_fee', { + amount: value, + rate: calculateRateFromFee(value, 300), + postProcess: 'capitalizeFirstChar', + }) + : ''; + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.update_foreign_fee', { + postProcess: 'capitalizeFirstChar', + }), + text2: `type: ${type === 'feerequired' ? 'unlocking' : 'locking'}`, + text3: i18n.t('question:value', { + value: text3, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:coin', { + coin: coin, + postProcess: 'capitalizeFirstChar', + }), + }, + isFromExtension + ); + + const { accepted } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const url = `/crosschain/${coin.toLowerCase()}/update${type}`; + const valueStringified = JSON.stringify(+value); + + const endpoint = await createEndpoint(url); + const response = await fetch(endpoint, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: valueStringified, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.update_foreign_fee', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + return res; // Return full response here +}; + +export const getServerConnectionHistory = async (data) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + + // Validate required fields + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const coin = data.coin.toLowerCase(); + const url = `/crosschain/${coin.toLowerCase()}/serverconnectionhistory`; + + try { + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available + const response = await fetch(endpoint, { + method: 'GET', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_connection_history', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return full response here + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.fetch_connection_history', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const setCurrentForeignServer = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const requiredFields = ['coin']; + const missingFields: string[] = []; + + // Validate required fields + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const { coin, host, port, type } = data; + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.set_current_server', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:server_type', { + type: type, + postProcess: 'capitalizeFirstChar', + }), + text3: i18n.t('question:server_host', { + host: host, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:coin', { + coin: coin, + postProcess: 'capitalizeFirstChar', + }), + }, + isFromExtension + ); + + const { accepted } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const body = { + hostName: host, + port: port, + connectionType: type, + }; + + const url = `/crosschain/${coin.toLowerCase()}/setcurrentserver`; + + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available + const response = await fetch(endpoint, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.server_current_set', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response +}; + +export const addForeignServer = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const requiredFields = ['coin']; + const missingFields: string[] = []; + + // Validate required fields + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const { coin, host, port, type } = data; + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.server_add', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:server_type', { + type: type, + postProcess: 'capitalizeFirstChar', + }), + text3: i18n.t('question:server_host', { + host: host, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:coin', { + coin: coin, + postProcess: 'capitalizeFirstChar', + }), + }, + isFromExtension + ); + + const { accepted } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const body = { + hostName: host, + port: port, + connectionType: type, + }; + + const url = `/crosschain/${coin.toLowerCase()}/addserver`; + + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available + const response = await fetch(endpoint, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.server_current_add', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response +}; + +export const removeForeignServer = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const requiredFields = ['coin']; + const missingFields: string[] = []; + + // Validate required fields + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const { coin, host, port, type } = data; + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.server_remove', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:server_type', { + type: type, + postProcess: 'capitalizeFirstChar', + }), + text3: i18n.t('question:server_host', { + host: host, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:coin', { + coin: coin, + postProcess: 'capitalizeFirstChar', + }), + }, + isFromExtension + ); + + const { accepted } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const body = { + hostName: host, + port: port, + connectionType: type, + }; + + const url = `/crosschain/${coin.toLowerCase()}/removeserver`; + + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available + const response = await fetch(endpoint, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.server_remove', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response +}; + +export const getDaySummary = async () => { + const url = `/admin/summary`; // Simplified endpoint URL + + try { + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL + const response = await fetch(endpoint, { + method: 'GET', + headers: { + Accept: '*/*', + }, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.retrieve_summary', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.retrieve_summary', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getNodeInfo = async () => { + const url = `/admin/info`; // Simplified endpoint URL + + try { + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL + const response = await fetch(endpoint, { + method: 'GET', + headers: { + Accept: '*/*', + }, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.node_info', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.node_info', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getNodeStatus = async () => { + const url = `/admin/status`; // Simplified endpoint URL + + try { + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL + const response = await fetch(endpoint, { + method: 'GET', + headers: { + Accept: '*/*', + }, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.node_status', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.node_status', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getArrrSyncStatus = async () => { + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const arrrSeed = parsedData.arrrSeed58; + const url = `/crosschain/arrr/syncstatus`; // Simplified endpoint URL + + try { + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL + const response = await fetch(endpoint, { + method: 'POST', + headers: { + Accept: '*/*', + }, + body: arrrSeed, + }); + + let res; + + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + return res; // Return the full response + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.retrieve_sync_status', { + token: 'ARRR', + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const sendCoin = async (data, isFromExtension) => { + const requiredFields = ['coin', 'amount']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + if (!data?.destinationAddress && !data?.recipient) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: 'recipient', + postProcess: 'capitalizeFirstChar', + }) + ); + } + let checkCoin = data.coin; + const wallet = await getSaveWallet(); + const address = wallet.address0; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const isGateway = await isRunningGateway(); + + if (checkCoin !== 'QORT' && isGateway) + throw new Error( + i18n.t('question:message.error.gateway_non_qort_local_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + if (checkCoin === 'QORT') { + // Params: data.coin, data.recipient, data.amount, data.fee + // TODO: prompt user to send. If they confirm, call `POST /crosschain/:coin/send`, or for QORT, broadcast a PAYMENT transaction + // then set the response string from the core to the `response` variable (defined above) + // If they decline, send back JSON that includes an `error` key, such as `{"error": "User declined request"}` + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + + const url = await createEndpoint(`/addresses/balance/${address}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_balance', { + postProcess: 'capitalizeFirstChar', + }) + ); + let walletBalance; + try { + walletBalance = await response.clone().json(); + } catch (e) { + walletBalance = await response.text(); + } + if (isNaN(Number(walletBalance))) { + const errorMsg = i18n.t('question:message.error.fetch_balance_token', { + token: 'QORT', + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const transformDecimals = (Number(walletBalance) * QORT_DECIMALS).toFixed( + 0 + ); + const walletBalanceDecimals = Number(transformDecimals); + const amountDecimals = Number(amount) * QORT_DECIMALS; + const fee: number = await sendQortFee(); + if (amountDecimals + fee * QORT_DECIMALS > walletBalanceDecimals) { + const errorMsg = i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + if (amount <= 0) { + const errorMsg = i18n.t('core:message.error.invalid_amount', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + if (recipient.length === 0) { + const errorMsg = i18n.t('question:message.error.empty_receiver', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:to_recipient', { + recipient: recipient, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${amount} ${checkCoin}`, + fee: fee, + confirmCheckbox: true, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const makePayment = await sendCoinFunc( + { amount, password: null, receiver: recipient }, + true + ); + return makePayment.res?.data; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else if (checkCoin === 'BTC') { + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + const xprv58 = parsedData.btcPrivateKey; + const feePerByte = data.fee ? data.fee : btcFeePerByte; + + const btcWalletBalance = await getWalletBalance({ coin: checkCoin }, true); + + if (isNaN(Number(btcWalletBalance))) { + throw new Error( + i18n.t('question:message.error.fetch_balance_token', { + token: 'BTC', + postProcess: 'capitalizeFirstChar', + }) + ); + } + const btcWalletBalanceDecimals = Number(btcWalletBalance); + const btcAmountDecimals = Number(amount); + const fee = feePerByte * 500; // default 0.00050000 + if (btcAmountDecimals + fee > btcWalletBalanceDecimals) { + throw new Error( + i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:to_recipient', { + recipient: recipient, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${amount} ${checkCoin}`, + foreignFee: `${fee} BTC`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const opts = { + xprv58: xprv58, + receivingAddress: recipient, + bitcoinAmount: amount, + feePerByte: feePerByte, + }; + const url = await createEndpoint(`/crosschain/btc/send`); + + const response = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(opts), + }); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.send', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else if (checkCoin === 'LTC') { + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + const xprv58 = parsedData.ltcPrivateKey; + const feePerByte = data.fee ? data.fee : ltcFeePerByte; + const ltcWalletBalance = await getWalletBalance({ coin: checkCoin }, true); + + if (isNaN(Number(ltcWalletBalance))) { + const errorMsg = i18n.t('question:message.error.fetch_balance_token', { + token: 'LTC', + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const ltcWalletBalanceDecimals = Number(ltcWalletBalance); + const ltcAmountDecimals = Number(amount); + const fee = feePerByte * 1000; // default 0.00030000 + if (ltcAmountDecimals + fee > ltcWalletBalanceDecimals) { + throw new Error( + i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:to_recipient', { + recipient: recipient, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${amount} ${checkCoin}`, + foreignFee: `${fee} LTC`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const url = await createEndpoint(`/crosschain/ltc/send`); + const opts = { + xprv58: xprv58, + receivingAddress: recipient, + litecoinAmount: amount, + feePerByte: feePerByte, + }; + const response = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(opts), + }); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.send', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else if (checkCoin === 'DOGE') { + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + const xprv58 = parsedData.dogePrivateKey; + const feePerByte = data.fee ? data.fee : dogeFeePerByte; + const dogeWalletBalance = await getWalletBalance({ coin: checkCoin }, true); + if (isNaN(Number(dogeWalletBalance))) { + const errorMsg = i18n.t('question:message.error.fetch_balance_token', { + token: 'DOGE', + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const dogeWalletBalanceDecimals = Number(dogeWalletBalance); + const dogeAmountDecimals = Number(amount); + const fee = feePerByte * 5000; // default 0.05000000 + if (dogeAmountDecimals + fee > dogeWalletBalanceDecimals) { + const errorMsg = i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:to_recipient', { + recipient: recipient, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${amount} ${checkCoin}`, + foreignFee: `${fee} DOGE`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const opts = { + xprv58: xprv58, + receivingAddress: recipient, + dogecoinAmount: amount, + feePerByte: feePerByte, + }; + const url = await createEndpoint(`/crosschain/doge/send`); + + const response = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(opts), + }); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.send', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else if (checkCoin === 'DGB') { + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + const xprv58 = parsedData.dbgPrivateKey; + const feePerByte = data.fee ? data.fee : dgbFeePerByte; + const dgbWalletBalance = await getWalletBalance({ coin: checkCoin }, true); + if (isNaN(Number(dgbWalletBalance))) { + const errorMsg = i18n.t('question:message.error.fetch_balance_token', { + token: 'DGB', + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const dgbWalletBalanceDecimals = Number(dgbWalletBalance); + const dgbAmountDecimals = Number(amount); + const fee = feePerByte * 500; // default 0.00005000 + if (dgbAmountDecimals + fee > dgbWalletBalanceDecimals) { + const errorMsg = i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: `To: ${recipient}`, + highlightedText: `${amount} ${checkCoin}`, + foreignFee: `${fee} DGB`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const opts = { + xprv58: xprv58, + receivingAddress: recipient, + digibyteAmount: amount, + feePerByte: feePerByte, + }; + const url = await createEndpoint(`/crosschain/dgb/send`); + + const response = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(opts), + }); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.send', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else if (checkCoin === 'RVN') { + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + const xprv58 = parsedData.rvnPrivateKey; + const feePerByte = data.fee ? data.fee : rvnFeePerByte; + const rvnWalletBalance = await getWalletBalance({ coin: checkCoin }, true); + if (isNaN(Number(rvnWalletBalance))) { + const errorMsg = i18n.t('question:message.error.fetch_balance_token', { + token: 'RVN', + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const rvnWalletBalanceDecimals = Number(rvnWalletBalance); + const rvnAmountDecimals = Number(amount); + const fee = feePerByte * 500; // default 0.00562500 + if (rvnAmountDecimals + fee > rvnWalletBalanceDecimals) { + const errorMsg = i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: `To: ${recipient}`, + highlightedText: `${amount} ${checkCoin}`, + foreignFee: `${fee} RVN`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const opts = { + xprv58: xprv58, + receivingAddress: recipient, + ravencoinAmount: amount, + feePerByte: feePerByte, + }; + const url = await createEndpoint(`/crosschain/rvn/send`); + + const response = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(opts), + }); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.send', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else if (checkCoin === 'ARRR') { + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + const memo = data?.memo; + const arrrWalletBalance = await getWalletBalance({ coin: checkCoin }, true); + + if (isNaN(Number(arrrWalletBalance))) { + const errorMsg = i18n.t('question:message.error.fetch_balance_token', { + token: 'ARR', + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const arrrWalletBalanceDecimals = Number(arrrWalletBalance); + const arrrAmountDecimals = Number(amount); + const fee = 0.0001; + if (arrrAmountDecimals + fee > arrrWalletBalanceDecimals) { + const errorMsg = i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: `To: ${recipient}`, + highlightedText: `${amount} ${checkCoin}`, + foreignFee: `${fee} ARRR`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const opts = { + entropy58: parsedData.arrrSeed58, + receivingAddress: recipient, + arrrAmount: amount, + memo: memo, + }; + const url = await createEndpoint(`/crosschain/arrr/send`); + + const response = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(opts), + }); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.send', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } +}; + +function calculateFeeFromRate(feePerKb, sizeInBytes) { + return (feePerKb / 1000) * sizeInBytes; +} + +const getBuyingFees = async (foreignBlockchain) => { + const ticker = sellerForeignFee[foreignBlockchain].ticker; + if (!ticker) throw new Error('invalid foreign blockchain'); + const unlockFee = await getForeignFee({ + coin: ticker, + type: 'feerequired', + }); + const lockFee = await getForeignFee({ + coin: ticker, + type: 'feekb', + }); + return { + ticker: ticker, + lock: { + sats: lockFee, + fee: lockFee / QORT_DECIMALS, + }, + unlock: { + sats: unlockFee, + fee: unlockFee / QORT_DECIMALS, + feePerKb: +calculateRateFromFee(+unlockFee, 300) / QORT_DECIMALS, + }, + }; +}; + +export const createBuyOrder = async (data, isFromExtension) => { + const requiredFields = ['crosschainAtInfo', 'foreignBlockchain']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const isGateway = await isRunningGateway(); + const foreignBlockchain = data.foreignBlockchain; + const atAddresses = data.crosschainAtInfo?.map( + (order) => order.qortalAtAddress + ); + + const atPromises = atAddresses.map((atAddress) => + requestQueueGetAtAddresses.enqueue(async () => { + const url = await createEndpoint(`/crosschain/trade/${atAddress}`); + const resAddress = await fetch(url); + const resData = await resAddress.json(); + if (foreignBlockchain !== resData?.foreignBlockchain) { + throw new Error( + i18n.t('core:message.error.same_foreign_blockchain', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + return resData; + }) + ); + + const crosschainAtInfo = await Promise.all(atPromises); + + try { + const buyingFees = await getBuyingFees(foreignBlockchain); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.buy_order', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:permission.buy_order_quantity', { + quantity: atAddresses?.length, + postProcess: 'capitalizeFirstChar', + }), + text3: i18n.t('question:permission.buy_order_ticker', { + qort_amount: crosschainAtInfo?.reduce((latest, cur) => { + return latest + +cur?.qortAmount; + }, 0), + foreign_amount: roundUpToDecimals( + crosschainAtInfo?.reduce((latest, cur) => { + return latest + +cur?.expectedForeignAmount; + }, 0) + ), + ticker: buyingFees.ticker, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('auth:node.using_public_gateway', { + gateway: isGateway, + postProcess: 'capitalizeFirstChar', + }), + fee: '', + html: ` +
    + + +
    +
    ${i18n.t('question:total_unlocking_fee', { + postProcess: 'capitalizeFirstChar', + })}
    +
    ${(+buyingFees?.unlock?.fee * atAddresses?.length)?.toFixed(8)} ${buyingFees.ticker}
    +
    + ${i18n.t('question:permission.buy_order_fee_estimation', { + quantity: atAddresses?.length, + fee: buyingFees?.unlock?.feePerKb?.toFixed(8), + ticker: buyingFees.ticker, + postProcess: 'capitalizeFirstChar', + })} +
    +
    ${i18n.t('question:total_locking_fee', { + postProcess: 'capitalizeFirstChar', + })}
    +
    ${i18n.t('question:permission.buy_order_per_kb', { + fee: +buyingFees?.lock.fee.toFixed(8), + ticker: buyingFees.ticker, + postProcess: 'capitalizeFirstChar', + })} +
    +
    +
    +`, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const resBuyOrder = await createBuyOrderTx({ + crosschainAtInfo, + isGateway, + foreignBlockchain, + }); + return resBuyOrder; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.buy_order', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +const cancelTradeOfferTradeBot = async (body, keyPair) => { + const txn = new DeleteTradeOffer().createTransaction(body); + const url = await createEndpoint(`/crosschain/tradeoffer`); + const bodyToString = JSON.stringify(txn); + + const deleteTradeBotResponse = await fetch(url, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + body: bodyToString, + }); + + if (!deleteTradeBotResponse.ok) { + throw new Error( + i18n.t('question:message.error.update_tradebot', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + const unsignedTxn = await deleteTradeBotResponse.text(); + const signedTxnBytes = await signTradeBotTransaction(unsignedTxn, keyPair); + const signedBytes = Base58.encode(signedTxnBytes); + + let res; + try { + res = await processTransactionVersion2(signedBytes); + } catch (error) { + return { + error: i18n.t('question:message.error.cancel_sell_order', { + postProcess: 'capitalizeFirstChar', + }), + failedTradeBot: { + atAddress: body.atAddress, + creatorAddress: body.creatorAddress, + }, + }; + } + if (res?.error) { + return { + error: i18n.t('question:message.error.cancel_sell_order', { + postProcess: 'capitalizeFirstChar', + }), + failedTradeBot: { + atAddress: body.atAddress, + creatorAddress: body.creatorAddress, + }, + }; + } + if (res?.signature) { + return res; + } else { + throw new Error( + i18n.t('question:message.error.cancel_sell_order', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; +const findFailedTradebot = async (createBotCreationTimestamp, body) => { + //wait 5 secs + const wallet = await getSaveWallet(); + const address = wallet.address0; + await new Promise((res) => { + setTimeout(() => { + res(null); + }, 5000); + }); + const url = await createEndpoint( + `/crosschain/tradebot?foreignBlockchain=LITECOIN` + ); + + const tradeBotsReponse = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + const data = await tradeBotsReponse.json(); + const latestItem2 = data + .filter((item) => item.creatorAddress === address) + .sort((a, b) => b.timestamp - a.timestamp)[0]; + const latestItem = data + .filter( + (item) => + item.creatorAddress === address && + +item.foreignAmount === +body.foreignAmount + ) + .sort((a, b) => b.timestamp - a.timestamp)[0]; + if ( + latestItem && + createBotCreationTimestamp - latestItem.timestamp <= 5000 && + createBotCreationTimestamp > latestItem.timestamp // Ensure latestItem's timestamp is before createBotCreationTimestamp + ) { + return latestItem; + } else { + return null; + } +}; +const tradeBotCreateRequest = async (body, keyPair) => { + const txn = new TradeBotCreateRequest().createTransaction(body); + const url = await createEndpoint(`/crosschain/tradebot/create`); + const bodyToString = JSON.stringify(txn); + + const unsignedTxnResponse = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: bodyToString, + }); + if (!unsignedTxnResponse.ok) + throw new Error( + i18n.t('question:message.error.create_tradebot', { + postProcess: 'capitalizeFirstChar', + }) + ); + const createBotCreationTimestamp = Date.now(); + const unsignedTxn = await unsignedTxnResponse.text(); + const signedTxnBytes = await signTradeBotTransaction(unsignedTxn, keyPair); + const signedBytes = Base58.encode(signedTxnBytes); + + let res; + try { + res = await processTransactionVersion2(signedBytes); + } catch (error) { + const findFailedTradeBot = await findFailedTradebot( + createBotCreationTimestamp, + body + ); + return { + error: i18n.t('question:message.error.create_sell_order', { + postProcess: 'capitalizeFirstChar', + }), + failedTradeBot: findFailedTradeBot, + }; + } + + if (res?.signature) { + return res; + } else { + throw new Error( + i18n.t('question:message.error.create_sell_order', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const createSellOrder = async (data, isFromExtension) => { + const requiredFields = ['qortAmount', 'foreignBlockchain', 'foreignAmount']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const parsedForeignAmount = Number(data.foreignAmount)?.toFixed(8); + + const receivingAddress = await getUserWalletFunc(data.foreignBlockchain); + try { + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.sell_order', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:permission.order_detail', { + qort_amount: data.qortAmount, + foreign_amount: parsedForeignAmount, + ticker: data.foreignBlockchain, + postProcess: 'capitalizeFirstChar', + }), + fee: '0.02', + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const userPublicKey = parsedData.publicKey; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + const response = await tradeBotCreateRequest( + { + creatorPublicKey: userPublicKey, + qortAmount: parseFloat(data.qortAmount), + fundingQortAmount: parseFloat(data.qortAmount) + 0.01, + foreignBlockchain: data.foreignBlockchain, + foreignAmount: parseFloat(parsedForeignAmount), + tradeTimeout: 120, + receivingAddress: receivingAddress.address, + }, + keyPair + ); + + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.submit_sell_order', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const cancelSellOrder = async (data, isFromExtension) => { + const requiredFields = ['atAddress']; + const missingFields: string[] = []; + + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const url = await createEndpoint(`/crosschain/trade/${data.atAddress}`); + const resAddress = await fetch(url); + const resData = await resAddress.json(); + + if (!resData?.qortalAtAddress) + throw new Error( + i18n.t('question:message.error.at_info', { + postProcess: 'capitalizeFirstChar', + }) + ); + + try { + const fee = await getFee('MESSAGE'); + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.cancel_sell_order', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:permission.order_detail', { + qort_amount: resData.qortAmount, + foreign_amount: resData.expectedForeignAmount, + ticker: resData.foreignBlockchain, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const userPublicKey = parsedData.publicKey; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + const response = await cancelTradeOfferTradeBot( + { + creatorPublicKey: userPublicKey, + atAddress: data.atAddress, + }, + keyPair + ); + + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.submit_sell_order', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const openNewTab = async (data, isFromExtension) => { + const requiredFields = ['qortalLink']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const res = extractComponents(data.qortalLink); + if (res) { + const { service, name, identifier, path } = res; + if (!service && !name) + throw new Error( + i18n.t('auth:message.error.invalid_qortal_link', { + postProcess: 'capitalizeFirstChar', + }) + ); + executeEvent('addTab', { data: { service, name, identifier, path } }); + executeEvent('open-apps-mode', {}); + return true; + } else { + throw new Error( + i18n.t('auth:message.error.invalid_qortal_link', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const adminAction = async (data, isFromExtension) => { + const requiredFields = ['type']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + // For actions that require a value, check for 'value' field + const actionsRequiringValue = [ + 'addpeer', + 'removepeer', + 'forcesync', + 'addmintingaccount', + 'removemintingaccount', + ]; + if (actionsRequiringValue.includes(data.type.toLowerCase()) && !data.value) { + missingFields.push('value'); + } + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + let apiEndpoint = ''; + let method = 'GET'; // Default method + let includeValueInBody = false; + switch (data.type.toLowerCase()) { + case 'stop': + apiEndpoint = await createEndpoint('/admin/stop'); + break; + case 'restart': + apiEndpoint = await createEndpoint('/admin/restart'); + break; + case 'bootstrap': + apiEndpoint = await createEndpoint('/admin/bootstrap'); + break; + case 'addmintingaccount': + apiEndpoint = await createEndpoint('/admin/mintingaccounts'); + method = 'POST'; + includeValueInBody = true; + break; + case 'removemintingaccount': + apiEndpoint = await createEndpoint('/admin/mintingaccounts'); + method = 'DELETE'; + includeValueInBody = true; + break; + case 'forcesync': + apiEndpoint = await createEndpoint('/admin/forcesync'); + method = 'POST'; + includeValueInBody = true; + break; + case 'addpeer': + apiEndpoint = await createEndpoint('/peers'); + method = 'POST'; + includeValueInBody = true; + break; + case 'removepeer': + apiEndpoint = await createEndpoint('/peers'); + method = 'DELETE'; + includeValueInBody = true; + break; + default: + throw new Error( + i18n.t('question:message.error.unknown_admin_action_type', { + type: data.type, + postProcess: 'capitalizeFirstChar', + }) + ); + } + // Prepare the permission prompt text + let permissionText = i18n.t('question:permission.perform_admin_action', { + type: data.type, + postProcess: 'capitalizeFirstChar', + }); + + if (data.value) { + permissionText += + ' ' + + i18n.t('question:permission.perform_admin_action_with_value', { + value: data.value, + postProcess: 'capitalizeFirstChar', + }); + } + + const resPermission = await getUserPermission( + { + text1: permissionText, + }, + isFromExtension + ); + + const { accepted } = resPermission; + + if (accepted) { + // Set up options for the API call + const options: RequestInit = { + method: method, + headers: {}, + }; + if (includeValueInBody) { + options.headers['Content-Type'] = 'text/plain'; + options.body = data.value; + } + const response = await fetch(apiEndpoint, options); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.perform_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const signTransaction = async (data, isFromExtension) => { + const requiredFields = ['unsignedBytes']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const shouldProcess = data?.process || false; + const _url = await createEndpoint( + '/transactions/decode?ignoreValidityChecks=false' + ); + + const _body = data.unsignedBytes; + const response = await fetch(_url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: _body, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.decode_transaction', { + postProcess: 'capitalizeFirstChar', + }) + ); + const decodedData = await response.json(); + const resPermission = await getUserPermission( + { + text1: shouldProcess + ? i18n.t('question:permission.sign_process_transaction', { + postProcess: 'capitalizeFirstChar', + }) + : i18n.t('question:permission.sign_transaction', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t( + 'question:message.generic.read_transaction_carefully', + { postProcess: 'capitalizeFirstChar' } + ), + text2: `Tx type: ${decodedData.type}`, + json: decodedData, + }, + isFromExtension + ); + + const { accepted } = resPermission; + if (accepted) { + let urlConverted = await createEndpoint('/transactions/convert'); + + const responseConverted = await fetch(urlConverted, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: data.unsignedBytes, + }); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + const convertedBytes = await responseConverted.text(); + const txBytes = Base58.decode(data.unsignedBytes); + const _arbitraryBytesBuffer = Object.keys(txBytes).map(function (key) { + return txBytes[key]; + }); + const arbitraryBytesBuffer = new Uint8Array(_arbitraryBytesBuffer); + const txByteSigned = Base58.decode(convertedBytes); + const _bytesForSigningBuffer = Object.keys(txByteSigned).map( + function (key) { + return txByteSigned[key]; + } + ); + const bytesForSigningBuffer = new Uint8Array(_bytesForSigningBuffer); + const signature = nacl.sign.detached( + bytesForSigningBuffer, + keyPair.privateKey + ); + const signedBytes = utils.appendBuffer(arbitraryBytesBuffer, signature); + const signedBytesToBase58 = Base58.encode(signedBytes); + if (!shouldProcess) { + return signedBytesToBase58; + } + const res = await processTransactionVersion2(signedBytesToBase58); + if (!res?.signature) + throw new Error( + res?.message || + i18n.t('question:message.error.process_transaction', { + postProcess: 'capitalizeFirstChar', + }) + ); + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +const missingFieldsFunc = (data, requiredFields) => { + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } +}; + +const encode = (value) => encodeURIComponent(value.trim()); // Helper to encode values +const buildQueryParams = (data) => { + const allowedParams = [ + 'name', + 'service', + 'identifier', + 'mimeType', + 'fileName', + 'encryptionType', + 'key', + ]; + return Object.entries(data) + .map(([key, value]) => { + if ( + value === undefined || + value === null || + value === false || + !allowedParams.includes(key) + ) + return null; // Skip null, undefined, or false + if (typeof value === 'boolean') return `${key}=${value}`; // Handle boolean values + return `${key}=${encode(value)}`; // Encode other values + }) + .filter(Boolean) // Remove null values + .join('&'); // Join with `&` +}; +export const createAndCopyEmbedLink = async (data, isFromExtension) => { + const requiredFields = ['type']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + switch (data.type) { + case 'POLL': { + missingFieldsFunc(data, ['type', 'name']); + + const queryParams = [ + `name=${encode(data.name)}`, + data.ref ? `ref=${encode(data.ref)}` : null, // Add only if ref exists + ] + .filter(Boolean) // Remove null values + .join('&'); // Join with `&` + const link = `qortal://use-embed/POLL?${queryParams}`; + try { + await navigator.clipboard.writeText(link); + } catch (error) { + throw new Error( + i18n.t('question:message.error.copy_clipboard', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + return link; + } + case 'IMAGE': + case 'ATTACHMENT': { + missingFieldsFunc(data, ['type', 'name', 'service', 'identifier']); + if (data?.encryptionType === 'private' && !data?.key) { + throw new Error( + i18n.t('question:message.generic.provide_key_shared_link', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const queryParams = buildQueryParams(data); + + const link = `qortal://use-embed/${data.type}?${queryParams}`; + + try { + await navigator.clipboard.writeText(link); + } catch (error) { + throw new Error( + i18n.t('question:message.error.copy_clipboard', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + return link; + } + + default: + throw new Error( + i18n.t('question:message.error.invalid_type', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const registerNameRequest = async (data, isFromExtension) => { + const requiredFields = ['name']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const fee = await getFee('REGISTER_NAME'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.register_name', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: data.name, + text2: data?.description, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const name = data.name; + const description = data?.description || ''; + const response = await registerName({ name, description }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const updateNameRequest = async (data, isFromExtension) => { + const requiredFields = ['newName', 'oldName']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const oldName = data.oldName; + const newName = data.newName; + const description = data?.description || ''; + const fee = await getFee('UPDATE_NAME'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.register_name', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: data.newName, + text2: data?.description, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await updateName({ oldName, newName, description }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const leaveGroupRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const fee = await getFee('LEAVE_GROUP'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.leave_group', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${groupInfo.groupName}`, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await leaveGroup({ groupId }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const inviteToGroupRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'inviteTime', 'inviteeAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.inviteeAddress; + const inviteTime = data?.inviteTime; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('GROUP_INVITE'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.invite', { + invitee: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await inviteToGroup({ + groupId, + qortalAddress, + inviteTime, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const kickFromGroupRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'qortalAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.qortalAddress; + const reason = data?.reason; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('GROUP_KICK'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.kick', { + partecipant: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await kickFromGroup({ + groupId, + qortalAddress, + rBanReason: reason, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const banFromGroupRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'qortalAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.qortalAddress; + const rBanTime = data?.banTime; + const reason = data?.reason; + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('GROUP_BAN'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.ban', { + partecipant: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await banFromGroup({ + groupId, + qortalAddress, + rBanTime, + rBanReason: reason, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const cancelGroupBanRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'qortalAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.qortalAddress; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('CANCEL_GROUP_BAN'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.cancel_ban', { + partecipant: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await cancelBan({ + groupId, + qortalAddress, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const addGroupAdminRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'qortalAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.qortalAddress; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('ADD_GROUP_ADMIN'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.add_admin', { + invitee: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await makeAdmin({ + groupId, + qortalAddress, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const removeGroupAdminRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'qortalAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.qortalAddress; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('REMOVE_GROUP_ADMIN'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.remove_admin', { + partecipant: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await removeAdmin({ + groupId, + qortalAddress, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const cancelGroupInviteRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'qortalAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.qortalAddress; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('CANCEL_GROUP_INVITE'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.cancel_group_invite', { + invitee: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + + const { accepted } = resPermission; + + if (accepted) { + const response = await cancelInvitationToGroup({ + groupId, + qortalAddress, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const createGroupRequest = async (data, isFromExtension) => { + const requiredFields = [ + 'approvalThreshold', + 'groupId', + 'groupName', + 'maxBlock', + 'minBlock', + 'qortalAddress', + 'type', + ]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupName = data.groupName; + const description = data?.description || ''; + const type = +data.type; + const approvalThreshold = +data?.approvalThreshold; + const minBlock = +data?.minBlock; + const maxBlock = +data.maxBlock; + + const fee = await getFee('CREATE_GROUP'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.create_group', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await createGroup({ + groupName, + groupDescription: description, + groupType: type, + groupApprovalThreshold: approvalThreshold, + minBlock, + maxBlock, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const updateGroupRequest = async (data, isFromExtension) => { + const requiredFields = [ + 'groupId', + 'newOwner', + 'type', + 'approvalThreshold', + 'minBlock', + 'maxBlock', + ]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = +data.groupId; + const newOwner = data.newOwner; + const description = data?.description || ''; + const type = +data.type; + const approvalThreshold = +data?.approvalThreshold; + const minBlock = +data?.minBlock; + const maxBlock = +data.maxBlock; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(newOwner); + + const fee = await getFee('CREATE_GROUP'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.update_group', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:permission.update_group_detail', { + owner: displayInvitee || newOwner, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await updateGroup({ + groupId, + newOwner, + newIsOpen: type, + newDescription: description, + newApprovalThreshold: approvalThreshold, + newMinimumBlockDelay: minBlock, + newMaximumBlockDelay: maxBlock, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const decryptAESGCMRequest = async (data, isFromExtension) => { + const requiredFields = ['encryptedData', 'iv', 'senderPublicKey']; + requiredFields.forEach((field) => { + if (!data[field]) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: field, + postProcess: 'capitalizeFirstChar', + }) + ); + } + }); + + const encryptedData = data.encryptedData; + const iv = data.iv; + const senderPublicKeyBase58 = data.senderPublicKey; + + // Decode keys and IV + const senderPublicKey = Base58.decode(senderPublicKeyBase58); + const resKeyPair = await getKeyPair(); // Assume this retrieves the current user's keypair + const uint8PrivateKey = Base58.decode(resKeyPair.privateKey); + + // Convert ed25519 keys to Curve25519 + const convertedPrivateKey = ed2curve.convertSecretKey(uint8PrivateKey); + const convertedPublicKey = ed2curve.convertPublicKey(senderPublicKey); + + // Generate shared secret + const sharedSecret = new Uint8Array(32); + nacl.lowlevel.crypto_scalarmult( + sharedSecret, + convertedPrivateKey, + convertedPublicKey + ); + + // Derive encryption key + const encryptionKey: Uint8Array = new Sha256() + .process(sharedSecret) + .finish().result; + + // Convert IV and ciphertext from Base64 + const base64ToUint8Array = (base64) => + Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)); + const ivUint8Array = base64ToUint8Array(iv); + const ciphertext = base64ToUint8Array(encryptedData); + // Validate IV and key lengths + if (ivUint8Array.length !== 12) { + throw new Error( + i18n.t('question:message.error.invalid_encryption_iv', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + if (encryptionKey.length !== 32) { + throw new Error( + i18n.t('question:message.error.invalid_encryption_key', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + try { + // Decrypt data + const algorithm = { name: 'AES-GCM', iv: ivUint8Array }; + const cryptoKey = await crypto.subtle.importKey( + 'raw', + encryptionKey, + algorithm, + false, + ['decrypt'] + ); + const decryptedArrayBuffer = await crypto.subtle.decrypt( + algorithm, + cryptoKey, + ciphertext + ); + + // Return decrypted data as Base64 + return uint8ArrayToBase64(new Uint8Array(decryptedArrayBuffer)); + } catch (error) { + console.error('Decryption failed:', error); + throw new Error( + i18n.t('question:message.error.decrypt_message', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const sellNameRequest = async (data, isFromExtension) => { + const requiredFields = ['salePrice', 'nameForSale']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const name = data.nameForSale; + const sellPrice = +data.salePrice; + + const validApi = await getBaseApi(); + + const response = await fetch(validApi + '/names/' + name); + const nameData = await response.json(); + if (!nameData) + throw new Error( + i18n.t('auth:message.error.name_not_existing', { + postProcess: 'capitalizeFirstChar', + }) + ); + + if (nameData?.isForSale) + throw new Error( + i18n.t('question:message.error.name_already_for_sale', { + postProcess: 'capitalizeFirstChar', + }) + ); + const fee = await getFee('SELL_NAME'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.sell_name_transaction', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t( + 'question:permission.sell_name_transaction_detail', + { + name: name, + price: sellPrice, + postProcess: 'capitalizeFirstChar', + } + ), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await sellName({ + name, + sellPrice, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const cancelSellNameRequest = async (data, isFromExtension) => { + const requiredFields = ['nameForSale']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const name = data.nameForSale; + const validApi = await getBaseApi(); + + const response = await fetch(validApi + '/names/' + name); + const nameData = await response.json(); + if (!nameData?.isForSale) + throw new Error( + i18n.t('question:message.error.name_not_for_sale', { + postProcess: 'capitalizeFirstChar', + }) + ); + + const fee = await getFee('CANCEL_SELL_NAME'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.sell_name_cancel', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:name', { + name: name, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await cancelSellName({ + name, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const buyNameRequest = async (data, isFromExtension) => { + const requiredFields = ['nameForSale']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const name = data.nameForSale; + const validApi = await getBaseApi(); + const response = await fetch(validApi + '/names/' + name); + const nameData = await response.json(); + + if (!nameData?.isForSale) + throw new Error( + i18n.t('question:message.error.name_not_for_sale', { + postProcess: 'capitalizeFirstChar', + }) + ); + + const sellerAddress = nameData.owner; + const sellPrice = +nameData.salePrice; + + const fee = await getFee('BUY_NAME'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.buy_name', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:permission.buy_name_detail', { + name: name, + price: sellPrice, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await buyName({ + name, + sellerAddress, + sellPrice, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const signForeignFees = async (data, isFromExtension) => { + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.sign_fee', { + postProcess: 'capitalizeFirstChar', + }), + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + const unsignedFeesUrl = await createEndpoint( + `/crosschain/unsignedfees/${address}` + ); + + const unsignedFeesResponse = await fetch(unsignedFeesUrl); + + const unsignedFees = await unsignedFeesResponse.json(); + + const signedFees = []; + + unsignedFees.forEach((unsignedFee) => { + const unsignedDataDecoded = Base58.decode(unsignedFee.data); + + const signature = nacl.sign.detached( + unsignedDataDecoded, + keyPair.privateKey + ); + + const signedFee = { + timestamp: unsignedFee.timestamp, + data: `${Base58.encode(signature)}`, + atAddress: unsignedFee.atAddress, + fee: unsignedFee.fee, + }; + + signedFees.push(signedFee); + }); + + const signedFeesUrl = await createEndpoint(`/crosschain/signedfees`); + + await fetch(signedFeesUrl, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: `${JSON.stringify(signedFees)}`, + }); + + return true; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; +export const multiPaymentWithPrivateData = async (data, isFromExtension) => { + const requiredFields = ['payments', 'assetId']; + requiredFields.forEach((field) => { + if (data[field] === undefined || data[field] === null) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: field, + postProcess: 'capitalizeFirstChar', + }) + ); + } + }); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const privateKey = parsedData.privateKey; + const userPublicKey = parsedData.publicKey; + const { fee: paymentFee } = await getFee('TRANSFER_ASSET'); + const { fee: arbitraryFee } = await getFee('ARBITRARY'); + + let name = null; + const payments = data.payments; + const assetId = data.assetId; + const pendingTransactions = []; + const pendingAdditionalArbitraryTxs = []; + const additionalArbitraryTxsWithoutPayment = + data?.additionalArbitraryTxsWithoutPayment || []; + let totalAmount = 0; + let fee = 0; + for (const payment of payments) { + const paymentRefId = uid.rnd(); + const requiredFieldsPayment = ['recipient', 'amount']; + + for (const field of requiredFieldsPayment) { + if (!payment[field]) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: field, + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + + const confirmReceiver = await getNameOrAddress(payment.recipient); + if (confirmReceiver.error) { + throw new Error( + i18n.t('question:message.error.invalid_receiver', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const receiverPublicKey = await getPublicKey(confirmReceiver); + + const amount = +payment.amount.toFixed(8); + + pendingTransactions.push({ + type: 'PAYMENT', + recipientAddress: confirmReceiver, + amount: amount, + paymentRefId, + }); + + fee = fee + +paymentFee; + totalAmount = totalAmount + amount; + + if (payment.arbitraryTxs && payment.arbitraryTxs.length > 0) { + for (const arbitraryTx of payment.arbitraryTxs) { + const requiredFieldsArbitraryTx = ['service', 'identifier', 'base64']; + + for (const field of requiredFieldsArbitraryTx) { + if (!arbitraryTx[field]) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: field, + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + + if (!name) { + const getName = await getNameInfo(); + if (!getName) + throw new Error( + i18n.t('question:message.error.registered_name', { + postProcess: 'capitalizeFirstChar', + }) + ); + name = getName; + } + + const isValid = isValidBase64WithDecode(arbitraryTx.base64); + if (!isValid) + throw new Error( + i18n.t('core:message.error.invalid_base64', { + postProcess: 'capitalizeFirstChar', + }) + ); + if (!arbitraryTx?.service?.includes('_PRIVATE')) + throw new Error( + i18n.t('question:message.generic.private_service', { + postProcess: 'capitalizeFirstChar', + }) + ); + const additionalPublicKeys = arbitraryTx?.additionalPublicKeys || []; + pendingTransactions.push({ + type: 'ARBITRARY', + identifier: arbitraryTx.identifier, + service: arbitraryTx.service, + base64: arbitraryTx.base64, + description: arbitraryTx?.description || '', + paymentRefId, + publicKeys: [receiverPublicKey, ...additionalPublicKeys], + }); + + fee = fee + +arbitraryFee; + } + } + } + + if ( + additionalArbitraryTxsWithoutPayment && + additionalArbitraryTxsWithoutPayment.length > 0 + ) { + for (const arbitraryTx of additionalArbitraryTxsWithoutPayment) { + const requiredFieldsArbitraryTx = ['service', 'identifier', 'base64']; + + for (const field of requiredFieldsArbitraryTx) { + if (!arbitraryTx[field]) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: field, + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + + if (!name) { + const getName = await getNameInfo(); + if (!getName) + throw new Error( + i18n.t('question:message.error.registered_name', { + postProcess: 'capitalizeFirstChar', + }) + ); + name = getName; + } + + const isValid = isValidBase64WithDecode(arbitraryTx.base64); + if (!isValid) + throw new Error( + i18n.t('core:message.error.invalid_base64', { + postProcess: 'capitalizeFirstChar', + }) + ); + if (!arbitraryTx?.service?.includes('_PRIVATE')) + throw new Error( + i18n.t('question:message.generic.private_service', { + postProcess: 'capitalizeFirstChar', + }) + ); + const additionalPublicKeys = arbitraryTx?.additionalPublicKeys || []; + pendingAdditionalArbitraryTxs.push({ + type: 'ARBITRARY', + identifier: arbitraryTx.identifier, + service: arbitraryTx.service, + base64: arbitraryTx.base64, + description: arbitraryTx?.description || '', + publicKeys: additionalPublicKeys, + }); + + fee = fee + +arbitraryFee; + } + } + + if (!name) + throw new Error( + i18n.t('question:message.error.registered_name', { + postProcess: 'capitalizeFirstChar', + }) + ); + const balance = await getBalanceInfo(); + + if (+balance < fee) + throw new Error( + i18n.t('question:message.error.insufficient_balance_qort', { + postProcess: 'capitalizeFirstChar', + }) + ); + const assetBalance = await getAssetBalanceInfo(assetId); + const assetInfo = await getAssetInfo(assetId); + if (assetBalance < totalAmount) + throw new Error( + i18n.t('question:message.error.insufficient_balance', { + postProcess: 'capitalizeFirstChar', + }) + ); + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.pay_publish', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:assets_used_pay', { + asset: assetInfo.name, + postProcess: 'capitalizeFirstChar', + }), + html: ` +
    + + + ${pendingTransactions + .filter((item) => item.type === 'PAYMENT') + .map( + (payment) => ` +
    +
    Recipient: ${ + payment.recipientAddress + }
    +
    Amount: ${payment.amount}
    +
    ` + ) + .join('')} + ${[...pendingTransactions, ...pendingAdditionalArbitraryTxs] + .filter((item) => item.type === 'ARBITRARY') + .map( + (arbitraryTx) => ` +
    +
    Service: ${ + arbitraryTx.service + }
    +
    Name: ${name}
    +
    Identifier: ${ + arbitraryTx.identifier + }
    +
    ` + ) + .join('')} +
    + + `, + highlightedText: `Total Amount: ${totalAmount}`, + fee: fee, + }, + isFromExtension + ); + + const { accepted, checkbox1 = false } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + // const failedTxs = [] + const paymentsDone = {}; + + const transactionsDone = []; + + for (const transaction of pendingTransactions) { + const type = transaction.type; + + if (type === 'PAYMENT') { + const makePayment = await retryTransaction( + transferAsset, + [ + { + amount: transaction.amount, + assetId, + recipient: transaction.recipientAddress, + }, + ], + true + ); + if (makePayment) { + transactionsDone.push(makePayment?.signature); + if (transaction.paymentRefId) { + paymentsDone[transaction.paymentRefId] = makePayment; + } + } + } else if (type === 'ARBITRARY' && paymentsDone[transaction.paymentRefId]) { + const objectToEncrypt = { + data: transaction.base64, + payment: paymentsDone[transaction.paymentRefId], + }; + + const toBase64 = await retryTransaction( + objectToBase64, + [objectToEncrypt], + true + ); + + if (!toBase64) continue; // Skip if encryption fails + + const encryptDataResponse = await retryTransaction( + encryptDataGroup, + [ + { + data64: toBase64, + publicKeys: transaction.publicKeys, + privateKey, + userPublicKey, + }, + ], + true + ); + + if (!encryptDataResponse) continue; // Skip if encryption fails + + const resPublish = await retryTransaction( + publishData, + [ + { + registeredName: encodeURIComponent(name), + file: encryptDataResponse, + service: transaction.service, + identifier: encodeURIComponent(transaction.identifier), + uploadType: 'file', + description: transaction?.description, + isBase64: true, + apiVersion: 2, + withFee: true, + }, + ], + true + ); + + if (resPublish?.signature) { + transactionsDone.push(resPublish?.signature); + } + } + } + + for (const transaction of pendingAdditionalArbitraryTxs) { + const objectToEncrypt = { + data: transaction.base64, + }; + + const toBase64 = await retryTransaction( + objectToBase64, + [objectToEncrypt], + true + ); + + if (!toBase64) continue; // Skip if encryption fails + + const encryptDataResponse = await retryTransaction( + encryptDataGroup, + [ + { + data64: toBase64, + publicKeys: transaction.publicKeys, + privateKey, + userPublicKey, + }, + ], + true + ); + + if (!encryptDataResponse) continue; // Skip if encryption fails + + const resPublish = await retryTransaction( + publishData, + [ + { + registeredName: encodeURIComponent(name), + file: encryptDataResponse, + service: transaction.service, + identifier: encodeURIComponent(transaction.identifier), + uploadType: 'file', + description: transaction?.description, + isBase64: true, + apiVersion: 2, + withFee: true, + }, + ], + true + ); + + if (resPublish?.signature) { + transactionsDone.push(resPublish?.signature); + } + } + + return transactionsDone; +}; + +export const transferAssetRequest = async (data, isFromExtension) => { + const requiredFields = ['amount', 'assetId', 'recipient']; + requiredFields.forEach((field) => { + if (data[field] === undefined || data[field] === null) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: field, + postProcess: 'capitalizeFirstChar', + }) + ); + } + }); + const amount = data.amount; + const assetId = data.assetId; + const recipient = data.recipient; + + const { fee } = await getFee('TRANSFER_ASSET'); + const balance = await getBalanceInfo(); + + if (+balance < +fee) + throw new Error( + i18n.t('question:message.error.insufficient_balance_qort', { + postProcess: 'capitalizeFirstChar', + }) + ); + const assetBalance = await getAssetBalanceInfo(assetId); + if (assetBalance < amount) + throw new Error( + i18n.t('question:message.error.insufficient_balance', { + postProcess: 'capitalizeFirstChar', + }) + ); + const confirmReceiver = await getNameOrAddress(recipient); + if (confirmReceiver.error) { + throw new Error( + i18n.t('question:message.error.invalid_receiver', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const assetInfo = await getAssetInfo(assetId); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.transfer_asset', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:asset_name', { + asset: assetInfo?.name, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:amount_qty', { + quantity: amount, + postProcess: 'capitalizeFirstChar', + }), + fee: fee, + }, + isFromExtension + ); + + const { accepted } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const res = await transferAsset({ + amount, + recipient: confirmReceiver, + assetId, + }); + return res; +}; diff --git a/src/qortalRequests/qortalRequests.ts b/src/qortal/qortal-requests.ts similarity index 100% rename from src/qortalRequests/qortalRequests.ts rename to src/qortal/qortal-requests.ts diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index 1878d3f..47d2fe1 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -39,16 +39,16 @@ import { transferAsset, } from '../background/background.ts'; import { getNameInfo, uint8ArrayToObject } from '../encryption/encryption.ts'; -import { showSaveFilePicker } from '../hooks/useQortalMessageListener'; -import { getPublishesFromAdminsAdminSpace } from '../components/Chat/AdminSpaceInner'; -import { extractComponents } from '../components/Chat/MessageDisplay'; +import { showSaveFilePicker } from '../hooks/useQortalMessageListener.tsx'; +import { getPublishesFromAdminsAdminSpace } from '../components/Chat/AdminSpaceInner.tsx'; +import { extractComponents } from '../components/Chat/MessageDisplay.tsx'; import { decryptResource, getGroupAdmins, getPublishesFromAdmins, validateSecretKey, -} from '../components/Group/Group'; -import { QORT_DECIMALS } from '../constants/constants'; +} from '../components/Group/Group.tsx'; +import { QORT_DECIMALS } from '../constants/constants.ts'; import Base58 from '../encryption/Base58.ts'; import ed2curve from '../encryption/ed2curve.ts'; import nacl from '../encryption/nacl-fast.ts'; @@ -64,24 +64,24 @@ import { objectToBase64, uint8ArrayStartsWith, uint8ArrayToBase64, -} from '../qdn/encryption/group-encryption'; +} from '../qdn/encryption/group-encryption.ts'; import { publishData } from '../qdn/publish/publish.ts'; import { getPermission, isRunningGateway, setPermission, } from './qortalRequests.ts'; -import TradeBotCreateRequest from '../transactions/TradeBotCreateRequest'; -import DeleteTradeOffer from '../transactions/TradeBotDeleteRequest'; -import signTradeBotTransaction from '../transactions/signTradeBotTransaction'; -import { createTransaction } from '../transactions/transactions'; -import { executeEvent } from '../utils/events'; -import { fileToBase64 } from '../utils/fileReading'; -import { mimeToExtensionMap } from '../utils/memeTypes'; -import { RequestQueueWithPromise } from '../utils/queue/queue'; -import utils from '../utils/utils'; +import TradeBotCreateRequest from '../transactions/TradeBotCreateRequest.ts'; +import DeleteTradeOffer from '../transactions/TradeBotDeleteRequest.ts'; +import signTradeBotTransaction from '../transactions/signTradeBotTransaction.ts'; +import { createTransaction } from '../transactions/transactions.ts'; +import { executeEvent } from '../utils/events.ts'; +import { fileToBase64 } from '../utils/fileReading/index.ts'; +import { mimeToExtensionMap } from '../utils/memeTypes.ts'; +import { RequestQueueWithPromise } from '../utils/queue/queue.ts'; +import utils from '../utils/utils.ts'; import ShortUniqueId from 'short-unique-id'; -import { isValidBase64WithDecode } from '../utils/decode'; +import { isValidBase64WithDecode } from '../utils/decode.ts'; import i18n from 'i18next'; const uid = new ShortUniqueId({ length: 6 }); From 5546ffdd7a4ca4c778b3075e10182af60d8d2137 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 12:12:25 +0200 Subject: [PATCH 460/717] Remove CustomSvg (unused) --- src/common/CustomSvg.tsx | 14 -------------- src/components/Embeds/AttachmentEmbed.tsx | 3 +-- src/components/Group/Forum/Thread.tsx | 1 + 3 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 src/common/CustomSvg.tsx diff --git a/src/common/CustomSvg.tsx b/src/common/CustomSvg.tsx deleted file mode 100644 index fc805a9..0000000 --- a/src/common/CustomSvg.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export const CustomSvg = ({ src, color = 'black', size = 24 }) => { - return ( - - {src} - - ); -}; diff --git a/src/components/Embeds/AttachmentEmbed.tsx b/src/components/Embeds/AttachmentEmbed.tsx index d7458a7..adbcd82 100644 --- a/src/components/Embeds/AttachmentEmbed.tsx +++ b/src/components/Embeds/AttachmentEmbed.tsx @@ -226,8 +226,7 @@ export const AttachmentCard = ({ width: '100%', }} > - {' '} - {' '} + )} {errorMsg && ( diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index bf8682f..a293d5f 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -931,6 +931,7 @@ export const Thread = ({ }} > + Date: Sat, 24 May 2025 12:14:06 +0200 Subject: [PATCH 461/717] List all namespaces in useTranslation --- src/App.tsx | 8 +++++++- src/Wallets.tsx | 16 ++++++++++++++-- src/components/Apps/AppInfo.tsx | 8 +++++++- src/components/Apps/AppInfoSnippet.tsx | 8 +++++++- src/components/Apps/AppPublish.tsx | 8 +++++++- src/components/Apps/AppRating.tsx | 8 +++++++- src/components/Apps/AppsCategoryDesktop.tsx | 8 +++++++- src/components/Apps/AppsDesktop.tsx | 8 +++++++- src/components/Apps/AppsDevMode.tsx | 8 +++++++- src/components/Apps/AppsDevModeHome.tsx | 8 +++++++- src/components/Apps/AppsHomeDesktop.tsx | 8 +++++++- src/components/Apps/AppsLibraryDesktop.tsx | 8 +++++++- src/components/Apps/AppsNavBarDesktop.tsx | 8 +++++++- src/components/Apps/AppsPrivate.tsx | 8 +++++++- src/components/BuyQortInformation.tsx | 8 +++++++- src/components/Chat/AdminSpace.tsx | 8 +++++++- src/components/Chat/AdminSpaceInner.tsx | 8 +++++++- src/components/Chat/AnnouncementDiscussion.tsx | 8 +++++++- src/components/Chat/AnnouncementItem.tsx | 8 +++++++- src/components/Chat/AnnouncementList.tsx | 8 +++++++- src/components/Chat/ChatDirect.tsx | 8 +++++++- src/components/Chat/ChatGroup.tsx | 8 +++++++- src/components/Chat/ChatList.tsx | 8 +++++++- src/components/Chat/ChatOptions.tsx | 8 +++++++- src/components/Chat/CreateCommonSecret.tsx | 8 +++++++- src/components/Chat/GroupAnnouncements.tsx | 8 +++++++- src/components/Chat/GroupAvatar.tsx | 16 ++++++++++++++-- src/components/Chat/MentionList.tsx | 8 +++++++- src/components/Chat/MessageItem.tsx | 16 ++++++++++++++-- src/components/Chat/TipTap.tsx | 8 +++++++- src/components/CoreSyncStatus.tsx | 8 +++++++- src/components/Desktop/DesktopFooter.tsx | 8 +++++++- src/components/Desktop/DesktopHeader.tsx | 8 +++++++- src/components/Desktop/DesktopSideBar.tsx | 8 +++++++- src/components/Embeds/AttachmentEmbed.tsx | 8 +++++++- src/components/Embeds/Embed.tsx | 8 +++++++- src/components/Embeds/ImageEmbed.tsx | 8 +++++++- src/components/Embeds/PollEmbed.tsx | 16 ++++++++++++++-- src/components/GeneralNotifications.tsx | 8 +++++++- src/components/GlobalActions/JoinGroup.tsx | 8 +++++++- src/components/Group/AddGroup.tsx | 8 +++++++- src/components/Group/AddGroupList.tsx | 8 +++++++- src/components/Group/BlockedUsersModal.tsx | 8 +++++++- src/components/Group/Forum/GroupMail.tsx | 8 +++++++- src/components/Group/Forum/NewThread.tsx | 8 +++++++- src/components/Group/Forum/Thread.tsx | 8 +++++++- src/components/Group/Group.tsx | 8 +++++++- src/components/Group/GroupInvites.tsx | 8 +++++++- src/components/Group/GroupJoinRequests.tsx | 8 +++++++- src/components/Group/GroupList.tsx | 8 +++++++- src/components/Group/HomeDesktop.tsx | 8 +++++++- src/components/Group/InviteMember.tsx | 8 +++++++- src/components/Group/ListOfBans.tsx | 8 +++++++- src/components/Group/ListOfGroupPromotions.tsx | 8 +++++++- src/components/Group/ListOfInvites.tsx | 8 +++++++- src/components/Group/ListOfJoinRequests.tsx | 8 +++++++- src/components/Group/ListOfMembers.tsx | 8 +++++++- .../Group/ListOfThreadPostsWatched.tsx | 8 +++++++- src/components/Group/ManageMembers.tsx | 8 +++++++- src/components/Group/QMailMessages.tsx | 8 +++++++- src/components/Group/Settings.tsx | 16 ++++++++++++++-- src/components/Group/UserListOfInvites.tsx | 8 +++++++- src/components/Group/WalletsAppWrapper.tsx | 8 +++++++- src/components/MainAvatar.tsx | 16 ++++++++++++++-- src/components/Minting/Minting.tsx | 8 +++++++- src/components/NewUsersCTA.tsx | 8 +++++++- src/components/QMailStatus.tsx | 8 +++++++- src/components/QortPayment.tsx | 8 +++++++- src/components/RegisterName.tsx | 8 +++++++- src/components/Save/Save.tsx | 8 +++++++- src/components/Theme/ThemeManager.tsx | 8 +++++++- src/components/Theme/ThemeSelector.tsx | 8 +++++++- src/components/UserLookup.tsx/UserLookup.tsx | 8 +++++++- src/components/WrapperUserAction.tsx | 16 ++++++++++++++-- src/hooks/useHandlePrivateApps.tsx | 8 +++++++- 75 files changed, 574 insertions(+), 82 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 4fd9057..f58c7c2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -308,7 +308,13 @@ function App() { const [isLoading, setIsLoading] = useState(false); const [isLoadingSendCoin, setIsLoadingSendCoin] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); const [ diff --git a/src/Wallets.tsx b/src/Wallets.tsx index e6e4906..c0aa098 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -53,7 +53,13 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { const [isOpenSeedModal, setIsOpenSeedModal] = useState(false); const [isLoadingEncryptSeed, setIsLoadingEncryptSeed] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const { isShow, onCancel, onOk, show } = useModal(); const { getRootProps, getInputProps } = useDropzone({ @@ -461,7 +467,13 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => { const [note, setNote] = useState(''); const [isEdit, setIsEdit] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); useEffect(() => { if (wallet?.name) { diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index 1c77d25..180dac4 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -37,7 +37,13 @@ export const AppInfo = ({ app, myName }) => { ); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const isSelectedAppPinned = !!sortablePinnedApps?.find( (item) => item?.name === app?.name && item?.service === app?.service diff --git a/src/components/Apps/AppInfoSnippet.tsx b/src/components/Apps/AppInfoSnippet.tsx index a0b8f72..41391dc 100644 --- a/src/components/Apps/AppInfoSnippet.tsx +++ b/src/components/Apps/AppInfoSnippet.tsx @@ -42,7 +42,13 @@ export const AppInfoSnippet = ({ ); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( { const [file, setFile] = useState(null); const { show } = useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [tag1, setTag1] = useState(''); const [tag2, setTag2] = useState(''); const [tag3, setTag3] = useState(''); diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index edd628a..b0f8258 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -20,7 +20,13 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { const [openSnack, setOpenSnack] = useState(false); const [infoSnack, setInfoSnack] = useState(null); const hasCalledRef = useRef(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getRating = useCallback(async (name, service) => { try { diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index 4c4a541..3752353 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -45,7 +45,13 @@ export const AppsCategoryDesktop = ({ const [searchValue, setSearchValue] = useState(''); const virtuosoRef = useRef(null); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const categoryList = useMemo(() => { if (category?.id === 'all') return availableQapps; diff --git a/src/components/Apps/AppsDesktop.tsx b/src/components/Apps/AppsDesktop.tsx index 0ef5366..27323fd 100644 --- a/src/components/Apps/AppsDesktop.tsx +++ b/src/components/Apps/AppsDesktop.tsx @@ -50,7 +50,13 @@ export const AppsDesktop = ({ const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); const { showTutorial } = useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const myApp = useMemo(() => { return availableQapps.find( diff --git a/src/components/Apps/AppsDevMode.tsx b/src/components/Apps/AppsDevMode.tsx index 89358f5..7d90e0b 100644 --- a/src/components/Apps/AppsDevMode.tsx +++ b/src/components/Apps/AppsDevMode.tsx @@ -47,7 +47,13 @@ export const AppsDevMode = ({ const [categories, setCategories] = useState([]); const iframeRefs = useRef({}); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); useEffect(() => { setTimeout(() => { diff --git a/src/components/Apps/AppsDevModeHome.tsx b/src/components/Apps/AppsDevModeHome.tsx index f9ade5d..88218ca 100644 --- a/src/components/Apps/AppsDevModeHome.tsx +++ b/src/components/Apps/AppsDevModeHome.tsx @@ -41,7 +41,13 @@ export const AppsDevModeHome = ({ const [domain, setDomain] = useState('127.0.0.1'); const [port, setPort] = useState(''); const [selectedPreviewFile, setSelectedPreviewFile] = useState(null); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const { isShow, onCancel, onOk, show, message } = useModal(); const { openSnackGlobal, diff --git a/src/components/Apps/AppsHomeDesktop.tsx b/src/components/Apps/AppsHomeDesktop.tsx index b1378f6..ffce539 100644 --- a/src/components/Apps/AppsHomeDesktop.tsx +++ b/src/components/Apps/AppsHomeDesktop.tsx @@ -27,7 +27,13 @@ export const AppsHomeDesktop = ({ }) => { const [qortalUrl, setQortalUrl] = useState(''); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const openQortalUrl = () => { try { diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index d953bff..d844dbf 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -105,7 +105,13 @@ export const AppsLibraryDesktop = ({ const [searchValue, setSearchValue] = useState(''); const virtuosoRef = useRef(null); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const officialApps = useMemo(() => { return availableQapps.filter( diff --git a/src/components/Apps/AppsNavBarDesktop.tsx b/src/components/Apps/AppsNavBarDesktop.tsx index e6d2ea2..616c09f 100644 --- a/src/components/Apps/AppsNavBarDesktop.tsx +++ b/src/components/Apps/AppsNavBarDesktop.tsx @@ -76,7 +76,13 @@ export const AppsNavBarDesktop = ({ disableBack }) => { ); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isNewTabWindow, setIsNewTabWindow] = useState(false); const tabsRef = useRef(null); const [anchorEl, setAnchorEl] = useState(null); diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index afbf186..355d868 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -63,7 +63,13 @@ export const AppsPrivate = ({ myName }) => { const [memberGroups] = useAtom(memberGroupsAtom); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const myGroupsPrivate = useMemo(() => { return memberGroups?.filter( diff --git a/src/components/BuyQortInformation.tsx b/src/components/BuyQortInformation.tsx index e10f535..c3519fa 100644 --- a/src/components/BuyQortInformation.tsx +++ b/src/components/BuyQortInformation.tsx @@ -27,7 +27,13 @@ import { useTranslation } from 'react-i18next'; export const BuyQortInformation = ({ balance }) => { const [isOpen, setIsOpen] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const openBuyQortInfoFunc = useCallback( (e) => { diff --git a/src/components/Chat/AdminSpace.tsx b/src/components/Chat/AdminSpace.tsx index 276369a..62a8832 100644 --- a/src/components/Chat/AdminSpace.tsx +++ b/src/components/Chat/AdminSpace.tsx @@ -19,7 +19,13 @@ export const AdminSpace = ({ isOwner, }) => { const [isMoved, setIsMoved] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); useEffect(() => { if (hide) { diff --git a/src/components/Chat/AdminSpaceInner.tsx b/src/components/Chat/AdminSpaceInner.tsx index 97e3fd1..7dbb705 100644 --- a/src/components/Chat/AdminSpaceInner.tsx +++ b/src/components/Chat/AdminSpaceInner.tsx @@ -74,7 +74,13 @@ export const AdminSpaceInner = ({ const [isLoadingPublishKey, setIsLoadingPublishKey] = useState(false); const { show, setInfoSnackCustom, setOpenSnackGlobal } = useContext(QORTAL_APP_CONTEXT); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getAdminGroupSecretKey = useCallback(async () => { try { diff --git a/src/components/Chat/AnnouncementDiscussion.tsx b/src/components/Chat/AnnouncementDiscussion.tsx index e0da144..b248735 100644 --- a/src/components/Chat/AnnouncementDiscussion.tsx +++ b/src/components/Chat/AnnouncementDiscussion.tsx @@ -40,7 +40,13 @@ export const AnnouncementDiscussion = ({ isPrivate, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isSending, setIsSending] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isFocusedParent, setIsFocusedParent] = useState(false); diff --git a/src/components/Chat/AnnouncementItem.tsx b/src/components/Chat/AnnouncementItem.tsx index 98ebe11..a20f58d 100644 --- a/src/components/Chat/AnnouncementItem.tsx +++ b/src/components/Chat/AnnouncementItem.tsx @@ -18,7 +18,13 @@ export const AnnouncementItem = ({ myName, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [commentLength, setCommentLength] = useState(0); const getNumberOfComments = useCallback(async () => { diff --git a/src/components/Chat/AnnouncementList.tsx b/src/components/Chat/AnnouncementList.tsx index ba2892e..eea6c83 100644 --- a/src/components/Chat/AnnouncementList.tsx +++ b/src/components/Chat/AnnouncementList.tsx @@ -20,7 +20,13 @@ export const AnnouncementList = ({ myName, }) => { const [messages, setMessages] = useState(initialMessages); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); useEffect(() => { cache.clearAll(); diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index 36681e9..0af54a6 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -42,7 +42,13 @@ export const ChatDirect = ({ setMobileViewModeKeepOpen, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue(); const [isFocusedParent, setIsFocusedParent] = useState(false); const [onEditMessage, setOnEditMessage] = useState(null); diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 783fec9..adc5048 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -96,7 +96,13 @@ export const ChatGroup = ({ const [, forceUpdate] = useReducer((x) => x + 1, 0); const lastReadTimestamp = useRef(null); const handleUpdateRef = useRef(null); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getTimestampEnterChat = async (selectedGroup) => { try { diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 96abf7a..1a47778 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -181,7 +181,13 @@ export const ChatList = ({ }, []); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( { const queryString = admins.map((name) => `name=${name}`).join('&'); diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index 18170cd..7e1e5f9 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -153,7 +153,13 @@ export const GroupAnnouncements = ({ editorRef.current = editorInstance; }; const [, forceUpdate] = useReducer((x) => x + 1, 0); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const triggerRerender = () => { forceUpdate(); // Trigger re-render by updating the state diff --git a/src/components/Chat/GroupAvatar.tsx b/src/components/Chat/GroupAvatar.tsx index f62a241..f71e8fe 100644 --- a/src/components/Chat/GroupAvatar.tsx +++ b/src/components/Chat/GroupAvatar.tsx @@ -33,7 +33,13 @@ export const GroupAvatar = ({ const [avatarFile, setAvatarFile] = useState(null); const [tempAvatar, setTempAvatar] = useState(null); const { show } = useContext(QORTAL_APP_CONTEXT); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [anchorEl, setAnchorEl] = useState(null); const [isLoading, setIsLoading] = useState(false); // Handle child element click to open Popover @@ -262,7 +268,13 @@ const PopoverComp = ({ myName, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( { - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [selectedIndex, setSelectedIndex] = useState(0); diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index a429846..f570ba2 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -170,7 +170,13 @@ export const MessageItem = memo( }, [message?.id]); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( <> @@ -612,7 +618,13 @@ export const MessageItem = memo( export const ReplyPreview = ({ message, isEdit = false }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( { if (editor && setEditorRef) { diff --git a/src/components/CoreSyncStatus.tsx b/src/components/CoreSyncStatus.tsx index 09a50ed..345678f 100644 --- a/src/components/CoreSyncStatus.tsx +++ b/src/components/CoreSyncStatus.tsx @@ -13,7 +13,13 @@ export const CoreSyncStatus = () => { const [coreInfos, setCoreInfos] = useState({}); const [isUsingGateway, setIsUsingGateway] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); useEffect(() => { diff --git a/src/components/Desktop/DesktopFooter.tsx b/src/components/Desktop/DesktopFooter.tsx index ac567ec..3441cf6 100644 --- a/src/components/Desktop/DesktopFooter.tsx +++ b/src/components/Desktop/DesktopFooter.tsx @@ -67,7 +67,13 @@ export const DesktopFooter = ({ }) => { const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); if (hide) return; return ( diff --git a/src/components/Desktop/DesktopHeader.tsx b/src/components/Desktop/DesktopHeader.tsx index c82eae0..0428462 100644 --- a/src/components/Desktop/DesktopHeader.tsx +++ b/src/components/Desktop/DesktopHeader.tsx @@ -84,7 +84,13 @@ export const DesktopHeader = ({ }) => { const [value, setValue] = useState(0); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( { const { name, service, identifier } = resourceData; diff --git a/src/components/Embeds/Embed.tsx b/src/components/Embeds/Embed.tsx index 6be25f7..0038830 100644 --- a/src/components/Embeds/Embed.tsx +++ b/src/components/Embeds/Embed.tsx @@ -63,7 +63,13 @@ export const Embed = ({ embedLink }) => { const [parsedData, setParsedData] = useState(null); const setBlobs = useSetAtom(blobControllerAtom); const [selectedGroupId] = useAtom(selectedGroupIdAtom); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const resourceData = useMemo(() => { const parsedDataOnTheFly = parseQortalLink(embedLink); if ( diff --git a/src/components/Embeds/ImageEmbed.tsx b/src/components/Embeds/ImageEmbed.tsx index a7b6534..ab0548d 100644 --- a/src/components/Embeds/ImageEmbed.tsx +++ b/src/components/Embeds/ImageEmbed.tsx @@ -30,7 +30,13 @@ export const ImageCard = ({ encryptionType, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isOpen, setIsOpen] = useState(true); const [height, setHeight] = useState('400px'); diff --git a/src/components/Embeds/PollEmbed.tsx b/src/components/Embeds/PollEmbed.tsx index 6d7a510..eb3e304 100644 --- a/src/components/Embeds/PollEmbed.tsx +++ b/src/components/Embeds/PollEmbed.tsx @@ -40,7 +40,13 @@ export const PollCard = ({ const { show, userInfo } = useContext(QORTAL_APP_CONTEXT); const [isLoadingSubmit, setIsLoadingSubmit] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const handleVote = async () => { const fee = await getFee('VOTE_ON_POLL'); @@ -379,7 +385,13 @@ const PollResults = ({ votes }) => { ...votes?.voteCounts?.map((option) => option.voteCount) ); const options = votes?.voteCounts; - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( diff --git a/src/components/GeneralNotifications.tsx b/src/components/GeneralNotifications.tsx index 3e23637..53e85a8 100644 --- a/src/components/GeneralNotifications.tsx +++ b/src/components/GeneralNotifications.tsx @@ -32,7 +32,13 @@ export const GeneralNotifications = ({ address }) => { setAnchorEl(event.currentTarget); }; - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); return ( diff --git a/src/components/GlobalActions/JoinGroup.tsx b/src/components/GlobalActions/JoinGroup.tsx index 95e95e6..aca7e1f 100644 --- a/src/components/GlobalActions/JoinGroup.tsx +++ b/src/components/GlobalActions/JoinGroup.tsx @@ -29,7 +29,13 @@ export const JoinGroup = () => { const [isLoadingInfo, setIsLoadingInfo] = useState(false); const [isOpen, setIsOpen] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isLoadingJoinGroup, setIsLoadingJoinGroup] = useState(false); const handleJoinGroup = async (e) => { diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index ec01443..3815d0d 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -97,7 +97,13 @@ export const AddGroup = ({ address, open, setOpen }) => { setMaxBlock(event.target.value as string); }; - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); const handleCreateGroup = async () => { diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index 8cad787..db4460c 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -42,7 +42,13 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const { show } = useContext(QORTAL_APP_CONTEXT); const [memberGroups] = useAtom(memberGroupsAtom); const setTxList = useSetAtom(txListAtom); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [groups, setGroups] = useState([]); const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index c8145b3..8cdd358 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -28,7 +28,13 @@ import { useTranslation } from 'react-i18next'; export const BlockedUsersModal = () => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isOpenBlockedModal, setIsOpenBlockedModal] = useAtom( isOpenBlockedModalAtom ); diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index afa6e91..ba4b620 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -73,7 +73,13 @@ export const GroupMail = ({ const anchorElInstanceFilter = useRef(null); const [tempPublishedList, setTempPublishedList] = useState([]); const dataPublishes = useRef({}); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); const [isLoading, setIsLoading] = useState(false); const groupIdRef = useRef(null); diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index 9749480..f89e2aa 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -144,7 +144,13 @@ export const NewThread = ({ setPostReply, isPrivate, }: NewMessageProps) => { - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const { show } = useContext(QORTAL_APP_CONTEXT); const [isOpen, setIsOpen] = useState(false); const [value, setValue] = useState(''); diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index a293d5f..762db5d 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -115,7 +115,13 @@ export const Thread = ({ const [isLoading, setIsLoading] = useState(true); const [postReply, setPostReply] = useState(null); const [hasLastPage, setHasLastPage] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); // Update: Use a new ref for the scrollable container const threadContainerRef = useRef(null); diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 469a86f..67e3690 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -444,7 +444,13 @@ export const Group = ({ const [isForceShowCreationKeyPopup, setIsForceShowCreationKeyPopup] = useState(false); const groupsOwnerNamesRef = useRef({}); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [groupsProperties, setGroupsProperties] = useAtom(groupsPropertiesAtom); const setGroupsOwnerNames = useSetAtom(groupsOwnerNamesAtom); diff --git a/src/components/Group/GroupInvites.tsx b/src/components/Group/GroupInvites.tsx index 96f375c..8bad077 100644 --- a/src/components/Group/GroupInvites.tsx +++ b/src/components/Group/GroupInvites.tsx @@ -18,7 +18,13 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { const [groupsWithJoinRequests, setGroupsWithJoinRequests] = useState([]); const [isExpanded, setIsExpanded] = useState(false); const [loading, setLoading] = useState(true); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); const getJoinRequests = async () => { diff --git a/src/components/Group/GroupJoinRequests.tsx b/src/components/Group/GroupJoinRequests.tsx index 8f617a1..1310174 100644 --- a/src/components/Group/GroupJoinRequests.tsx +++ b/src/components/Group/GroupJoinRequests.tsx @@ -28,7 +28,13 @@ export const GroupJoinRequests = ({ setDesktopViewMode, }) => { const [isExpanded, setIsExpanded] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [groupsWithJoinRequests, setGroupsWithJoinRequests] = useState([]); const [loading, setLoading] = useState(true); const [txList] = useAtom(txListAtom); diff --git a/src/components/Group/GroupList.tsx b/src/components/Group/GroupList.tsx index 156d744..e928331 100644 --- a/src/components/Group/GroupList.tsx +++ b/src/components/Group/GroupList.tsx @@ -49,7 +49,13 @@ export const GroupList = ({ myAddress, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isRunningPublicNode] = useAtom(isRunningPublicNodeAtom); return ( diff --git a/src/components/Group/HomeDesktop.tsx b/src/components/Group/HomeDesktop.tsx index a5cf85e..f148be1 100644 --- a/src/components/Group/HomeDesktop.tsx +++ b/src/components/Group/HomeDesktop.tsx @@ -31,7 +31,13 @@ export const HomeDesktop = ({ const [checked1, setChecked1] = useState(false); const [checked2, setChecked2] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); useEffect(() => { diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx index 8a562d0..8ac8d93 100644 --- a/src/components/Group/InviteMember.tsx +++ b/src/components/Group/InviteMember.tsx @@ -10,7 +10,13 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const [value, setValue] = useState(''); const [expiryTime, setExpiryTime] = useState('259200'); const [isLoadingInvite, setIsLoadingInvite] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const inviteMember = async () => { try { diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index 122ce27..fc84cc9 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -56,7 +56,13 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const listRef = useRef(null); const [isLoadingUnban, setIsLoadingUnban] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getInvites = async (groupId) => { try { diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index b3bedab..47f6e10 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -91,7 +91,13 @@ export const ListOfGroupPromotions = () => { const { show } = useContext(QORTAL_APP_CONTEXT); const setTxList = useSetAtom(txListAtom); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const listRef = useRef(null); const rowVirtualizer = useVirtualizer({ count: promotions.length, diff --git a/src/components/Group/ListOfInvites.tsx b/src/components/Group/ListOfInvites.tsx index 037dddc..3758863 100644 --- a/src/components/Group/ListOfInvites.tsx +++ b/src/components/Group/ListOfInvites.tsx @@ -60,7 +60,13 @@ export const ListOfInvites = ({ const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const [isLoadingCancelInvite, setIsLoadingCancelInvite] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const listRef = useRef(null); const getInvites = async (groupId) => { diff --git a/src/components/Group/ListOfJoinRequests.tsx b/src/components/Group/ListOfJoinRequests.tsx index 5fbfe9e..2a65cfe 100644 --- a/src/components/Group/ListOfJoinRequests.tsx +++ b/src/components/Group/ListOfJoinRequests.tsx @@ -64,7 +64,13 @@ export const ListOfJoinRequests = ({ const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const listRef = useRef(null); const [isLoadingAccept, setIsLoadingAccept] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getInvites = async (groupId) => { try { diff --git a/src/components/Group/ListOfMembers.tsx b/src/components/Group/ListOfMembers.tsx index c57e501..1d23b44 100644 --- a/src/components/Group/ListOfMembers.tsx +++ b/src/components/Group/ListOfMembers.tsx @@ -42,7 +42,13 @@ const ListOfMembers = ({ const [isLoadingMakeAdmin, setIsLoadingMakeAdmin] = useState(false); const [isLoadingRemoveAdmin, setIsLoadingRemoveAdmin] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const listRef = useRef(null); const handlePopoverOpen = (event, index) => { diff --git a/src/components/Group/ListOfThreadPostsWatched.tsx b/src/components/Group/ListOfThreadPostsWatched.tsx index 0868a53..8a3c3d2 100644 --- a/src/components/Group/ListOfThreadPostsWatched.tsx +++ b/src/components/Group/ListOfThreadPostsWatched.tsx @@ -14,7 +14,13 @@ import { useTranslation } from 'react-i18next'; export const ListOfThreadPostsWatched = () => { const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getPosts = async () => { try { diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index e87b002..b8c7052 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -71,7 +71,13 @@ export const ManageMembers = ({ setValue(newValue); }; const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const { show } = useContext(QORTAL_APP_CONTEXT); const setTxList = useSetAtom(txListAtom); diff --git a/src/components/Group/QMailMessages.tsx b/src/components/Group/QMailMessages.tsx index f6490e0..93d96cb 100644 --- a/src/components/Group/QMailMessages.tsx +++ b/src/components/Group/QMailMessages.tsx @@ -54,7 +54,13 @@ export const QMailMessages = ({ userName, userAddress }) => { const [loading, setLoading] = useState(true); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getMails = useCallback(async () => { try { diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index 78db9bc..ef6d0cb 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -87,7 +87,13 @@ export const Settings = ({ open, setOpen, rawWallet }) => { const [checked, setChecked] = useState(false); const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const handleChange = (event: ChangeEvent) => { setChecked(event.target.checked); @@ -235,7 +241,13 @@ const ExportPrivateKey = ({ rawWallet }) => { const [isOpen, setIsOpen] = useState(false); const { setOpenSnackGlobal, setInfoSnackCustom } = useContext(QORTAL_APP_CONTEXT); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const exportPrivateKeyFunc = async () => { try { diff --git a/src/components/Group/UserListOfInvites.tsx b/src/components/Group/UserListOfInvites.tsx index 2717af9..50cbb5b 100644 --- a/src/components/Group/UserListOfInvites.tsx +++ b/src/components/Group/UserListOfInvites.tsx @@ -61,7 +61,13 @@ export const UserListOfInvites = ({ const [invites, setInvites] = useState([]); const [isLoading, setIsLoading] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const listRef = useRef(null); diff --git a/src/components/Group/WalletsAppWrapper.tsx b/src/components/Group/WalletsAppWrapper.tsx index ee9885a..990544d 100644 --- a/src/components/Group/WalletsAppWrapper.tsx +++ b/src/components/Group/WalletsAppWrapper.tsx @@ -15,7 +15,13 @@ import { useAtom } from 'jotai'; import { useTranslation } from 'react-i18next'; export const WalletsAppWrapper = () => { - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const iframeRef = useRef(null); const [isOpen, setIsOpen] = useState(false); const [navigationController, setNavigationController] = useAtom( diff --git a/src/components/MainAvatar.tsx b/src/components/MainAvatar.tsx index 8e03e97..c985bb2 100644 --- a/src/components/MainAvatar.tsx +++ b/src/components/MainAvatar.tsx @@ -45,7 +45,13 @@ export const MainAvatar = ({ myName, balance, setOpenSnack, setInfoSnack }) => { const open = Boolean(anchorEl); const id = open ? 'avatar-img' : undefined; - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const checkIfAvatarExists = async () => { try { @@ -257,7 +263,13 @@ const PopoverComp = ({ myName, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( { const { show: showKey, message } = useModal(); const { isShow: isShowNext, onOk, show: showNext } = useModal(); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [info, setInfo] = useState(null); const [names, setNames] = useState({}); const [accountInfos, setAccountInfos] = useState({}); diff --git a/src/components/NewUsersCTA.tsx b/src/components/NewUsersCTA.tsx index 94606d1..d444717 100644 --- a/src/components/NewUsersCTA.tsx +++ b/src/components/NewUsersCTA.tsx @@ -3,7 +3,13 @@ import { Spacer } from '../common/Spacer'; import { useTranslation } from 'react-i18next'; export const NewUsersCTA = ({ balance }) => { - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); if (balance === undefined || +balance > 0) return null; diff --git a/src/components/QMailStatus.tsx b/src/components/QMailStatus.tsx index 45e51c8..f0b51f3 100644 --- a/src/components/QMailStatus.tsx +++ b/src/components/QMailStatus.tsx @@ -8,7 +8,13 @@ import { useTranslation } from 'react-i18next'; import { useAtom } from 'jotai'; export const QMailStatus = () => { - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); const [lastEnteredTimestamp, setLastEnteredTimestamp] = useAtom( diff --git a/src/components/QortPayment.tsx b/src/components/QortPayment.tsx index 1907686..f31c7c0 100644 --- a/src/components/QortPayment.tsx +++ b/src/components/QortPayment.tsx @@ -7,7 +7,13 @@ import { useTranslation } from 'react-i18next'; export const QortPayment = ({ balance, show, onSuccess, defaultPaymentTo }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [paymentTo, setPaymentTo] = useState(defaultPaymentTo); const [paymentAmount, setPaymentAmount] = useState(0); const [paymentPassword, setPaymentPassword] = useState(''); diff --git a/src/components/RegisterName.tsx b/src/components/RegisterName.tsx index 2b5b224..eb988ff 100644 --- a/src/components/RegisterName.tsx +++ b/src/components/RegisterName.tsx @@ -51,7 +51,13 @@ export const RegisterName = ({ ); const [nameFee, setNameFee] = useState(null); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const checkIfNameExisits = async (name) => { if (!name?.trim()) { setIsNameAvailable(Availability.NULL); diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index ed5c77e..9a9af08 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -84,7 +84,13 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { const [anchorEl, setAnchorEl] = useState(null); const { show } = useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const hasChanged = useMemo(() => { const newChanges = { diff --git a/src/components/Theme/ThemeManager.tsx b/src/components/Theme/ThemeManager.tsx index 69c63c9..f6b1551 100644 --- a/src/components/Theme/ThemeManager.tsx +++ b/src/components/Theme/ThemeManager.tsx @@ -82,7 +82,13 @@ export default function ThemeManager() { }); const [currentTab, setCurrentTab] = useState('light'); const nameInputRef = useRef(null); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); useEffect(() => { if (openEditor && nameInputRef.current) { diff --git a/src/components/Theme/ThemeSelector.tsx b/src/components/Theme/ThemeSelector.tsx index dd0dbba..6c25e3c 100644 --- a/src/components/Theme/ThemeSelector.tsx +++ b/src/components/Theme/ThemeSelector.tsx @@ -5,7 +5,13 @@ import DarkModeIcon from '@mui/icons-material/DarkMode'; import { useTranslation } from 'react-i18next'; const ThemeSelector = () => { - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const { themeMode, toggleTheme } = useThemeContext(); return ( diff --git a/src/components/UserLookup.tsx/UserLookup.tsx b/src/components/UserLookup.tsx/UserLookup.tsx index 3be4ef6..bd52b2a 100644 --- a/src/components/UserLookup.tsx/UserLookup.tsx +++ b/src/components/UserLookup.tsx/UserLookup.tsx @@ -49,7 +49,13 @@ function formatAddress(str) { export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [nameOrAddress, setNameOrAddress] = useState(''); const [inputValue, setInputValue] = useState(''); const { results, isLoading } = useNameSearch(inputValue); diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx index 490333d..caaee97 100644 --- a/src/components/WrapperUserAction.tsx +++ b/src/components/WrapperUserAction.tsx @@ -14,7 +14,13 @@ import { useTranslation } from 'react-i18next'; export const WrapperUserAction = ({ children, address, name, disabled }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isRunningPublicNode] = useAtom(isRunningPublicNodeAtom); const [anchorEl, setAnchorEl] = useState(null); @@ -177,7 +183,13 @@ const BlockUser = ({ address, name, handleClose }) => { const { isUserBlocked, addToBlockList, removeBlockFromList } = useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); useEffect(() => { if (!address) return; diff --git a/src/hooks/useHandlePrivateApps.tsx b/src/hooks/useHandlePrivateApps.tsx index 3876130..1065502 100644 --- a/src/hooks/useHandlePrivateApps.tsx +++ b/src/hooks/useHandlePrivateApps.tsx @@ -22,7 +22,13 @@ export const useHandlePrivateApps = () => { } = useContext(QORTAL_APP_CONTEXT); const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom); const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const openApp = async ( privateAppProperties, From cb86a9b89da89a29b72c09a5fa3efcdaef0aed88 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 12:19:11 +0200 Subject: [PATCH 462/717] Rename files and folders (uniform the case) --- src/App.tsx | 2 +- src/Wallets.tsx | 2 +- src/components/Chat/ChatDirect.tsx | 2 +- src/components/Chat/ChatGroup.tsx | 2 +- src/components/Group/Group.tsx | 2 +- src/main.tsx | 4 +- src/{ => messaging}/MessageQueueContext.tsx | 0 ...ackground.tsx => MessagesToBackground.tsx} | 44 ++++++++++++++----- ...enerator.ts => randomSentenceGenerator.ts} | 0 src/utils/{Size => size}/index.ts | 0 10 files changed, 40 insertions(+), 18 deletions(-) rename src/{ => messaging}/MessageQueueContext.tsx (100%) rename src/messaging/{messagesToBackground.tsx => MessagesToBackground.tsx} (71%) rename src/utils/seedPhrase/{RandomSentenceGenerator.ts => randomSentenceGenerator.ts} (100%) rename src/utils/{Size => size}/index.ts (100%) diff --git a/src/App.tsx b/src/App.tsx index f58c7c2..9e1332b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -35,7 +35,7 @@ import PersonSearchIcon from '@mui/icons-material/PersonSearch'; import qortLogo from './assets/qort.png'; import { Return } from './assets/Icons/Return.tsx'; import WarningIcon from '@mui/icons-material/Warning'; -import './utils/seedPhrase/RandomSentenceGenerator'; +import './utils/seedPhrase/randomSentenceGenerator.ts'; import EngineeringIcon from '@mui/icons-material/Engineering'; import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; import PriorityHighIcon from '@mui/icons-material/PriorityHigh'; diff --git a/src/Wallets.tsx b/src/Wallets.tsx index c0aa098..1fe7583 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -75,7 +75,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { const fileContents = await new Promise((resolve, reject) => { const reader = new FileReader(); - reader.onabort = () => reject('File reading was aborted'); + reader.onabort = () => reject('File reading was aborted'); // TODO translate reader.onerror = () => reject('File reading has failed'); reader.onload = () => { // Resolve the promise with the reader result when reading completes diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index 0af54a6..6f95eee 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -15,7 +15,7 @@ import { resumeAllQueues, } from '../../App'; import { getPublicKey } from '../../background/background.ts'; -import { useMessageQueue } from '../../MessageQueueContext'; +import { useMessageQueue } from '../../messaging/MessageQueueContext.tsx'; import { executeEvent, subscribeToEvent, diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index adc5048..3d2f067 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -25,7 +25,7 @@ import { } from '../../App'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../../constants/constants'; -import { useMessageQueue } from '../../MessageQueueContext'; +import { useMessageQueue } from '../../messaging/MessageQueueContext.tsx'; import { executeEvent, subscribeToEvent, diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 67e3690..c64c8cc 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -43,7 +43,7 @@ import { } from '../../utils/events'; import { RequestQueueWithPromise } from '../../utils/queue/queue'; import { WebSocketActive } from './WebsocketActive'; -import { useMessageQueue } from '../../MessageQueueContext'; +import { useMessageQueue } from '../../messaging/MessageQueueContext'; import { HomeDesktop } from './HomeDesktop'; import { IconWrapper } from '../Desktop/DesktopFooter'; import { DesktopHeader } from '../Desktop/DesktopHeader'; diff --git a/src/main.tsx b/src/main.tsx index d6da0d2..2b224c8 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,8 +1,8 @@ import { createRoot } from 'react-dom/client'; import App from './App.tsx'; import '../src/styles/index.css'; -import './messaging/messagesToBackground'; -import { MessageQueueProvider } from './MessageQueueContext.tsx'; +import './messaging/MessagesToBackground.tsx'; +import { MessageQueueProvider } from './messaging/MessageQueueContext.tsx'; import { ThemeProvider } from './components/Theme/ThemeContext.tsx'; import { CssBaseline } from '@mui/material'; import './i18n/i18n.js'; diff --git a/src/MessageQueueContext.tsx b/src/messaging/MessageQueueContext.tsx similarity index 100% rename from src/MessageQueueContext.tsx rename to src/messaging/MessageQueueContext.tsx diff --git a/src/messaging/messagesToBackground.tsx b/src/messaging/MessagesToBackground.tsx similarity index 71% rename from src/messaging/messagesToBackground.tsx rename to src/messaging/MessagesToBackground.tsx index 4eebc5e..a325352 100644 --- a/src/messaging/messagesToBackground.tsx +++ b/src/messaging/MessagesToBackground.tsx @@ -1,5 +1,3 @@ - - // Utility to generate unique request IDs function generateRequestId() { return `msg-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; @@ -9,34 +7,55 @@ function generateRequestId() { const callbackMap = new Map(); // Global listener for handling message responses -window.addEventListener("message", (event) => { +window.addEventListener('message', (event) => { const { type, requestId, payload, error, message } = event.data; // Only process messages of type `backgroundMessageResponse` - if (type !== "backgroundMessageResponse") return; + if (type !== 'backgroundMessageResponse') return; // Check if there’s a callback stored for this requestId if (callbackMap.has(requestId)) { const { resolve, reject } = callbackMap.get(requestId); callbackMap.delete(requestId); // Remove callback after use - resolve(event.data) + resolve(event.data); } }); -export const sendMessageBackground = (action, data = {}, timeout = 240000, isExtension, appInfo, skipAuth) => { +export const sendMessageBackground = ( + action, + data = {}, + timeout = 240000, + isExtension, + appInfo, + skipAuth +) => { return new Promise((resolve, reject) => { const requestId = generateRequestId(); // Unique ID for each request callbackMap.set(requestId, { resolve, reject }); // Store both resolve and reject callbacks - const targetOrigin = window.location.origin + const targetOrigin = window.location.origin; // Send the message with `backgroundMessage` type - window.postMessage({ type: "backgroundMessage", action, requestId, payload: data, isExtension, appInfo, skipAuth }, targetOrigin); + window.postMessage( + { + type: 'backgroundMessage', + action, + requestId, + payload: data, + isExtension, + appInfo, + skipAuth, + }, + targetOrigin + ); // Set up a timeout to automatically reject if no response is received const timeoutId = setTimeout(() => { // Remove the callback to prevent memory leaks callbackMap.delete(requestId); - reject({ error: "timeout", message: `Request timed out after ${timeout} ms` }); + reject({ + error: 'timeout', + message: `Request timed out after ${timeout} ms`, + }); }, timeout); // Adjust resolve/reject to clear the timeout when a response arrives @@ -48,14 +67,17 @@ export const sendMessageBackground = (action, data = {}, timeout = 240000, isExt reject: (error) => { clearTimeout(timeoutId); // Clear the timeout if an error occurs reject(error); - } + }, }); }).then((response) => { // Return payload or error based on response content if (response?.payload !== null && response?.payload !== undefined) { return response.payload; } else if (response?.error) { - return { error: response.error, message: response?.message || "An error occurred" }; + return { + error: response.error, + message: response?.message || 'An error occurred', + }; } }); }; diff --git a/src/utils/seedPhrase/RandomSentenceGenerator.ts b/src/utils/seedPhrase/randomSentenceGenerator.ts similarity index 100% rename from src/utils/seedPhrase/RandomSentenceGenerator.ts rename to src/utils/seedPhrase/randomSentenceGenerator.ts diff --git a/src/utils/Size/index.ts b/src/utils/size/index.ts similarity index 100% rename from src/utils/Size/index.ts rename to src/utils/size/index.ts From 0209418c1d5eff1736f74bbf46eae973791a2588 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 12:21:37 +0200 Subject: [PATCH 463/717] Move file into components --- src/App.tsx | 2 +- src/{ => components}/Wallets.tsx | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) rename src/{ => components}/Wallets.tsx (96%) diff --git a/src/App.tsx b/src/App.tsx index 9e1332b..ed70e64 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -118,7 +118,7 @@ import { } from './atoms/global'; import { NotAuthenticated } from './components/NotAuthenticated.tsx'; import { handleGetFileFromIndexedDB } from './utils/indexedDB'; -import { Wallets } from './Wallets'; +import { Wallets } from './components/Wallets.tsx'; import { useFetchResources } from './common/useFetchResources'; import { Tutorials } from './components/Tutorials/Tutorials'; import { useHandleTutorials } from './hooks/useHandleTutorials.tsx'; diff --git a/src/Wallets.tsx b/src/components/Wallets.tsx similarity index 96% rename from src/Wallets.tsx rename to src/components/Wallets.tsx index 1fe7583..24aa4705 100644 --- a/src/Wallets.tsx +++ b/src/components/Wallets.tsx @@ -18,24 +18,24 @@ import { Input, useTheme, } from '@mui/material'; -import { CustomButton } from './styles/App-styles'; +import { CustomButton } from '../styles/App-styles.ts'; import { useDropzone } from 'react-dropzone'; import EditIcon from '@mui/icons-material/Edit'; -import { Label } from './components/Group/AddGroup'; -import { Spacer } from './common/Spacer'; +import { Label } from './Group/AddGroup.tsx'; +import { Spacer } from '../common/Spacer.tsx'; import { getWallets, storeWallets, walletVersion, -} from './background/background.ts'; -import { useModal } from './common/useModal'; -import PhraseWallet from './utils/generateWallet/phrase-wallet'; -import { decryptStoredWalletFromSeedPhrase } from './utils/decryptWallet'; -import { crypto } from './constants/decryptWallet'; +} from '../background/background.ts'; +import { useModal } from '../common/useModal.tsx'; +import PhraseWallet from '../utils/generateWallet/phrase-wallet.ts'; +import { decryptStoredWalletFromSeedPhrase } from '../utils/decryptWallet.ts'; +import { crypto } from '../constants/decryptWallet.ts'; import { LoadingButton } from '@mui/lab'; -import { PasswordField } from './components'; -import { HtmlTooltip } from './components/NotAuthenticated'; -import { QORTAL_APP_CONTEXT } from './App'; +import { PasswordField } from './index.ts'; +import { HtmlTooltip } from './NotAuthenticated.tsx'; +import { QORTAL_APP_CONTEXT } from '../App.tsx'; import { useTranslation } from 'react-i18next'; const parsefilenameQortal = (filename) => { From b23ce1a13640bff279d88a8e67ad2afd1603bae9 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 12:26:21 +0200 Subject: [PATCH 464/717] Move hooks into proper folder --- src/App.tsx | 4 ++-- src/components/Apps/AppsDevModeHome.tsx | 2 +- src/components/Group/BlockedUsersModal.tsx | 2 +- src/components/Minting/Minting.tsx | 2 +- src/components/Wallets.tsx | 2 +- src/{common => hooks}/useFetchResources.tsx | 0 src/{common => hooks}/useModal.tsx | 0 7 files changed, 6 insertions(+), 6 deletions(-) rename src/{common => hooks}/useFetchResources.tsx (100%) rename src/{common => hooks}/useModal.tsx (100%) diff --git a/src/App.tsx b/src/App.tsx index ed70e64..4aad404 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -64,7 +64,7 @@ import { Loader } from './components/Loader'; import { PasswordField, ErrorText } from './components'; import { Group, requestQueueMemberNames } from './components/Group/Group'; import { TaskManager } from './components/TaskManager/TaskManager.tsx'; -import { useModal } from './common/useModal'; +import { useModal } from './hooks/useModal.tsx'; import { CustomizedSnackbars } from './components/Snackbar/Snackbar'; import SettingsIcon from '@mui/icons-material/Settings'; import LogoutIcon from '@mui/icons-material/Logout'; @@ -119,7 +119,7 @@ import { import { NotAuthenticated } from './components/NotAuthenticated.tsx'; import { handleGetFileFromIndexedDB } from './utils/indexedDB'; import { Wallets } from './components/Wallets.tsx'; -import { useFetchResources } from './common/useFetchResources'; +import { useFetchResources } from './hooks/useFetchResources.tsx'; import { Tutorials } from './components/Tutorials/Tutorials'; import { useHandleTutorials } from './hooks/useHandleTutorials.tsx'; import { useHandleUserInfo } from './hooks/useHandleUserInfo.tsx'; diff --git a/src/components/Apps/AppsDevModeHome.tsx b/src/components/Apps/AppsDevModeHome.tsx index 88218ca..db32248 100644 --- a/src/components/Apps/AppsDevModeHome.tsx +++ b/src/components/Apps/AppsDevModeHome.tsx @@ -22,7 +22,7 @@ import { Add } from '@mui/icons-material'; import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { executeEvent } from '../../utils/events'; import { Spacer } from '../../common/Spacer'; -import { useModal } from '../../common/useModal'; +import { useModal } from '../../hooks/useModal.tsx'; import { createEndpoint, isUsingLocal } from '../../background/background.ts'; import { Label } from '../Group/AddGroup'; import ShortUniqueId from 'short-unique-id'; diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index 8cdd358..487711e 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -20,7 +20,7 @@ import { } from '../../utils/events'; import { validateAddress } from '../../utils/validateAddress'; import { getNameInfo, requestQueueMemberNames } from './Group'; -import { useModal } from '../../common/useModal'; +import { useModal } from '../../hooks/useModal'; import { isOpenBlockedModalAtom } from '../../atoms/global'; import InfoIcon from '@mui/icons-material/Info'; import { useAtom } from 'jotai'; diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index ce3846b..cddd67c 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -24,7 +24,7 @@ import { import { getFee } from '../../background/background.ts'; import { Spacer } from '../../common/Spacer'; import { FidgetSpinner } from 'react-loader-spinner'; -import { useModal } from '../../common/useModal'; +import { useModal } from '../../hooks/useModal.tsx'; import { useAtom, useSetAtom } from 'jotai'; import { memberGroupsAtom, txListAtom } from '../../atoms/global'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/Wallets.tsx b/src/components/Wallets.tsx index 24aa4705..ae749d3 100644 --- a/src/components/Wallets.tsx +++ b/src/components/Wallets.tsx @@ -28,7 +28,7 @@ import { storeWallets, walletVersion, } from '../background/background.ts'; -import { useModal } from '../common/useModal.tsx'; +import { useModal } from '../hooks/useModal.tsx'; import PhraseWallet from '../utils/generateWallet/phrase-wallet.ts'; import { decryptStoredWalletFromSeedPhrase } from '../utils/decryptWallet.ts'; import { crypto } from '../constants/decryptWallet.ts'; diff --git a/src/common/useFetchResources.tsx b/src/hooks/useFetchResources.tsx similarity index 100% rename from src/common/useFetchResources.tsx rename to src/hooks/useFetchResources.tsx diff --git a/src/common/useModal.tsx b/src/hooks/useModal.tsx similarity index 100% rename from src/common/useModal.tsx rename to src/hooks/useModal.tsx From 9d36c1e7339b38c3588242ea1cb6d704eacae9c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 May 2025 18:06:16 +0000 Subject: [PATCH 465/717] Bump axios from 1.7.7 to 1.8.2 Bumps [axios](https://github.com/axios/axios) from 1.7.7 to 1.8.2. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.7.7...v1.8.2) --- updated-dependencies: - dependency-name: axios dependency-version: 1.8.2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 9 +++++---- package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index fbba254..11070dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "@uiw/react-color": "^2.5.1", "adm-zip": "^0.5.16", "asmcrypto.js": "2.3.2", - "axios": "^1.7.7", + "axios": "^1.8.2", "bcryptjs": "2.4.3", "buffer": "6.0.3", "chokidar": "^3.6.0", @@ -6963,9 +6963,10 @@ } }, "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz", + "integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", diff --git a/package.json b/package.json index 2869cb9..2f922de 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@uiw/react-color": "^2.5.1", "adm-zip": "^0.5.16", "asmcrypto.js": "2.3.2", - "axios": "^1.7.7", + "axios": "^1.8.2", "bcryptjs": "2.4.3", "buffer": "6.0.3", "chokidar": "^3.6.0", From 9edb2cf4d4bbbfc54d3e1ef8f183055e9478d0ed Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 11:27:14 +0200 Subject: [PATCH 466/717] Rename context name --- src/App.tsx | 7 ++++--- src/Wallets.tsx | 4 ++-- src/components/Apps/AppPublish.tsx | 4 ++-- src/components/Apps/AppRating.tsx | 4 ++-- src/components/Apps/AppsDesktop.tsx | 4 ++-- src/components/Apps/AppsDevModeHome.tsx | 4 ++-- src/components/Apps/AppsPrivate.tsx | 4 ++-- src/components/Chat/AdminSpaceInner.tsx | 4 ++-- src/components/Chat/ChatGroup.tsx | 4 ++-- src/components/Chat/CreateCommonSecret.tsx | 4 ++-- src/components/Chat/GroupAnnouncements.tsx | 4 ++-- src/components/Chat/GroupAvatar.tsx | 4 ++-- src/components/Chat/MessageItem.tsx | 4 ++-- src/components/Embeds/AttachmentEmbed.tsx | 4 ++-- src/components/Embeds/PollEmbed.tsx | 4 ++-- src/components/Embeds/VideoPlayer.tsx | 4 ++-- src/components/GlobalActions/JoinGroup.tsx | 4 ++-- src/components/Group/AddGroup.tsx | 4 ++-- src/components/Group/AddGroupList.tsx | 4 ++-- src/components/Group/BlockedUsersModal.tsx | 4 ++-- src/components/Group/Forum/NewThread.tsx | 8 ++++++-- src/components/Group/ListOfGroupPromotions.tsx | 4 ++-- src/components/Group/ManageMembers.tsx | 4 ++-- src/components/Group/Settings.tsx | 5 +++-- src/components/Group/UserListOfInvites.tsx | 4 ++-- src/components/MainAvatar.tsx | 8 ++++++-- src/components/NotAuthenticated.tsx | 5 +++-- src/components/Save/Save.tsx | 4 ++-- src/components/Tutorials/Tutorials.tsx | 5 +++-- src/components/WrapperUserAction.tsx | 4 ++-- src/hooks/useHandlePrivateApps.tsx | 4 ++-- src/hooks/useQortalMessageListener.tsx | 4 ++-- 32 files changed, 77 insertions(+), 65 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index d756f44..777454d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -236,7 +236,8 @@ const defaultValuesGlobal = { setOpenTutorialModal: () => {}, }; -export const MyContext = createContext(defaultValues); +export const QORTAL_APP_CONTEXT = + createContext(defaultValues); export let globalApiKey: string | null = null; @@ -2016,7 +2017,7 @@ function App() { > - + {extState === 'not-authenticated' && ( - + {extState === 'create-wallet' && walletToBeDownloaded && ( { @@ -44,7 +44,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { const [seedValue, setSeedValue] = useState(''); const [seedName, setSeedName] = useState(''); const [seedError, setSeedError] = useState(''); - const { hasSeenGettingStarted } = useContext(MyContext); + const { hasSeenGettingStarted } = useContext(QORTAL_APP_CONTEXT); const [password, setPassword] = useState(''); const [isOpenSeedModal, setIsOpenSeedModal] = useState(false); const [isLoadingEncryptSeed, setIsLoadingEncryptSeed] = useState(false); diff --git a/src/components/Apps/AppPublish.tsx b/src/components/Apps/AppPublish.tsx index 697805f..1e102ee 100644 --- a/src/components/Apps/AppPublish.tsx +++ b/src/components/Apps/AppPublish.tsx @@ -16,7 +16,7 @@ import { useTheme, } from '@mui/material'; import { styled } from '@mui/system'; -import { MyContext, getBaseApiReact } from '../../App'; +import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { Spacer } from '../../common/Spacer'; import { executeEvent } from '../../utils/events'; import { useDropzone } from 'react-dropzone'; @@ -67,7 +67,7 @@ export const AppPublish = ({ categories, myAddress, myName }) => { const [category, setCategory] = useState(''); const [appType, setAppType] = useState('APP'); const [file, setFile] = useState(null); - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); const { t } = useTranslation(['auth', 'core', 'group']); const [tag1, setTag1] = useState(''); diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index 17d2cf0..9d63213 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -1,7 +1,7 @@ import { Box, Rating } from '@mui/material'; import { useCallback, useContext, useEffect, useRef, useState } from 'react'; import { getFee } from '../../background'; -import { MyContext, getBaseApiReact } from '../../App'; +import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { StarFilledIcon } from '../../assets/Icons/StarFilled'; import { StarEmptyIcon } from '../../assets/Icons/StarEmpty'; @@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next'; export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { const [value, setValue] = useState(0); - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const [hasPublishedRating, setHasPublishedRating] = useState( null ); diff --git a/src/components/Apps/AppsDesktop.tsx b/src/components/Apps/AppsDesktop.tsx index 1cc6d0d..71472db 100644 --- a/src/components/Apps/AppsDesktop.tsx +++ b/src/components/Apps/AppsDesktop.tsx @@ -1,7 +1,7 @@ import React, { useContext, useEffect, useMemo, useRef, useState } from 'react'; import { AppsHomeDesktop } from './AppsHomeDesktop'; import { Spacer } from '../../common/Spacer'; -import { MyContext, getBaseApiReact } from '../../App'; +import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { AppInfo } from './AppInfo'; import { executeEvent, @@ -49,7 +49,7 @@ export const AppsDesktop = ({ const [categories, setCategories] = useState([]); const iframeRefs = useRef({}); const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); - const { showTutorial } = useContext(MyContext); + const { showTutorial } = useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); const { t } = useTranslation(['auth', 'core', 'group']); diff --git a/src/components/Apps/AppsDevModeHome.tsx b/src/components/Apps/AppsDevModeHome.tsx index 74bce21..d21618b 100644 --- a/src/components/Apps/AppsDevModeHome.tsx +++ b/src/components/Apps/AppsDevModeHome.tsx @@ -19,7 +19,7 @@ import { Input, } from '@mui/material'; import { Add } from '@mui/icons-material'; -import { MyContext, getBaseApiReact } from '../../App'; +import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { executeEvent } from '../../utils/events'; import { Spacer } from '../../common/Spacer'; import { useModal } from '../../common/useModal'; @@ -48,7 +48,7 @@ export const AppsDevModeHome = ({ setOpenSnackGlobal, infoSnackCustom, setInfoSnackCustom, - } = useContext(MyContext); + } = useContext(QORTAL_APP_CONTEXT); const handleSelectFile = async (existingFilePath) => { const filePath = existingFilePath || (await window.electron.selectFile()); diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index da74b64..298fc6a 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -37,7 +37,7 @@ import { } from './Apps-styles'; import AddIcon from '@mui/icons-material/Add'; import ImageUploader from '../../common/ImageUploader'; -import { getBaseApiReact, MyContext } from '../../App'; +import { QORTAL_APP_CONTEXT } from '../../App'; import { fileToBase64 } from '../../utils/fileReading'; import { objectToBase64 } from '../../qdn/encryption/group-encryption'; import { getFee } from '../../background'; @@ -69,7 +69,7 @@ export const AppsPrivate = ({ myName, myAddress }) => { const [isOpenPrivateModal, setIsOpenPrivateModal] = useState(false); const { show, setInfoSnackCustom, setOpenSnackGlobal } = - useContext(MyContext); + useContext(QORTAL_APP_CONTEXT); const [memberGroups] = useAtom(memberGroupsAtom); const theme = useTheme(); diff --git a/src/components/Chat/AdminSpaceInner.tsx b/src/components/Chat/AdminSpaceInner.tsx index 0312e5d..0a68048 100644 --- a/src/components/Chat/AdminSpaceInner.tsx +++ b/src/components/Chat/AdminSpaceInner.tsx @@ -1,6 +1,6 @@ import { useCallback, useContext, useEffect, useState } from 'react'; import { - MyContext, + QORTAL_APP_CONTEXT, getArbitraryEndpointReact, getBaseApiReact, } from '../../App'; @@ -73,7 +73,7 @@ export const AdminSpaceInner = ({ useState(null); const [isLoadingPublishKey, setIsLoadingPublishKey] = useState(false); const { show, setInfoSnackCustom, setOpenSnackGlobal } = - useContext(MyContext); + useContext(QORTAL_APP_CONTEXT); const { t } = useTranslation(['auth', 'core', 'group']); const getAdminGroupSecretKey = useCallback(async () => { diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index f83005c..0b2ede3 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -19,7 +19,7 @@ import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { getBaseApiReact, getBaseApiReactSocket, - MyContext, + QORTAL_APP_CONTEXT, pauseAllQueues, resumeAllQueues, } from '../../App'; @@ -71,7 +71,7 @@ export const ChatGroup = ({ hideView, isPrivate, }) => { - const { isUserBlocked, show } = useContext(MyContext); + const { isUserBlocked, show } = useContext(QORTAL_APP_CONTEXT); const [messages, setMessages] = useState([]); const [chatReferences, setChatReferences] = useState({}); const [isSending, setIsSending] = useState(false); diff --git a/src/components/Chat/CreateCommonSecret.tsx b/src/components/Chat/CreateCommonSecret.tsx index bb5834e..e7472f6 100644 --- a/src/components/Chat/CreateCommonSecret.tsx +++ b/src/components/Chat/CreateCommonSecret.tsx @@ -3,7 +3,7 @@ import { Box, Button, Typography, useTheme } from '@mui/material'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { LoadingButton } from '@mui/lab'; import { - MyContext, + QORTAL_APP_CONTEXT, getArbitraryEndpointReact, getBaseApiReact, pauseAllQueues, @@ -32,7 +32,7 @@ export const CreateCommonSecret = ({ setIsForceShowCreationKeyPopup, isForceShowCreationKeyPopup, }) => { - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const setTxList = useSetAtom(txListAtom); const [openSnack, setOpenSnack] = useState(false); diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index 0ece390..a9d1e0a 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -24,7 +24,7 @@ import { AnnouncementList } from './AnnouncementList'; import CampaignIcon from '@mui/icons-material/Campaign'; import { AnnouncementDiscussion } from './AnnouncementDiscussion'; import { - MyContext, + QORTAL_APP_CONTEXT, getArbitraryEndpointReact, getBaseApiReact, pauseAllQueues, @@ -142,7 +142,7 @@ export const GroupAnnouncements = ({ const [selectedAnnouncement, setSelectedAnnouncement] = useState(null); const [isFocusedParent, setIsFocusedParent] = useState(false); - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const [openSnack, setOpenSnack] = useState(false); const [infoSnack, setInfoSnack] = useState(null); const hasInitialized = useRef(false); diff --git a/src/components/Chat/GroupAvatar.tsx b/src/components/Chat/GroupAvatar.tsx index 903092f..17bf99b 100644 --- a/src/components/Chat/GroupAvatar.tsx +++ b/src/components/Chat/GroupAvatar.tsx @@ -1,7 +1,7 @@ import { useCallback, useContext, useEffect, useState } from 'react'; import Logo2 from '../../assets/svgs/Logo2.svg'; import { - MyContext, + QORTAL_APP_CONTEXT, getArbitraryEndpointReact, getBaseApiReact, } from '../../App'; @@ -32,7 +32,7 @@ export const GroupAvatar = ({ const [hasAvatar, setHasAvatar] = useState(false); const [avatarFile, setAvatarFile] = useState(null); const [tempAvatar, setTempAvatar] = useState(null); - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const { t } = useTranslation(['auth', 'core', 'group']); const [anchorEl, setAnchorEl] = useState(null); const [isLoading, setIsLoading] = useState(false); diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index a2e7fd5..a429846 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -22,7 +22,7 @@ import { useTheme, } from '@mui/material'; import { formatTimestamp } from '../../utils/time'; -import { MyContext, getBaseApiReact } from '../../App'; +import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { generateHTML } from '@tiptap/react'; import Highlight from '@tiptap/extension-highlight'; import Mention from '@tiptap/extension-mention'; @@ -113,7 +113,7 @@ export const MessageItem = memo( onEdit, isPrivate, }) => { - const { getIndividualUserInfo } = useContext(MyContext); + const { getIndividualUserInfo } = useContext(QORTAL_APP_CONTEXT); const [anchorEl, setAnchorEl] = useState(null); const [selectedReaction, setSelectedReaction] = useState(null); const [userInfo, setUserInfo] = useState(null); diff --git a/src/components/Embeds/AttachmentEmbed.tsx b/src/components/Embeds/AttachmentEmbed.tsx index 5a30d2d..d7458a7 100644 --- a/src/components/Embeds/AttachmentEmbed.tsx +++ b/src/components/Embeds/AttachmentEmbed.tsx @@ -1,5 +1,5 @@ import { useContext, useState } from 'react'; -import { MyContext, getBaseApiReact } from '../../App'; +import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { Card, CardContent, @@ -36,7 +36,7 @@ export const AttachmentCard = ({ selectedGroupId, }) => { const [isOpen, setIsOpen] = useState(true); - const { downloadResource } = useContext(MyContext); + const { downloadResource } = useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); const { t } = useTranslation(['auth', 'core', 'group']); diff --git a/src/components/Embeds/PollEmbed.tsx b/src/components/Embeds/PollEmbed.tsx index cffe724..bc246ba 100644 --- a/src/components/Embeds/PollEmbed.tsx +++ b/src/components/Embeds/PollEmbed.tsx @@ -1,5 +1,5 @@ import { useContext, useEffect, useState } from 'react'; -import { MyContext } from '../../App'; +import { QORTAL_APP_CONTEXT } from '../../App'; import { Card, CardContent, @@ -37,7 +37,7 @@ export const PollCard = ({ const [ownerName, setOwnerName] = useState(''); const [showResults, setShowResults] = useState(false); const [isOpen, setIsOpen] = useState(false); - const { show, userInfo } = useContext(MyContext); + const { show, userInfo } = useContext(QORTAL_APP_CONTEXT); const [isLoadingSubmit, setIsLoadingSubmit] = useState(false); const theme = useTheme(); const { t } = useTranslation(['auth', 'core', 'group']); diff --git a/src/components/Embeds/VideoPlayer.tsx b/src/components/Embeds/VideoPlayer.tsx index 958b2ad..ab9d3a6 100644 --- a/src/components/Embeds/VideoPlayer.tsx +++ b/src/components/Embeds/VideoPlayer.tsx @@ -21,7 +21,7 @@ import { } from '@mui/icons-material'; import { styled } from '@mui/system'; import { Refresh } from '@mui/icons-material'; -import { MyContext, getBaseApiReact } from '../../App'; +import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { resourceKeySelector } from '../../atoms/global'; import { useAtomValue } from 'jotai'; @@ -88,7 +88,7 @@ export const VideoPlayer: FC = ({ }, [service, name, identifier]); const download = useAtomValue(resourceKeySelector(keyIdentifier)); - const { downloadResource } = useContext(MyContext); + const { downloadResource } = useContext(QORTAL_APP_CONTEXT); const videoRef = useRef(null); const [playing, setPlaying] = useState(false); const [volume, setVolume] = useState(1); diff --git a/src/components/GlobalActions/JoinGroup.tsx b/src/components/GlobalActions/JoinGroup.tsx index de3fd22..48ea442 100644 --- a/src/components/GlobalActions/JoinGroup.tsx +++ b/src/components/GlobalActions/JoinGroup.tsx @@ -11,7 +11,7 @@ import { useTheme, } from '@mui/material'; import { CustomButtonAccept } from '../../styles/App-styles'; -import { getBaseApiReact, MyContext } from '../../App'; +import { getBaseApiReact, QORTAL_APP_CONTEXT } from '../../App'; import { getFee } from '../../background'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { FidgetSpinner } from 'react-loader-spinner'; @@ -20,7 +20,7 @@ import { memberGroupsAtom, txListAtom } from '../../atoms/global'; import { useTranslation } from 'react-i18next'; export const JoinGroup = () => { - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const setTxList = useSetAtom(txListAtom); const [memberGroups] = useAtom(memberGroupsAtom); const [openSnack, setOpenSnack] = useState(false); diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 1916aaa..2e0ca92 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -35,7 +35,7 @@ import { AddGroupList } from './AddGroupList'; import { UserListOfInvites } from './UserListOfInvites'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { getFee } from '../../background'; -import { MyContext } from '../../App'; +import { QORTAL_APP_CONTEXT } from '../../App'; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useTranslation } from 'react-i18next'; import { useSetAtom } from 'jotai'; @@ -59,7 +59,7 @@ const Transition = forwardRef(function Transition( }); export const AddGroup = ({ address, open, setOpen }) => { - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const setTxList = useSetAtom(txListAtom); const [openAdvance, setOpenAdvance] = useState(false); diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index eaa7f4d..e9316d8 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -23,7 +23,7 @@ import { List, } from 'react-virtualized'; import _ from 'lodash'; -import { MyContext, getBaseApiReact } from '../../App'; +import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { LoadingButton } from '@mui/lab'; import { getFee } from '../../background'; import LockIcon from '@mui/icons-material/Lock'; @@ -39,7 +39,7 @@ const cache = new CellMeasurerCache({ }); export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const [memberGroups] = useAtom(memberGroupsAtom); const setTxList = useSetAtom(txListAtom); const { t } = useTranslation(['auth', 'core', 'group']); diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index 99c9191..c8145b3 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -11,7 +11,7 @@ import { useTheme, } from '@mui/material'; import { useContext, useEffect, useState } from 'react'; -import { getBaseApiReact, MyContext } from '../../App'; +import { getBaseApiReact, QORTAL_APP_CONTEXT } from '../../App'; import { Spacer } from '../../common/Spacer'; import { executeEvent, @@ -42,7 +42,7 @@ export const BlockedUsersModal = () => { addToBlockList, setOpenSnackGlobal, setInfoSnackCustom, - } = useContext(MyContext); + } = useContext(QORTAL_APP_CONTEXT); const [blockedUsers, setBlockedUsers] = useState({ addresses: {}, diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index 763ecb6..b9d8048 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -17,7 +17,11 @@ import { ReusableModal } from './ReusableModal'; import { Spacer } from '../../../common/Spacer'; import { CreateThreadIcon } from '../../../assets/Icons/CreateThreadIcon'; import { SendNewMessage } from '../../../assets/Icons/SendNewMessage'; -import { MyContext, pauseAllQueues, resumeAllQueues } from '../../../App'; +import { + QORTAL_APP_CONTEXT, + pauseAllQueues, + resumeAllQueues, +} from '../../../App'; import { getFee } from '../../../background'; import TipTap from '../../Chat/TipTap'; import { MessageDisplay } from '../../Chat/MessageDisplay'; @@ -141,7 +145,7 @@ export const NewThread = ({ isPrivate, }: NewMessageProps) => { const { t } = useTranslation(['auth', 'core', 'group']); - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const [isOpen, setIsOpen] = useState(false); const [value, setValue] = useState(''); const [isSending, setIsSending] = useState(false); diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index ad3dd2d..75fe480 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -21,7 +21,7 @@ import { LoadingButton } from '@mui/lab'; import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { - MyContext, + QORTAL_APP_CONTEXT, getArbitraryEndpointReact, getBaseApiReact, } from '../../App'; @@ -88,7 +88,7 @@ export const ListOfGroupPromotions = () => { const [fee, setFee] = useState(null); const [isLoadingJoinGroup, setIsLoadingJoinGroup] = useState(false); const [isLoadingPublish, setIsLoadingPublish] = useState(false); - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const setTxList = useSetAtom(txListAtom); const theme = useTheme(); const { t } = useTranslation(['auth', 'core', 'group']); diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index e64eeab..9fd3150 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -25,7 +25,7 @@ import { ListOfBans } from './ListOfBans'; import { ListOfJoinRequests } from './ListOfJoinRequests'; import { Box, ButtonBase, Card, Tab, Tabs, useTheme } from '@mui/material'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; -import { MyContext, getBaseApiReact } from '../../App'; +import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { getGroupMembers, getNames } from './Group'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { getFee } from '../../background'; @@ -72,7 +72,7 @@ export const ManageMembers = ({ }; const theme = useTheme(); const { t } = useTranslation(['auth', 'core', 'group']); - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const setTxList = useSetAtom(txListAtom); const handleClose = () => { diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index c285fa0..058adde 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -38,7 +38,7 @@ import { Spacer } from '../../common/Spacer'; import PhraseWallet from '../../utils/generateWallet/phrase-wallet'; import { walletVersion } from '../../background'; import Base58 from '../../deps/Base58'; -import { MyContext } from '../../App'; +import { QORTAL_APP_CONTEXT } from '../../App'; import { useTranslation } from 'react-i18next'; const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ @@ -233,7 +233,8 @@ const ExportPrivateKey = ({ rawWallet }) => { const [password, setPassword] = useState(''); const [privateKey, setPrivateKey] = useState(''); const [isOpen, setIsOpen] = useState(false); - const { setOpenSnackGlobal, setInfoSnackCustom } = useContext(MyContext); + const { setOpenSnackGlobal, setInfoSnackCustom } = + useContext(QORTAL_APP_CONTEXT); const { t } = useTranslation(['auth', 'core', 'group']); const exportPrivateKeyFunc = async () => { diff --git a/src/components/Group/UserListOfInvites.tsx b/src/components/Group/UserListOfInvites.tsx index 3b32f54..064296a 100644 --- a/src/components/Group/UserListOfInvites.tsx +++ b/src/components/Group/UserListOfInvites.tsx @@ -14,7 +14,7 @@ import { CellMeasurerCache, List, } from 'react-virtualized'; -import { MyContext, getBaseApiReact } from '../../App'; +import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { LoadingButton } from '@mui/lab'; import { getFee } from '../../background'; import LockIcon from '@mui/icons-material/Lock'; @@ -55,7 +55,7 @@ export const UserListOfInvites = ({ setInfoSnack, setOpenSnack, }) => { - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const setTxList = useSetAtom(txListAtom); const [invites, setInvites] = useState([]); diff --git a/src/components/MainAvatar.tsx b/src/components/MainAvatar.tsx index 24f5744..47c985c 100644 --- a/src/components/MainAvatar.tsx +++ b/src/components/MainAvatar.tsx @@ -1,6 +1,10 @@ import { useContext, useEffect, useState } from 'react'; import Logo2 from '../assets/svgs/Logo2.svg'; -import { MyContext, getArbitraryEndpointReact, getBaseApiReact } from '../App'; +import { + QORTAL_APP_CONTEXT, + getArbitraryEndpointReact, + getBaseApiReact, +} from '../App'; import { Avatar, Box, @@ -22,7 +26,7 @@ export const MainAvatar = ({ myName, balance, setOpenSnack, setInfoSnack }) => { const [hasAvatar, setHasAvatar] = useState(false); const [avatarFile, setAvatarFile] = useState(null); const [tempAvatar, setTempAvatar] = useState(null); - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const [anchorEl, setAnchorEl] = useState(null); const [isLoading, setIsLoading] = useState(false); diff --git a/src/components/NotAuthenticated.tsx b/src/components/NotAuthenticated.tsx index a87fdce..c9be445 100644 --- a/src/components/NotAuthenticated.tsx +++ b/src/components/NotAuthenticated.tsx @@ -32,7 +32,7 @@ import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip'; import ThemeSelector from './Theme/ThemeSelector'; import { useTranslation } from 'react-i18next'; import LanguageSelector from './Language/LanguageSelector'; -import { MyContext } from '../App'; +import { QORTAL_APP_CONTEXT } from '../App'; export const manifestData = { version: '0.5.4', @@ -81,7 +81,8 @@ export const NotAuthenticated = ({ const [showSelectApiKey, setShowSelectApiKey] = useState(false); const [enteredApiKey, setEnteredApiKey] = useState(''); const [customNodeToSaveIndex, setCustomNodeToSaveIndex] = useState(null); - const { showTutorial, hasSeenGettingStarted } = useContext(MyContext); + const { showTutorial, hasSeenGettingStarted } = + useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); const { t } = useTranslation(['auth', 'core']); diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index 11d2f9b..67950e6 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -18,7 +18,7 @@ import { useTheme, } from '@mui/material'; import { objectToBase64 } from '../../qdn/encryption/group-encryption'; -import { MyContext } from '../../App'; +import { QORTAL_APP_CONTEXT } from '../../App'; import { getFee } from '../../background'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { SaveIcon } from '../../assets/Icons/SaveIcon'; @@ -82,7 +82,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { const [oldPinnedApps, setOldPinnedApps] = useAtom(oldPinnedAppsAtom); const [anchorEl, setAnchorEl] = useState(null); - const { show } = useContext(MyContext); + const { show } = useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); const { t } = useTranslation(['auth', 'core', 'group']); diff --git a/src/components/Tutorials/Tutorials.tsx b/src/components/Tutorials/Tutorials.tsx index d480b1c..068c3ad 100644 --- a/src/components/Tutorials/Tutorials.tsx +++ b/src/components/Tutorials/Tutorials.tsx @@ -1,5 +1,5 @@ import { useContext, useState } from 'react'; -import { MyContext } from '../../App'; +import { QORTAL_APP_CONTEXT } from '../../App'; import { Button, Dialog, @@ -16,7 +16,8 @@ import { VideoPlayer } from '../Embeds/VideoPlayer'; import { useTranslation } from 'react-i18next'; export const Tutorials = () => { - const { openTutorialModal, setOpenTutorialModal } = useContext(MyContext); + const { openTutorialModal, setOpenTutorialModal } = + useContext(QORTAL_APP_CONTEXT); const [multiNumber, setMultiNumber] = useState(0); const theme = useTheme(); const { t } = useTranslation(['core', 'tutorial']); diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx index 591c200..490333d 100644 --- a/src/components/WrapperUserAction.tsx +++ b/src/components/WrapperUserAction.tsx @@ -7,7 +7,7 @@ import { useTheme, } from '@mui/material'; import { executeEvent } from '../utils/events'; -import { MyContext } from '../App'; +import { QORTAL_APP_CONTEXT } from '../App'; import { useAtom } from 'jotai'; import { isRunningPublicNodeAtom } from '../atoms/global'; import { useTranslation } from 'react-i18next'; @@ -175,7 +175,7 @@ const BlockUser = ({ address, name, handleClose }) => { const [isAlreadyBlocked, setIsAlreadyBlocked] = useState(null); const [isLoading, setIsLoading] = useState(false); const { isUserBlocked, addToBlockList, removeBlockFromList } = - useContext(MyContext); + useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); const { t } = useTranslation(['auth', 'core', 'group']); diff --git a/src/hooks/useHandlePrivateApps.tsx b/src/hooks/useHandlePrivateApps.tsx index fdeee79..e1d8288 100644 --- a/src/hooks/useHandlePrivateApps.tsx +++ b/src/hooks/useHandlePrivateApps.tsx @@ -1,6 +1,6 @@ import { useContext, useState } from 'react'; import { executeEvent } from '../utils/events'; -import { getBaseApiReact, MyContext } from '../App'; +import { getBaseApiReact, QORTAL_APP_CONTEXT } from '../App'; import { createEndpoint } from '../background'; import { settingsLocalLastUpdatedAtom, @@ -19,7 +19,7 @@ export const useHandlePrivateApps = () => { setOpenSnackGlobal, infoSnackCustom, setInfoSnackCustom, - } = useContext(MyContext); + } = useContext(QORTAL_APP_CONTEXT); const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom); const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom); const { t } = useTranslation(['auth', 'core', 'group']); diff --git a/src/hooks/useQortalMessageListener.tsx b/src/hooks/useQortalMessageListener.tsx index 81f697a..c6c24da 100644 --- a/src/hooks/useQortalMessageListener.tsx +++ b/src/hooks/useQortalMessageListener.tsx @@ -4,7 +4,7 @@ import { navigationControllerAtom } from '../atoms/global'; import { Filesystem, Directory } from '@capacitor/filesystem'; import { saveFile } from '../qortalRequests/get'; import { mimeToExtensionMap } from '../utils/memeTypes'; -import { MyContext } from '../App'; +import { QORTAL_APP_CONTEXT } from '../App'; import FileSaver from 'file-saver'; import { useSetAtom } from 'jotai'; @@ -526,7 +526,7 @@ export const useQortalMessageListener = ( setOpenSnackGlobal, infoSnackCustom, setInfoSnackCustom, - } = useContext(MyContext); + } = useContext(QORTAL_APP_CONTEXT); useEffect(() => { if (tabId && !isNaN(history?.currentIndex)) { From cec429c691634bec9db781437198bdbf78817106 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 11:53:08 +0200 Subject: [PATCH 467/717] Create encryption folder and move files --- src/App.tsx | 4 +- src/Wallets.tsx | 6 +- src/{ => background}/background-cases.ts | 16 +- src/{ => background}/background.ts | 41 +- src/components/Apps/AppPublish.tsx | 2 +- src/components/Apps/AppRating.tsx | 2 +- src/components/Apps/AppsDevModeHome.tsx | 2 +- src/components/Apps/AppsPrivate.tsx | 2 +- src/components/Chat/AdminSpaceInner.tsx | 4 +- .../Chat/AnnouncementDiscussion.tsx | 2 +- src/components/Chat/ChatDirect.tsx | 2 +- src/components/Chat/ChatGroup.tsx | 2 +- src/components/Chat/CreateCommonSecret.tsx | 4 +- src/components/Chat/GroupAnnouncements.tsx | 4 +- src/components/Chat/GroupAvatar.tsx | 2 +- src/components/Embeds/PollEmbed.tsx | 2 +- src/components/GlobalActions/JoinGroup.tsx | 2 +- src/components/Group/AddGroup.tsx | 2 +- src/components/Group/AddGroupList.tsx | 2 +- src/components/Group/Forum/NewThread.tsx | 2 +- src/components/Group/Group.tsx | 2 +- src/components/Group/InviteMember.tsx | 2 +- src/components/Group/ListOfBans.tsx | 2 +- .../Group/ListOfGroupPromotions.tsx | 2 +- src/components/Group/ListOfInvites.tsx | 2 +- src/components/Group/ListOfJoinRequests.tsx | 2 +- src/components/Group/ListOfMembers.tsx | 2 +- src/components/Group/ManageMembers.tsx | 2 +- src/components/Group/Settings.tsx | 4 +- src/components/Group/UserListOfInvites.tsx | 2 +- src/components/MainAvatar.tsx | 2 +- src/components/Minting/Minting.tsx | 2 +- src/components/NotAuthenticated.tsx | 2 +- src/components/QortPayment.tsx | 2 +- src/components/RegisterName.tsx | 2 +- src/components/Save/Save.tsx | 4 +- src/components/UserLookup.tsx/UserLookup.tsx | 5 +- src/deps/AltcoinHDWallet.ts | 864 ----------------- src/encryption/AltcoinHDWallet.ts | 881 ++++++++++++++++++ src/{deps => encryption}/Base58.ts | 0 .../bcryptworker.worker.js | 0 .../bcryptworkerwasm.worker.js | 0 src/{deps => encryption}/broken-ripemd160.ts | 0 src/{deps => encryption}/ecbn.ts | 0 src/{deps => encryption}/ed2curve.ts | 0 .../encryption.ts | 12 +- src/{deps => encryption}/kdf.ts | 0 src/{deps => encryption}/nacl-fast.ts | 0 src/{deps => encryption}/ripemd160.ts | 0 src/hooks/useHandlePaymentNotification.tsx | 5 +- src/hooks/useHandlePrivateApps.tsx | 4 +- src/hooks/useQortalGetSaveSettings.tsx | 2 +- src/qdn/encryption/group-encryption.ts | 6 +- src/qdn/publish/pubish.ts | 90 +- src/qortalRequests/get.ts | 13 +- src/{ => qortalRequests}/qortalRequests.ts | 10 +- src/transactions/ChatBase.ts | 4 +- src/transactions/ChatTransaction.ts | 4 +- .../RemoveRewardShareTransaction.ts | 2 +- src/transactions/RewardShareTransaction.ts | 4 +- src/transactions/TransactionBase.ts | 4 +- src/transactions/signChat.ts | 2 +- src/transactions/signTradeBotTransaction.ts | 4 +- src/utils/decryptChatMessage.ts | 6 +- src/utils/decryptWallet.ts | 4 +- src/utils/generateWallet/generateWallet.ts | 2 +- src/utils/generateWallet/phrase-wallet.ts | 346 ++++--- .../generateWallet/publicKeyToAddress.ts | 6 +- src/utils/generateWallet/storeWallet.ts | 4 +- src/utils/validateAddress.ts | 2 +- 70 files changed, 1272 insertions(+), 1155 deletions(-) rename src/{ => background}/background-cases.ts (99%) rename src/{ => background}/background.ts (98%) delete mode 100644 src/deps/AltcoinHDWallet.ts create mode 100644 src/encryption/AltcoinHDWallet.ts rename src/{deps => encryption}/Base58.ts (100%) rename src/{deps => encryption}/bcryptworker.worker.js (100%) rename src/{deps => encryption}/bcryptworkerwasm.worker.js (100%) rename src/{deps => encryption}/broken-ripemd160.ts (100%) rename src/{deps => encryption}/ecbn.ts (100%) rename src/{deps => encryption}/ed2curve.ts (100%) rename src/{backgroundFunctions => encryption}/encryption.ts (96%) rename src/{deps => encryption}/kdf.ts (100%) rename src/{deps => encryption}/nacl-fast.ts (100%) rename src/{deps => encryption}/ripemd160.ts (100%) rename src/{ => qortalRequests}/qortalRequests.ts (99%) diff --git a/src/App.tsx b/src/App.tsx index 777454d..fe721a7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -76,7 +76,7 @@ import { groupApi, groupApiSocket, storeWallets, -} from './background'; +} from './background/background.ts'; import { executeEvent, subscribeToEvent, @@ -124,7 +124,7 @@ import { Tutorials } from './components/Tutorials/Tutorials'; import { useHandleTutorials } from './hooks/useHandleTutorials.tsx'; import { useHandleUserInfo } from './hooks/useHandleUserInfo.tsx'; import { Minting } from './components/Minting/Minting'; -import { isRunningGateway } from './qortalRequests'; +import { isRunningGateway } from './qortalRequests/qortalRequests.ts'; import { QMailStatus } from './components/QMailStatus'; import { GlobalActions } from './components/GlobalActions/GlobalActions'; import { useBlockedAddresses } from './hooks/useBlockUsers.tsx'; diff --git a/src/Wallets.tsx b/src/Wallets.tsx index ef259fc..e6e4906 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -23,7 +23,11 @@ import { useDropzone } from 'react-dropzone'; import EditIcon from '@mui/icons-material/Edit'; import { Label } from './components/Group/AddGroup'; import { Spacer } from './common/Spacer'; -import { getWallets, storeWallets, walletVersion } from './background'; +import { + getWallets, + storeWallets, + walletVersion, +} from './background/background.ts'; import { useModal } from './common/useModal'; import PhraseWallet from './utils/generateWallet/phrase-wallet'; import { decryptStoredWalletFromSeedPhrase } from './utils/decryptWallet'; diff --git a/src/background-cases.ts b/src/background/background-cases.ts similarity index 99% rename from src/background-cases.ts rename to src/background/background-cases.ts index 29dab32..f6c0158 100644 --- a/src/background-cases.ts +++ b/src/background/background-cases.ts @@ -55,20 +55,20 @@ import { setGroupData, updateThreadActivity, walletVersion, -} from './background'; +} from '../background/background.ts'; import { decryptGroupEncryption, encryptAndPublishSymmetricKeyGroupChat, encryptAndPublishSymmetricKeyGroupChatForAdmins, publishGroupEncryptedResource, publishOnQDN, -} from './backgroundFunctions/encryption'; -import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from './constants/constants'; -import Base58 from './deps/Base58'; -import { encryptSingle } from './qdn/encryption/group-encryption'; -import { _createPoll, _voteOnPoll } from './qortalRequests/get'; -import { createTransaction } from './transactions/transactions'; -import { getData, storeData } from './utils/chromeStorage'; +} from '../encryption/encryption.ts'; +import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../constants/constants'; +import Base58 from '../encryption/Base58.ts'; +import { encryptSingle } from '../qdn/encryption/group-encryption'; +import { _createPoll, _voteOnPoll } from '../qortalRequests/get'; +import { createTransaction } from '../transactions/transactions'; +import { getData, storeData } from '../utils/chromeStorage'; export function versionCase(request, event) { event.source.postMessage( diff --git a/src/background.ts b/src/background/background.ts similarity index 98% rename from src/background.ts rename to src/background/background.ts index cbc96b3..ec55028 100644 --- a/src/background.ts +++ b/src/background/background.ts @@ -1,27 +1,27 @@ // @ts-nocheck -import './qortalRequests'; +import '../qortalRequests/qortalRequests'; import { isArray } from 'lodash'; -import { uint8ArrayToObject } from './backgroundFunctions/encryption'; -import Base58 from './deps/Base58'; +import { uint8ArrayToObject } from '../encryption/encryption.ts'; +import Base58 from '../encryption/Base58'; import axios from 'axios'; import { base64ToUint8Array, decryptSingle, encryptSingle, objectToBase64, -} from './qdn/encryption/group-encryption'; -import ChatComputePowWorker from './chatComputePow.worker.js?worker'; -import { reusableGet } from './qdn/publish/pubish'; -import { signChat } from './transactions/signChat'; -import { createTransaction } from './transactions/transactions'; -import { decryptChatMessage } from './utils/decryptChatMessage'; -import { decryptStoredWallet } from './utils/decryptWallet'; -import PhraseWallet from './utils/generateWallet/phrase-wallet'; -import { RequestQueueWithPromise } from './utils/queue/queue'; -import { validateAddress } from './utils/validateAddress'; +} from '../qdn/encryption/group-encryption'; +import ChatComputePowWorker from '../chatComputePow.worker.js?worker'; +import { reusableGet } from '../qdn/publish/pubish'; +import { signChat } from '../transactions/signChat'; +import { createTransaction } from '../transactions/transactions'; +import { decryptChatMessage } from '../utils/decryptChatMessage'; +import { decryptStoredWallet } from '../utils/decryptWallet'; +import PhraseWallet from '../utils/generateWallet/phrase-wallet'; +import { RequestQueueWithPromise } from '../utils/queue/queue'; +import { validateAddress } from '../utils/validateAddress'; import { Sha256 } from 'asmcrypto.js'; -import { TradeBotRespondMultipleRequest } from './transactions/TradeBotRespondMultipleRequest'; -import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from './constants/constants'; +import { TradeBotRespondMultipleRequest } from '../transactions/TradeBotRespondMultipleRequest'; +import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from '../constants/constants'; import { addDataPublishesCase, addEnteredQmailTimestampCase, @@ -68,7 +68,6 @@ import { ltcBalanceCase, makeAdminCase, nameCase, - notificationCase, notifyAdminRegenerateSecretKeyCase, pauseAllQueuesCase, publishGroupEncryptedResourceCase, @@ -90,9 +89,13 @@ import { validApiCase, versionCase, voteOnPollCase, -} from './background-cases'; -import { getData, removeKeysAndLogout, storeData } from './utils/chromeStorage'; -import TradeBotRespondRequest from './transactions/TradeBotRespondRequest'; +} from '../background/background-cases'; +import { + getData, + removeKeysAndLogout, + storeData, +} from '../utils/chromeStorage'; +import TradeBotRespondRequest from '../transactions/TradeBotRespondRequest'; export let groupSecretkeys = {}; diff --git a/src/components/Apps/AppPublish.tsx b/src/components/Apps/AppPublish.tsx index 1e102ee..da58cc9 100644 --- a/src/components/Apps/AppPublish.tsx +++ b/src/components/Apps/AppPublish.tsx @@ -22,7 +22,7 @@ import { executeEvent } from '../../utils/events'; import { useDropzone } from 'react-dropzone'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { fileToBase64 } from '../../utils/fileReading'; import { useTranslation } from 'react-i18next'; import { useSortedMyNames } from '../../hooks/useSortedMyNames'; diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index 9d63213..edd628a 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -1,6 +1,6 @@ import { Box, Rating } from '@mui/material'; import { useCallback, useContext, useEffect, useRef, useState } from 'react'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { StarFilledIcon } from '../../assets/Icons/StarFilled'; diff --git a/src/components/Apps/AppsDevModeHome.tsx b/src/components/Apps/AppsDevModeHome.tsx index d21618b..f9ade5d 100644 --- a/src/components/Apps/AppsDevModeHome.tsx +++ b/src/components/Apps/AppsDevModeHome.tsx @@ -23,7 +23,7 @@ import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { executeEvent } from '../../utils/events'; import { Spacer } from '../../common/Spacer'; import { useModal } from '../../common/useModal'; -import { createEndpoint, isUsingLocal } from '../../background'; +import { createEndpoint, isUsingLocal } from '../../background/background.ts'; import { Label } from '../Group/AddGroup'; import ShortUniqueId from 'short-unique-id'; import swaggerSVG from '../../assets/svgs/swagger.svg'; diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index 298fc6a..f24d15a 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -40,7 +40,7 @@ import ImageUploader from '../../common/ImageUploader'; import { QORTAL_APP_CONTEXT } from '../../App'; import { fileToBase64 } from '../../utils/fileReading'; import { objectToBase64 } from '../../qdn/encryption/group-encryption'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { useAtom } from 'jotai'; import { useTranslation } from 'react-i18next'; import { useSortedMyNames } from '../../hooks/useSortedMyNames'; diff --git a/src/components/Chat/AdminSpaceInner.tsx b/src/components/Chat/AdminSpaceInner.tsx index 0a68048..97e3fd1 100644 --- a/src/components/Chat/AdminSpaceInner.tsx +++ b/src/components/Chat/AdminSpaceInner.tsx @@ -10,9 +10,9 @@ import { getPublishesFromAdmins, validateSecretKey, } from '../Group/Group'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { base64ToUint8Array } from '../../qdn/encryption/group-encryption'; -import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; +import { uint8ArrayToObject } from '../../encryption/encryption.ts'; import { formatTimestampForum } from '../../utils/time'; import { Spacer } from '../../common/Spacer'; import { GroupAvatar } from './GroupAvatar'; diff --git a/src/components/Chat/AnnouncementDiscussion.tsx b/src/components/Chat/AnnouncementDiscussion.tsx index 8959a9f..e0da144 100644 --- a/src/components/Chat/AnnouncementDiscussion.tsx +++ b/src/components/Chat/AnnouncementDiscussion.tsx @@ -8,7 +8,7 @@ import { Box, CircularProgress, useTheme } from '@mui/material'; import { objectToBase64 } from '../../qdn/encryption/group-encryption'; import ShortUniqueId from 'short-unique-id'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { decryptPublishes, getTempPublish, diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index 35370ad..36681e9 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -14,7 +14,7 @@ import { pauseAllQueues, resumeAllQueues, } from '../../App'; -import { getPublicKey } from '../../background'; +import { getPublicKey } from '../../background/background.ts'; import { useMessageQueue } from '../../MessageQueueContext'; import { executeEvent, diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 0b2ede3..e6b75d0 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -44,7 +44,7 @@ import ShortUniqueId from 'short-unique-id'; import { ReplyPreview } from './MessageItem'; import { ExitIcon } from '../../assets/Icons/ExitIcon'; import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from '../../constants/constants'; -import { getFee, isExtMsg } from '../../background'; +import { getFee, isExtMsg } from '../../background/background.ts'; import AppViewerContainer from '../Apps/AppViewerContainer'; import CloseIcon from '@mui/icons-material/Close'; import { throttle } from 'lodash'; diff --git a/src/components/Chat/CreateCommonSecret.tsx b/src/components/Chat/CreateCommonSecret.tsx index e7472f6..46f76d6 100644 --- a/src/components/Chat/CreateCommonSecret.tsx +++ b/src/components/Chat/CreateCommonSecret.tsx @@ -8,14 +8,14 @@ import { getBaseApiReact, pauseAllQueues, } from '../../App'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { decryptResource, getGroupAdmins, validateSecretKey, } from '../Group/Group'; import { base64ToUint8Array } from '../../qdn/encryption/group-encryption'; -import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; +import { uint8ArrayToObject } from '../../encryption/encryption.ts'; import { useSetAtom } from 'jotai'; import { txListAtom } from '../../atoms/global'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index a9d1e0a..18170cd 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -7,7 +7,7 @@ import { useRef, useState, } from 'react'; -import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; +import { uint8ArrayToObject } from '../../encryption/encryption.ts'; import { base64ToUint8Array, objectToBase64, @@ -15,7 +15,7 @@ import { import Tiptap from './TipTap'; import { CustomButton } from '../../styles/App-styles'; import CircularProgress from '@mui/material/CircularProgress'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { Box, Typography, useTheme } from '@mui/material'; import { Spacer } from '../../common/Spacer'; diff --git a/src/components/Chat/GroupAvatar.tsx b/src/components/Chat/GroupAvatar.tsx index 17bf99b..ecc7c86 100644 --- a/src/components/Chat/GroupAvatar.tsx +++ b/src/components/Chat/GroupAvatar.tsx @@ -16,7 +16,7 @@ import { } from '@mui/material'; import { Spacer } from '../../common/Spacer'; import ImageUploader from '../../common/ImageUploader'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { fileToBase64 } from '../../utils/fileReading'; import { LoadingButton } from '@mui/lab'; import ErrorIcon from '@mui/icons-material/Error'; diff --git a/src/components/Embeds/PollEmbed.tsx b/src/components/Embeds/PollEmbed.tsx index bc246ba..6d7a510 100644 --- a/src/components/Embeds/PollEmbed.tsx +++ b/src/components/Embeds/PollEmbed.tsx @@ -16,7 +16,7 @@ import { } from '@mui/material'; import { getNameInfo } from '../Group/Group'; import PollIcon from '@mui/icons-material/Poll'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import RefreshIcon from '@mui/icons-material/Refresh'; import { Spacer } from '../../common/Spacer'; import OpenInNewIcon from '@mui/icons-material/OpenInNew'; diff --git a/src/components/GlobalActions/JoinGroup.tsx b/src/components/GlobalActions/JoinGroup.tsx index 48ea442..95e95e6 100644 --- a/src/components/GlobalActions/JoinGroup.tsx +++ b/src/components/GlobalActions/JoinGroup.tsx @@ -12,7 +12,7 @@ import { } from '@mui/material'; import { CustomButtonAccept } from '../../styles/App-styles'; import { getBaseApiReact, QORTAL_APP_CONTEXT } from '../../App'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { FidgetSpinner } from 'react-loader-spinner'; import { useAtom, useSetAtom } from 'jotai'; diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 2e0ca92..ec01443 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -34,7 +34,7 @@ import { import { AddGroupList } from './AddGroupList'; import { UserListOfInvites } from './UserListOfInvites'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { QORTAL_APP_CONTEXT } from '../../App'; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index e9316d8..8cad787 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -25,7 +25,7 @@ import { import _ from 'lodash'; import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { LoadingButton } from '@mui/lab'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { Spacer } from '../../common/Spacer'; diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index b9d8048..9749480 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -22,7 +22,7 @@ import { pauseAllQueues, resumeAllQueues, } from '../../../App'; -import { getFee } from '../../../background'; +import { getFee } from '../../../background/background'; import TipTap from '../../Chat/TipTap'; import { MessageDisplay } from '../../Chat/MessageDisplay'; import { CustomizedSnackbars } from '../../Snackbar/Snackbar'; diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 13a807b..368dd15 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -13,7 +13,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ChatGroup } from '../Chat/ChatGroup'; import { CreateCommonSecret } from '../Chat/CreateCommonSecret'; import { base64ToUint8Array } from '../../qdn/encryption/group-encryption'; -import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; +import { uint8ArrayToObject } from '../../encryption/encryption'; import { AddGroup } from './AddGroup'; import CreateIcon from '@mui/icons-material/Create'; import { diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx index 07ee3a7..8a562d0 100644 --- a/src/components/Group/InviteMember.tsx +++ b/src/components/Group/InviteMember.tsx @@ -3,7 +3,7 @@ import { Box, Input, MenuItem, Select, SelectChangeEvent } from '@mui/material'; import { useState } from 'react'; import { Spacer } from '../../common/Spacer'; import { Label } from './AddGroup'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { useTranslation } from 'react-i18next'; export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index 9399b7d..122ce27 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -15,7 +15,7 @@ import { List, } from 'react-virtualized'; import { getNameInfo } from './Group'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { LoadingButton } from '@mui/lab'; import { getBaseApiReact } from '../../App'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index 75fe480..01fbed3 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -42,7 +42,7 @@ import { useVirtualizer } from '@tanstack/react-virtual'; import ErrorBoundary from '../../common/ErrorBoundary'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { useAtom, useSetAtom } from 'jotai'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/Group/ListOfInvites.tsx b/src/components/Group/ListOfInvites.tsx index 8c4fe9b..037dddc 100644 --- a/src/components/Group/ListOfInvites.tsx +++ b/src/components/Group/ListOfInvites.tsx @@ -15,7 +15,7 @@ import { List, } from 'react-virtualized'; import { getNameInfo } from './Group'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { LoadingButton } from '@mui/lab'; import { getBaseApiReact } from '../../App'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/Group/ListOfJoinRequests.tsx b/src/components/Group/ListOfJoinRequests.tsx index 1805733..5fbfe9e 100644 --- a/src/components/Group/ListOfJoinRequests.tsx +++ b/src/components/Group/ListOfJoinRequests.tsx @@ -15,7 +15,7 @@ import { List, } from 'react-virtualized'; import { getNameInfo } from './Group'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { LoadingButton } from '@mui/lab'; import { getBaseApiReact } from '../../App'; import { txListAtom } from '../../atoms/global'; diff --git a/src/components/Group/ListOfMembers.tsx b/src/components/Group/ListOfMembers.tsx index 7a0048f..c57e501 100644 --- a/src/components/Group/ListOfMembers.tsx +++ b/src/components/Group/ListOfMembers.tsx @@ -17,7 +17,7 @@ import { List, } from 'react-virtualized'; import { LoadingButton } from '@mui/lab'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { getBaseApiReact } from '../../App'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index 9fd3150..e87b002 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -28,7 +28,7 @@ import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { getGroupMembers, getNames } from './Group'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { LoadingButton } from '@mui/lab'; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { Spacer } from '../../common/Spacer'; diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index 058adde..78db9bc 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -36,8 +36,8 @@ import { useAtom } from 'jotai'; import { decryptStoredWallet } from '../../utils/decryptWallet'; import { Spacer } from '../../common/Spacer'; import PhraseWallet from '../../utils/generateWallet/phrase-wallet'; -import { walletVersion } from '../../background'; -import Base58 from '../../deps/Base58'; +import { walletVersion } from '../../background/background.ts'; +import Base58 from '../../encryption/Base58.ts'; import { QORTAL_APP_CONTEXT } from '../../App'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/Group/UserListOfInvites.tsx b/src/components/Group/UserListOfInvites.tsx index 064296a..2717af9 100644 --- a/src/components/Group/UserListOfInvites.tsx +++ b/src/components/Group/UserListOfInvites.tsx @@ -16,7 +16,7 @@ import { } from 'react-virtualized'; import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { LoadingButton } from '@mui/lab'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { Spacer } from '../../common/Spacer'; diff --git a/src/components/MainAvatar.tsx b/src/components/MainAvatar.tsx index 47c985c..b458145 100644 --- a/src/components/MainAvatar.tsx +++ b/src/components/MainAvatar.tsx @@ -16,7 +16,7 @@ import { } from '@mui/material'; import { Spacer } from '../common/Spacer'; import ImageUploader from '../common/ImageUploader'; -import { getFee } from '../background'; +import { getFee } from '../background/background.ts'; import { fileToBase64 } from '../utils/fileReading'; import { LoadingButton } from '@mui/lab'; import ErrorIcon from '@mui/icons-material/Error'; diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index 114d521..2c2d877 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -21,7 +21,7 @@ import { subscribeToEvent, unsubscribeFromEvent, } from '../../utils/events'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { Spacer } from '../../common/Spacer'; import { FidgetSpinner } from 'react-loader-spinner'; import { useModal } from '../../common/useModal'; diff --git a/src/components/NotAuthenticated.tsx b/src/components/NotAuthenticated.tsx index c9be445..0d2949c 100644 --- a/src/components/NotAuthenticated.tsx +++ b/src/components/NotAuthenticated.tsx @@ -27,7 +27,7 @@ import { import Logo1Dark from '../assets/svgs/Logo1Dark.svg'; import HelpIcon from '@mui/icons-material/Help'; import { CustomizedSnackbars } from './Snackbar/Snackbar'; -import { cleanUrl, gateways } from '../background'; +import { cleanUrl, gateways } from '../background/background.ts'; import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip'; import ThemeSelector from './Theme/ThemeSelector'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/QortPayment.tsx b/src/components/QortPayment.tsx index c031914..1907686 100644 --- a/src/components/QortPayment.tsx +++ b/src/components/QortPayment.tsx @@ -2,7 +2,7 @@ import { Box, useTheme } from '@mui/material'; import { useState } from 'react'; import { TextP } from '../styles/App-styles'; import { Spacer } from '../common/Spacer'; -import { getFee } from '../background'; +import { getFee } from '../background/background.ts'; import { useTranslation } from 'react-i18next'; export const QortPayment = ({ balance, show, onSuccess, defaultPaymentTo }) => { diff --git a/src/components/RegisterName.tsx b/src/components/RegisterName.tsx index 6e2bfe3..2b5b224 100644 --- a/src/components/RegisterName.tsx +++ b/src/components/RegisterName.tsx @@ -17,7 +17,7 @@ import { import { Label } from './Group/AddGroup'; import { Spacer } from '../common/Spacer'; import { getBaseApiReact } from '../App'; -import { getFee } from '../background'; +import { getFee } from '../background/background.ts'; import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked'; import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events'; import { BarSpinner } from '../common/Spinners/BarSpinner/BarSpinner'; diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index 67950e6..e213174 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -19,7 +19,7 @@ import { } from '@mui/material'; import { objectToBase64 } from '../../qdn/encryption/group-encryption'; import { QORTAL_APP_CONTEXT } from '../../App'; -import { getFee } from '../../background'; +import { getFee } from '../../background/background.ts'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { SaveIcon } from '../../assets/Icons/SaveIcon'; import { IconWrapper } from '../Desktop/DesktopFooter'; @@ -31,7 +31,7 @@ import { saveFileToDiskGeneric } from '../../utils/generateWallet/generateWallet import { base64ToUint8Array, uint8ArrayToObject, -} from '../../backgroundFunctions/encryption'; +} from '../../encryption/encryption.ts'; import { useTranslation } from 'react-i18next'; import { useAtom, useSetAtom } from 'jotai'; diff --git a/src/components/UserLookup.tsx/UserLookup.tsx b/src/components/UserLookup.tsx/UserLookup.tsx index 675d661..3be4ef6 100644 --- a/src/components/UserLookup.tsx/UserLookup.tsx +++ b/src/components/UserLookup.tsx/UserLookup.tsx @@ -19,7 +19,10 @@ import { useTheme, Autocomplete, } from '@mui/material'; -import { getAddressInfo, getNameOrAddress } from '../../background'; +import { + getAddressInfo, + getNameOrAddress, +} from '../../background/background.ts'; import { getBaseApiReact } from '../../App'; import { getNameInfo } from '../Group/Group'; import AccountCircleIcon from '@mui/icons-material/AccountCircle'; diff --git a/src/deps/AltcoinHDWallet.ts b/src/deps/AltcoinHDWallet.ts deleted file mode 100644 index cd4a10b..0000000 --- a/src/deps/AltcoinHDWallet.ts +++ /dev/null @@ -1,864 +0,0 @@ -// @ts-nocheck -; -import Base58 from '../deps/Base58.js' -import {Sha256, Sha512} from 'asmcrypto.js' -import jsSHA from 'jssha' -import RIPEMD160 from '../deps/ripemd160.js' -import utils from '../utils/utils' -import {BigInteger, EllipticCurve} from './ecbn' -import {Buffer} from 'buffer' - -export default class AltcoinHDWallet { - - constructor(addressParams) { - - /** - * Seed - 32 bytes - */ - - this.seed = new Uint8Array(32) - - /** - * Version Bytes - 4 byte - */ - - this.versionBytes = addressParams - - /** - * Depth - 1 byte - */ - - this.depth = 0 - - /** - * Parent Fingerprint - 4 bytes - */ - - this.parentFingerprint = '0x00000000' // master key - - /** - * Child Index - 4 bytes - */ - - this.childIndex = '0x00000000' // master key - - /** - * Chain Code - 32 bytes - */ - - this.chainCode = new Uint8Array(32) - - /** - * Key Data - 33 bytes - */ - - this.keyData = new Uint8Array(33) - - /** - * Seed Hash - 64 bytes - */ - - this.seedHash = new Uint8Array(64) - - /** - * Private Key - 32 bytes - */ - - this.privateKey = new Uint8Array(32) - - /** - * Public Key - 33 bytes (compressed) - */ - - this.publicKey = new Uint8Array(33) - - /** - * Public Key Hash160 (used to derive the parent fingerprint for derived) - */ - - this.publicKeyHash = new Uint8Array(20) - - /** - * Master Private Key (Base58 encoded) - */ - - this.masterPrivateKey = '' - - /** - * Master Public Key (Base58 encoded) - */ - - this.masterPublicKey = '' - - /** - * Testnet Master Private Key (Base58 encoded) - THIS IS TESTNET - */ - - this._tMasterPrivateKey = '' - - /** - * Testnet Master Public Key (Base58 encoded) - THIS IS TESTNET - */ - - this._tmasterPublicKey = '' - - /** - * Child Keys Derivation from the Parent Keys - */ - - /** - * Child Private Key - 32 bytes - */ - - this.childPrivateKey = new Uint8Array(32) - - /** - * Child Chain Code - 32 bytes - */ - - this.childChainCode = new Uint8Array(32) - - /** - * Child Public Key - 33 bytes (compressed) - */ - - this.childPublicKey = new Uint8Array(33) - - /** - * Child Public Key Hash160 (used to derive the parent fingerprint for derived) - */ - - this.childPublicKeyHash = new Uint8Array(20) - - /** - * Extended Private Child Key - Base58 encoded - */ - - this.xPrivateChildKey = '' - - /** - * Extended Public Child Key - Base58 encoded - */ - - this.xPublicChildKey = '' - - /** - * Grand Child Keys Derivation from the Child Keys - */ - - /** - * Grand Child Private Key - 32 bytes - */ - - this.grandChildPrivateKey = new Uint8Array(32) - - /** - * Grand Child Chain Code - 32 bytes - */ - - this.grandChildChainCode = new Uint8Array(32) - - /** - * Grand Child Public Key - 33 bytes (compressed) - */ - - this.grandChildPublicKey = new Uint8Array(33) - - /** - * Grand Public Key Hash160 (used to derive the parent fingerprint for derived) - */ - - this.grandChildPublicKeyHash = new Uint8Array(20) - - /** - * Extended Private Grand Child Key - Base58 encoded - */ - - this.xPrivateGrandChildKey = '' - - /** - * Extended Public Grand Child Key - Base58 encoded - */ - - this.xPublicGrandChildKey = '' - - /** - * Litecoin Legacy Address - Derived from the Grand Child Public Key Hash - */ - - this.litecoinLegacyAddress = '' - - /** - * TESTNET Litecoin Legacy Address (Derived from the Grand Child Public Key Hash) - THIS IS TESTNET - */ - - this._tlitecoinLegacyAddress = '' - - /** - * Wallet - Wallet Object (keys...) - */ - - this.wallet = {} - } - - setSeed(seed) { - this.seed = seed - } - - createWallet(seed, isBIP44, indicator = null) { - - // Set Seeed - this.setSeed(seed) - - // Generate Seed Hash - this.generateSeedHash(this.seed, isBIP44, indicator) - - // Generate Private Key - this.generatePrivateKey(this.seedHash) - - // Generate Chain Code - this.generateChainCode(this.seedHash) - - // Generate Public Key from Private Key - this.generatePublicKey(this.privateKey) - - // Generate Mainnet Master Private Key - this.generateMainnetMasterPrivateKey() - - // Generate Mainnet Master Public Key - this.generateMainnetMasterPublicKey() - - // Generate Testnet Master Private Key - this.generateTestnetMasterPrivateKey() - - // Generate Testnet Master Public Key - this.generateTestnetMasterPublicKey() - - // Generate Child and Grand Child Keys - this.generateDerivedChildKeys() - - // Return Wallet Object Specification - return this.returnWallet() - } - - - generateSeedHash(seed, isBIP44, indicator = null) { - let buffer - - if (isBIP44) { - buffer = utils.appendBuffer(seed.reverse(), utils.int32ToBytes(indicator)) - } else { - if(indicator !== null) { - const indicatorString = utils.stringtoUTF8Array(indicator) - buffer = utils.appendBuffer(seed.reverse(), indicatorString) - } - else - { - buffer = seed.reverse() - } - } - - const _reverseSeedHash = new Sha256().process(buffer).finish().result - this.seedHash = new Sha512().process(utils.appendBuffer(seed, _reverseSeedHash)).finish().result - } - - generatePrivateKey(seedHash) { - const SECP256K1_CURVE_ORDER = new BigInteger("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141") - - const privateKeyHash = seedHash.slice(0, 32) - - this.seed58 = Base58.encode(privateKeyHash) - - const _privateKeyHash = [...privateKeyHash] - let privateKeyBigInt = BigInteger.fromByteArrayUnsigned(_privateKeyHash) - - const privateKey = (privateKeyBigInt.mod(SECP256K1_CURVE_ORDER.subtract(BigInteger.ONE))).add(BigInteger.ONE) - this.privateKey = privateKey.toByteArrayUnsigned() - } - - generateChainCode(seedHash) { - this.chainCode = new Sha256().process(seedHash.slice(32, 64)).finish().result - } - - generatePublicKey(privateKey) { - const _privateKey = [...privateKey] - const privateKeyBigInt = BigInteger.fromByteArrayUnsigned(_privateKey) - - const epCurve = EllipticCurve.getSECCurveByName("secp256k1") - const curvePoints = epCurve.getG().multiply(privateKeyBigInt) - - - const x = curvePoints.getX().toBigInteger() - const y = curvePoints.getY().toBigInteger() - - /** - * Deriving Uncompressed Public Key (65 bytes) - * - * const publicKeyBytes = EllipticCurve.integerToBytes(x, 32) - * this.publicKey = publicKeyBytes.concat(EllipticCurve.integerToBytes(y, 32)) - * this.publicKey.unshift(0x04) // append point indicator - */ - - // Compressed Public Key (33 bytes) - this.publicKey = EllipticCurve.integerToBytes(x, 32) - - - if (y.isEven()) { - this.publicKey.unshift(0x02) // append point indicator - } else { - this.publicKey.unshift(0x03) // append point indicator - } - - // PublicKey Hash - const publicKeySHA256 = new Sha256().process(new Uint8Array(this.publicKey)).finish().result - const _publicKeyHash = new RIPEMD160().update(Buffer.from(publicKeySHA256)).digest('hex') - this.publicKeyHash = _publicKeyHash - } - - generateMainnetMasterPrivateKey() { - // Serialization Variable - const s = [] - - // Append Version Byte - s.push(...(utils.int32ToBytes(this.versionBytes.mainnet.private))) - - // Append Depth - s.push(this.depth) - - // Append Parent Fingerprint - s.push(...(utils.int32ToBytes(this.parentFingerprint))) - - // Append Child Number - s.push(...(utils.int32ToBytes(this.childIndex))) - - // Append Chain Code - s.push(...this.chainCode) - - // Append 1 byte '0x00' (to make the key data 33 bytes, DO THIS ONLY FOR PRIVATE KEYS ) - s.push(0) - - //if the private key length is less than 32 let's add leading zeros - if(this.privateKey.length<32){ - for(let i=this.privateKey.length;i<32;i++){ - s.push(0) - } - } - - // Append Private Key - s.push(...this.privateKey) - - // Generate CheckSum - const _s = new Uint8Array(s) - const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result - const checkSum = _checkSum.slice(0, 4) - - // Append CheckSum - s.push(...checkSum) // And this brings us to the end of the serialization... - - // Save to Private Key as Base58 encoded - this.masterPrivateKey = Base58.encode(s) - } - - generateMainnetMasterPublicKey() { - // Serialization Variable - const s = [] - - // Append Version Byte - s.push(...(utils.int32ToBytes(this.versionBytes.mainnet.public))) - - // Append Depth - s.push(this.depth) - - // Append Parent Fingerprint - s.push(...(utils.int32ToBytes(this.parentFingerprint))) - - // Append Child Number - s.push(...(utils.int32ToBytes(this.childIndex))) - - // Append Chain Code - s.push(...this.chainCode) - - // Append Public Key - s.push(...this.publicKey) - - // Generate CheckSum - const _s = new Uint8Array(s) - const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result - const checkSum = _checkSum.slice(0, 4) - - // Append CheckSum - s.push(...checkSum) // And this brings us to the end of the serialization... - - // Save to Public Key as Base58 encoded - this.masterPublicKey = Base58.encode(s) - } - - generateTestnetMasterPrivateKey() { - - // To be Used ONLY in Testnet... - - // Serialization Variable - const s = [] - - // Append Version Byte - s.push(...(utils.int32ToBytes(this.versionBytes.testnet.private))) - - // Append Depth - s.push(this.depth) - - // Append Parent Fingerprint - s.push(...(utils.int32ToBytes(this.parentFingerprint))) - - // Append Child Number - s.push(...(utils.int32ToBytes(this.childIndex))) - - // Append Chain Code - s.push(...this.chainCode) - - // Append 1 byte '0x00' (to make the key data 33 bytes, DO THIS ONLY FOR PRIVATE KEYS ) - s.push(0) - - // Append Private Key - s.push(...this.privateKey) - - // Generate CheckSum - const _s = new Uint8Array(s) - const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result - const checkSum = _checkSum.slice(0, 4) - - // Append CheckSum - s.push(...checkSum) // And this brings us to the end of the serialization... - - // Save to Private Key as Base58 encoded - this._tMasterPrivateKey = Base58.encode(s) - } - - generateTestnetMasterPublicKey() { - - // To be Used ONLY in Testnet... - - // Serialization Variable - const s = [] - - // Append Version Byte - s.push(...(utils.int32ToBytes(this.versionBytes.testnet.public))) - - // Append Depth - s.push(this.depth) - - // Append Parent Fingerprint - s.push(...(utils.int32ToBytes(this.parentFingerprint))) - - // Append Child Number - s.push(...(utils.int32ToBytes(this.childIndex))) - - // Append Chain Code - s.push(...this.chainCode) - - // Append Private Key - s.push(...this.publicKey) - - // Generate CheckSum - const _s = new Uint8Array(s) - const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result - const checkSum = _checkSum.slice(0, 4) - - // Append CheckSum - s.push(...checkSum) // And this brings us to the end of the serialization... - - // Save to Private Key as Base58 encoded - this._tmasterPublicKey = Base58.encode(s) - } - - generateDerivedChildKeys() { - - // SPEC INFO: https://en.bitcoin.it/wiki/BIP_0032#Child_key_derivation_.28CKD.29_functions - // NOTE: will not be using some of derivations func as the value is known. (So I'd rather shove in the values and rewrite out the derivations later ?) - - // NOTE: I "re-wrote" and "reduplicate" the code for child and grandChild keys derivations inorder to get the child and grandchild from the child - // TODO: Make this more better in the future - - const path = 'm/0/0' - // let p = path.split('/') - - // Get Public kEY - const derivePublicChildKey = () => { - - const _privateKey = [...this.childPrivateKey] - const privateKeyBigInt = BigInteger.fromByteArrayUnsigned(_privateKey) - - const epCurve = EllipticCurve.getSECCurveByName("secp256k1") - const curvePoints = epCurve.getG().multiply(privateKeyBigInt) - - const x = curvePoints.getX().toBigInteger() - const y = curvePoints.getY().toBigInteger() - - // Compressed Public Key (33 bytes) - this.childPublicKey = EllipticCurve.integerToBytes(x, 32) - - - if (y.isEven()) { - - this.childPublicKey.unshift(0x02) // append point indicator - } else { - - this.childPublicKey.unshift(0x03) // append point indicator - } - - // PublicKey Hash - const childPublicKeySHA256 = new Sha256().process(new Uint8Array(this.childPublicKey)).finish().result - const _childPublicKeyHash = new RIPEMD160().update(Buffer.from(childPublicKeySHA256)).digest('hex') - this.childPublicKeyHash = _childPublicKeyHash - - - // Call deriveExtendedPublicChildKey // WIll be hardcoding the values... - deriveExtendedPublicChildKey(1, 0) - } - - const derivePrivateChildKey = (cI) => { - - let ib = [] - ib.push((cI >> 24) & 0xff) - ib.push((cI >> 16) & 0xff) - ib.push((cI >> 8) & 0xff) - ib.push(cI & 0xff) - - const s = [...this.publicKey].concat(ib) - - const _hmacSha512 = new jsSHA("SHA-512", "UINT8ARRAY", { numRounds: 1, hmacKey: { value: this.chainCode, format: "UINT8ARRAY" } }) - _hmacSha512.update(new Uint8Array(s)) - - - const IL = BigInteger.fromByteArrayUnsigned([..._hmacSha512.getHMAC('UINT8ARRAY').slice(0, 32)]) - this.childChainCode = _hmacSha512.getHMAC('UINT8ARRAY').slice(32, 64) // IR according to the SPEC - - - // SECP256k1 init - const epCurve = EllipticCurve.getSECCurveByName("secp256k1") - - - const ki = IL.add(BigInteger.fromByteArrayUnsigned(this.privateKey)).mod(epCurve.getN()) // parse256(IL) + kpar (mod n) ==> ki - this.childPrivateKey = ki.toByteArrayUnsigned() - - // Call deriveExtendedPrivateChildKey - deriveExtendedPrivateChildKey(1, 0) - } - - - const deriveExtendedPrivateChildKey = (i, childIndex) => { - - // Serialization Variable - const s = [] - - // Append Version Byte - s.push(...(utils.int32ToBytes(this.versionBytes.mainnet.private))) - - // Append Depth (using the index as depth) - i = parseInt(i) - s.push(i) - - // Append Parent Fingerprint - s.push(...(this.publicKeyHash.slice(0, 4))) - - // Append Child Index - s.push(childIndex >>> 24) - s.push((childIndex >>> 16) & 0xff) - s.push((childIndex >>> 8) & 0xff) - s.push(childIndex & 0xff) - - // Append Chain Code - s.push(...this.childChainCode) - - // Append 1 byte '0x00' (to make the key data 33 bytes, DO THIS ONLY FOR PRIVATE KEYS ) - s.push(0) - - // Append Private Key - s.push(...this.childPrivateKey) - - // Generate CheckSum - const _s = new Uint8Array(s) - const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result - const checkSum = _checkSum.slice(0, 4) - - // Append CheckSum - s.push(...checkSum) // And this brings us to the end of the serialization... - - // Save to Private Key as Base58 encoded - this.xPrivateChildKey = Base58.encode(s) - } - - const deriveExtendedPublicChildKey = (i, childIndex) => { - - // Serialization Variable - const s = [] - - // Append Version Byte - s.push(...(utils.int32ToBytes(this.versionBytes.mainnet.public))) - - // Append Depth - i = parseInt(i) - s.push(i) - - // Append Parent Fingerprint - s.push(...(this.publicKeyHash.slice(0, 4))) - - // Append Child Index - s.push(childIndex >>> 24) - s.push((childIndex >>> 16) & 0xff) - s.push((childIndex >>> 8) & 0xff) - s.push(childIndex & 0xff) - - // Append Chain Code - s.push(...this.childChainCode) - - // Append Public Key - s.push(...this.childPublicKey) - - // Generate CheckSum - const _s = new Uint8Array(s) - const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result - const checkSum = _checkSum.slice(0, 4) - - // Append CheckSum - s.push(...checkSum) // And this brings us to the end of the serialization... - - - // Save to Public Key as Base58 encoded - this.xPublicChildKey = Base58.encode(s) - } - - - /** - * GRAND CHILD KEYS - * - * NOTE: I know this is not the best way to generate this (even though it works the way it ought) - * Things to rewrite will be and not limited to deriving this through a for loop, removing hard code values, etc... - */ - - const derivePublicGrandChildKey = () => { - - const _privateKey = [...this.grandChildPrivateKey] - const privateKeyBigInt = BigInteger.fromByteArrayUnsigned(_privateKey) - - - const epCurve = EllipticCurve.getSECCurveByName("secp256k1") - const curvePoints = epCurve.getG().multiply(privateKeyBigInt) - - const x = curvePoints.getX().toBigInteger() - const y = curvePoints.getY().toBigInteger() - - // Compressed Public Key (33 bytes) - this.grandChildPublicKey = EllipticCurve.integerToBytes(x, 32) - - - if (y.isEven()) { - this.grandChildPublicKey.unshift(0x02) // append point indicator - } else { - this.grandChildPublicKey.unshift(0x03) // append point indicator - } - - - // PublicKey Hash - const grandChildPublicKeySHA256 = new Sha256().process(new Uint8Array(this.grandChildPublicKey)).finish().result - const _grandChildPublicKeyHash = new RIPEMD160().update(Buffer.from(grandChildPublicKeySHA256)).digest('hex') - this.grandChildPublicKeyHash = _grandChildPublicKeyHash - - - // Call deriveExtendedPublicChildKey // WIll be hardcoding the values... - deriveExtendedPublicGrandChildKey(2, 0) - - /** - * Derive Litecoin Legacy Address - */ - - // Append Address Prefix - let prefix = [this.versionBytes.mainnet.prefix] - if (2 == this.versionBytes.mainnet.prefix.length) { - prefix = [this.versionBytes.mainnet.prefix[0]] - prefix.push([this.versionBytes.mainnet.prefix[1]]) - } - - const k = prefix.concat(...this.grandChildPublicKeyHash) - - // Derive Checksum - const _addressCheckSum = new Sha256().process(new Sha256().process(new Uint8Array(k)).finish().result).finish().result - const addressCheckSum = _addressCheckSum.slice(0, 4) - - // Append CheckSum - const _litecoinLegacyAddress = k.concat(...addressCheckSum) - - // Convert to Base58 - this.litecoinLegacyAddress = Base58.encode(_litecoinLegacyAddress) - - - /** - * Derive TESTNET Litecoin Legacy Address - */ - - // Append Version Byte - const tK = [this.versionBytes.testnet.prefix].concat(...this.grandChildPublicKeyHash) - - // Derive Checksum - const _tAddressCheckSum = new Sha256().process(new Sha256().process(new Uint8Array(tK)).finish().result).finish().result - const tAddressCheckSum = _tAddressCheckSum.slice(0, 4) - - // Append CheckSum - const _tlitecoinLegacyAddress = tK.concat(...tAddressCheckSum) - - // Convert to Base58 - this._tlitecoinLegacyAddress = Base58.encode(_tlitecoinLegacyAddress) - } - - const derivePrivateGrandChildKey = (cI, i) => { - - let ib = [] - ib.push((cI >> 24) & 0xff) - ib.push((cI >> 16) & 0xff) - ib.push((cI >> 8) & 0xff) - ib.push(cI & 0xff) - - const s = [...this.childPublicKey].concat(ib) - - - const _hmacSha512 = new jsSHA("SHA-512", "UINT8ARRAY", { numRounds: 1, hmacKey: { value: this.childChainCode, format: "UINT8ARRAY" } }) - _hmacSha512.update(new Uint8Array(s)) - - - const IL = BigInteger.fromByteArrayUnsigned([..._hmacSha512.getHMAC('UINT8ARRAY').slice(0, 32)]) - this.grandChildChainCode = _hmacSha512.getHMAC('UINT8ARRAY').slice(32, 64) // IR according to the SPEC - - // SECP256k1 init - const epCurve = EllipticCurve.getSECCurveByName("secp256k1") - - - const ki = IL.add(BigInteger.fromByteArrayUnsigned(this.childPrivateKey)).mod(epCurve.getN()) // parse256(IL) + kpar (mod n) ==> ki - this.grandChildPrivateKey = ki.toByteArrayUnsigned() - - - // Call deriveExtendedPrivateChildKey - deriveExtendedPrivateGrandChildKey(2, 0) - } - - - const deriveExtendedPrivateGrandChildKey = (i, childIndex) => { - - // Serialization Variable - const s = [] - - // Append Version Byte - s.push(...(utils.int32ToBytes(this.versionBytes.mainnet.private))) - - // Append Depth (using the index as depth) - i = parseInt(i) - s.push(i) - - // Append Parent Fingerprint - s.push(...(this.childPublicKeyHash.slice(0, 4))) - - // Append Child Index - s.push(childIndex >>> 24) - s.push((childIndex >>> 16) & 0xff) - s.push((childIndex >>> 8) & 0xff) - s.push(childIndex & 0xff) - - // Append Chain Code - s.push(...this.grandChildChainCode) - - // Append 1 byte '0x00' (to make the key data 33 bytes, DO THIS ONLY FOR PRIVATE KEYS ) - s.push(0) - - // Append Private Key - s.push(...this.grandChildPrivateKey) - - // Generate CheckSum - const _s = new Uint8Array(s) - const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result - const checkSum = _checkSum.slice(0, 4) - - // Append CheckSum - s.push(...checkSum) // And this brings us to the end of the serialization... - - // Save to Private Key as Base58 encoded - this.xPrivateGrandChildKey = Base58.encode(s) - } - - const deriveExtendedPublicGrandChildKey = (i, childIndex) => { - - // Serialization Variable - const s = [] - - // Append Version Byte - s.push(...(utils.int32ToBytes(this.versionBytes.mainnet.public))) - - // Append Depth - i = parseInt(i) - s.push(i) - - // Append Parent Fingerprint - s.push(...(this.childPublicKeyHash.slice(0, 4))) - - // Append Child Index - s.push(childIndex >>> 24) - s.push((childIndex >>> 16) & 0xff) - s.push((childIndex >>> 8) & 0xff) - s.push(childIndex & 0xff) - - // Append Chain Code - s.push(...this.grandChildChainCode) - - // Append Public Key - s.push(...this.grandChildPublicKey) - - // Generate CheckSum - const _s = new Uint8Array(s) - const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result - const checkSum = _checkSum.slice(0, 4) - - // Append CheckSum - s.push(...checkSum) // And this brings us to the end of the serialization... - - // Save to Public Key as Base58 encoded - this.xPublicGrandChildKey = Base58.encode(s) - } - - - - // Hard Code value.. - let childIndex = 0 - - // Call derivePrivateChildKey //Hard code value - derivePrivateChildKey(childIndex) - - // Call derivePublicChildKey - derivePublicChildKey() - - - // Call derivePrivateGrandChildKey // Hard Code value... - derivePrivateGrandChildKey(0, 2) - - // Call derivePublicGrandChildKey - derivePublicGrandChildKey() - } - - returnWallet() { - - // Will be limiting the exported Wallet Object to just the Master keys and Legacy Addresses - - const wallet = { - derivedMasterPrivateKey: this.masterPrivateKey, - derivedMasterPublicKey: this.masterPublicKey, - _tDerivedMasterPrivateKey: this._tMasterPrivateKey, - _tDerivedmasterPublicKey: this._tmasterPublicKey, - seed58: this.seed58, - // derivedPrivateChildKey: this.xPrivateChildKey, - // derivedPublicChildKey: this.xPublicChildKey, - // derivedPrivateGrandChildKey: this.xPrivateGrandChildKey, - // derivedPublicGrandChildKey: this.xPublicGrandChildKey, - address: this.litecoinLegacyAddress, - _taddress: this._tlitecoinLegacyAddress - } - - this.wallet = wallet - return wallet - } -} diff --git a/src/encryption/AltcoinHDWallet.ts b/src/encryption/AltcoinHDWallet.ts new file mode 100644 index 0000000..0af6875 --- /dev/null +++ b/src/encryption/AltcoinHDWallet.ts @@ -0,0 +1,881 @@ +// @ts-nocheck +import Base58 from './Base58.js'; +import { Sha256, Sha512 } from 'asmcrypto.js'; +import jsSHA from 'jssha'; +import RIPEMD160 from './ripemd160.js'; +import utils from '../utils/utils.js'; +import { BigInteger, EllipticCurve } from './ecbn'; +import { Buffer } from 'buffer'; + +export default class AltcoinHDWallet { + constructor(addressParams) { + /** + * Seed - 32 bytes + */ + + this.seed = new Uint8Array(32); + + /** + * Version Bytes - 4 byte + */ + + this.versionBytes = addressParams; + + /** + * Depth - 1 byte + */ + + this.depth = 0; + + /** + * Parent Fingerprint - 4 bytes + */ + + this.parentFingerprint = '0x00000000'; // master key + + /** + * Child Index - 4 bytes + */ + + this.childIndex = '0x00000000'; // master key + + /** + * Chain Code - 32 bytes + */ + + this.chainCode = new Uint8Array(32); + + /** + * Key Data - 33 bytes + */ + + this.keyData = new Uint8Array(33); + + /** + * Seed Hash - 64 bytes + */ + + this.seedHash = new Uint8Array(64); + + /** + * Private Key - 32 bytes + */ + + this.privateKey = new Uint8Array(32); + + /** + * Public Key - 33 bytes (compressed) + */ + + this.publicKey = new Uint8Array(33); + + /** + * Public Key Hash160 (used to derive the parent fingerprint for derived) + */ + + this.publicKeyHash = new Uint8Array(20); + + /** + * Master Private Key (Base58 encoded) + */ + + this.masterPrivateKey = ''; + + /** + * Master Public Key (Base58 encoded) + */ + + this.masterPublicKey = ''; + + /** + * Testnet Master Private Key (Base58 encoded) - THIS IS TESTNET + */ + + this._tMasterPrivateKey = ''; + + /** + * Testnet Master Public Key (Base58 encoded) - THIS IS TESTNET + */ + + this._tmasterPublicKey = ''; + + /** + * Child Keys Derivation from the Parent Keys + */ + + /** + * Child Private Key - 32 bytes + */ + + this.childPrivateKey = new Uint8Array(32); + + /** + * Child Chain Code - 32 bytes + */ + + this.childChainCode = new Uint8Array(32); + + /** + * Child Public Key - 33 bytes (compressed) + */ + + this.childPublicKey = new Uint8Array(33); + + /** + * Child Public Key Hash160 (used to derive the parent fingerprint for derived) + */ + + this.childPublicKeyHash = new Uint8Array(20); + + /** + * Extended Private Child Key - Base58 encoded + */ + + this.xPrivateChildKey = ''; + + /** + * Extended Public Child Key - Base58 encoded + */ + + this.xPublicChildKey = ''; + + /** + * Grand Child Keys Derivation from the Child Keys + */ + + /** + * Grand Child Private Key - 32 bytes + */ + + this.grandChildPrivateKey = new Uint8Array(32); + + /** + * Grand Child Chain Code - 32 bytes + */ + + this.grandChildChainCode = new Uint8Array(32); + + /** + * Grand Child Public Key - 33 bytes (compressed) + */ + + this.grandChildPublicKey = new Uint8Array(33); + + /** + * Grand Public Key Hash160 (used to derive the parent fingerprint for derived) + */ + + this.grandChildPublicKeyHash = new Uint8Array(20); + + /** + * Extended Private Grand Child Key - Base58 encoded + */ + + this.xPrivateGrandChildKey = ''; + + /** + * Extended Public Grand Child Key - Base58 encoded + */ + + this.xPublicGrandChildKey = ''; + + /** + * Litecoin Legacy Address - Derived from the Grand Child Public Key Hash + */ + + this.litecoinLegacyAddress = ''; + + /** + * TESTNET Litecoin Legacy Address (Derived from the Grand Child Public Key Hash) - THIS IS TESTNET + */ + + this._tlitecoinLegacyAddress = ''; + + /** + * Wallet - Wallet Object (keys...) + */ + + this.wallet = {}; + } + + setSeed(seed) { + this.seed = seed; + } + + createWallet(seed, isBIP44, indicator = null) { + // Set Seeed + this.setSeed(seed); + + // Generate Seed Hash + this.generateSeedHash(this.seed, isBIP44, indicator); + + // Generate Private Key + this.generatePrivateKey(this.seedHash); + + // Generate Chain Code + this.generateChainCode(this.seedHash); + + // Generate Public Key from Private Key + this.generatePublicKey(this.privateKey); + + // Generate Mainnet Master Private Key + this.generateMainnetMasterPrivateKey(); + + // Generate Mainnet Master Public Key + this.generateMainnetMasterPublicKey(); + + // Generate Testnet Master Private Key + this.generateTestnetMasterPrivateKey(); + + // Generate Testnet Master Public Key + this.generateTestnetMasterPublicKey(); + + // Generate Child and Grand Child Keys + this.generateDerivedChildKeys(); + + // Return Wallet Object Specification + return this.returnWallet(); + } + + generateSeedHash(seed, isBIP44, indicator = null) { + let buffer; + + if (isBIP44) { + buffer = utils.appendBuffer( + seed.reverse(), + utils.int32ToBytes(indicator) + ); + } else { + if (indicator !== null) { + const indicatorString = utils.stringtoUTF8Array(indicator); + buffer = utils.appendBuffer(seed.reverse(), indicatorString); + } else { + buffer = seed.reverse(); + } + } + + const _reverseSeedHash = new Sha256().process(buffer).finish().result; + this.seedHash = new Sha512() + .process(utils.appendBuffer(seed, _reverseSeedHash)) + .finish().result; + } + + generatePrivateKey(seedHash) { + const SECP256K1_CURVE_ORDER = new BigInteger( + '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141' + ); + + const privateKeyHash = seedHash.slice(0, 32); + + this.seed58 = Base58.encode(privateKeyHash); + + const _privateKeyHash = [...privateKeyHash]; + let privateKeyBigInt = BigInteger.fromByteArrayUnsigned(_privateKeyHash); + + const privateKey = privateKeyBigInt + .mod(SECP256K1_CURVE_ORDER.subtract(BigInteger.ONE)) + .add(BigInteger.ONE); + this.privateKey = privateKey.toByteArrayUnsigned(); + } + + generateChainCode(seedHash) { + this.chainCode = new Sha256() + .process(seedHash.slice(32, 64)) + .finish().result; + } + + generatePublicKey(privateKey) { + const _privateKey = [...privateKey]; + const privateKeyBigInt = BigInteger.fromByteArrayUnsigned(_privateKey); + + const epCurve = EllipticCurve.getSECCurveByName('secp256k1'); + const curvePoints = epCurve.getG().multiply(privateKeyBigInt); + + const x = curvePoints.getX().toBigInteger(); + const y = curvePoints.getY().toBigInteger(); + + /** + * Deriving Uncompressed Public Key (65 bytes) + * + * const publicKeyBytes = EllipticCurve.integerToBytes(x, 32) + * this.publicKey = publicKeyBytes.concat(EllipticCurve.integerToBytes(y, 32)) + * this.publicKey.unshift(0x04) // append point indicator + */ + + // Compressed Public Key (33 bytes) + this.publicKey = EllipticCurve.integerToBytes(x, 32); + + if (y.isEven()) { + this.publicKey.unshift(0x02); // append point indicator + } else { + this.publicKey.unshift(0x03); // append point indicator + } + + // PublicKey Hash + const publicKeySHA256 = new Sha256() + .process(new Uint8Array(this.publicKey)) + .finish().result; + const _publicKeyHash = new RIPEMD160() + .update(Buffer.from(publicKeySHA256)) + .digest('hex'); + this.publicKeyHash = _publicKeyHash; + } + + generateMainnetMasterPrivateKey() { + // Serialization Variable + const s = []; + + // Append Version Byte + s.push(...utils.int32ToBytes(this.versionBytes.mainnet.private)); + + // Append Depth + s.push(this.depth); + + // Append Parent Fingerprint + s.push(...utils.int32ToBytes(this.parentFingerprint)); + + // Append Child Number + s.push(...utils.int32ToBytes(this.childIndex)); + + // Append Chain Code + s.push(...this.chainCode); + + // Append 1 byte '0x00' (to make the key data 33 bytes, DO THIS ONLY FOR PRIVATE KEYS ) + s.push(0); + + //if the private key length is less than 32 let's add leading zeros + if (this.privateKey.length < 32) { + for (let i = this.privateKey.length; i < 32; i++) { + s.push(0); + } + } + + // Append Private Key + s.push(...this.privateKey); + + // Generate CheckSum + const _s = new Uint8Array(s); + const _checkSum = new Sha256() + .process(new Sha256().process(_s).finish().result) + .finish().result; + const checkSum = _checkSum.slice(0, 4); + + // Append CheckSum + s.push(...checkSum); // And this brings us to the end of the serialization... + + // Save to Private Key as Base58 encoded + this.masterPrivateKey = Base58.encode(s); + } + + generateMainnetMasterPublicKey() { + // Serialization Variable + const s = []; + + // Append Version Byte + s.push(...utils.int32ToBytes(this.versionBytes.mainnet.public)); + + // Append Depth + s.push(this.depth); + + // Append Parent Fingerprint + s.push(...utils.int32ToBytes(this.parentFingerprint)); + + // Append Child Number + s.push(...utils.int32ToBytes(this.childIndex)); + + // Append Chain Code + s.push(...this.chainCode); + + // Append Public Key + s.push(...this.publicKey); + + // Generate CheckSum + const _s = new Uint8Array(s); + const _checkSum = new Sha256() + .process(new Sha256().process(_s).finish().result) + .finish().result; + const checkSum = _checkSum.slice(0, 4); + + // Append CheckSum + s.push(...checkSum); // And this brings us to the end of the serialization... + + // Save to Public Key as Base58 encoded + this.masterPublicKey = Base58.encode(s); + } + + generateTestnetMasterPrivateKey() { + // To be Used ONLY in Testnet... + + // Serialization Variable + const s = []; + + // Append Version Byte + s.push(...utils.int32ToBytes(this.versionBytes.testnet.private)); + + // Append Depth + s.push(this.depth); + + // Append Parent Fingerprint + s.push(...utils.int32ToBytes(this.parentFingerprint)); + + // Append Child Number + s.push(...utils.int32ToBytes(this.childIndex)); + + // Append Chain Code + s.push(...this.chainCode); + + // Append 1 byte '0x00' (to make the key data 33 bytes, DO THIS ONLY FOR PRIVATE KEYS ) + s.push(0); + + // Append Private Key + s.push(...this.privateKey); + + // Generate CheckSum + const _s = new Uint8Array(s); + const _checkSum = new Sha256() + .process(new Sha256().process(_s).finish().result) + .finish().result; + const checkSum = _checkSum.slice(0, 4); + + // Append CheckSum + s.push(...checkSum); // And this brings us to the end of the serialization... + + // Save to Private Key as Base58 encoded + this._tMasterPrivateKey = Base58.encode(s); + } + + generateTestnetMasterPublicKey() { + // To be Used ONLY in Testnet... + + // Serialization Variable + const s = []; + + // Append Version Byte + s.push(...utils.int32ToBytes(this.versionBytes.testnet.public)); + + // Append Depth + s.push(this.depth); + + // Append Parent Fingerprint + s.push(...utils.int32ToBytes(this.parentFingerprint)); + + // Append Child Number + s.push(...utils.int32ToBytes(this.childIndex)); + + // Append Chain Code + s.push(...this.chainCode); + + // Append Private Key + s.push(...this.publicKey); + + // Generate CheckSum + const _s = new Uint8Array(s); + const _checkSum = new Sha256() + .process(new Sha256().process(_s).finish().result) + .finish().result; + const checkSum = _checkSum.slice(0, 4); + + // Append CheckSum + s.push(...checkSum); // And this brings us to the end of the serialization... + + // Save to Private Key as Base58 encoded + this._tmasterPublicKey = Base58.encode(s); + } + + generateDerivedChildKeys() { + // SPEC INFO: https://en.bitcoin.it/wiki/BIP_0032#Child_key_derivation_.28CKD.29_functions + // NOTE: will not be using some of derivations func as the value is known. (So I'd rather shove in the values and rewrite out the derivations later ?) + + // NOTE: I "re-wrote" and "reduplicate" the code for child and grandChild keys derivations inorder to get the child and grandchild from the child + // TODO: Make this more better in the future + + const path = 'm/0/0'; + // let p = path.split('/') + + // Get Public kEY + const derivePublicChildKey = () => { + const _privateKey = [...this.childPrivateKey]; + const privateKeyBigInt = BigInteger.fromByteArrayUnsigned(_privateKey); + + const epCurve = EllipticCurve.getSECCurveByName('secp256k1'); + const curvePoints = epCurve.getG().multiply(privateKeyBigInt); + + const x = curvePoints.getX().toBigInteger(); + const y = curvePoints.getY().toBigInteger(); + + // Compressed Public Key (33 bytes) + this.childPublicKey = EllipticCurve.integerToBytes(x, 32); + + if (y.isEven()) { + this.childPublicKey.unshift(0x02); // append point indicator + } else { + this.childPublicKey.unshift(0x03); // append point indicator + } + + // PublicKey Hash + const childPublicKeySHA256 = new Sha256() + .process(new Uint8Array(this.childPublicKey)) + .finish().result; + const _childPublicKeyHash = new RIPEMD160() + .update(Buffer.from(childPublicKeySHA256)) + .digest('hex'); + this.childPublicKeyHash = _childPublicKeyHash; + + // Call deriveExtendedPublicChildKey // WIll be hardcoding the values... + deriveExtendedPublicChildKey(1, 0); + }; + + const derivePrivateChildKey = (cI) => { + let ib = []; + ib.push((cI >> 24) & 0xff); + ib.push((cI >> 16) & 0xff); + ib.push((cI >> 8) & 0xff); + ib.push(cI & 0xff); + + const s = [...this.publicKey].concat(ib); + + const _hmacSha512 = new jsSHA('SHA-512', 'UINT8ARRAY', { + numRounds: 1, + hmacKey: { value: this.chainCode, format: 'UINT8ARRAY' }, + }); + _hmacSha512.update(new Uint8Array(s)); + + const IL = BigInteger.fromByteArrayUnsigned([ + ..._hmacSha512.getHMAC('UINT8ARRAY').slice(0, 32), + ]); + this.childChainCode = _hmacSha512.getHMAC('UINT8ARRAY').slice(32, 64); // IR according to the SPEC + + // SECP256k1 init + const epCurve = EllipticCurve.getSECCurveByName('secp256k1'); + + const ki = IL.add(BigInteger.fromByteArrayUnsigned(this.privateKey)).mod( + epCurve.getN() + ); // parse256(IL) + kpar (mod n) ==> ki + this.childPrivateKey = ki.toByteArrayUnsigned(); + + // Call deriveExtendedPrivateChildKey + deriveExtendedPrivateChildKey(1, 0); + }; + + const deriveExtendedPrivateChildKey = (i, childIndex) => { + // Serialization Variable + const s = []; + + // Append Version Byte + s.push(...utils.int32ToBytes(this.versionBytes.mainnet.private)); + + // Append Depth (using the index as depth) + i = parseInt(i); + s.push(i); + + // Append Parent Fingerprint + s.push(...this.publicKeyHash.slice(0, 4)); + + // Append Child Index + s.push(childIndex >>> 24); + s.push((childIndex >>> 16) & 0xff); + s.push((childIndex >>> 8) & 0xff); + s.push(childIndex & 0xff); + + // Append Chain Code + s.push(...this.childChainCode); + + // Append 1 byte '0x00' (to make the key data 33 bytes, DO THIS ONLY FOR PRIVATE KEYS ) + s.push(0); + + // Append Private Key + s.push(...this.childPrivateKey); + + // Generate CheckSum + const _s = new Uint8Array(s); + const _checkSum = new Sha256() + .process(new Sha256().process(_s).finish().result) + .finish().result; + const checkSum = _checkSum.slice(0, 4); + + // Append CheckSum + s.push(...checkSum); // And this brings us to the end of the serialization... + + // Save to Private Key as Base58 encoded + this.xPrivateChildKey = Base58.encode(s); + }; + + const deriveExtendedPublicChildKey = (i, childIndex) => { + // Serialization Variable + const s = []; + + // Append Version Byte + s.push(...utils.int32ToBytes(this.versionBytes.mainnet.public)); + + // Append Depth + i = parseInt(i); + s.push(i); + + // Append Parent Fingerprint + s.push(...this.publicKeyHash.slice(0, 4)); + + // Append Child Index + s.push(childIndex >>> 24); + s.push((childIndex >>> 16) & 0xff); + s.push((childIndex >>> 8) & 0xff); + s.push(childIndex & 0xff); + + // Append Chain Code + s.push(...this.childChainCode); + + // Append Public Key + s.push(...this.childPublicKey); + + // Generate CheckSum + const _s = new Uint8Array(s); + const _checkSum = new Sha256() + .process(new Sha256().process(_s).finish().result) + .finish().result; + const checkSum = _checkSum.slice(0, 4); + + // Append CheckSum + s.push(...checkSum); // And this brings us to the end of the serialization... + + // Save to Public Key as Base58 encoded + this.xPublicChildKey = Base58.encode(s); + }; + + /** + * GRAND CHILD KEYS + * + * NOTE: I know this is not the best way to generate this (even though it works the way it ought) + * Things to rewrite will be and not limited to deriving this through a for loop, removing hard code values, etc... + */ + + const derivePublicGrandChildKey = () => { + const _privateKey = [...this.grandChildPrivateKey]; + const privateKeyBigInt = BigInteger.fromByteArrayUnsigned(_privateKey); + + const epCurve = EllipticCurve.getSECCurveByName('secp256k1'); + const curvePoints = epCurve.getG().multiply(privateKeyBigInt); + + const x = curvePoints.getX().toBigInteger(); + const y = curvePoints.getY().toBigInteger(); + + // Compressed Public Key (33 bytes) + this.grandChildPublicKey = EllipticCurve.integerToBytes(x, 32); + + if (y.isEven()) { + this.grandChildPublicKey.unshift(0x02); // append point indicator + } else { + this.grandChildPublicKey.unshift(0x03); // append point indicator + } + + // PublicKey Hash + const grandChildPublicKeySHA256 = new Sha256() + .process(new Uint8Array(this.grandChildPublicKey)) + .finish().result; + const _grandChildPublicKeyHash = new RIPEMD160() + .update(Buffer.from(grandChildPublicKeySHA256)) + .digest('hex'); + this.grandChildPublicKeyHash = _grandChildPublicKeyHash; + + // Call deriveExtendedPublicChildKey // WIll be hardcoding the values... + deriveExtendedPublicGrandChildKey(2, 0); + + /** + * Derive Litecoin Legacy Address + */ + + // Append Address Prefix + let prefix = [this.versionBytes.mainnet.prefix]; + if (2 == this.versionBytes.mainnet.prefix.length) { + prefix = [this.versionBytes.mainnet.prefix[0]]; + prefix.push([this.versionBytes.mainnet.prefix[1]]); + } + + const k = prefix.concat(...this.grandChildPublicKeyHash); + + // Derive Checksum + const _addressCheckSum = new Sha256() + .process(new Sha256().process(new Uint8Array(k)).finish().result) + .finish().result; + const addressCheckSum = _addressCheckSum.slice(0, 4); + + // Append CheckSum + const _litecoinLegacyAddress = k.concat(...addressCheckSum); + + // Convert to Base58 + this.litecoinLegacyAddress = Base58.encode(_litecoinLegacyAddress); + + /** + * Derive TESTNET Litecoin Legacy Address + */ + + // Append Version Byte + const tK = [this.versionBytes.testnet.prefix].concat( + ...this.grandChildPublicKeyHash + ); + + // Derive Checksum + const _tAddressCheckSum = new Sha256() + .process(new Sha256().process(new Uint8Array(tK)).finish().result) + .finish().result; + const tAddressCheckSum = _tAddressCheckSum.slice(0, 4); + + // Append CheckSum + const _tlitecoinLegacyAddress = tK.concat(...tAddressCheckSum); + + // Convert to Base58 + this._tlitecoinLegacyAddress = Base58.encode(_tlitecoinLegacyAddress); + }; + + const derivePrivateGrandChildKey = (cI, i) => { + let ib = []; + ib.push((cI >> 24) & 0xff); + ib.push((cI >> 16) & 0xff); + ib.push((cI >> 8) & 0xff); + ib.push(cI & 0xff); + + const s = [...this.childPublicKey].concat(ib); + + const _hmacSha512 = new jsSHA('SHA-512', 'UINT8ARRAY', { + numRounds: 1, + hmacKey: { value: this.childChainCode, format: 'UINT8ARRAY' }, + }); + _hmacSha512.update(new Uint8Array(s)); + + const IL = BigInteger.fromByteArrayUnsigned([ + ..._hmacSha512.getHMAC('UINT8ARRAY').slice(0, 32), + ]); + this.grandChildChainCode = _hmacSha512 + .getHMAC('UINT8ARRAY') + .slice(32, 64); // IR according to the SPEC + + // SECP256k1 init + const epCurve = EllipticCurve.getSECCurveByName('secp256k1'); + + const ki = IL.add( + BigInteger.fromByteArrayUnsigned(this.childPrivateKey) + ).mod(epCurve.getN()); // parse256(IL) + kpar (mod n) ==> ki + this.grandChildPrivateKey = ki.toByteArrayUnsigned(); + + // Call deriveExtendedPrivateChildKey + deriveExtendedPrivateGrandChildKey(2, 0); + }; + + const deriveExtendedPrivateGrandChildKey = (i, childIndex) => { + // Serialization Variable + const s = []; + + // Append Version Byte + s.push(...utils.int32ToBytes(this.versionBytes.mainnet.private)); + + // Append Depth (using the index as depth) + i = parseInt(i); + s.push(i); + + // Append Parent Fingerprint + s.push(...this.childPublicKeyHash.slice(0, 4)); + + // Append Child Index + s.push(childIndex >>> 24); + s.push((childIndex >>> 16) & 0xff); + s.push((childIndex >>> 8) & 0xff); + s.push(childIndex & 0xff); + + // Append Chain Code + s.push(...this.grandChildChainCode); + + // Append 1 byte '0x00' (to make the key data 33 bytes, DO THIS ONLY FOR PRIVATE KEYS ) + s.push(0); + + // Append Private Key + s.push(...this.grandChildPrivateKey); + + // Generate CheckSum + const _s = new Uint8Array(s); + const _checkSum = new Sha256() + .process(new Sha256().process(_s).finish().result) + .finish().result; + const checkSum = _checkSum.slice(0, 4); + + // Append CheckSum + s.push(...checkSum); // And this brings us to the end of the serialization... + + // Save to Private Key as Base58 encoded + this.xPrivateGrandChildKey = Base58.encode(s); + }; + + const deriveExtendedPublicGrandChildKey = (i, childIndex) => { + // Serialization Variable + const s = []; + + // Append Version Byte + s.push(...utils.int32ToBytes(this.versionBytes.mainnet.public)); + + // Append Depth + i = parseInt(i); + s.push(i); + + // Append Parent Fingerprint + s.push(...this.childPublicKeyHash.slice(0, 4)); + + // Append Child Index + s.push(childIndex >>> 24); + s.push((childIndex >>> 16) & 0xff); + s.push((childIndex >>> 8) & 0xff); + s.push(childIndex & 0xff); + + // Append Chain Code + s.push(...this.grandChildChainCode); + + // Append Public Key + s.push(...this.grandChildPublicKey); + + // Generate CheckSum + const _s = new Uint8Array(s); + const _checkSum = new Sha256() + .process(new Sha256().process(_s).finish().result) + .finish().result; + const checkSum = _checkSum.slice(0, 4); + + // Append CheckSum + s.push(...checkSum); // And this brings us to the end of the serialization... + + // Save to Public Key as Base58 encoded + this.xPublicGrandChildKey = Base58.encode(s); + }; + + // Hard Code value.. + let childIndex = 0; + + // Call derivePrivateChildKey //Hard code value + derivePrivateChildKey(childIndex); + + // Call derivePublicChildKey + derivePublicChildKey(); + + // Call derivePrivateGrandChildKey // Hard Code value... + derivePrivateGrandChildKey(0, 2); + + // Call derivePublicGrandChildKey + derivePublicGrandChildKey(); + } + + returnWallet() { + // Will be limiting the exported Wallet Object to just the Master keys and Legacy Addresses + + const wallet = { + derivedMasterPrivateKey: this.masterPrivateKey, + derivedMasterPublicKey: this.masterPublicKey, + _tDerivedMasterPrivateKey: this._tMasterPrivateKey, + _tDerivedmasterPublicKey: this._tmasterPublicKey, + seed58: this.seed58, + // derivedPrivateChildKey: this.xPrivateChildKey, + // derivedPublicChildKey: this.xPublicChildKey, + // derivedPrivateGrandChildKey: this.xPrivateGrandChildKey, + // derivedPublicGrandChildKey: this.xPublicGrandChildKey, + address: this.litecoinLegacyAddress, + _taddress: this._tlitecoinLegacyAddress, + }; + + this.wallet = wallet; + return wallet; + } +} diff --git a/src/deps/Base58.ts b/src/encryption/Base58.ts similarity index 100% rename from src/deps/Base58.ts rename to src/encryption/Base58.ts diff --git a/src/deps/bcryptworker.worker.js b/src/encryption/bcryptworker.worker.js similarity index 100% rename from src/deps/bcryptworker.worker.js rename to src/encryption/bcryptworker.worker.js diff --git a/src/deps/bcryptworkerwasm.worker.js b/src/encryption/bcryptworkerwasm.worker.js similarity index 100% rename from src/deps/bcryptworkerwasm.worker.js rename to src/encryption/bcryptworkerwasm.worker.js diff --git a/src/deps/broken-ripemd160.ts b/src/encryption/broken-ripemd160.ts similarity index 100% rename from src/deps/broken-ripemd160.ts rename to src/encryption/broken-ripemd160.ts diff --git a/src/deps/ecbn.ts b/src/encryption/ecbn.ts similarity index 100% rename from src/deps/ecbn.ts rename to src/encryption/ecbn.ts diff --git a/src/deps/ed2curve.ts b/src/encryption/ed2curve.ts similarity index 100% rename from src/deps/ed2curve.ts rename to src/encryption/ed2curve.ts diff --git a/src/backgroundFunctions/encryption.ts b/src/encryption/encryption.ts similarity index 96% rename from src/backgroundFunctions/encryption.ts rename to src/encryption/encryption.ts index def79c0..97f6d75 100644 --- a/src/backgroundFunctions/encryption.ts +++ b/src/encryption/encryption.ts @@ -1,14 +1,14 @@ -import { getBaseApi } from '../background'; -import i18n from '../i18n/i18n'; +import { getBaseApi } from '../background/background.ts'; +import i18n from '../i18n/i18n.ts'; import { createSymmetricKeyAndNonce, decryptGroupData, encryptDataGroup, objectToBase64, -} from '../qdn/encryption/group-encryption'; -import { publishData } from '../qdn/publish/pubish'; -import { getData } from '../utils/chromeStorage'; -import { RequestQueueWithPromise } from '../utils/queue/queue'; +} from '../qdn/encryption/group-encryption.ts'; +import { publishData } from '../qdn/publish/pubish.ts'; +import { getData } from '../utils/chromeStorage.ts'; +import { RequestQueueWithPromise } from '../utils/queue/queue.ts'; export const requestQueueGetPublicKeys = new RequestQueueWithPromise(10); diff --git a/src/deps/kdf.ts b/src/encryption/kdf.ts similarity index 100% rename from src/deps/kdf.ts rename to src/encryption/kdf.ts diff --git a/src/deps/nacl-fast.ts b/src/encryption/nacl-fast.ts similarity index 100% rename from src/deps/nacl-fast.ts rename to src/encryption/nacl-fast.ts diff --git a/src/deps/ripemd160.ts b/src/encryption/ripemd160.ts similarity index 100% rename from src/deps/ripemd160.ts rename to src/encryption/ripemd160.ts diff --git a/src/hooks/useHandlePaymentNotification.tsx b/src/hooks/useHandlePaymentNotification.tsx index 7a13072..e25fae2 100644 --- a/src/hooks/useHandlePaymentNotification.tsx +++ b/src/hooks/useHandlePaymentNotification.tsx @@ -1,7 +1,10 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { getBaseApiReact } from '../App'; import { getData, storeData } from '../utils/chromeStorage'; -import { checkDifference, getNameInfoForOthers } from '../background'; +import { + checkDifference, + getNameInfoForOthers, +} from '../background/background.ts'; import { lastPaymentSeenTimestampAtom } from '../atoms/global'; import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events'; import { useAtom } from 'jotai'; diff --git a/src/hooks/useHandlePrivateApps.tsx b/src/hooks/useHandlePrivateApps.tsx index e1d8288..3876130 100644 --- a/src/hooks/useHandlePrivateApps.tsx +++ b/src/hooks/useHandlePrivateApps.tsx @@ -1,14 +1,14 @@ import { useContext, useState } from 'react'; import { executeEvent } from '../utils/events'; import { getBaseApiReact, QORTAL_APP_CONTEXT } from '../App'; -import { createEndpoint } from '../background'; +import { createEndpoint } from '../background/background.ts'; import { settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom, } from '../atoms/global'; import { saveToLocalStorage } from '../components/Apps/AppsNavBarDesktop'; import { base64ToUint8Array } from '../qdn/encryption/group-encryption'; -import { uint8ArrayToObject } from '../backgroundFunctions/encryption'; +import { uint8ArrayToObject } from '../encryption/encryption.ts'; import { useSetAtom } from 'jotai'; import { useTranslation } from 'react-i18next'; diff --git a/src/hooks/useQortalGetSaveSettings.tsx b/src/hooks/useQortalGetSaveSettings.tsx index 214455b..5fb62fc 100644 --- a/src/hooks/useQortalGetSaveSettings.tsx +++ b/src/hooks/useQortalGetSaveSettings.tsx @@ -12,7 +12,7 @@ import { decryptResource } from '../components/Group/Group'; import { base64ToUint8Array, uint8ArrayToObject, -} from '../backgroundFunctions/encryption'; +} from '../encryption/encryption'; import { useAtom, useSetAtom } from 'jotai'; function fetchFromLocalStorage(key) { diff --git a/src/qdn/encryption/group-encryption.ts b/src/qdn/encryption/group-encryption.ts index 24998e9..6f018ba 100644 --- a/src/qdn/encryption/group-encryption.ts +++ b/src/qdn/encryption/group-encryption.ts @@ -1,8 +1,8 @@ // @ts-nocheck -import Base58 from '../../deps/Base58'; -import ed2curve from '../../deps/ed2curve'; -import nacl from '../../deps/nacl-fast'; +import Base58 from '../../encryption/Base58'; +import ed2curve from '../../encryption/ed2curve'; +import nacl from '../../encryption/nacl-fast'; import i18n from '../../i18n/i18n'; export function base64ToUint8Array(base64: string) { diff --git a/src/qdn/publish/pubish.ts b/src/qdn/publish/pubish.ts index 5f00ffb..d49791b 100644 --- a/src/qdn/publish/pubish.ts +++ b/src/qdn/publish/pubish.ts @@ -1,10 +1,10 @@ // @ts-nocheck import { Buffer } from 'buffer'; -import Base58 from '../../deps/Base58'; -import nacl from '../../deps/nacl-fast'; +import Base58 from '../../encryption/Base58'; +import nacl from '../../encryption/nacl-fast'; import utils from '../../utils/utils'; -import { createEndpoint, getBaseApi } from '../../background'; +import { createEndpoint, getBaseApi } from '../../background/background'; import { getData } from '../../utils/chromeStorage'; export async function reusableGet(endpoint) { @@ -30,6 +30,7 @@ async function reusablePost(endpoint, _body) { data = await response.clone().json(); } catch (e) { data = await response.text(); +<<<<<<< HEAD } return data; } @@ -70,6 +71,7 @@ async function uploadChunkWithRetry(endpoint, formData, index, maxRetries = 3) { await new Promise((res) => setTimeout(res, 10_000)); } } + return data; } async function getKeyPair() { @@ -87,6 +89,11 @@ export const publishData = async ({ service, identifier, uploadType, + file, + service, + identifier, + uploadType, + isBase64, filename, withFee, title, @@ -245,6 +252,7 @@ export const publishData = async ({ return signAndProcessRes; }; +<<<<<<< HEAD const uploadData = async (registeredName: string, data: any, fee: number) => { console.log('data', uploadType, data); let postBody = ''; @@ -359,6 +367,82 @@ export const publishData = async ({ const result = await response.text(); // Base58-encoded unsigned transaction return result; +} + + const uploadData = async (registeredName: string, file: any, fee: number) => { + let postBody = ''; + let urlSuffix = ''; + + if (file != null) { + // If we're sending zipped data, make sure to use the /zip version of the POST /arbitrary/* API + if (uploadType === 'zip') { + urlSuffix = '/zip'; + } + + // If we're sending file data, use the /base64 version of the POST /arbitrary/* API + else if (uploadType === 'file') { + urlSuffix = '/base64'; + } + + // Base64 encode the file to work around compatibility issues between javascript and java byte arrays + if (isBase64) { + postBody = file; + } + + if (!isBase64) { + let fileBuffer = new Uint8Array(await file.arrayBuffer()); + postBody = Buffer.from(fileBuffer).toString('base64'); + } + } + + let uploadDataUrl = `/arbitrary/${service}/${registeredName}${urlSuffix}`; + if (identifier?.trim().length > 0) { + uploadDataUrl = `/arbitrary/${service}/${registeredName}/${identifier}${urlSuffix}`; + } + + uploadDataUrl = uploadDataUrl + `?fee=${fee}`; + + if (filename != null && filename != 'undefined') { + uploadDataUrl = + uploadDataUrl + '&filename=' + encodeURIComponent(filename); + } + + if (title != null && title != 'undefined') { + uploadDataUrl = uploadDataUrl + '&title=' + encodeURIComponent(title); + } + + if (description != null && description != 'undefined') { + uploadDataUrl = + uploadDataUrl + '&description=' + encodeURIComponent(description); + } + + if (category != null && category != 'undefined') { + uploadDataUrl = + uploadDataUrl + '&category=' + encodeURIComponent(category); + } + + if (tag1 != null && tag1 != 'undefined') { + uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag1); + } + + if (tag2 != null && tag2 != 'undefined') { + uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag2); + } + + if (tag3 != null && tag3 != 'undefined') { + uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag3); + } + + if (tag4 != null && tag4 != 'undefined') { + uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag4); + } + + if (tag5 != null && tag5 != 'undefined') { + uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag5); + } + + return await reusablePost(uploadDataUrl, postBody); +>>>>>>> 2d01b3e (Create encryption folder and move files) }; try { diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index a698fcc..15576aa 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -37,12 +37,17 @@ import { getAssetInfo, getPublicKey, transferAsset, +<<<<<<< HEAD } from '../background'; import { getAllUserNames, getNameInfo, uint8ArrayToObject, } from '../backgroundFunctions/encryption'; +======= +} from '../background/background.ts'; +import { getNameInfo, uint8ArrayToObject } from '../encryption/encryption.ts'; +>>>>>>> 2d01b3e (Create encryption folder and move files) import { showSaveFilePicker } from '../hooks/useQortalMessageListener'; import { getPublishesFromAdminsAdminSpace } from '../components/Chat/AdminSpaceInner'; import { extractComponents } from '../components/Chat/MessageDisplay'; @@ -53,9 +58,9 @@ import { validateSecretKey, } from '../components/Group/Group'; import { QORT_DECIMALS } from '../constants/constants'; -import Base58 from '../deps/Base58'; -import ed2curve from '../deps/ed2curve'; -import nacl from '../deps/nacl-fast'; +import Base58 from '../encryption/Base58.ts'; +import ed2curve from '../encryption/ed2curve.ts'; +import nacl from '../encryption/nacl-fast.ts'; import { base64ToUint8Array, createSymmetricKeyAndNonce, @@ -74,7 +79,7 @@ import { getPermission, isRunningGateway, setPermission, -} from '../qortalRequests'; +} from './qortalRequests.ts'; import TradeBotCreateRequest from '../transactions/TradeBotCreateRequest'; import DeleteTradeOffer from '../transactions/TradeBotDeleteRequest'; import signTradeBotTransaction from '../transactions/signTradeBotTransaction'; diff --git a/src/qortalRequests.ts b/src/qortalRequests/qortalRequests.ts similarity index 99% rename from src/qortalRequests.ts rename to src/qortalRequests/qortalRequests.ts index 08a7c4f..9681790 100644 --- a/src/qortalRequests.ts +++ b/src/qortalRequests/qortalRequests.ts @@ -1,5 +1,5 @@ -import { gateways, getApiKeyFromStorage } from './background'; -import { listOfAllQortalRequests } from './hooks/useQortalMessageListener'; +import { gateways, getApiKeyFromStorage } from '../background/background.ts'; +import { listOfAllQortalRequests } from '../hooks/useQortalMessageListener.tsx'; import { addForeignServer, addGroupAdminRequest, @@ -63,9 +63,9 @@ import { signForeignFees, multiPaymentWithPrivateData, transferAssetRequest, -} from './qortalRequests/get'; -import { getData, storeData } from './utils/chromeStorage'; -import { executeEvent } from './utils/events'; +} from './get.ts'; +import { getData, storeData } from '../utils/chromeStorage.ts'; +import { executeEvent } from '../utils/events.ts'; function getLocalStorage(key) { return getData(key).catch((error) => { diff --git a/src/transactions/ChatBase.ts b/src/transactions/ChatBase.ts index c88d634..17cb509 100644 --- a/src/transactions/ChatBase.ts +++ b/src/transactions/ChatBase.ts @@ -1,8 +1,8 @@ // @ts-nocheck import { QORT_DECIMALS, TX_TYPES } from '../constants/constants'; -import nacl from '../deps/nacl-fast'; -import Base58 from '../deps/Base58'; +import nacl from '../encryption/nacl-fast'; +import Base58 from '../encryption/Base58'; import utils from '../utils/utils'; export default class ChatBase { static get utils() { diff --git a/src/transactions/ChatTransaction.ts b/src/transactions/ChatTransaction.ts index fc67531..7abf040 100644 --- a/src/transactions/ChatTransaction.ts +++ b/src/transactions/ChatTransaction.ts @@ -1,8 +1,8 @@ // @ts-nocheck import ChatBase from './ChatBase'; -import nacl from '../deps/nacl-fast'; -import ed2curve from '../deps/ed2curve'; +import nacl from '../encryption/nacl-fast'; +import ed2curve from '../encryption/ed2curve'; import { Sha256 } from 'asmcrypto.js'; import { CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP } from '../constants/constants'; export default class ChatTransaction extends ChatBase { diff --git a/src/transactions/RemoveRewardShareTransaction.ts b/src/transactions/RemoveRewardShareTransaction.ts index c91d389..078c8ed 100644 --- a/src/transactions/RemoveRewardShareTransaction.ts +++ b/src/transactions/RemoveRewardShareTransaction.ts @@ -1,7 +1,7 @@ // @ts-nocheck import { DYNAMIC_FEE_TIMESTAMP } from '../constants/constants'; -import Base58 from '../deps/Base58'; +import Base58 from '../encryption/Base58'; import publicKeyToAddress from '../utils/generateWallet/publicKeyToAddress'; import TransactionBase from './TransactionBase'; export default class RemoveRewardShareTransaction extends TransactionBase { diff --git a/src/transactions/RewardShareTransaction.ts b/src/transactions/RewardShareTransaction.ts index 67131b3..661f7f8 100644 --- a/src/transactions/RewardShareTransaction.ts +++ b/src/transactions/RewardShareTransaction.ts @@ -2,8 +2,8 @@ import TransactionBase from './TransactionBase'; import { Sha256 } from 'asmcrypto.js'; -import nacl from '../deps/nacl-fast'; -import ed2curve from '../deps/ed2curve'; +import nacl from '../encryption/nacl-fast'; +import ed2curve from '../encryption/ed2curve'; import { DYNAMIC_FEE_TIMESTAMP } from '../constants/constants'; import publicKeyToAddress from '../utils/generateWallet/publicKeyToAddress'; export default class RewardShareTransaction extends TransactionBase { diff --git a/src/transactions/TransactionBase.ts b/src/transactions/TransactionBase.ts index 055a08b..f4abf0f 100644 --- a/src/transactions/TransactionBase.ts +++ b/src/transactions/TransactionBase.ts @@ -1,7 +1,7 @@ // @ts-nocheck -import nacl from '../deps/nacl-fast'; -import Base58 from '../deps/Base58'; +import nacl from '../encryption/nacl-fast.js'; +import Base58 from '../encryption/Base58.js'; import utils from '../utils/utils'; import { QORT_DECIMALS, TX_TYPES } from '../constants/constants.js'; export default class TransactionBase { diff --git a/src/transactions/signChat.ts b/src/transactions/signChat.ts index 5995128..bdc3cb9 100644 --- a/src/transactions/signChat.ts +++ b/src/transactions/signChat.ts @@ -1,6 +1,6 @@ // @ts-nocheck -import nacl from '../deps/nacl-fast'; +import nacl from '../encryption/nacl-fast'; import utils from '../utils/utils'; export const signChat = (chatBytes, nonce, keyPair) => { diff --git a/src/transactions/signTradeBotTransaction.ts b/src/transactions/signTradeBotTransaction.ts index 31b7430..fc0e0e5 100644 --- a/src/transactions/signTradeBotTransaction.ts +++ b/src/transactions/signTradeBotTransaction.ts @@ -1,7 +1,7 @@ // @ts-nocheck -import nacl from '../deps/nacl-fast'; -import Base58 from '../deps/Base58'; +import nacl from '../encryption/nacl-fast'; +import Base58 from '../encryption/Base58'; import utils from '../utils/utils'; const signTradeBotTransaction = async (unsignedTxn, keyPair) => { diff --git a/src/utils/decryptChatMessage.ts b/src/utils/decryptChatMessage.ts index 39085e8..d58009f 100644 --- a/src/utils/decryptChatMessage.ts +++ b/src/utils/decryptChatMessage.ts @@ -1,8 +1,8 @@ // @ts-nocheck -import Base58 from '../deps/Base58'; -import ed2curve from '../deps/ed2curve'; -import nacl from '../deps/nacl-fast'; +import Base58 from '../encryption/Base58'; +import ed2curve from '../encryption/ed2curve'; +import nacl from '../encryption/nacl-fast'; import { Sha256 } from 'asmcrypto.js'; export const decryptChatMessage = ( diff --git a/src/utils/decryptWallet.ts b/src/utils/decryptWallet.ts index bf27e8a..3fa002a 100644 --- a/src/utils/decryptWallet.ts +++ b/src/utils/decryptWallet.ts @@ -1,9 +1,9 @@ // @ts-nocheck import { crypto } from '../constants/decryptWallet'; -import Base58 from '../deps/Base58'; +import Base58 from '../encryption/Base58'; import { AES_CBC, HmacSha512 } from 'asmcrypto.js'; -import { doInitWorkers, kdf } from '../deps/kdf'; +import { doInitWorkers, kdf } from '../encryption/kdf'; import i18n from 'i18next'; export const decryptStoredWallet = async (password, wallet) => { diff --git a/src/utils/generateWallet/generateWallet.ts b/src/utils/generateWallet/generateWallet.ts index ee3a683..977f903 100644 --- a/src/utils/generateWallet/generateWallet.ts +++ b/src/utils/generateWallet/generateWallet.ts @@ -1,7 +1,7 @@ // @ts-nocheck import { crypto, walletVersion } from '../../constants/decryptWallet'; -import { doInitWorkers, kdf } from '../../deps/kdf'; +import { doInitWorkers, kdf } from '../../encryption/kdf'; import PhraseWallet from './phrase-wallet'; import * as WORDLISTS from './wordlists'; import FileSaver from 'file-saver'; diff --git a/src/utils/generateWallet/phrase-wallet.ts b/src/utils/generateWallet/phrase-wallet.ts index 0f21cba..3cd9ed1 100644 --- a/src/utils/generateWallet/phrase-wallet.ts +++ b/src/utils/generateWallet/phrase-wallet.ts @@ -3,208 +3,206 @@ Copyright 2017-2018 @ irontiga and vbcs (original developer) */ -import Base58 from '../../deps/Base58' -import {Sha256, Sha512} from 'asmcrypto.js' -import nacl from '../../deps/nacl-fast' -import utils from '../../utils/utils' +import Base58 from '../../encryption/Base58.js'; +import { Sha256, Sha512 } from 'asmcrypto.js'; +import nacl from '../../encryption/nacl-fast.js'; +import utils from '../../utils/utils'; -import {generateSaveWalletData} from './storeWallet.js' +import { generateSaveWalletData } from './storeWallet.js'; -import publicKeyToAddress from './publicKeyToAddress' -import AltcoinHDWallet from "../../deps/AltcoinHDWallet" +import publicKeyToAddress from './publicKeyToAddress'; +import AltcoinHDWallet from '../../encryption/AltcoinHDWallet.js'; export default class PhraseWallet { - constructor(seed, walletVersion) { + constructor(seed, walletVersion) { + this._walletVersion = walletVersion || 2; + this.seed = seed; - this._walletVersion = walletVersion || 2 - this.seed = seed + this.savedSeedData = {}; + this.hasBeenSaved = false; + } - this.savedSeedData = {} - this.hasBeenSaved = false - } + set seed(seed) { + this._byteSeed = seed; + this._base58Seed = Base58.encode(seed); - set seed(seed) { - this._byteSeed = seed - this._base58Seed = Base58.encode(seed) + this._addresses = []; - this._addresses = [] + this.genAddress(0); + } - this.genAddress(0) - } + getAddress(nonce) { + return this._addresses[nonce]; + } - getAddress(nonce) { - return this._addresses[nonce] - } + get addresses() { + return this._addresses; + } - get addresses() { - return this._addresses - } + get addressIDs() { + return this._addresses.map((addr) => { + return addr.address; + }); + } - get addressIDs() { - return this._addresses.map(addr => { - return addr.address - }) - } + get seed() { + return this._byteSeed; + } - get seed() { - return this._byteSeed - } + addressExists(nonce) { + return this._addresses[nonce] != undefined; + } - addressExists(nonce) { - return this._addresses[nonce] != undefined - } + _genAddressSeed(seed) { + let newSeed = new Sha512().process(seed).finish().result; + newSeed = new Sha512() + .process(utils.appendBuffer(newSeed, seed)) + .finish().result; + return newSeed; + } - _genAddressSeed(seed) { - let newSeed = new Sha512().process(seed).finish().result - newSeed = new Sha512().process(utils.appendBuffer(newSeed, seed)).finish().result - return newSeed - } + genAddress(nonce) { + if (nonce >= this._addresses.length) { + this._addresses.length = nonce + 1; + } - genAddress(nonce) { - if (nonce >= this._addresses.length) { - this._addresses.length = nonce + 1 - } + if (this.addressExists(nonce)) { + return this.addresses[nonce]; + } - if (this.addressExists(nonce)) { - return this.addresses[nonce] - } + const nonceBytes = utils.int32ToBytes(nonce); - const nonceBytes = utils.int32ToBytes(nonce) + let addrSeed = new Uint8Array(); + addrSeed = utils.appendBuffer(addrSeed, nonceBytes); + addrSeed = utils.appendBuffer(addrSeed, this._byteSeed); + addrSeed = utils.appendBuffer(addrSeed, nonceBytes); - let addrSeed = new Uint8Array() - addrSeed = utils.appendBuffer(addrSeed, nonceBytes) - addrSeed = utils.appendBuffer(addrSeed, this._byteSeed) - addrSeed = utils.appendBuffer(addrSeed, nonceBytes) + if (this._walletVersion == 1) { + addrSeed = new Sha256() + .process(new Sha256().process(addrSeed).finish().result) + .finish().result; - if (this._walletVersion == 1) { - addrSeed = new Sha256().process( - new Sha256() - .process(addrSeed) - .finish() - .result - ).finish().result + addrSeed = this._byteSeed; + } else { + addrSeed = this._genAddressSeed(addrSeed).slice(0, 32); + } - addrSeed = this._byteSeed - } else { - addrSeed = this._genAddressSeed(addrSeed).slice(0, 32) - } + const addrKeyPair = nacl.sign.keyPair.fromSeed(new Uint8Array(addrSeed)); - const addrKeyPair = nacl.sign.keyPair.fromSeed(new Uint8Array(addrSeed)); + const address = publicKeyToAddress(addrKeyPair.publicKey); + const qoraAddress = publicKeyToAddress(addrKeyPair.publicKey, true); - const address = publicKeyToAddress(addrKeyPair.publicKey); - const qoraAddress = publicKeyToAddress(addrKeyPair.publicKey, true); + // Create Bitcoin HD Wallet + const btcSeed = [...addrSeed]; + const btcWallet = new AltcoinHDWallet({ + mainnet: { + private: 0x0488ade4, + public: 0x0488b21e, + prefix: 0, + }, + testnet: { + private: 0x04358394, + public: 0x043587cf, + prefix: 0x6f, + }, + }).createWallet(new Uint8Array(btcSeed), false); - // Create Bitcoin HD Wallet - const btcSeed = [...addrSeed]; - const btcWallet = new AltcoinHDWallet({ - mainnet: { - private: 0x0488ADE4, - public: 0x0488B21E, - prefix: 0 - }, - testnet: { - private: 0x04358394, - public: 0x043587CF, - prefix: 0x6F - } - }).createWallet(new Uint8Array(btcSeed), false); + // Create Litecoin HD Wallet + const ltcSeed = [...addrSeed]; + const ltcWallet = new AltcoinHDWallet({ + mainnet: { + private: 0x0488ade4, + public: 0x0488b21e, + prefix: 0x30, + }, + testnet: { + private: 0x04358394, + public: 0x043587cf, + prefix: 0x6f, + }, + }).createWallet(new Uint8Array(ltcSeed), false, 'LTC'); - // Create Litecoin HD Wallet - const ltcSeed = [...addrSeed]; - const ltcWallet = new AltcoinHDWallet({ - mainnet: { - private: 0x0488ADE4, - public: 0x0488B21E, - prefix: 0x30 - }, - testnet: { - private: 0x04358394, - public: 0x043587CF, - prefix: 0x6F - } - }).createWallet(new Uint8Array(ltcSeed), false, 'LTC'); + // Create Dogecoin HD Wallet + const dogeSeed = [...addrSeed]; + const dogeWallet = new AltcoinHDWallet({ + mainnet: { + private: 0x02fac398, + public: 0x02facafd, + prefix: 0x1e, + }, + testnet: { + private: 0x04358394, + public: 0x043587cf, + prefix: 0x71, + }, + }).createWallet(new Uint8Array(dogeSeed), false, 'DOGE'); - // Create Dogecoin HD Wallet - const dogeSeed = [...addrSeed]; - const dogeWallet = new AltcoinHDWallet({ - mainnet: { - private: 0x02FAC398, - public: 0x02FACAFD, - prefix: 0x1E - }, - testnet: { - private: 0x04358394, - public: 0x043587CF, - prefix: 0x71 - } - }).createWallet(new Uint8Array(dogeSeed), false, 'DOGE'); + // Create Digibyte HD Wallet + const dgbSeed = [...addrSeed]; + const dgbWallet = new AltcoinHDWallet({ + mainnet: { + private: 0x0488ade4, + public: 0x0488b21e, + prefix: 0x1e, + }, + testnet: { + private: 0x04358394, + public: 0x043587cf, + prefix: 0x7e, + }, + }).createWallet(new Uint8Array(dgbSeed), false, 'DGB'); - // Create Digibyte HD Wallet - const dgbSeed = [...addrSeed]; - const dgbWallet = new AltcoinHDWallet({ - mainnet: { - private: 0x0488ADE4, - public: 0x0488B21E, - prefix: 0x1E - }, - testnet: { - private: 0x04358394, - public: 0x043587CF, - prefix: 0x7E - } - }).createWallet(new Uint8Array(dgbSeed), false, 'DGB'); + // Create Ravencoin HD Wallet + const rvnSeed = [...addrSeed]; + const rvnWallet = new AltcoinHDWallet({ + mainnet: { + private: 0x0488ade4, + public: 0x0488b21e, + prefix: 0x3c, + }, + testnet: { + private: 0x04358394, + public: 0x043587cf, + prefix: 0x6f, + }, + }).createWallet(new Uint8Array(rvnSeed), false, 'RVN'); - // Create Ravencoin HD Wallet - const rvnSeed = [...addrSeed]; - const rvnWallet = new AltcoinHDWallet({ - mainnet: { - private: 0x0488ADE4, - public: 0x0488B21E, - prefix: 0x3C - }, - testnet: { - private: 0x04358394, - public: 0x043587CF, - prefix: 0x6F - } - }).createWallet(new Uint8Array(rvnSeed), false, 'RVN'); + // Create Pirate Chain HD Wallet + const arrrSeed = [...addrSeed]; + const arrrWallet = new AltcoinHDWallet({ + mainnet: { + private: 0x0488ade4, + public: 0x0488b21e, + prefix: [0x16, 0x9a], + }, + testnet: { + private: 0x04358394, + public: 0x043587cf, + prefix: [0x14, 0x51], + }, + }).createWallet(new Uint8Array(arrrSeed), false, 'ARRR'); - // Create Pirate Chain HD Wallet - const arrrSeed = [...addrSeed]; - const arrrWallet = new AltcoinHDWallet({ - mainnet: { - private: 0x0488ADE4, - public: 0x0488B21E, - prefix: [0x16, 0x9A] - }, - testnet: { - private: 0x04358394, - public: 0x043587CF, - prefix: [0x14, 0x51] - } - }).createWallet(new Uint8Array(arrrSeed), false, 'ARRR'); + this._addresses[nonce] = { + address, + btcWallet, + ltcWallet, + dogeWallet, + dgbWallet, + rvnWallet, + arrrWallet, + qoraAddress, + keyPair: { + publicKey: addrKeyPair.publicKey, + privateKey: addrKeyPair.secretKey, + }, + base58PublicKey: Base58.encode(addrKeyPair.publicKey), + seed: addrSeed, + nonce: nonce, + }; + return this._addresses[nonce]; + } - this._addresses[nonce] = { - address, - btcWallet, - ltcWallet, - dogeWallet, - dgbWallet, - rvnWallet, - arrrWallet, - qoraAddress, - keyPair: { - publicKey: addrKeyPair.publicKey, - privateKey: addrKeyPair.secretKey - }, - base58PublicKey: Base58.encode(addrKeyPair.publicKey), - seed: addrSeed, - nonce: nonce - } - return this._addresses[nonce] - } - - generateSaveWalletData(...args) { - return generateSaveWalletData(this, ...args) - } + generateSaveWalletData(...args) { + return generateSaveWalletData(this, ...args); + } } diff --git a/src/utils/generateWallet/publicKeyToAddress.ts b/src/utils/generateWallet/publicKeyToAddress.ts index ef91420..dba25a1 100644 --- a/src/utils/generateWallet/publicKeyToAddress.ts +++ b/src/utils/generateWallet/publicKeyToAddress.ts @@ -1,7 +1,7 @@ // @ts-nocheck -import Base58 from '../../deps/Base58'; -import BROKEN_RIPEMD160 from '../../deps/broken-ripemd160'; -import RIPEMD160 from '../../deps/ripemd160'; +import Base58 from '../../encryption/Base58.js'; +import BROKEN_RIPEMD160 from '../../encryption/broken-ripemd160.js'; +import RIPEMD160 from '../../encryption/ripemd160.js'; import utils from '../../utils/utils'; import { Buffer } from 'buffer'; import { Sha256 } from 'asmcrypto.js'; diff --git a/src/utils/generateWallet/storeWallet.ts b/src/utils/generateWallet/storeWallet.ts index 458543f..c90b549 100644 --- a/src/utils/generateWallet/storeWallet.ts +++ b/src/utils/generateWallet/storeWallet.ts @@ -1,8 +1,8 @@ // @ts-nocheck import { AES_CBC, HmacSha512 } from 'asmcrypto.js'; -import Base58 from '../../deps/Base58'; -import { doInitWorkers, kdf } from '../../deps/kdf.js'; +import Base58 from '../../encryption/Base58.js'; +import { doInitWorkers, kdf } from '../../encryption/kdf.js'; import { crypto as cryptoVals } from '../../constants/decryptWallet.js'; const getRandomValues = crypto diff --git a/src/utils/validateAddress.ts b/src/utils/validateAddress.ts index 4238311..aa01af1 100644 --- a/src/utils/validateAddress.ts +++ b/src/utils/validateAddress.ts @@ -1,6 +1,6 @@ // @ts-nocheck -import Base58 from '../deps/Base58'; +import Base58 from '../encryption/Base58'; export const validateAddress = (address) => { let isAddress = false; From f6a89d6d1faf7043c0e1181d65a9e6a2416d231d Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 11:55:40 +0200 Subject: [PATCH 468/717] Rename file (typo) --- src/background/background.ts | 2 +- src/encryption/encryption.ts | 2 +- src/qdn/publish/{pubish.ts => publish.ts} | 2 +- src/qortalRequests/get.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/qdn/publish/{pubish.ts => publish.ts} (99%) diff --git a/src/background/background.ts b/src/background/background.ts index ec55028..075abea 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -11,7 +11,7 @@ import { objectToBase64, } from '../qdn/encryption/group-encryption'; import ChatComputePowWorker from '../chatComputePow.worker.js?worker'; -import { reusableGet } from '../qdn/publish/pubish'; +import { reusableGet } from '../qdn/publish/publish.ts'; import { signChat } from '../transactions/signChat'; import { createTransaction } from '../transactions/transactions'; import { decryptChatMessage } from '../utils/decryptChatMessage'; diff --git a/src/encryption/encryption.ts b/src/encryption/encryption.ts index 97f6d75..d8f9d25 100644 --- a/src/encryption/encryption.ts +++ b/src/encryption/encryption.ts @@ -6,7 +6,7 @@ import { encryptDataGroup, objectToBase64, } from '../qdn/encryption/group-encryption.ts'; -import { publishData } from '../qdn/publish/pubish.ts'; +import { publishData } from '../qdn/publish/publish.ts'; import { getData } from '../utils/chromeStorage.ts'; import { RequestQueueWithPromise } from '../utils/queue/queue.ts'; diff --git a/src/qdn/publish/pubish.ts b/src/qdn/publish/publish.ts similarity index 99% rename from src/qdn/publish/pubish.ts rename to src/qdn/publish/publish.ts index d49791b..f1f75ad 100644 --- a/src/qdn/publish/pubish.ts +++ b/src/qdn/publish/publish.ts @@ -135,7 +135,7 @@ export const publishData = async ({ keyPair ) => { if (!arbitraryBytesBase58) { - throw new Error('ArbitraryBytesBase58 not defined'); + throw new Error('ArbitraryBytesBase58 not defined'); // TODO translate } if (!keyPair) { diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index 15576aa..7957dc4 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -74,7 +74,7 @@ import { uint8ArrayStartsWith, uint8ArrayToBase64, } from '../qdn/encryption/group-encryption'; -import { publishData } from '../qdn/publish/pubish'; +import { publishData } from '../qdn/publish/publish.ts'; import { getPermission, isRunningGateway, From c151a4d2249b73ac1abe6e32a349d10f89a76ef8 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 11:59:25 +0200 Subject: [PATCH 469/717] Delete audio and image files (unused) --- public/appsBg.svg | 9 --------- public/msg-not1.wav | Bin 27712 -> 0 bytes public/vite.svg | 1 - 3 files changed, 10 deletions(-) delete mode 100644 public/appsBg.svg delete mode 100644 public/msg-not1.wav delete mode 100644 public/vite.svg diff --git a/public/appsBg.svg b/public/appsBg.svg deleted file mode 100644 index 9775d89..0000000 --- a/public/appsBg.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/public/msg-not1.wav b/public/msg-not1.wav deleted file mode 100644 index 475c2100cf3ab731bb890a9f83d583cbf33d3a02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27712 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

    g&=ve&U*g@HwZ`Caq6e)cHz7-K)q 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 Ytu@+R+{@*3otInL+T-(HUu}DqFHwX?ZvX%Q diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 9626caca71c99704b6411f3e7feca089a9563360 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 12:05:23 +0200 Subject: [PATCH 470/717] Remove empty function clearAllNotifications --- src/background/background-cases.ts | 27 --------------------- src/background/background.ts | 21 +++-------------- src/encryption/encryption.ts | 38 +----------------------------- 3 files changed, 4 insertions(+), 82 deletions(-) diff --git a/src/background/background-cases.ts b/src/background/background-cases.ts index f6c0158..c1aa7df 100644 --- a/src/background/background-cases.ts +++ b/src/background/background-cases.ts @@ -11,7 +11,6 @@ import { checkLocalFunc, checkNewMessages, checkThreads, - clearAllNotifications, createEndpoint, createGroup, decryptDirectFunc, @@ -1142,32 +1141,6 @@ export async function getEnteredQmailTimestampCase(request, event) { } } -export async function clearAllNotificationsCase(request, event) { - try { - await clearAllNotifications(); - - event.source.postMessage( - { - requestId: request.requestId, - action: 'clearAllNotifications', - payload: true, - type: 'backgroundMessageResponse', - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: 'clearAllNotifications', - error: error?.message, - type: 'backgroundMessageResponse', - }, - event.origin - ); - } -} - export async function setGroupDataCase(request, event) { try { const { groupId, secretKeyData, secretKeyResource, admins } = diff --git a/src/background/background.ts b/src/background/background.ts index 075abea..90b2f2e 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -34,7 +34,6 @@ import { cancelBanCase, cancelInvitationToGroupCase, checkLocalCase, - clearAllNotificationsCase, createGroupCase, createPollCase, createRewardShareCase, @@ -303,6 +302,7 @@ export const createEndpoint = async (endpoint, customApi?: string) => { }; export const walletVersion = 2; + // List of your API endpoints const apiEndpoints = [ 'https://api.qortal.org', @@ -436,10 +436,6 @@ export async function performPowTask(chatBytes, difficulty) { }); } -function playNotificationSound() { - // chrome.runtime.sendMessage({ action: "PLAY_NOTIFICATION_SOUND" }); -} - const handleNotificationDirect = async (directs) => { let isFocused; const wallet = await getSaveWallet(); @@ -603,7 +599,7 @@ export function updateThreadActivity({ threads = JSON.parse(storedData); } - let lastResetTime = threads.lastResetTime || 0; + const lastResetTime = threads.lastResetTime || 0; // Check if a week has passed since the last reset if (currentTime - lastResetTime > ONE_WEEK_IN_MS) { @@ -653,7 +649,7 @@ export function updateThreadActivity({ const handleNotification = async (groups) => { const wallet = await getSaveWallet(); const address = wallet.address0; - let isDisableNotifications = + const isDisableNotifications = (await getUserSettings({ key: 'disable-push-notifications' })) || false; let mutedGroups = (await getUserSettings({ key: 'mutedGroups' })) || []; @@ -678,7 +674,6 @@ const handleNotification = async (groups) => { const newActiveChats = data; const oldActiveChats = await getChatHeads(); - let results = []; let newestLatestTimestamp = null; let oldestLatestTimestamp = null; // Find the latest timestamp from newActiveChats @@ -878,13 +873,6 @@ export async function storeWallets(wallets) { }); } -export async function clearAllNotifications() { - // const notifications = await chrome.notifications.getAll(); - // for (const notificationId of Object.keys(notifications)) { - // await chrome.notifications.clear(notificationId); - // } -} - export async function getUserInfo() { const wallet = await getSaveWallet(); const address = wallet.address0; @@ -3233,9 +3221,6 @@ function setupMessageListener() { case 'addGroupNotificationTimestamp': addGroupNotificationTimestampCase(request, event); break; - case 'clearAllNotifications': - clearAllNotificationsCase(request, event); - break; case 'setGroupData': setGroupDataCase(request, event); break; diff --git a/src/encryption/encryption.ts b/src/encryption/encryption.ts index d8f9d25..dae1709 100644 --- a/src/encryption/encryption.ts +++ b/src/encryption/encryption.ts @@ -12,49 +12,13 @@ import { RequestQueueWithPromise } from '../utils/queue/queue.ts'; export const requestQueueGetPublicKeys = new RequestQueueWithPromise(10); -const apiEndpoints = [ - 'https://api.qortal.org', - 'https://api2.qortal.org', - 'https://appnode.qortal.org', - 'https://apinode.qortalnodes.live', - 'https://apinode1.qortalnodes.live', - 'https://apinode2.qortalnodes.live', - 'https://apinode3.qortalnodes.live', - 'https://apinode4.qortalnodes.live', -]; - -async function findUsableApi() { - for (const endpoint of apiEndpoints) { - try { - const response = await fetch(`${endpoint}/admin/status`); - if (!response.ok) throw new Error('Failed to fetch'); - - const data = await response.json(); - if (data.isSynchronizing === false && data.syncPercent === 100) { - console.log(`Usable API found: ${endpoint}`); - return endpoint; - } else { - console.log(`API not ready: ${endpoint}`); - } - } catch (error) { - console.error(`Error checking API ${endpoint}:`, error); - } - } - - throw new Error( - i18n.t('question:message.error.no_api_found', { - postProcess: 'capitalizeFirstChar', - }) - ); -} - async function getSaveWallet() { const res = await getData('walletInfo').catch(() => null); if (res) { return res; } else { - throw new Error('No wallet saved'); + throw new Error('No wallet saved'); // TODO translate } } From e8a09bd805b17e3073adf4b4497ca8fd4ab3032c Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 12:09:50 +0200 Subject: [PATCH 471/717] Rename folder and file --- src/App.tsx | 2 +- src/background/background-cases.ts | 2 +- src/background/background.ts | 2 +- src/components/Save/Save.tsx | 2 +- src/hooks/useQortalMessageListener.tsx | 2 +- src/qortal/get.ts | 7005 +++++++++++++++++ .../qortal-requests.ts} | 0 src/qortalRequests/get.ts | 41 +- 8 files changed, 7026 insertions(+), 30 deletions(-) create mode 100644 src/qortal/get.ts rename src/{qortalRequests/qortalRequests.ts => qortal/qortal-requests.ts} (100%) diff --git a/src/App.tsx b/src/App.tsx index fe721a7..4fd9057 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -124,7 +124,7 @@ import { Tutorials } from './components/Tutorials/Tutorials'; import { useHandleTutorials } from './hooks/useHandleTutorials.tsx'; import { useHandleUserInfo } from './hooks/useHandleUserInfo.tsx'; import { Minting } from './components/Minting/Minting'; -import { isRunningGateway } from './qortalRequests/qortalRequests.ts'; +import { isRunningGateway } from './qortal/qortal-requests.ts'; import { QMailStatus } from './components/QMailStatus'; import { GlobalActions } from './components/GlobalActions/GlobalActions'; import { useBlockedAddresses } from './hooks/useBlockUsers.tsx'; diff --git a/src/background/background-cases.ts b/src/background/background-cases.ts index c1aa7df..0392893 100644 --- a/src/background/background-cases.ts +++ b/src/background/background-cases.ts @@ -65,7 +65,7 @@ import { import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../constants/constants'; import Base58 from '../encryption/Base58.ts'; import { encryptSingle } from '../qdn/encryption/group-encryption'; -import { _createPoll, _voteOnPoll } from '../qortalRequests/get'; +import { _createPoll, _voteOnPoll } from '../qortal/get.ts'; import { createTransaction } from '../transactions/transactions'; import { getData, storeData } from '../utils/chromeStorage'; diff --git a/src/background/background.ts b/src/background/background.ts index 90b2f2e..2a7b158 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import '../qortalRequests/qortalRequests'; +import '../qortal/qortal-requests.ts'; import { isArray } from 'lodash'; import { uint8ArrayToObject } from '../encryption/encryption.ts'; import Base58 from '../encryption/Base58'; diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index e213174..cebccf8 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -26,7 +26,7 @@ import { IconWrapper } from '../Desktop/DesktopFooter'; import { Spacer } from '../../common/Spacer'; import { LoadingButton } from '@mui/lab'; import { saveToLocalStorage } from '../Apps/AppsNavBarDesktop'; -import { decryptData, encryptData } from '../../qortalRequests/get'; +import { decryptData, encryptData } from '../../qortal/get.ts'; import { saveFileToDiskGeneric } from '../../utils/generateWallet/generateWallet'; import { base64ToUint8Array, diff --git a/src/hooks/useQortalMessageListener.tsx b/src/hooks/useQortalMessageListener.tsx index c6c24da..29549dc 100644 --- a/src/hooks/useQortalMessageListener.tsx +++ b/src/hooks/useQortalMessageListener.tsx @@ -2,7 +2,7 @@ import { useCallback, useContext, useEffect, useState } from 'react'; import { executeEvent } from '../utils/events'; import { navigationControllerAtom } from '../atoms/global'; import { Filesystem, Directory } from '@capacitor/filesystem'; -import { saveFile } from '../qortalRequests/get'; +import { saveFile } from '../qortal/get'; import { mimeToExtensionMap } from '../utils/memeTypes'; import { QORTAL_APP_CONTEXT } from '../App'; import FileSaver from 'file-saver'; diff --git a/src/qortal/get.ts b/src/qortal/get.ts new file mode 100644 index 0000000..102a9bf --- /dev/null +++ b/src/qortal/get.ts @@ -0,0 +1,7005 @@ +import { Sha256 } from 'asmcrypto.js'; +import { + createEndpoint, + getBalanceInfo, + getFee, + getKeyPair, + getLastRef, + getSaveWallet, + processTransactionVersion2, + signChatFunc, + joinGroup as joinGroupFunc, + sendQortFee, + sendCoin as sendCoinFunc, + createBuyOrderTx, + performPowTask, + parseErrorResponse, + groupSecretkeys, + registerName, + updateName, + leaveGroup, + inviteToGroup, + getNameInfoForOthers, + kickFromGroup, + banFromGroup, + cancelBan, + makeAdmin, + removeAdmin, + cancelInvitationToGroup, + createGroup, + updateGroup, + sellName, + cancelSellName, + buyName, + getBaseApi, + getAssetBalanceInfo, + getNameOrAddress, + getAssetInfo, + getPublicKey, + transferAsset, +} from '../background/background.ts'; +import { getNameInfo, uint8ArrayToObject } from '../encryption/encryption.ts'; +import { showSaveFilePicker } from '../hooks/useQortalMessageListener.tsx'; +import { getPublishesFromAdminsAdminSpace } from '../components/Chat/AdminSpaceInner.tsx'; +import { extractComponents } from '../components/Chat/MessageDisplay.tsx'; +import { + decryptResource, + getGroupAdmins, + getPublishesFromAdmins, + validateSecretKey, +} from '../components/Group/Group.tsx'; +import { QORT_DECIMALS } from '../constants/constants.ts'; +import Base58 from '../encryption/Base58.ts'; +import ed2curve from '../encryption/ed2curve.ts'; +import nacl from '../encryption/nacl-fast.ts'; +import { + base64ToUint8Array, + createSymmetricKeyAndNonce, + decryptDeprecatedSingle, + decryptGroupDataQortalRequest, + decryptGroupEncryptionWithSharingKey, + decryptSingle, + encryptDataGroup, + encryptSingle, + objectToBase64, + uint8ArrayStartsWith, + uint8ArrayToBase64, +} from '../qdn/encryption/group-encryption.ts'; +import { publishData } from '../qdn/publish/publish.ts'; +import { + getPermission, + isRunningGateway, + setPermission, +} from './qortal-requests.ts'; +import TradeBotCreateRequest from '../transactions/TradeBotCreateRequest.ts'; +import DeleteTradeOffer from '../transactions/TradeBotDeleteRequest.ts'; +import signTradeBotTransaction from '../transactions/signTradeBotTransaction.ts'; +import { createTransaction } from '../transactions/transactions.ts'; +import { executeEvent } from '../utils/events.ts'; +import { fileToBase64 } from '../utils/fileReading/index.ts'; +import { mimeToExtensionMap } from '../utils/memeTypes.ts'; +import { RequestQueueWithPromise } from '../utils/queue/queue.ts'; +import utils from '../utils/utils.ts'; +import ShortUniqueId from 'short-unique-id'; +import { isValidBase64WithDecode } from '../utils/decode.ts'; +import i18n from 'i18next'; + +const uid = new ShortUniqueId({ length: 6 }); + +export const requestQueueGetAtAddresses = new RequestQueueWithPromise(10); + +const sellerForeignFee = { + LITECOIN: { + value: '~0.00005', + ticker: 'LTC', + }, + DOGECOIN: { + value: '~0.005', + ticker: 'DOGE', + }, + BITCOIN: { + value: '~0.0001', + ticker: 'BTC', + }, + DIGIBYTE: { + value: '~0.0005', + ticker: 'DGB', + }, + RAVENCOIN: { + value: '~0.006', + ticker: 'RVN', + }, + PIRATECHAIN: { + value: '~0.0002', + ticker: 'ARRR', + }, +}; + +const btcFeePerByte = 0.000001; +const ltcFeePerByte = 0.0000003; +const dogeFeePerByte = 0.00001; +const dgbFeePerByte = 0.0000001; +const rvnFeePerByte = 0.00001125; + +const MAX_RETRIES = 3; // Set max number of retries + +export async function retryTransaction( + fn, + args, + throwError, + retries = MAX_RETRIES +) { + let attempt = 0; + while (attempt < retries) { + try { + return await fn(...args); + } catch (error) { + console.error(`Attempt ${attempt + 1} failed: ${error.message}`); + attempt++; + if (attempt === retries) { + console.error( + i18n.t('question:message.generic.max_retry_transaction', { + postProcess: 'capitalizeFirstChar', + }) + ); + if (throwError) { + throw new Error( + error?.message || + i18n.t('question:message.error.process_transaction', { + postProcess: 'capitalizeFirstChar', + }) + ); + } else { + throw new Error( + error?.message || + i18n.t('question:message.error.process_transaction', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + await new Promise((res) => setTimeout(res, 10000)); + } + } +} + +function roundUpToDecimals(number, decimals = 8) { + const factor = Math.pow(10, decimals); // Create a factor based on the number of decimals + return Math.ceil(+number * factor) / factor; +} + +export const _createPoll = async ( + { pollName, pollDescription, options }, + isFromExtension, + skipPermission +) => { + const fee = await getFee('CREATE_POLL'); + let resPermission = {}; + if (!skipPermission) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:request_create_poll', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:poll', { + name: pollName, + postProcess: 'capitalizeFirstChar', + }), + text3: i18n.t('question:description', { + description: pollDescription, + postProcess: 'capitalizeFirstChar', + }), + text4: i18n.t('question:options', { + optionList: options?.join(', '), + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + } + + const { accepted = false } = resPermission; + + if (accepted || skipPermission) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + let lastRef = await getLastRef(); + + const tx = await createTransaction(8, keyPair, { + fee: fee.fee, + ownerAddress: address, + rPollName: pollName, + rPollDesc: pollDescription, + rOptions: options, + lastReference: lastRef, + }); + const signedBytes = Base58.encode(tx.signedBytes); + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error( + res?.message || + i18n.t('question:message.error.process_transaction', { + postProcess: 'capitalizeFirstChar', + }) + ); + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +const _deployAt = async ( + { name, description, tags, creationBytes, amount, assetId, atType }, + isFromExtension +) => { + const fee = await getFee('DEPLOY_AT'); + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:deploy_at', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:name', { + name: name, + postProcess: 'capitalizeFirstChar', + }), + text3: i18n.t('question:description', { + description: description, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + + const { accepted } = resPermission; + + if (accepted) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + const lastReference = await getLastRef(); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + const tx = await createTransaction(16, keyPair, { + fee: fee.fee, + rName: name, + rDescription: description, + rTags: tags, + rAmount: amount, + rAssetId: assetId, + rCreationBytes: creationBytes, + atType: atType, + lastReference: lastReference, + }); + + const signedBytes = Base58.encode(tx.signedBytes); + + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error( + res?.message || + i18n.t('question:message.error.process_transaction', { + postProcess: 'capitalizeFirstChar', + }) + ); + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const _voteOnPoll = async ( + { pollName, optionIndex, optionName }, + isFromExtension, + skipPermission +) => { + const fee = await getFee('VOTE_ON_POLL'); + let resPermission = {}; + + if (!skipPermission) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:request_vote_poll', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:poll', { + name: pollName, + postProcess: 'capitalizeFirstChar', + }), + text3: i18n.t('question:option', { + option: optionName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + } + + const { accepted = false } = resPermission; + + if (accepted || skipPermission) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + let lastRef = await getLastRef(); + + const tx = await createTransaction(9, keyPair, { + fee: fee.fee, + voterAddress: address, + rPollName: pollName, + rOptionIndex: optionIndex, + lastReference: lastRef, + }); + const signedBytes = Base58.encode(tx.signedBytes); + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error( + res?.message || + i18n.t('question:message.error.process_transaction', { + postProcess: 'capitalizeFirstChar', + }) + ); + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +// Map to store resolvers and rejectors by requestId +const fileRequestResolvers = new Map(); + +const handleFileMessage = (event) => { + const { action, requestId, result, error } = event.data; + + if ( + action === 'getFileFromIndexedDBResponse' && + fileRequestResolvers.has(requestId) + ) { + const { resolve, reject } = fileRequestResolvers.get(requestId); + fileRequestResolvers.delete(requestId); // Clean up after resolving + + if (result) { + resolve(result); + } else { + reject( + error || + i18n.t('question:message.error.retrieve_file', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } +}; + +window.addEventListener('message', handleFileMessage); + +function getFileFromContentScript(fileId) { + return new Promise((resolve, reject) => { + const requestId = `getFile_${fileId}_${Date.now()}`; + + fileRequestResolvers.set(requestId, { resolve, reject }); // Store resolvers by requestId + const targetOrigin = window.location.origin; + + // Send the request message + window.postMessage( + { action: 'getFileFromIndexedDB', fileId, requestId }, + targetOrigin + ); + + // Timeout to handle no response scenario + setTimeout(() => { + if (fileRequestResolvers.has(requestId)) { + fileRequestResolvers.get(requestId).reject( + i18n.t('question:message.error.timeout_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + fileRequestResolvers.delete(requestId); // Clean up on timeout + } + }, 10000); // 10-second timeout + }); +} + +const responseResolvers = new Map(); + +const handleMessage = (event) => { + const { action, requestId, result } = event.data; + + // Check if this is the expected response action and if we have a stored resolver + if ( + action === 'QORTAL_REQUEST_PERMISSION_RESPONSE' && + responseResolvers.has(requestId) + ) { + // Resolve the stored promise with the result + responseResolvers.get(requestId)(result || false); + responseResolvers.delete(requestId); // Clean up after resolving + } +}; + +window.addEventListener('message', handleMessage); + +async function getUserPermission(payload, isFromExtension) { + return new Promise((resolve) => { + const requestId = `qortalRequest_${Date.now()}`; + responseResolvers.set(requestId, resolve); // Store resolver by requestId + const targetOrigin = window.location.origin; + + // Send the request message + window.postMessage( + { + action: 'QORTAL_REQUEST_PERMISSION', + payload, + requestId, + isFromExtension, + }, + targetOrigin + ); + + // Optional timeout to handle no response scenario + setTimeout(() => { + if (responseResolvers.has(requestId)) { + responseResolvers.get(requestId)(false); // Resolve with `false` if no response + responseResolvers.delete(requestId); + } + }, 60000); // 30-second timeout + }); +} + +export const getUserAccount = async ({ + isFromExtension, + appInfo, + skipAuth, +}) => { + try { + const value = + (await getPermission(`qAPPAutoAuth-${appInfo?.name}`)) || false; + let skip = false; + if (value) { + skip = true; + } + if (skipAuth) { + skip = true; + } + let resPermission; + if (!skip) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.authenticate', { + postProcess: 'capitalizeFirstChar', + }), + checkbox1: { + value: false, + label: i18n.t('question:always_authenticate', { + postProcess: 'capitalizeFirstChar', + }), + }, + }, + isFromExtension + ); + } + + const { accepted = false, checkbox1 = false } = resPermission || {}; + if (resPermission) { + setPermission(`qAPPAutoAuth-${appInfo?.name}`, checkbox1); + } + if (accepted || skip) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + const publicKey = wallet.publicKey; + return { + address, + publicKey, + }; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } catch (error) { + throw new Error( + i18n.t('auth:message.error.fetch_user_account', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const encryptData = async (data, sender) => { + let data64 = data.data64 || data.base64; + let publicKeys = data.publicKeys || []; + if (data?.file || data?.blob) { + data64 = await fileToBase64(data?.file || data?.blob); + } + if (!data64) { + throw new Error( + i18n.t('question:message.generic.include_data_encrypt', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const privateKey = parsedData.privateKey; + const userPublicKey = parsedData.publicKey; + + const encryptDataResponse = encryptDataGroup({ + data64, + publicKeys: publicKeys, + privateKey, + userPublicKey, + }); + if (encryptDataResponse) { + return encryptDataResponse; + } else { + throw new Error( + i18n.t('question:message.error.encrypt', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const encryptQortalGroupData = async (data, sender) => { + let data64 = data?.data64 || data?.base64; + let groupId = data?.groupId; + let isAdmins = data?.isAdmins; + if (!groupId) { + throw new Error( + i18n.t('question:message.generic.provide_group_id', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + if (data?.file || data?.blob) { + data64 = await fileToBase64(data?.file || data?.blob); + } + if (!data64) { + throw new Error( + i18n.t('question:message.generic.include_data_encrypt', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + let secretKeyObject; + if (!isAdmins) { + if ( + groupSecretkeys[groupId] && + groupSecretkeys[groupId].secretKeyObject && + groupSecretkeys[groupId]?.timestamp && + Date.now() - groupSecretkeys[groupId]?.timestamp < 1200000 + ) { + secretKeyObject = groupSecretkeys[groupId].secretKeyObject; + } + + if (!secretKeyObject) { + const { names } = await getGroupAdmins(groupId); + + const publish = await getPublishesFromAdmins(names, groupId); + if (publish === false) + throw new Error( + i18n.t('question:message.error.no_group_key', { + postProcess: 'capitalizeFirstChar', + }) + ); + const url = await createEndpoint( + `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ + publish.identifier + }?encoding=base64&rebuild=true` + ); + + const res = await fetch(url); + const resData = await res.text(); + + const decryptedKey: any = await decryptResource(resData, true); + + const dataint8Array = base64ToUint8Array(decryptedKey.data); + const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); + + if (!validateSecretKey(decryptedKeyToObject)) + throw new Error( + i18n.t('auth:message.error.invalid_secret_key', { + postProcess: 'capitalizeFirstChar', + }) + ); + secretKeyObject = decryptedKeyToObject; + groupSecretkeys[groupId] = { + secretKeyObject, + timestamp: Date.now(), + }; + } + } else { + if ( + groupSecretkeys[`admins-${groupId}`] && + groupSecretkeys[`admins-${groupId}`].secretKeyObject && + groupSecretkeys[`admins-${groupId}`]?.timestamp && + Date.now() - groupSecretkeys[`admins-${groupId}`]?.timestamp < 1200000 + ) { + secretKeyObject = groupSecretkeys[`admins-${groupId}`].secretKeyObject; + } + + if (!secretKeyObject) { + const { names } = await getGroupAdmins(groupId); + + const publish = await getPublishesFromAdminsAdminSpace(names, groupId); + if (publish === false) + throw new Error( + i18n.t('question:message.error.no_group_key', { + postProcess: 'capitalizeFirstChar', + }) + ); + const url = await createEndpoint( + `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ + publish.identifier + }?encoding=base64&rebuild=true` + ); + + const res = await fetch(url); + const resData = await res.text(); + const decryptedKey: any = await decryptResource(resData, true); + const dataint8Array = base64ToUint8Array(decryptedKey.data); + const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); + + if (!validateSecretKey(decryptedKeyToObject)) + throw new Error( + i18n.t('auth:message.error.invalid_secret_key', { + postProcess: 'capitalizeFirstChar', + }) + ); + secretKeyObject = decryptedKeyToObject; + groupSecretkeys[`admins-${groupId}`] = { + secretKeyObject, + timestamp: Date.now(), + }; + } + } + + const resGroupEncryptedResource = encryptSingle({ + data64, + secretKeyObject: secretKeyObject, + }); + + if (resGroupEncryptedResource) { + return resGroupEncryptedResource; + } else { + throw new Error( + i18n.t('question:message.error.encrypt', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const decryptQortalGroupData = async (data, sender) => { + let data64 = data?.data64 || data?.base64; + let groupId = data?.groupId; + let isAdmins = data?.isAdmins; + if (!groupId) { + throw new Error( + i18n.t('question:message.generic.provide_group_id', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + if (!data64) { + throw new Error( + i18n.t('question:message.generic.include_data_encrypt', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + let secretKeyObject; + if (!isAdmins) { + if ( + groupSecretkeys[groupId] && + groupSecretkeys[groupId].secretKeyObject && + groupSecretkeys[groupId]?.timestamp && + Date.now() - groupSecretkeys[groupId]?.timestamp < 1200000 + ) { + secretKeyObject = groupSecretkeys[groupId].secretKeyObject; + } + if (!secretKeyObject) { + const { names } = await getGroupAdmins(groupId); + + const publish = await getPublishesFromAdmins(names, groupId); + if (publish === false) + throw new Error( + i18n.t('question:message.error.no_group_key', { + postProcess: 'capitalizeFirstChar', + }) + ); + const url = await createEndpoint( + `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ + publish.identifier + }?encoding=base64&rebuild=true` + ); + + const res = await fetch(url); + const resData = await res.text(); + const decryptedKey: any = await decryptResource(resData, true); + + const dataint8Array = base64ToUint8Array(decryptedKey.data); + const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); + if (!validateSecretKey(decryptedKeyToObject)) + throw new Error( + i18n.t('auth:message.error.invalid_secret_key', { + postProcess: 'capitalizeFirstChar', + }) + ); + secretKeyObject = decryptedKeyToObject; + groupSecretkeys[groupId] = { + secretKeyObject, + timestamp: Date.now(), + }; + } + } else { + if ( + groupSecretkeys[`admins-${groupId}`] && + groupSecretkeys[`admins-${groupId}`].secretKeyObject && + groupSecretkeys[`admins-${groupId}`]?.timestamp && + Date.now() - groupSecretkeys[`admins-${groupId}`]?.timestamp < 1200000 + ) { + secretKeyObject = groupSecretkeys[`admins-${groupId}`].secretKeyObject; + } + if (!secretKeyObject) { + const { names } = await getGroupAdmins(groupId); + + const publish = await getPublishesFromAdminsAdminSpace(names, groupId); + if (publish === false) + throw new Error( + i18n.t('question:message.error.no_group_key', { + postProcess: 'capitalizeFirstChar', + }) + ); + const url = await createEndpoint( + `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ + publish.identifier + }?encoding=base64&rebuild=true` + ); + + const res = await fetch(url); + const resData = await res.text(); + const decryptedKey: any = await decryptResource(resData, true); + + const dataint8Array = base64ToUint8Array(decryptedKey.data); + const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); + if (!validateSecretKey(decryptedKeyToObject)) + throw new Error( + i18n.t('auth:message.error.invalid_secret_key', { + postProcess: 'capitalizeFirstChar', + }) + ); + secretKeyObject = decryptedKeyToObject; + groupSecretkeys[`admins-${groupId}`] = { + secretKeyObject, + timestamp: Date.now(), + }; + } + } + + const resGroupDecryptResource = decryptSingle({ + data64, + secretKeyObject: secretKeyObject, + skipDecodeBase64: true, + }); + if (resGroupDecryptResource) { + return resGroupDecryptResource; + } else { + throw new Error( + i18n.t('question:message.error.encrypt', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const encryptDataWithSharingKey = async (data, sender) => { + let data64 = data?.data64 || data?.base64; + let publicKeys = data.publicKeys || []; + if (data?.file || data?.blob) { + data64 = await fileToBase64(data?.file || data?.blob); + } + if (!data64) { + throw new Error( + i18n.t('question:message.generic.include_data_encrypt', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const symmetricKey = createSymmetricKeyAndNonce(); + const dataObject = { + data: data64, + key: symmetricKey.messageKey, + }; + const dataObjectBase64 = await objectToBase64(dataObject); + + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const privateKey = parsedData.privateKey; + const userPublicKey = parsedData.publicKey; + + const encryptDataResponse = encryptDataGroup({ + data64: dataObjectBase64, + publicKeys: publicKeys, + privateKey, + userPublicKey, + customSymmetricKey: symmetricKey.messageKey, + }); + if (encryptDataResponse) { + return encryptDataResponse; + } else { + throw new Error( + i18n.t('question:message.error.encrypt', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const decryptDataWithSharingKey = async (data, sender) => { + const { encryptedData, key } = data; + + if (!encryptedData) { + throw new Error( + i18n.t('question:message.generic.include_data_decrypt', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + const decryptedData = await decryptGroupEncryptionWithSharingKey({ + data64EncryptedData: encryptedData, + key, + }); + const base64ToObject = JSON.parse(atob(decryptedData)); + + if (!base64ToObject.data) + throw new Error( + i18n.t('question:message.error.no_data_encrypted_resource', { + postProcess: 'capitalizeFirstChar', + }) + ); + return base64ToObject.data; +}; + +export const getHostedData = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const resPermission = await getUserPermission( + { + text1: i18n.t('question:message.error.submit_sell_order', { + postProcess: 'capitalizeFirstChar', + }), + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const limit = data?.limit ? data?.limit : 20; + const query = data?.query ? data?.query : ''; + const offset = data?.offset ? data?.offset : 0; + + let urlPath = `/arbitrary/hosted/resources/?limit=${limit}&offset=${offset}`; + if (query) { + urlPath = urlPath + `&query=${query}`; + } + + const url = await createEndpoint(urlPath); + const response = await fetch(url); + const dataResponse = await response.json(); + return dataResponse; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_list', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const deleteHostedData = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const requiredFields = ['hostedData']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.delete_hosts_resources', { + size: data?.hostedData?.length, + postProcess: 'capitalizeFirstChar', + }), + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const { hostedData } = data; + + for (const hostedDataItem of hostedData) { + try { + const url = await createEndpoint( + `/arbitrary/resource/${hostedDataItem.service}/${hostedDataItem.name}/${hostedDataItem.identifier}` + ); + await fetch(url, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (error) { + console.log(error); + } + } + + return true; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_delete_hosted_resources', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; +export const decryptData = async (data) => { + const { encryptedData, publicKey } = data; + + if (!encryptedData) { + throw new Error(`Missing fields: encryptedData`); + } + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8Array = base64ToUint8Array(encryptedData); + const startsWithQortalEncryptedData = uint8ArrayStartsWith( + uint8Array, + 'qortalEncryptedData' + ); + if (startsWithQortalEncryptedData) { + if (!publicKey) { + throw new Error(`Missing fields: publicKey`); + } + + const decryptedDataToBase64 = decryptDeprecatedSingle( + uint8Array, + publicKey, + uint8PrivateKey + ); + return decryptedDataToBase64; + } + const startsWithQortalGroupEncryptedData = uint8ArrayStartsWith( + uint8Array, + 'qortalGroupEncryptedData' + ); + if (startsWithQortalGroupEncryptedData) { + const decryptedData = decryptGroupDataQortalRequest( + encryptedData, + parsedData.privateKey + ); + const decryptedDataToBase64 = uint8ArrayToBase64(decryptedData); + return decryptedDataToBase64; + } + throw new Error( + i18n.t('question:message.error.encrypt', { + postProcess: 'capitalizeFirstChar', + }) + ); +}; + +export const getListItems = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const requiredFields = ['list_name']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const value = (await getPermission('qAPPAutoLists')) || false; + + let skip = false; + if (value) { + skip = true; + } + let resPermission; + let acceptedVar; + let checkbox1Var; + if (!skip) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.access_list', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: data.list_name, + checkbox1: { + value: value, + label: i18n.t('question:always_retrieve_list', { + postProcess: 'capitalizeFirstChar', + }), + }, + }, + isFromExtension + ); + const { accepted, checkbox1 } = resPermission; + acceptedVar = accepted; + checkbox1Var = checkbox1; + setPermission('qAPPAutoLists', checkbox1); + } + + if (acceptedVar || skip) { + const url = await createEndpoint(`/lists/${data.list_name}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_list', { + postProcess: 'capitalizeFirstChar', + }) + ); + + const list = await response.json(); + return list; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_share_list', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const addListItems = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const requiredFields = ['list_name', 'items']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const items = data.items; + const list_name = data.list_name; + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.all_item_list', { + name: list_name, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: items.join(', '), + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const url = await createEndpoint(`/lists/${list_name}`); + const body = { + items: items, + }; + const bodyToString = JSON.stringify(body); + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: bodyToString, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.add_to_list', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_add_list', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const deleteListItems = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const requiredFields = ['list_name']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + if (!data?.item && !data?.items) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: 'items', + postProcess: 'capitalizeFirstChar', + }) + ); + } + const item = data?.item; + const items = data?.items; + const list_name = data.list_name; + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.remove_from_list', { + name: list_name, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: items ? JSON.stringify(items) : item, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const url = await createEndpoint(`/lists/${list_name}`); + const body = { + items: items || [item], + }; + const bodyToString = JSON.stringify(body); + const response = await fetch(url, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + body: bodyToString, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.add_to_list', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_delete_from_list', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const publishQDNResource = async ( + data: any, + sender, + isFromExtension +) => { + const requiredFields = ['service']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + if (!data.file && !data.data64 && !data.base64) { + throw new Error( + i18n.t('question:message.error.no_data_file_submitted', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + // Use "default" if user hasn't specified an identifier + const service = data.service; + const appFee = data?.appFee ? +data.appFee : undefined; + const appFeeRecipient = data?.appFeeRecipient; + let hasAppFee = false; + if (appFee && appFee > 0 && appFeeRecipient) { + hasAppFee = true; + } + const registeredName = await getNameInfo(); + const name = registeredName; + if (!name) { + throw new Error( + i18n.t('question:message.error.user_qortal_name', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + let identifier = data.identifier; + let data64 = data.data64 || data.base64; + const filename = data.filename; + const title = data.title; + const description = data.description; + const category = data.category; + + const tags = data?.tags || []; + const result = {}; + + // Fill tags dynamically while maintaining backward compatibility + for (let i = 0; i < 5; i++) { + result[`tag${i + 1}`] = tags[i] || data[`tag${i + 1}`] || undefined; + } + + // Access tag1 to tag5 from result + const { tag1, tag2, tag3, tag4, tag5 } = result; + + if (data.identifier == null) { + identifier = 'default'; + } + if (data?.file || data?.blob) { + data64 = await fileToBase64(data?.file || data?.blob); + } + if ( + data.encrypt && + (!data.publicKeys || + (Array.isArray(data.publicKeys) && data.publicKeys.length === 0)) + ) { + throw new Error( + i18n.t('question:message.error.encryption_requires_public_key', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + if (data.encrypt) { + try { + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const privateKey = parsedData.privateKey; + const userPublicKey = parsedData.publicKey; + const encryptDataResponse = encryptDataGroup({ + data64, + publicKeys: data.publicKeys, + privateKey, + userPublicKey, + }); + if (encryptDataResponse) { + data64 = encryptDataResponse; + } + } catch (error) { + throw new Error( + error.message || + i18n.t('question:message.error.upload_encryption', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + + const fee = await getFee('ARBITRARY'); + + const handleDynamicValues = {}; + if (hasAppFee) { + const feePayment = await getFee('PAYMENT'); + + (handleDynamicValues['appFee'] = +appFee + +feePayment.fee), + (handleDynamicValues['checkbox1'] = { + value: true, + label: i18n.t('question:accept_app_fee', { + postProcess: 'capitalizeFirstChar', + }), + }); + } + if (!!data?.encrypt) { + handleDynamicValues['highlightedText'] = `isEncrypted: ${!!data.encrypt}`; + } + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.publish_qdn', { + postProcess: 'capitalizeFirstChar', + }), + text2: `service: ${service}`, + text3: `identifier: ${identifier || null}`, + fee: fee.fee, + ...handleDynamicValues, + }, + isFromExtension + ); + const { accepted, checkbox1 = false } = resPermission; + if (accepted) { + try { + const resPublish = await publishData({ + registeredName: encodeURIComponent(name), + file: data64, + service: service, + identifier: encodeURIComponent(identifier), + uploadType: 'file', + isBase64: true, + filename: filename, + title, + description, + category, + tag1, + tag2, + tag3, + tag4, + tag5, + apiVersion: 2, + withFee: true, + }); + if (resPublish?.signature && hasAppFee && checkbox1) { + sendCoinFunc( + { + amount: appFee, + receiver: appFeeRecipient, + }, + true + ); + } + return resPublish; + } catch (error) { + throw new Error(error?.message || 'Upload failed'); + } + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const checkArrrSyncStatus = async (seed) => { + const _url = await createEndpoint(`/crosschain/arrr/syncstatus`); + let tries = 0; // Track the number of attempts + + while (tries < 36) { + const response = await fetch(_url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: seed, + }); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res.indexOf('<') > -1 || res !== 'Synchronized') { + // Wait 2 seconds before trying again + await new Promise((resolve) => setTimeout(resolve, 2000)); + tries += 1; + } else { + // If the response doesn't meet the two conditions, exit the function + return; + } + } + + // If we exceed N tries, throw an error + throw new Error( + i18n.t('question:message.error.synchronization_attempts', { + quantity: 36, + postProcess: 'capitalizeFirstChar', + }) + ); +}; + +export const publishMultipleQDNResources = async ( + data: any, + sender, + isFromExtension +) => { + const requiredFields = ['resources']; + const missingFields: string[] = []; + + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const resources = data.resources; + if (!Array.isArray(resources)) { + throw new Error( + i18n.t('group:message.generic.invalid_data', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + if (resources.length === 0) { + throw new Error( + i18n.t('question:message.error.no_resources_publish', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + const encrypt = data?.encrypt; + + for (const resource of resources) { + const resourceEncrypt = encrypt && resource?.disableEncrypt !== true; + + if (!resourceEncrypt && resource?.service.endsWith('_PRIVATE')) { + const errorMsg = i18n.t('question:message.error.only_encrypted_data', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } else if (resourceEncrypt && !resource?.service.endsWith('_PRIVATE')) { + const errorMsg = i18n.t('question:message.error.use_private_service', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + } + + const fee = await getFee('ARBITRARY'); + const registeredName = await getNameInfo(); + const name = registeredName; + + if (!name) { + throw new Error( + i18n.t('question:message.error.registered_name', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + const appFee = data?.appFee ? +data.appFee : undefined; + const appFeeRecipient = data?.appFeeRecipient; + let hasAppFee = false; + + if (appFee && appFee > 0 && appFeeRecipient) { + hasAppFee = true; + } + + const handleDynamicValues = {}; + if (hasAppFee) { + const feePayment = await getFee('PAYMENT'); + + (handleDynamicValues['appFee'] = +appFee + +feePayment.fee), + (handleDynamicValues['checkbox1'] = { + value: true, + label: i18n.t('question:accept_app_fee', { + postProcess: 'capitalizeFirstChar', + }), + }); + } + if (data?.encrypt) { + handleDynamicValues['highlightedText'] = `isEncrypted: ${!!data.encrypt}`; + } + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.publish_qdn', { + postProcess: 'capitalizeFirstChar', + }), + html: ` +

    + + + ${data.resources + .map( + (resource) => ` +
    +
    Service: ${ + resource.service + }
    +
    Name: ${name}
    +
    Identifier: ${ + resource.identifier + }
    + ${ + resource.filename + ? `
    Filename: ${resource.filename}
    ` + : '' + } +
    ` + ) + .join('')} +
    + + `, + fee: +fee.fee * resources.length, + ...handleDynamicValues, + }, + isFromExtension + ); + + const { accepted, checkbox1 = false } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + type FailedPublish = { + reason: string; + identifier: any; + service: any; + }; + + const failedPublishesIdentifiers: FailedPublish[] = []; + + for (const resource of resources) { + try { + const requiredFields = ['service']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!resource[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + failedPublishesIdentifiers.push({ + reason: errorMsg, + identifier: resource.identifier, + service: resource.service, + }); + continue; + } + if (!resource.file && !resource.data64 && !resource?.base64) { + const errorMsg = i18n.t( + 'question:message.error.no_data_file_submitted', + { + postProcess: 'capitalizeFirstChar', + } + ); + failedPublishesIdentifiers.push({ + reason: errorMsg, + identifier: resource.identifier, + service: resource.service, + }); + continue; + } + const service = resource.service; + let identifier = resource.identifier; + let data64 = resource?.data64 || resource?.base64; + const filename = resource.filename; + const title = resource.title; + const description = resource.description; + const category = resource.category; + const tags = resource?.tags || []; + const result = {}; + + // Fill tags dynamically while maintaining backward compatibility + for (let i = 0; i < 5; i++) { + result[`tag${i + 1}`] = tags[i] || resource[`tag${i + 1}`] || undefined; + } + + // Access tag1 to tag5 from result + const { tag1, tag2, tag3, tag4, tag5 } = result; + const resourceEncrypt = encrypt && resource?.disableEncrypt !== true; + if (resource.identifier == null) { + identifier = 'default'; + } + if (!resourceEncrypt && service.endsWith('_PRIVATE')) { + const errorMsg = i18n.t('question:message.error.only_encrypted_data', { + postProcess: 'capitalizeFirstChar', + }); + failedPublishesIdentifiers.push({ + reason: errorMsg, + identifier: resource.identifier, + service: resource.service, + }); + continue; + } + if (resource.file) { + data64 = await fileToBase64(resource.file); + } + if (resourceEncrypt) { + try { + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const privateKey = parsedData.privateKey; + const userPublicKey = parsedData.publicKey; + const encryptDataResponse = encryptDataGroup({ + data64, + publicKeys: data.publicKeys, + privateKey, + userPublicKey, + }); + if (encryptDataResponse) { + data64 = encryptDataResponse; + } + } catch (error) { + const errorMsg = + error?.message || + i18n.t('question:message.error.upload_encryption', { + postProcess: 'capitalizeFirstChar', + }); + failedPublishesIdentifiers.push({ + reason: errorMsg, + identifier: resource.identifier, + service: resource.service, + }); + continue; + } + } + + try { + await retryTransaction( + publishData, + [ + { + registeredName: encodeURIComponent(name), + file: data64, + service: service, + identifier: encodeURIComponent(identifier), + uploadType: 'file', + isBase64: true, + filename: filename, + title, + description, + category, + tag1, + tag2, + tag3, + tag4, + tag5, + apiVersion: 2, + withFee: true, + }, + ], + true + ); + await new Promise((res) => { + setTimeout(() => { + res(); + }, 1000); + }); + } catch (error) { + const errorMsg = + error.message || + i18n.t('question:message.error.upload', { + postProcess: 'capitalizeFirstChar', + }); + failedPublishesIdentifiers.push({ + reason: errorMsg, + identifier: resource.identifier, + service: resource.service, + }); + } + } catch (error) { + failedPublishesIdentifiers.push({ + reason: + error?.message || + i18n.t('question:message.error.unknown_error', { + postProcess: 'capitalizeFirstChar', + }), + identifier: resource.identifier, + service: resource.service, + }); + } + } + if (failedPublishesIdentifiers.length > 0) { + const obj = { + message: i18n.t('question:message.error.resources_publish', { + postProcess: 'capitalizeFirstChar', + }), + }; + obj['error'] = { + unsuccessfulPublishes: failedPublishesIdentifiers, + }; + return obj; + } + if (hasAppFee && checkbox1) { + sendCoinFunc( + { + amount: appFee, + receiver: appFeeRecipient, + }, + true + ); + } + return true; +}; + +export const voteOnPoll = async (data, isFromExtension) => { + const requiredFields = ['pollName', 'optionIndex']; + const missingFields: string[] = []; + + requiredFields.forEach((field) => { + if (!data[field] && data[field] !== 0) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const pollName = data.pollName; + const optionIndex = data.optionIndex; + let pollInfo = null; + try { + const url = await createEndpoint(`/polls/${encodeURIComponent(pollName)}`); + const response = await fetch(url); + if (!response.ok) { + const errorMessage = await parseErrorResponse( + response, + i18n.t('question:message.error.fetch_poll', { + postProcess: 'capitalizeFirstChar', + }) + ); + throw new Error(errorMessage); + } + + pollInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_poll', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + if (!pollInfo || pollInfo.error) { + const errorMsg = + (pollInfo && pollInfo.message) || + i18n.t('question:message.error.no_poll', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + try { + const optionName = pollInfo.pollOptions[optionIndex].optionName; + const resVoteOnPoll = await _voteOnPoll( + { pollName, optionIndex, optionName }, + isFromExtension + ); + return resVoteOnPoll; + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.poll_vote', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const createPoll = async (data, isFromExtension) => { + const requiredFields = [ + 'pollName', + 'pollDescription', + 'pollOptions', + 'pollOwnerAddress', + ]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const pollName = data.pollName; + const pollDescription = data.pollDescription; + const pollOptions = data.pollOptions; + try { + const resCreatePoll = await _createPoll( + { + pollName, + pollDescription, + options: pollOptions, + }, + isFromExtension + ); + return resCreatePoll; + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.poll_create', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +function isBase64(str) { + const base64Regex = + /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; + return base64Regex.test(str) && str.length % 4 === 0; +} + +function checkValue(value) { + if (typeof value === 'string') { + if (isBase64(value)) { + return 'string'; + } else { + return 'string'; + } + } else if (typeof value === 'object' && value !== null) { + return 'object'; + } else { + throw new Error( + i18n.t('question:message.error.invalid_fullcontent', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +} + +export const sendChatMessage = async (data, isFromExtension, appInfo) => { + const message = data?.message; + const fullMessageObject = data?.fullMessageObject || data?.fullContent; + const recipient = data?.destinationAddress || data.recipient; + const groupId = data.groupId; + const isRecipient = groupId === undefined; + const chatReference = data?.chatReference; + if (groupId === undefined && recipient === undefined) { + throw new Error( + i18n.t('question:provide_recipient_group_id', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + let fullMessageObjectType; + if (fullMessageObject) { + fullMessageObjectType = checkValue(fullMessageObject); + } + const value = + (await getPermission(`qAPPSendChatMessage-${appInfo?.name}`)) || false; + let skip = false; + if (value) { + skip = true; + } + let resPermission; + if (!skip) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_chat_message', { + postProcess: 'capitalizeFirstChar', + }), + text2: isRecipient + ? i18n.t('question:to_recipient', { + recipient: recipient, + postProcess: 'capitalizeFirstChar', + }) + : i18n.t('question:to_group', { + group_id: groupId, + postProcess: 'capitalizeFirstChar', + }), + text3: fullMessageObject + ? fullMessageObjectType === 'string' + ? `${fullMessageObject?.slice(0, 25)}${fullMessageObject?.length > 25 ? '...' : ''}` + : `${JSON.stringify(fullMessageObject)?.slice(0, 25)}${JSON.stringify(fullMessageObject)?.length > 25 ? '...' : ''}` + : `${message?.slice(0, 25)}${message?.length > 25 ? '...' : ''}`, + checkbox1: { + value: false, + label: i18n.t('question:always_chat_messages', { + postProcess: 'capitalizeFirstChar', + }), + }, + }, + isFromExtension + ); + } + const { accepted = false, checkbox1 = false } = resPermission || {}; + if (resPermission && accepted) { + setPermission(`qAPPSendChatMessage-${appInfo?.name}`, checkbox1); + } + if (accepted || skip) { + const tiptapJson = { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: message, + }, + ], + }, + ], + }; + const messageObject = fullMessageObject + ? fullMessageObject + : { + messageText: tiptapJson, + images: [], + repliedTo: '', + version: 3, + }; + + let stringifyMessageObject = JSON.stringify(messageObject); + if (fullMessageObjectType === 'string') { + stringifyMessageObject = messageObject; + } + + const balance = await getBalanceInfo(); + const hasEnoughBalance = +balance < 4 ? false : true; + if (!hasEnoughBalance) { + throw new Error( + i18n.t('group:message.error.qortals_required', { + quantity: 4, + postProcess: 'capitalizeFirstChar', + }) + ); + } + if (isRecipient && recipient) { + const url = await createEndpoint(`/addresses/publickey/${recipient}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_recipient_public_key', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let key; + let hasPublicKey; + let res; + const contentType = response.headers.get('content-type'); + + // If the response is JSON, parse it as JSON + if (contentType && contentType.includes('application/json')) { + res = await response.json(); + } else { + // Otherwise, treat it as plain text + res = await response.text(); + } + if (res?.error === 102) { + key = ''; + hasPublicKey = false; + } else if (res !== false) { + key = res; + hasPublicKey = true; + } else { + key = ''; + hasPublicKey = false; + } + + if (!hasPublicKey && isRecipient) { + throw new Error( + 'Cannot send an encrypted message to this user since they do not have their publickey on chain.' + ); + } + let _reference = new Uint8Array(64); + self.crypto.getRandomValues(_reference); + + let sendTimestamp = Date.now(); + + let reference = Base58.encode(_reference); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + let handleDynamicValues = {}; + if (chatReference) { + handleDynamicValues['chatReference'] = chatReference; + } + + const tx = await createTransaction(18, keyPair, { + timestamp: sendTimestamp, + recipient: recipient, + recipientPublicKey: key, + hasChatReference: chatReference ? 1 : 0, + message: stringifyMessageObject, + lastReference: reference, + proofOfWorkNonce: 0, + isEncrypted: 1, + isText: 1, + ...handleDynamicValues, + }); + + const chatBytes = tx.chatBytes; + const difficulty = 8; + const { nonce, chatBytesArray } = await performPowTask( + chatBytes, + difficulty + ); + + let _response = await signChatFunc(chatBytesArray, nonce, null, keyPair); + if (_response?.error) { + throw new Error(_response?.message); + } + return _response; + } else if (!isRecipient && groupId) { + let _reference = new Uint8Array(64); + self.crypto.getRandomValues(_reference); + + let reference = Base58.encode(_reference); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + let handleDynamicValues = {}; + if (chatReference) { + handleDynamicValues['chatReference'] = chatReference; + } + + const txBody = { + timestamp: Date.now(), + groupID: Number(groupId), + hasReceipient: 0, + hasChatReference: chatReference ? 1 : 0, + message: stringifyMessageObject, + lastReference: reference, + proofOfWorkNonce: 0, + isEncrypted: 0, // Set default to not encrypted for groups + isText: 1, + ...handleDynamicValues, + }; + + const tx = await createTransaction(181, keyPair, txBody); + + // if (!hasEnoughBalance) { + // throw new Error("Must have at least 4 QORT to send a chat message"); + // } + + const chatBytes = tx.chatBytes; + const difficulty = 8; + const { nonce, chatBytesArray } = await performPowTask( + chatBytes, + difficulty + ); + + let _response = await signChatFunc(chatBytesArray, nonce, null, keyPair); + if (_response?.error) { + throw new Error(_response?.message); + } + return _response; + } else { + throw new Error( + i18n.t('question:provide_recipient_group_id', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_send_message', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const joinGroup = async (data, isFromExtension) => { + const requiredFields = ['groupId']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${data.groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const fee = await getFee('JOIN_GROUP'); + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:message.generic.confirm_join_group', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${groupInfo.groupName}`, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const groupId = data.groupId; + + if (!groupInfo || groupInfo.error) { + const errorMsg = + (groupInfo && groupInfo.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + try { + const resJoinGroup = await joinGroupFunc({ groupId }); + return resJoinGroup; + } catch (error) { + throw new Error( + error?.message || + i18n.t('group:message.error.group_join', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_join', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const saveFile = async (data, sender, isFromExtension, snackMethods) => { + try { + const requiredFields = ['filename', 'blob']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const filename = data.filename; + const blob = data.blob; + const resPermission = await getUserPermission( + { + text1: i18n.t('question:download_file', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${filename}`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const mimeType = blob.type || data.mimeType; + let backupExention = filename.split('.').pop(); + if (backupExention) { + backupExention = '.' + backupExention; + } + const fileExtension = mimeToExtensionMap[mimeType] || backupExention; + let fileHandleOptions = {}; + if (!mimeType) { + throw new Error( + i18n.t('question:message.error.mime_type', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + if (!fileExtension) { + throw new Error( + i18n.t('question:message.error.file_extension', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + if (fileExtension && mimeType) { + fileHandleOptions = { + accept: { + [mimeType]: [fileExtension], + }, + }; + } + + showSaveFilePicker( + { + filename, + mimeType, + blob, + }, + snackMethods + ); + return true; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_save_file', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } catch (error) { + throw new Error( + error?.message || + i18n.t('core:message.error.initiate_download', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const deployAt = async (data, isFromExtension) => { + const requiredFields = [ + 'name', + 'description', + 'tags', + 'creationBytes', + 'amount', + 'assetId', + 'type', + ]; + + const missingFields: string[] = []; + + requiredFields.forEach((field) => { + if (!data[field] && data[field] !== 0) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + try { + const resDeployAt = await _deployAt( + { + name: data.name, + description: data.description, + tags: data.tags, + creationBytes: data.creationBytes, + amount: data.amount, + assetId: data.assetId, + atType: data.type, + }, + isFromExtension + ); + return resDeployAt; + } catch (error) { + throw new Error( + error?.message || + i18n.t('group:message.error.group_join', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getUserWallet = async (data, isFromExtension, appInfo) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const isGateway = await isRunningGateway(); + + if (data?.coin === 'ARRR' && isGateway) + throw new Error( + i18n.t('question:message.error.gateway_wallet_local_node', { + token: 'ARRR', + postProcess: 'capitalizeFirstChar', + }) + ); + + const value = + (await getPermission( + `qAPPAutoGetUserWallet-${appInfo?.name}-${data.coin}` + )) || false; + let skip = false; + if (value) { + skip = true; + } + + let resPermission; + + if (!skip) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.get_wallet_info', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `coin: ${data.coin}`, + checkbox1: { + value: true, + label: i18n.t('question:always_retrieve_wallet', { + postProcess: 'capitalizeFirstChar', + }), + }, + }, + isFromExtension + ); + } + const { accepted = false, checkbox1 = false } = resPermission || {}; + + if (resPermission) { + setPermission( + `qAPPAutoGetUserWallet-${appInfo?.name}-${data.coin}`, + checkbox1 + ); + } + + if (accepted || skip) { + let coin = data.coin; + let userWallet = {}; + let arrrAddress = ''; + const wallet = await getSaveWallet(); + const address = wallet.address0; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const arrrSeed58 = parsedData.arrrSeed58; + if (coin === 'ARRR') { + const bodyToString = arrrSeed58; + const url = await createEndpoint(`/crosschain/arrr/walletaddress`); + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: bodyToString, + }); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + arrrAddress = res; + } + switch (coin) { + case 'QORT': + userWallet['address'] = address; + userWallet['publickey'] = parsedData.publicKey; + break; + case 'BTC': + userWallet['address'] = parsedData.btcAddress; + userWallet['publickey'] = parsedData.btcPublicKey; + break; + case 'LTC': + userWallet['address'] = parsedData.ltcAddress; + userWallet['publickey'] = parsedData.ltcPublicKey; + break; + case 'DOGE': + userWallet['address'] = parsedData.dogeAddress; + userWallet['publickey'] = parsedData.dogePublicKey; + break; + case 'DGB': + userWallet['address'] = parsedData.dgbAddress; + userWallet['publickey'] = parsedData.dgbPublicKey; + break; + case 'RVN': + userWallet['address'] = parsedData.rvnAddress; + userWallet['publickey'] = parsedData.rvnPublicKey; + break; + case 'ARRR': + await checkArrrSyncStatus(parsedData.arrrSeed58); + userWallet['address'] = arrrAddress; + break; + default: + break; + } + return userWallet; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getWalletBalance = async ( + data, + bypassPermission?: boolean, + isFromExtension?: boolean, + appInfo?: any +) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const isGateway = await isRunningGateway(); + + if (data?.coin === 'ARRR' && isGateway) + throw new Error( + i18n.t('question:message.error.gateway_balance_local_node', { + token: 'ARRR', + postProcess: 'capitalizeFirstChar', + }) + ); + + const value = + (await getPermission( + `qAPPAutoWalletBalance-${appInfo?.name}-${data.coin}` + )) || false; + let skip = false; + if (value) { + skip = true; + } + let resPermission; + + if (!bypassPermission && !skip) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.fetch_balance', { + coin: data.coin, // TODO highlight coin in the modal + postProcess: 'capitalizeFirstChar', + }), + checkbox1: { + value: true, + label: i18n.t('question:always_retrieve_balance', { + postProcess: 'capitalizeFirstChar', + }), + }, + }, + isFromExtension + ); + } + const { accepted = false, checkbox1 = false } = resPermission || {}; + if (resPermission) { + setPermission( + `qAPPAutoWalletBalance-${appInfo?.name}-${data.coin}`, + checkbox1 + ); + } + if (accepted || bypassPermission || skip) { + let coin = data.coin; + const wallet = await getSaveWallet(); + const address = wallet.address0; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + if (coin === 'QORT') { + let qortAddress = address; + try { + const url = await createEndpoint(`/addresses/balance/${qortAddress}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_balance', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.fetch_wallet', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else { + let _url = ``; + let _body = null; + switch (coin) { + case 'BTC': + _url = await createEndpoint(`/crosschain/btc/walletbalance`); + + _body = parsedData.btcPublicKey; + break; + case 'LTC': + _url = await createEndpoint(`/crosschain/ltc/walletbalance`); + _body = parsedData.ltcPublicKey; + break; + case 'DOGE': + _url = await createEndpoint(`/crosschain/doge/walletbalance`); + _body = parsedData.dogePublicKey; + break; + case 'DGB': + _url = await createEndpoint(`/crosschain/dgb/walletbalance`); + _body = parsedData.dgbPublicKey; + break; + case 'RVN': + _url = await createEndpoint(`/crosschain/rvn/walletbalance`); + _body = parsedData.rvnPublicKey; + break; + case 'ARRR': + await checkArrrSyncStatus(parsedData.arrrSeed58); + _url = await createEndpoint(`/crosschain/arrr/walletbalance`); + _body = parsedData.arrrSeed58; + break; + default: + break; + } + try { + const response = await fetch(_url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: _body, + }); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + if (isNaN(Number(res))) { + throw new Error( + i18n.t('question:message.error.fetch_balance', { + postProcess: 'capitalizeFirstChar', + }) + ); + } else { + return (Number(res) / 1e8).toFixed(8); + } + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.fetch_balance', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +const getPirateWallet = async (arrrSeed58) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.error.gateway_retrieve_balance', { + token: 'PIRATECHAIN', + postProcess: 'capitalizeFirstChar', + }) + ); + } + const bodyToString = arrrSeed58; + await checkArrrSyncStatus(bodyToString); + const url = await createEndpoint(`/crosschain/arrr/walletaddress`); + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: bodyToString, + }); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + return res; +}; + +export const getUserWalletFunc = async (coin) => { + let userWallet = {}; + const wallet = await getSaveWallet(); + const address = wallet.address0; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + switch (coin) { + case 'QORT': + userWallet['address'] = address; + userWallet['publickey'] = parsedData.publicKey; + break; + case 'BTC': + case 'BITCOIN': + userWallet['address'] = parsedData.btcAddress; + userWallet['publickey'] = parsedData.btcPublicKey; + break; + case 'LTC': + case 'LITECOIN': + userWallet['address'] = parsedData.ltcAddress; + userWallet['publickey'] = parsedData.ltcPublicKey; + break; + case 'DOGE': + case 'DOGECOIN': + userWallet['address'] = parsedData.dogeAddress; + userWallet['publickey'] = parsedData.dogePublicKey; + break; + case 'DGB': + case 'DIGIBYTE': + userWallet['address'] = parsedData.dgbAddress; + userWallet['publickey'] = parsedData.dgbPublicKey; + break; + case 'RVN': + case 'RAVENCOIN': + userWallet['address'] = parsedData.rvnAddress; + userWallet['publickey'] = parsedData.rvnPublicKey; + break; + case 'ARRR': + case 'PIRATECHAIN': + const arrrAddress = await getPirateWallet(parsedData.arrrSeed58); + userWallet['address'] = arrrAddress; + break; + default: + break; + } + return userWallet; +}; + +export const getUserWalletInfo = async (data, isFromExtension, appInfo) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + if (data?.coin === 'ARRR') { + throw new Error( + i18n.t('question:message.error.token_not_supported', { + token: 'ARRR', + postProcess: 'capitalizeFirstChar', + }) + ); + } + const value = + (await getPermission(`getUserWalletInfo-${appInfo?.name}-${data.coin}`)) || + false; + let skip = false; + if (value) { + skip = true; + } + let resPermission; + + if (!skip) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.get_wallet_info', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `coin: ${data.coin}`, + checkbox1: { + value: true, + label: i18n.t('question:always_retrieve_wallet', { + postProcess: 'capitalizeFirstChar', + }), + }, + }, + isFromExtension + ); + } + const { accepted = false, checkbox1 = false } = resPermission || {}; + + if (resPermission) { + setPermission(`getUserWalletInfo-${appInfo?.name}-${data.coin}`, checkbox1); + } + + if (accepted || skip) { + let coin = data.coin; + let walletKeys = await getUserWalletFunc(coin); + + const _url = await createEndpoint( + `/crosschain/` + data.coin.toLowerCase() + `/addressinfos` + ); + let _body = { xpub58: walletKeys['publickey'] }; + try { + const response = await fetch(_url, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(_body), + }); + if (!response?.ok) + throw new Error( + i18n.t('question:message.error.fetch_wallet_info', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.fetch_wallet', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getUserWalletTransactions = async ( + data, + isFromExtension, + appInfo +) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const value = + (await getPermission( + `getUserWalletTransactions-${appInfo?.name}-${data.coin}` + )) || false; + let skip = false; + if (value) { + skip = true; + } + let resPermission; + + if (!skip) { + resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.get_wallet_transactions', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `coin: ${data.coin}`, + checkbox1: { + value: true, + label: i18n.t('question:always_retrieve_wallet_transactions', { + postProcess: 'capitalizeFirstChar', + }), + }, + }, + isFromExtension + ); + } + const { accepted = false, checkbox1 = false } = resPermission || {}; + + if (resPermission) { + setPermission( + `getUserWalletTransactions-${appInfo?.name}-${data.coin}`, + checkbox1 + ); + } + + if (accepted || skip) { + const coin = data.coin; + const walletKeys = await getUserWalletFunc(coin); + let publicKey; + if (data?.coin === 'ARRR') { + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + publicKey = parsedData.arrrSeed58; + } else { + publicKey = walletKeys['publickey']; + } + + const _url = await createEndpoint( + `/crosschain/` + data.coin.toLowerCase() + `/wallettransactions` + ); + const _body = publicKey; + try { + const response = await fetch(_url, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: _body, + }); + if (!response?.ok) + throw new Error( + i18n.t('question:message.error.fetch_wallet_transactions', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.fetch_wallet_transactions', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getCrossChainServerInfo = async (data) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const _url = `/crosschain/` + data.coin.toLowerCase() + `/serverinfos`; + try { + const url = await createEndpoint(_url); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_generic', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + return res.servers; + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.server_info', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getTxActivitySummary = async (data) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const coin = data.coin; + const url = `/crosschain/txactivity?foreignBlockchain=${coin}`; // No apiKey here + + try { + const endpoint = await createEndpoint(url); + const response = await fetch(endpoint, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_generic', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + return res; // Return full response here + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.transaction_activity_summary', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getForeignFee = async (data) => { + const requiredFields = ['coin', 'type']; + const missingFields: string[] = []; + + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const { coin, type } = data; + const url = `/crosschain/${coin.toLowerCase()}/${type}`; + + try { + const endpoint = await createEndpoint(url); + const response = await fetch(endpoint, { + method: 'GET', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_generic', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + return res; // Return full response here + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.get_foreign_fee', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +function calculateRateFromFee(totalFee, sizeInBytes) { + const fee = (totalFee / sizeInBytes) * 1000; + return fee.toFixed(0); +} + +export const updateForeignFee = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const requiredFields = ['coin', 'type', 'value']; + const missingFields: string[] = []; + + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const { coin, type, value } = data; + + const text3 = + type === 'feerequired' + ? i18n.t('question:sats', { + amount: value, + postProcess: 'capitalizeFirstChar', + }) + : i18n.t('question:sats_per_kb', { + amount: value, + postProcess: 'capitalizeFirstChar', + }); + const text4 = + type === 'feerequired' + ? i18n.t('question:message.generic.calculate_fee', { + amount: value, + rate: calculateRateFromFee(value, 300), + postProcess: 'capitalizeFirstChar', + }) + : ''; + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.update_foreign_fee', { + postProcess: 'capitalizeFirstChar', + }), + text2: `type: ${type === 'feerequired' ? 'unlocking' : 'locking'}`, + text3: i18n.t('question:value', { + value: text3, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:coin', { + coin: coin, + postProcess: 'capitalizeFirstChar', + }), + }, + isFromExtension + ); + + const { accepted } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const url = `/crosschain/${coin.toLowerCase()}/update${type}`; + const valueStringified = JSON.stringify(+value); + + const endpoint = await createEndpoint(url); + const response = await fetch(endpoint, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: valueStringified, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.update_foreign_fee', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + if (res?.error && res?.message) { + throw new Error(res.message); + } + return res; // Return full response here +}; + +export const getServerConnectionHistory = async (data) => { + const requiredFields = ['coin']; + const missingFields: string[] = []; + + // Validate required fields + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const coin = data.coin.toLowerCase(); + const url = `/crosschain/${coin.toLowerCase()}/serverconnectionhistory`; + + try { + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available + const response = await fetch(endpoint, { + method: 'GET', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_connection_history', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return full response here + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.fetch_connection_history', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const setCurrentForeignServer = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const requiredFields = ['coin']; + const missingFields: string[] = []; + + // Validate required fields + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const { coin, host, port, type } = data; + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.set_current_server', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:server_type', { + type: type, + postProcess: 'capitalizeFirstChar', + }), + text3: i18n.t('question:server_host', { + host: host, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:coin', { + coin: coin, + postProcess: 'capitalizeFirstChar', + }), + }, + isFromExtension + ); + + const { accepted } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const body = { + hostName: host, + port: port, + connectionType: type, + }; + + const url = `/crosschain/${coin.toLowerCase()}/setcurrentserver`; + + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available + const response = await fetch(endpoint, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.server_current_set', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response +}; + +export const addForeignServer = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const requiredFields = ['coin']; + const missingFields: string[] = []; + + // Validate required fields + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const { coin, host, port, type } = data; + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.server_add', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:server_type', { + type: type, + postProcess: 'capitalizeFirstChar', + }), + text3: i18n.t('question:server_host', { + host: host, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:coin', { + coin: coin, + postProcess: 'capitalizeFirstChar', + }), + }, + isFromExtension + ); + + const { accepted } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const body = { + hostName: host, + port: port, + connectionType: type, + }; + + const url = `/crosschain/${coin.toLowerCase()}/addserver`; + + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available + const response = await fetch(endpoint, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.server_current_add', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response +}; + +export const removeForeignServer = async (data, isFromExtension) => { + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const requiredFields = ['coin']; + const missingFields: string[] = []; + + // Validate required fields + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const { coin, host, port, type } = data; + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.server_remove', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:server_type', { + type: type, + postProcess: 'capitalizeFirstChar', + }), + text3: i18n.t('question:server_host', { + host: host, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:coin', { + coin: coin, + postProcess: 'capitalizeFirstChar', + }), + }, + isFromExtension + ); + + const { accepted } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const body = { + hostName: host, + port: port, + connectionType: type, + }; + + const url = `/crosschain/${coin.toLowerCase()}/removeserver`; + + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available + const response = await fetch(endpoint, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.server_remove', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response +}; + +export const getDaySummary = async () => { + const url = `/admin/summary`; // Simplified endpoint URL + + try { + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL + const response = await fetch(endpoint, { + method: 'GET', + headers: { + Accept: '*/*', + }, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.retrieve_summary', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.retrieve_summary', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getNodeInfo = async () => { + const url = `/admin/info`; // Simplified endpoint URL + + try { + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL + const response = await fetch(endpoint, { + method: 'GET', + headers: { + Accept: '*/*', + }, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.node_info', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.node_info', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getNodeStatus = async () => { + const url = `/admin/status`; // Simplified endpoint URL + + try { + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL + const response = await fetch(endpoint, { + method: 'GET', + headers: { + Accept: '*/*', + }, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.node_status', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + if (res?.error && res?.message) { + throw new Error(res.message); + } + + return res; // Return the full response + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.node_status', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const getArrrSyncStatus = async () => { + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const arrrSeed = parsedData.arrrSeed58; + const url = `/crosschain/arrr/syncstatus`; // Simplified endpoint URL + + try { + const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL + const response = await fetch(endpoint, { + method: 'POST', + headers: { + Accept: '*/*', + }, + body: arrrSeed, + }); + + let res; + + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + + return res; // Return the full response + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.retrieve_sync_status', { + token: 'ARRR', + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const sendCoin = async (data, isFromExtension) => { + const requiredFields = ['coin', 'amount']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + if (!data?.destinationAddress && !data?.recipient) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: 'recipient', + postProcess: 'capitalizeFirstChar', + }) + ); + } + let checkCoin = data.coin; + const wallet = await getSaveWallet(); + const address = wallet.address0; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const isGateway = await isRunningGateway(); + + if (checkCoin !== 'QORT' && isGateway) + throw new Error( + i18n.t('question:message.error.gateway_non_qort_local_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + if (checkCoin === 'QORT') { + // Params: data.coin, data.recipient, data.amount, data.fee + // TODO: prompt user to send. If they confirm, call `POST /crosschain/:coin/send`, or for QORT, broadcast a PAYMENT transaction + // then set the response string from the core to the `response` variable (defined above) + // If they decline, send back JSON that includes an `error` key, such as `{"error": "User declined request"}` + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + + const url = await createEndpoint(`/addresses/balance/${address}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_balance', { + postProcess: 'capitalizeFirstChar', + }) + ); + let walletBalance; + try { + walletBalance = await response.clone().json(); + } catch (e) { + walletBalance = await response.text(); + } + if (isNaN(Number(walletBalance))) { + const errorMsg = i18n.t('question:message.error.fetch_balance_token', { + token: 'QORT', + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const transformDecimals = (Number(walletBalance) * QORT_DECIMALS).toFixed( + 0 + ); + const walletBalanceDecimals = Number(transformDecimals); + const amountDecimals = Number(amount) * QORT_DECIMALS; + const fee: number = await sendQortFee(); + if (amountDecimals + fee * QORT_DECIMALS > walletBalanceDecimals) { + const errorMsg = i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + if (amount <= 0) { + const errorMsg = i18n.t('core:message.error.invalid_amount', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + if (recipient.length === 0) { + const errorMsg = i18n.t('question:message.error.empty_receiver', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:to_recipient', { + recipient: recipient, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${amount} ${checkCoin}`, + fee: fee, + confirmCheckbox: true, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const makePayment = await sendCoinFunc( + { amount, password: null, receiver: recipient }, + true + ); + return makePayment.res?.data; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else if (checkCoin === 'BTC') { + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + const xprv58 = parsedData.btcPrivateKey; + const feePerByte = data.fee ? data.fee : btcFeePerByte; + + const btcWalletBalance = await getWalletBalance({ coin: checkCoin }, true); + + if (isNaN(Number(btcWalletBalance))) { + throw new Error( + i18n.t('question:message.error.fetch_balance_token', { + token: 'BTC', + postProcess: 'capitalizeFirstChar', + }) + ); + } + const btcWalletBalanceDecimals = Number(btcWalletBalance); + const btcAmountDecimals = Number(amount); + const fee = feePerByte * 500; // default 0.00050000 + if (btcAmountDecimals + fee > btcWalletBalanceDecimals) { + throw new Error( + i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:to_recipient', { + recipient: recipient, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${amount} ${checkCoin}`, + foreignFee: `${fee} BTC`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const opts = { + xprv58: xprv58, + receivingAddress: recipient, + bitcoinAmount: amount, + feePerByte: feePerByte, + }; + const url = await createEndpoint(`/crosschain/btc/send`); + + const response = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(opts), + }); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.send', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else if (checkCoin === 'LTC') { + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + const xprv58 = parsedData.ltcPrivateKey; + const feePerByte = data.fee ? data.fee : ltcFeePerByte; + const ltcWalletBalance = await getWalletBalance({ coin: checkCoin }, true); + + if (isNaN(Number(ltcWalletBalance))) { + const errorMsg = i18n.t('question:message.error.fetch_balance_token', { + token: 'LTC', + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const ltcWalletBalanceDecimals = Number(ltcWalletBalance); + const ltcAmountDecimals = Number(amount); + const fee = feePerByte * 1000; // default 0.00030000 + if (ltcAmountDecimals + fee > ltcWalletBalanceDecimals) { + throw new Error( + i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:to_recipient', { + recipient: recipient, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${amount} ${checkCoin}`, + foreignFee: `${fee} LTC`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const url = await createEndpoint(`/crosschain/ltc/send`); + const opts = { + xprv58: xprv58, + receivingAddress: recipient, + litecoinAmount: amount, + feePerByte: feePerByte, + }; + const response = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(opts), + }); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.send', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else if (checkCoin === 'DOGE') { + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + const xprv58 = parsedData.dogePrivateKey; + const feePerByte = data.fee ? data.fee : dogeFeePerByte; + const dogeWalletBalance = await getWalletBalance({ coin: checkCoin }, true); + if (isNaN(Number(dogeWalletBalance))) { + const errorMsg = i18n.t('question:message.error.fetch_balance_token', { + token: 'DOGE', + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const dogeWalletBalanceDecimals = Number(dogeWalletBalance); + const dogeAmountDecimals = Number(amount); + const fee = feePerByte * 5000; // default 0.05000000 + if (dogeAmountDecimals + fee > dogeWalletBalanceDecimals) { + const errorMsg = i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:to_recipient', { + recipient: recipient, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${amount} ${checkCoin}`, + foreignFee: `${fee} DOGE`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const opts = { + xprv58: xprv58, + receivingAddress: recipient, + dogecoinAmount: amount, + feePerByte: feePerByte, + }; + const url = await createEndpoint(`/crosschain/doge/send`); + + const response = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(opts), + }); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.send', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else if (checkCoin === 'DGB') { + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + const xprv58 = parsedData.dbgPrivateKey; + const feePerByte = data.fee ? data.fee : dgbFeePerByte; + const dgbWalletBalance = await getWalletBalance({ coin: checkCoin }, true); + if (isNaN(Number(dgbWalletBalance))) { + const errorMsg = i18n.t('question:message.error.fetch_balance_token', { + token: 'DGB', + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const dgbWalletBalanceDecimals = Number(dgbWalletBalance); + const dgbAmountDecimals = Number(amount); + const fee = feePerByte * 500; // default 0.00005000 + if (dgbAmountDecimals + fee > dgbWalletBalanceDecimals) { + const errorMsg = i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: `To: ${recipient}`, + highlightedText: `${amount} ${checkCoin}`, + foreignFee: `${fee} DGB`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const opts = { + xprv58: xprv58, + receivingAddress: recipient, + digibyteAmount: amount, + feePerByte: feePerByte, + }; + const url = await createEndpoint(`/crosschain/dgb/send`); + + const response = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(opts), + }); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.send', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else if (checkCoin === 'RVN') { + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + const xprv58 = parsedData.rvnPrivateKey; + const feePerByte = data.fee ? data.fee : rvnFeePerByte; + const rvnWalletBalance = await getWalletBalance({ coin: checkCoin }, true); + if (isNaN(Number(rvnWalletBalance))) { + const errorMsg = i18n.t('question:message.error.fetch_balance_token', { + token: 'RVN', + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const rvnWalletBalanceDecimals = Number(rvnWalletBalance); + const rvnAmountDecimals = Number(amount); + const fee = feePerByte * 500; // default 0.00562500 + if (rvnAmountDecimals + fee > rvnWalletBalanceDecimals) { + const errorMsg = i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: `To: ${recipient}`, + highlightedText: `${amount} ${checkCoin}`, + foreignFee: `${fee} RVN`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const opts = { + xprv58: xprv58, + receivingAddress: recipient, + ravencoinAmount: amount, + feePerByte: feePerByte, + }; + const url = await createEndpoint(`/crosschain/rvn/send`); + + const response = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(opts), + }); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.send', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } else if (checkCoin === 'ARRR') { + const amount = Number(data.amount); + const recipient = data?.recipient || data.destinationAddress; + const memo = data?.memo; + const arrrWalletBalance = await getWalletBalance({ coin: checkCoin }, true); + + if (isNaN(Number(arrrWalletBalance))) { + const errorMsg = i18n.t('question:message.error.fetch_balance_token', { + token: 'ARR', + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const arrrWalletBalanceDecimals = Number(arrrWalletBalance); + const arrrAmountDecimals = Number(amount); + const fee = 0.0001; + if (arrrAmountDecimals + fee > arrrWalletBalanceDecimals) { + const errorMsg = i18n.t('question:message.error.insufficient_funds', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.send_coins', { + postProcess: 'capitalizeFirstChar', + }), + text2: `To: ${recipient}`, + highlightedText: `${amount} ${checkCoin}`, + foreignFee: `${fee} ARRR`, + }, + isFromExtension + ); + const { accepted } = resPermission; + + if (accepted) { + const opts = { + entropy58: parsedData.arrrSeed58, + receivingAddress: recipient, + arrrAmount: amount, + memo: memo, + }; + const url = await createEndpoint(`/crosschain/arrr/send`); + + const response = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(opts), + }); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.send', { + postProcess: 'capitalizeFirstChar', + }) + ); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } +}; + +function calculateFeeFromRate(feePerKb, sizeInBytes) { + return (feePerKb / 1000) * sizeInBytes; +} + +const getBuyingFees = async (foreignBlockchain) => { + const ticker = sellerForeignFee[foreignBlockchain].ticker; + if (!ticker) throw new Error('invalid foreign blockchain'); + const unlockFee = await getForeignFee({ + coin: ticker, + type: 'feerequired', + }); + const lockFee = await getForeignFee({ + coin: ticker, + type: 'feekb', + }); + return { + ticker: ticker, + lock: { + sats: lockFee, + fee: lockFee / QORT_DECIMALS, + }, + unlock: { + sats: unlockFee, + fee: unlockFee / QORT_DECIMALS, + feePerKb: +calculateRateFromFee(+unlockFee, 300) / QORT_DECIMALS, + }, + }; +}; + +export const createBuyOrder = async (data, isFromExtension) => { + const requiredFields = ['crosschainAtInfo', 'foreignBlockchain']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const isGateway = await isRunningGateway(); + const foreignBlockchain = data.foreignBlockchain; + const atAddresses = data.crosschainAtInfo?.map( + (order) => order.qortalAtAddress + ); + + const atPromises = atAddresses.map((atAddress) => + requestQueueGetAtAddresses.enqueue(async () => { + const url = await createEndpoint(`/crosschain/trade/${atAddress}`); + const resAddress = await fetch(url); + const resData = await resAddress.json(); + if (foreignBlockchain !== resData?.foreignBlockchain) { + throw new Error( + i18n.t('core:message.error.same_foreign_blockchain', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + return resData; + }) + ); + + const crosschainAtInfo = await Promise.all(atPromises); + + try { + const buyingFees = await getBuyingFees(foreignBlockchain); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.buy_order', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:permission.buy_order_quantity', { + quantity: atAddresses?.length, + postProcess: 'capitalizeFirstChar', + }), + text3: i18n.t('question:permission.buy_order_ticker', { + qort_amount: crosschainAtInfo?.reduce((latest, cur) => { + return latest + +cur?.qortAmount; + }, 0), + foreign_amount: roundUpToDecimals( + crosschainAtInfo?.reduce((latest, cur) => { + return latest + +cur?.expectedForeignAmount; + }, 0) + ), + ticker: buyingFees.ticker, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('auth:node.using_public_gateway', { + gateway: isGateway, + postProcess: 'capitalizeFirstChar', + }), + fee: '', + html: ` +
    + + +
    +
    ${i18n.t('question:total_unlocking_fee', { + postProcess: 'capitalizeFirstChar', + })}
    +
    ${(+buyingFees?.unlock?.fee * atAddresses?.length)?.toFixed(8)} ${buyingFees.ticker}
    +
    + ${i18n.t('question:permission.buy_order_fee_estimation', { + quantity: atAddresses?.length, + fee: buyingFees?.unlock?.feePerKb?.toFixed(8), + ticker: buyingFees.ticker, + postProcess: 'capitalizeFirstChar', + })} +
    +
    ${i18n.t('question:total_locking_fee', { + postProcess: 'capitalizeFirstChar', + })}
    +
    ${i18n.t('question:permission.buy_order_per_kb', { + fee: +buyingFees?.lock.fee.toFixed(8), + ticker: buyingFees.ticker, + postProcess: 'capitalizeFirstChar', + })} +
    +
    +
    +`, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const resBuyOrder = await createBuyOrderTx({ + crosschainAtInfo, + isGateway, + foreignBlockchain, + }); + return resBuyOrder; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.buy_order', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +const cancelTradeOfferTradeBot = async (body, keyPair) => { + const txn = new DeleteTradeOffer().createTransaction(body); + const url = await createEndpoint(`/crosschain/tradeoffer`); + const bodyToString = JSON.stringify(txn); + + const deleteTradeBotResponse = await fetch(url, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + body: bodyToString, + }); + + if (!deleteTradeBotResponse.ok) { + throw new Error( + i18n.t('question:message.error.update_tradebot', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + const unsignedTxn = await deleteTradeBotResponse.text(); + const signedTxnBytes = await signTradeBotTransaction(unsignedTxn, keyPair); + const signedBytes = Base58.encode(signedTxnBytes); + + let res; + try { + res = await processTransactionVersion2(signedBytes); + } catch (error) { + return { + error: i18n.t('question:message.error.cancel_sell_order', { + postProcess: 'capitalizeFirstChar', + }), + failedTradeBot: { + atAddress: body.atAddress, + creatorAddress: body.creatorAddress, + }, + }; + } + if (res?.error) { + return { + error: i18n.t('question:message.error.cancel_sell_order', { + postProcess: 'capitalizeFirstChar', + }), + failedTradeBot: { + atAddress: body.atAddress, + creatorAddress: body.creatorAddress, + }, + }; + } + if (res?.signature) { + return res; + } else { + throw new Error( + i18n.t('question:message.error.cancel_sell_order', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; +const findFailedTradebot = async (createBotCreationTimestamp, body) => { + //wait 5 secs + const wallet = await getSaveWallet(); + const address = wallet.address0; + await new Promise((res) => { + setTimeout(() => { + res(null); + }, 5000); + }); + const url = await createEndpoint( + `/crosschain/tradebot?foreignBlockchain=LITECOIN` + ); + + const tradeBotsReponse = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + const data = await tradeBotsReponse.json(); + const latestItem2 = data + .filter((item) => item.creatorAddress === address) + .sort((a, b) => b.timestamp - a.timestamp)[0]; + const latestItem = data + .filter( + (item) => + item.creatorAddress === address && + +item.foreignAmount === +body.foreignAmount + ) + .sort((a, b) => b.timestamp - a.timestamp)[0]; + if ( + latestItem && + createBotCreationTimestamp - latestItem.timestamp <= 5000 && + createBotCreationTimestamp > latestItem.timestamp // Ensure latestItem's timestamp is before createBotCreationTimestamp + ) { + return latestItem; + } else { + return null; + } +}; +const tradeBotCreateRequest = async (body, keyPair) => { + const txn = new TradeBotCreateRequest().createTransaction(body); + const url = await createEndpoint(`/crosschain/tradebot/create`); + const bodyToString = JSON.stringify(txn); + + const unsignedTxnResponse = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: bodyToString, + }); + if (!unsignedTxnResponse.ok) + throw new Error( + i18n.t('question:message.error.create_tradebot', { + postProcess: 'capitalizeFirstChar', + }) + ); + const createBotCreationTimestamp = Date.now(); + const unsignedTxn = await unsignedTxnResponse.text(); + const signedTxnBytes = await signTradeBotTransaction(unsignedTxn, keyPair); + const signedBytes = Base58.encode(signedTxnBytes); + + let res; + try { + res = await processTransactionVersion2(signedBytes); + } catch (error) { + const findFailedTradeBot = await findFailedTradebot( + createBotCreationTimestamp, + body + ); + return { + error: i18n.t('question:message.error.create_sell_order', { + postProcess: 'capitalizeFirstChar', + }), + failedTradeBot: findFailedTradeBot, + }; + } + + if (res?.signature) { + return res; + } else { + throw new Error( + i18n.t('question:message.error.create_sell_order', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const createSellOrder = async (data, isFromExtension) => { + const requiredFields = ['qortAmount', 'foreignBlockchain', 'foreignAmount']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const parsedForeignAmount = Number(data.foreignAmount)?.toFixed(8); + + const receivingAddress = await getUserWalletFunc(data.foreignBlockchain); + try { + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.sell_order', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:permission.order_detail', { + qort_amount: data.qortAmount, + foreign_amount: parsedForeignAmount, + ticker: data.foreignBlockchain, + postProcess: 'capitalizeFirstChar', + }), + fee: '0.02', + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const userPublicKey = parsedData.publicKey; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + const response = await tradeBotCreateRequest( + { + creatorPublicKey: userPublicKey, + qortAmount: parseFloat(data.qortAmount), + fundingQortAmount: parseFloat(data.qortAmount) + 0.01, + foreignBlockchain: data.foreignBlockchain, + foreignAmount: parseFloat(parsedForeignAmount), + tradeTimeout: 120, + receivingAddress: receivingAddress.address, + }, + keyPair + ); + + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.submit_sell_order', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const cancelSellOrder = async (data, isFromExtension) => { + const requiredFields = ['atAddress']; + const missingFields: string[] = []; + + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const url = await createEndpoint(`/crosschain/trade/${data.atAddress}`); + const resAddress = await fetch(url); + const resData = await resAddress.json(); + + if (!resData?.qortalAtAddress) + throw new Error( + i18n.t('question:message.error.at_info', { + postProcess: 'capitalizeFirstChar', + }) + ); + + try { + const fee = await getFee('MESSAGE'); + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.cancel_sell_order', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:permission.order_detail', { + qort_amount: resData.qortAmount, + foreign_amount: resData.expectedForeignAmount, + ticker: resData.foreignBlockchain, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const userPublicKey = parsedData.publicKey; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + const response = await cancelTradeOfferTradeBot( + { + creatorPublicKey: userPublicKey, + atAddress: data.atAddress, + }, + keyPair + ); + + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + } catch (error) { + throw new Error( + error?.message || + i18n.t('question:message.error.submit_sell_order', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const openNewTab = async (data, isFromExtension) => { + const requiredFields = ['qortalLink']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const res = extractComponents(data.qortalLink); + if (res) { + const { service, name, identifier, path } = res; + if (!service && !name) + throw new Error( + i18n.t('auth:message.error.invalid_qortal_link', { + postProcess: 'capitalizeFirstChar', + }) + ); + executeEvent('addTab', { data: { service, name, identifier, path } }); + executeEvent('open-apps-mode', {}); + return true; + } else { + throw new Error( + i18n.t('auth:message.error.invalid_qortal_link', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const adminAction = async (data, isFromExtension) => { + const requiredFields = ['type']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + // For actions that require a value, check for 'value' field + const actionsRequiringValue = [ + 'addpeer', + 'removepeer', + 'forcesync', + 'addmintingaccount', + 'removemintingaccount', + ]; + if (actionsRequiringValue.includes(data.type.toLowerCase()) && !data.value) { + missingFields.push('value'); + } + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const isGateway = await isRunningGateway(); + if (isGateway) { + throw new Error( + i18n.t('question:message.generic.no_action_public_node', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + let apiEndpoint = ''; + let method = 'GET'; // Default method + let includeValueInBody = false; + switch (data.type.toLowerCase()) { + case 'stop': + apiEndpoint = await createEndpoint('/admin/stop'); + break; + case 'restart': + apiEndpoint = await createEndpoint('/admin/restart'); + break; + case 'bootstrap': + apiEndpoint = await createEndpoint('/admin/bootstrap'); + break; + case 'addmintingaccount': + apiEndpoint = await createEndpoint('/admin/mintingaccounts'); + method = 'POST'; + includeValueInBody = true; + break; + case 'removemintingaccount': + apiEndpoint = await createEndpoint('/admin/mintingaccounts'); + method = 'DELETE'; + includeValueInBody = true; + break; + case 'forcesync': + apiEndpoint = await createEndpoint('/admin/forcesync'); + method = 'POST'; + includeValueInBody = true; + break; + case 'addpeer': + apiEndpoint = await createEndpoint('/peers'); + method = 'POST'; + includeValueInBody = true; + break; + case 'removepeer': + apiEndpoint = await createEndpoint('/peers'); + method = 'DELETE'; + includeValueInBody = true; + break; + default: + throw new Error( + i18n.t('question:message.error.unknown_admin_action_type', { + type: data.type, + postProcess: 'capitalizeFirstChar', + }) + ); + } + // Prepare the permission prompt text + let permissionText = i18n.t('question:permission.perform_admin_action', { + type: data.type, + postProcess: 'capitalizeFirstChar', + }); + + if (data.value) { + permissionText += + ' ' + + i18n.t('question:permission.perform_admin_action_with_value', { + value: data.value, + postProcess: 'capitalizeFirstChar', + }); + } + + const resPermission = await getUserPermission( + { + text1: permissionText, + }, + isFromExtension + ); + + const { accepted } = resPermission; + + if (accepted) { + // Set up options for the API call + const options: RequestInit = { + method: method, + headers: {}, + }; + if (includeValueInBody) { + options.headers['Content-Type'] = 'text/plain'; + options.body = data.value; + } + const response = await fetch(apiEndpoint, options); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.perform_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const signTransaction = async (data, isFromExtension) => { + const requiredFields = ['unsignedBytes']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const shouldProcess = data?.process || false; + const _url = await createEndpoint( + '/transactions/decode?ignoreValidityChecks=false' + ); + + const _body = data.unsignedBytes; + const response = await fetch(_url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: _body, + }); + + if (!response.ok) + throw new Error( + i18n.t('question:message.error.decode_transaction', { + postProcess: 'capitalizeFirstChar', + }) + ); + const decodedData = await response.json(); + const resPermission = await getUserPermission( + { + text1: shouldProcess + ? i18n.t('question:permission.sign_process_transaction', { + postProcess: 'capitalizeFirstChar', + }) + : i18n.t('question:permission.sign_transaction', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t( + 'question:message.generic.read_transaction_carefully', + { postProcess: 'capitalizeFirstChar' } + ), + text2: `Tx type: ${decodedData.type}`, + json: decodedData, + }, + isFromExtension + ); + + const { accepted } = resPermission; + if (accepted) { + let urlConverted = await createEndpoint('/transactions/convert'); + + const responseConverted = await fetch(urlConverted, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: data.unsignedBytes, + }); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + const convertedBytes = await responseConverted.text(); + const txBytes = Base58.decode(data.unsignedBytes); + const _arbitraryBytesBuffer = Object.keys(txBytes).map(function (key) { + return txBytes[key]; + }); + const arbitraryBytesBuffer = new Uint8Array(_arbitraryBytesBuffer); + const txByteSigned = Base58.decode(convertedBytes); + const _bytesForSigningBuffer = Object.keys(txByteSigned).map( + function (key) { + return txByteSigned[key]; + } + ); + const bytesForSigningBuffer = new Uint8Array(_bytesForSigningBuffer); + const signature = nacl.sign.detached( + bytesForSigningBuffer, + keyPair.privateKey + ); + const signedBytes = utils.appendBuffer(arbitraryBytesBuffer, signature); + const signedBytesToBase58 = Base58.encode(signedBytes); + if (!shouldProcess) { + return signedBytesToBase58; + } + const res = await processTransactionVersion2(signedBytesToBase58); + if (!res?.signature) + throw new Error( + res?.message || + i18n.t('question:message.error.process_transaction', { + postProcess: 'capitalizeFirstChar', + }) + ); + return res; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +const missingFieldsFunc = (data, requiredFields) => { + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } +}; + +const encode = (value) => encodeURIComponent(value.trim()); // Helper to encode values +const buildQueryParams = (data) => { + const allowedParams = [ + 'name', + 'service', + 'identifier', + 'mimeType', + 'fileName', + 'encryptionType', + 'key', + ]; + return Object.entries(data) + .map(([key, value]) => { + if ( + value === undefined || + value === null || + value === false || + !allowedParams.includes(key) + ) + return null; // Skip null, undefined, or false + if (typeof value === 'boolean') return `${key}=${value}`; // Handle boolean values + return `${key}=${encode(value)}`; // Encode other values + }) + .filter(Boolean) // Remove null values + .join('&'); // Join with `&` +}; +export const createAndCopyEmbedLink = async (data, isFromExtension) => { + const requiredFields = ['type']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + switch (data.type) { + case 'POLL': { + missingFieldsFunc(data, ['type', 'name']); + + const queryParams = [ + `name=${encode(data.name)}`, + data.ref ? `ref=${encode(data.ref)}` : null, // Add only if ref exists + ] + .filter(Boolean) // Remove null values + .join('&'); // Join with `&` + const link = `qortal://use-embed/POLL?${queryParams}`; + try { + await navigator.clipboard.writeText(link); + } catch (error) { + throw new Error( + i18n.t('question:message.error.copy_clipboard', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + return link; + } + case 'IMAGE': + case 'ATTACHMENT': { + missingFieldsFunc(data, ['type', 'name', 'service', 'identifier']); + if (data?.encryptionType === 'private' && !data?.key) { + throw new Error( + i18n.t('question:message.generic.provide_key_shared_link', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const queryParams = buildQueryParams(data); + + const link = `qortal://use-embed/${data.type}?${queryParams}`; + + try { + await navigator.clipboard.writeText(link); + } catch (error) { + throw new Error( + i18n.t('question:message.error.copy_clipboard', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + return link; + } + + default: + throw new Error( + i18n.t('question:message.error.invalid_type', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const registerNameRequest = async (data, isFromExtension) => { + const requiredFields = ['name']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const fee = await getFee('REGISTER_NAME'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.register_name', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: data.name, + text2: data?.description, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const name = data.name; + const description = data?.description || ''; + const response = await registerName({ name, description }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const updateNameRequest = async (data, isFromExtension) => { + const requiredFields = ['newName', 'oldName']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const oldName = data.oldName; + const newName = data.newName; + const description = data?.description || ''; + const fee = await getFee('UPDATE_NAME'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.register_name', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: data.newName, + text2: data?.description, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await updateName({ oldName, newName, description }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const leaveGroupRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const fee = await getFee('LEAVE_GROUP'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.leave_group', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: `${groupInfo.groupName}`, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await leaveGroup({ groupId }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const inviteToGroupRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'inviteTime', 'inviteeAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.inviteeAddress; + const inviteTime = data?.inviteTime; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('GROUP_INVITE'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.invite', { + invitee: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await inviteToGroup({ + groupId, + qortalAddress, + inviteTime, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const kickFromGroupRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'qortalAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.qortalAddress; + const reason = data?.reason; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('GROUP_KICK'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.kick', { + partecipant: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await kickFromGroup({ + groupId, + qortalAddress, + rBanReason: reason, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const banFromGroupRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'qortalAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.qortalAddress; + const rBanTime = data?.banTime; + const reason = data?.reason; + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('GROUP_BAN'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.ban', { + partecipant: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await banFromGroup({ + groupId, + qortalAddress, + rBanTime, + rBanReason: reason, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const cancelGroupBanRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'qortalAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.qortalAddress; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('CANCEL_GROUP_BAN'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.cancel_ban', { + partecipant: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await cancelBan({ + groupId, + qortalAddress, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const addGroupAdminRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'qortalAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.qortalAddress; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('ADD_GROUP_ADMIN'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.add_admin', { + invitee: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await makeAdmin({ + groupId, + qortalAddress, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const removeGroupAdminRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'qortalAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.qortalAddress; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('REMOVE_GROUP_ADMIN'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.remove_admin', { + partecipant: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await removeAdmin({ + groupId, + qortalAddress, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const cancelGroupInviteRequest = async (data, isFromExtension) => { + const requiredFields = ['groupId', 'qortalAddress']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (!data[field]) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = data.groupId; + const qortalAddress = data?.qortalAddress; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(qortalAddress); + + const fee = await getFee('CANCEL_GROUP_INVITE'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.cancel_group_invite', { + invitee: displayInvitee || qortalAddress, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + + const { accepted } = resPermission; + + if (accepted) { + const response = await cancelInvitationToGroup({ + groupId, + qortalAddress, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const createGroupRequest = async (data, isFromExtension) => { + const requiredFields = [ + 'approvalThreshold', + 'groupId', + 'groupName', + 'maxBlock', + 'minBlock', + 'qortalAddress', + 'type', + ]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupName = data.groupName; + const description = data?.description || ''; + const type = +data.type; + const approvalThreshold = +data?.approvalThreshold; + const minBlock = +data?.minBlock; + const maxBlock = +data.maxBlock; + + const fee = await getFee('CREATE_GROUP'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.create_group', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await createGroup({ + groupName, + groupDescription: description, + groupType: type, + groupApprovalThreshold: approvalThreshold, + minBlock, + maxBlock, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const updateGroupRequest = async (data, isFromExtension) => { + const requiredFields = [ + 'groupId', + 'newOwner', + 'type', + 'approvalThreshold', + 'minBlock', + 'maxBlock', + ]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const groupId = +data.groupId; + const newOwner = data.newOwner; + const description = data?.description || ''; + const type = +data.type; + const approvalThreshold = +data?.approvalThreshold; + const minBlock = +data?.minBlock; + const maxBlock = +data.maxBlock; + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) + throw new Error( + i18n.t('question:message.error.fetch_group', { + postProcess: 'capitalizeFirstChar', + }) + ); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = + (error && error.message) || + i18n.t('question:message.error.no_group_found', { + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(newOwner); + + const fee = await getFee('CREATE_GROUP'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.update_group', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:permission.update_group_detail', { + owner: displayInvitee || newOwner, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('group:group.group_name', { + name: groupInfo?.groupName, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await updateGroup({ + groupId, + newOwner, + newIsOpen: type, + newDescription: description, + newApprovalThreshold: approvalThreshold, + newMinimumBlockDelay: minBlock, + newMaximumBlockDelay: maxBlock, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const decryptAESGCMRequest = async (data, isFromExtension) => { + const requiredFields = ['encryptedData', 'iv', 'senderPublicKey']; + requiredFields.forEach((field) => { + if (!data[field]) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: field, + postProcess: 'capitalizeFirstChar', + }) + ); + } + }); + + const encryptedData = data.encryptedData; + const iv = data.iv; + const senderPublicKeyBase58 = data.senderPublicKey; + + // Decode keys and IV + const senderPublicKey = Base58.decode(senderPublicKeyBase58); + const resKeyPair = await getKeyPair(); // Assume this retrieves the current user's keypair + const uint8PrivateKey = Base58.decode(resKeyPair.privateKey); + + // Convert ed25519 keys to Curve25519 + const convertedPrivateKey = ed2curve.convertSecretKey(uint8PrivateKey); + const convertedPublicKey = ed2curve.convertPublicKey(senderPublicKey); + + // Generate shared secret + const sharedSecret = new Uint8Array(32); + nacl.lowlevel.crypto_scalarmult( + sharedSecret, + convertedPrivateKey, + convertedPublicKey + ); + + // Derive encryption key + const encryptionKey: Uint8Array = new Sha256() + .process(sharedSecret) + .finish().result; + + // Convert IV and ciphertext from Base64 + const base64ToUint8Array = (base64) => + Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)); + const ivUint8Array = base64ToUint8Array(iv); + const ciphertext = base64ToUint8Array(encryptedData); + // Validate IV and key lengths + if (ivUint8Array.length !== 12) { + throw new Error( + i18n.t('question:message.error.invalid_encryption_iv', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + if (encryptionKey.length !== 32) { + throw new Error( + i18n.t('question:message.error.invalid_encryption_key', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + try { + // Decrypt data + const algorithm = { name: 'AES-GCM', iv: ivUint8Array }; + const cryptoKey = await crypto.subtle.importKey( + 'raw', + encryptionKey, + algorithm, + false, + ['decrypt'] + ); + const decryptedArrayBuffer = await crypto.subtle.decrypt( + algorithm, + cryptoKey, + ciphertext + ); + + // Return decrypted data as Base64 + return uint8ArrayToBase64(new Uint8Array(decryptedArrayBuffer)); + } catch (error) { + console.error('Decryption failed:', error); + throw new Error( + i18n.t('question:message.error.decrypt_message', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const sellNameRequest = async (data, isFromExtension) => { + const requiredFields = ['salePrice', 'nameForSale']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const name = data.nameForSale; + const sellPrice = +data.salePrice; + + const validApi = await getBaseApi(); + + const response = await fetch(validApi + '/names/' + name); + const nameData = await response.json(); + if (!nameData) + throw new Error( + i18n.t('auth:message.error.name_not_existing', { + postProcess: 'capitalizeFirstChar', + }) + ); + + if (nameData?.isForSale) + throw new Error( + i18n.t('question:message.error.name_already_for_sale', { + postProcess: 'capitalizeFirstChar', + }) + ); + const fee = await getFee('SELL_NAME'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.sell_name_transaction', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t( + 'question:permission.sell_name_transaction_detail', + { + name: name, + price: sellPrice, + postProcess: 'capitalizeFirstChar', + } + ), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await sellName({ + name, + sellPrice, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const cancelSellNameRequest = async (data, isFromExtension) => { + const requiredFields = ['nameForSale']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + const name = data.nameForSale; + const validApi = await getBaseApi(); + + const response = await fetch(validApi + '/names/' + name); + const nameData = await response.json(); + if (!nameData?.isForSale) + throw new Error( + i18n.t('question:message.error.name_not_for_sale', { + postProcess: 'capitalizeFirstChar', + }) + ); + + const fee = await getFee('CANCEL_SELL_NAME'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.sell_name_cancel', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:name', { + name: name, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await cancelSellName({ + name, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const buyNameRequest = async (data, isFromExtension) => { + const requiredFields = ['nameForSale']; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(', '); + const errorMsg = i18n.t('question:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalizeFirstChar', + }); + throw new Error(errorMsg); + } + + const name = data.nameForSale; + const validApi = await getBaseApi(); + const response = await fetch(validApi + '/names/' + name); + const nameData = await response.json(); + + if (!nameData?.isForSale) + throw new Error( + i18n.t('question:message.error.name_not_for_sale', { + postProcess: 'capitalizeFirstChar', + }) + ); + + const sellerAddress = nameData.owner; + const sellPrice = +nameData.salePrice; + + const fee = await getFee('BUY_NAME'); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.buy_name', { + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:permission.buy_name_detail', { + name: name, + price: sellPrice, + postProcess: 'capitalizeFirstChar', + }), + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await buyName({ + name, + sellerAddress, + sellPrice, + }); + return response; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; + +export const signForeignFees = async (data, isFromExtension) => { + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.sign_fee', { + postProcess: 'capitalizeFirstChar', + }), + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + const unsignedFeesUrl = await createEndpoint( + `/crosschain/unsignedfees/${address}` + ); + + const unsignedFeesResponse = await fetch(unsignedFeesUrl); + + const unsignedFees = await unsignedFeesResponse.json(); + + const signedFees = []; + + unsignedFees.forEach((unsignedFee) => { + const unsignedDataDecoded = Base58.decode(unsignedFee.data); + + const signature = nacl.sign.detached( + unsignedDataDecoded, + keyPair.privateKey + ); + + const signedFee = { + timestamp: unsignedFee.timestamp, + data: `${Base58.encode(signature)}`, + atAddress: unsignedFee.atAddress, + fee: unsignedFee.fee, + }; + + signedFees.push(signedFee); + }); + + const signedFeesUrl = await createEndpoint(`/crosschain/signedfees`); + + await fetch(signedFeesUrl, { + method: 'POST', + headers: { + Accept: '*/*', + 'Content-Type': 'application/json', + }, + body: `${JSON.stringify(signedFees)}`, + }); + + return true; + } else { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } +}; +export const multiPaymentWithPrivateData = async (data, isFromExtension) => { + const requiredFields = ['payments', 'assetId']; + requiredFields.forEach((field) => { + if (data[field] === undefined || data[field] === null) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: field, + postProcess: 'capitalizeFirstChar', + }) + ); + } + }); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const privateKey = parsedData.privateKey; + const userPublicKey = parsedData.publicKey; + const { fee: paymentFee } = await getFee('TRANSFER_ASSET'); + const { fee: arbitraryFee } = await getFee('ARBITRARY'); + + let name = null; + const payments = data.payments; + const assetId = data.assetId; + const pendingTransactions = []; + const pendingAdditionalArbitraryTxs = []; + const additionalArbitraryTxsWithoutPayment = + data?.additionalArbitraryTxsWithoutPayment || []; + let totalAmount = 0; + let fee = 0; + for (const payment of payments) { + const paymentRefId = uid.rnd(); + const requiredFieldsPayment = ['recipient', 'amount']; + + for (const field of requiredFieldsPayment) { + if (!payment[field]) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: field, + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + + const confirmReceiver = await getNameOrAddress(payment.recipient); + if (confirmReceiver.error) { + throw new Error( + i18n.t('question:message.error.invalid_receiver', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const receiverPublicKey = await getPublicKey(confirmReceiver); + + const amount = +payment.amount.toFixed(8); + + pendingTransactions.push({ + type: 'PAYMENT', + recipientAddress: confirmReceiver, + amount: amount, + paymentRefId, + }); + + fee = fee + +paymentFee; + totalAmount = totalAmount + amount; + + if (payment.arbitraryTxs && payment.arbitraryTxs.length > 0) { + for (const arbitraryTx of payment.arbitraryTxs) { + const requiredFieldsArbitraryTx = ['service', 'identifier', 'base64']; + + for (const field of requiredFieldsArbitraryTx) { + if (!arbitraryTx[field]) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: field, + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + + if (!name) { + const getName = await getNameInfo(); + if (!getName) + throw new Error( + i18n.t('question:message.error.registered_name', { + postProcess: 'capitalizeFirstChar', + }) + ); + name = getName; + } + + const isValid = isValidBase64WithDecode(arbitraryTx.base64); + if (!isValid) + throw new Error( + i18n.t('core:message.error.invalid_base64', { + postProcess: 'capitalizeFirstChar', + }) + ); + if (!arbitraryTx?.service?.includes('_PRIVATE')) + throw new Error( + i18n.t('question:message.generic.private_service', { + postProcess: 'capitalizeFirstChar', + }) + ); + const additionalPublicKeys = arbitraryTx?.additionalPublicKeys || []; + pendingTransactions.push({ + type: 'ARBITRARY', + identifier: arbitraryTx.identifier, + service: arbitraryTx.service, + base64: arbitraryTx.base64, + description: arbitraryTx?.description || '', + paymentRefId, + publicKeys: [receiverPublicKey, ...additionalPublicKeys], + }); + + fee = fee + +arbitraryFee; + } + } + } + + if ( + additionalArbitraryTxsWithoutPayment && + additionalArbitraryTxsWithoutPayment.length > 0 + ) { + for (const arbitraryTx of additionalArbitraryTxsWithoutPayment) { + const requiredFieldsArbitraryTx = ['service', 'identifier', 'base64']; + + for (const field of requiredFieldsArbitraryTx) { + if (!arbitraryTx[field]) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: field, + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + + if (!name) { + const getName = await getNameInfo(); + if (!getName) + throw new Error( + i18n.t('question:message.error.registered_name', { + postProcess: 'capitalizeFirstChar', + }) + ); + name = getName; + } + + const isValid = isValidBase64WithDecode(arbitraryTx.base64); + if (!isValid) + throw new Error( + i18n.t('core:message.error.invalid_base64', { + postProcess: 'capitalizeFirstChar', + }) + ); + if (!arbitraryTx?.service?.includes('_PRIVATE')) + throw new Error( + i18n.t('question:message.generic.private_service', { + postProcess: 'capitalizeFirstChar', + }) + ); + const additionalPublicKeys = arbitraryTx?.additionalPublicKeys || []; + pendingAdditionalArbitraryTxs.push({ + type: 'ARBITRARY', + identifier: arbitraryTx.identifier, + service: arbitraryTx.service, + base64: arbitraryTx.base64, + description: arbitraryTx?.description || '', + publicKeys: additionalPublicKeys, + }); + + fee = fee + +arbitraryFee; + } + } + + if (!name) + throw new Error( + i18n.t('question:message.error.registered_name', { + postProcess: 'capitalizeFirstChar', + }) + ); + const balance = await getBalanceInfo(); + + if (+balance < fee) + throw new Error( + i18n.t('question:message.error.insufficient_balance_qort', { + postProcess: 'capitalizeFirstChar', + }) + ); + const assetBalance = await getAssetBalanceInfo(assetId); + const assetInfo = await getAssetInfo(assetId); + if (assetBalance < totalAmount) + throw new Error( + i18n.t('question:message.error.insufficient_balance', { + postProcess: 'capitalizeFirstChar', + }) + ); + + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.pay_publish', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:assets_used_pay', { + asset: assetInfo.name, + postProcess: 'capitalizeFirstChar', + }), + html: ` +
    + + + ${pendingTransactions + .filter((item) => item.type === 'PAYMENT') + .map( + (payment) => ` +
    +
    Recipient: ${ + payment.recipientAddress + }
    +
    Amount: ${payment.amount}
    +
    ` + ) + .join('')} + ${[...pendingTransactions, ...pendingAdditionalArbitraryTxs] + .filter((item) => item.type === 'ARBITRARY') + .map( + (arbitraryTx) => ` +
    +
    Service: ${ + arbitraryTx.service + }
    +
    Name: ${name}
    +
    Identifier: ${ + arbitraryTx.identifier + }
    +
    ` + ) + .join('')} +
    + + `, + highlightedText: `Total Amount: ${totalAmount}`, + fee: fee, + }, + isFromExtension + ); + + const { accepted, checkbox1 = false } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + + // const failedTxs = [] + const paymentsDone = {}; + + const transactionsDone = []; + + for (const transaction of pendingTransactions) { + const type = transaction.type; + + if (type === 'PAYMENT') { + const makePayment = await retryTransaction( + transferAsset, + [ + { + amount: transaction.amount, + assetId, + recipient: transaction.recipientAddress, + }, + ], + true + ); + if (makePayment) { + transactionsDone.push(makePayment?.signature); + if (transaction.paymentRefId) { + paymentsDone[transaction.paymentRefId] = makePayment; + } + } + } else if (type === 'ARBITRARY' && paymentsDone[transaction.paymentRefId]) { + const objectToEncrypt = { + data: transaction.base64, + payment: paymentsDone[transaction.paymentRefId], + }; + + const toBase64 = await retryTransaction( + objectToBase64, + [objectToEncrypt], + true + ); + + if (!toBase64) continue; // Skip if encryption fails + + const encryptDataResponse = await retryTransaction( + encryptDataGroup, + [ + { + data64: toBase64, + publicKeys: transaction.publicKeys, + privateKey, + userPublicKey, + }, + ], + true + ); + + if (!encryptDataResponse) continue; // Skip if encryption fails + + const resPublish = await retryTransaction( + publishData, + [ + { + registeredName: encodeURIComponent(name), + file: encryptDataResponse, + service: transaction.service, + identifier: encodeURIComponent(transaction.identifier), + uploadType: 'file', + description: transaction?.description, + isBase64: true, + apiVersion: 2, + withFee: true, + }, + ], + true + ); + + if (resPublish?.signature) { + transactionsDone.push(resPublish?.signature); + } + } + } + + for (const transaction of pendingAdditionalArbitraryTxs) { + const objectToEncrypt = { + data: transaction.base64, + }; + + const toBase64 = await retryTransaction( + objectToBase64, + [objectToEncrypt], + true + ); + + if (!toBase64) continue; // Skip if encryption fails + + const encryptDataResponse = await retryTransaction( + encryptDataGroup, + [ + { + data64: toBase64, + publicKeys: transaction.publicKeys, + privateKey, + userPublicKey, + }, + ], + true + ); + + if (!encryptDataResponse) continue; // Skip if encryption fails + + const resPublish = await retryTransaction( + publishData, + [ + { + registeredName: encodeURIComponent(name), + file: encryptDataResponse, + service: transaction.service, + identifier: encodeURIComponent(transaction.identifier), + uploadType: 'file', + description: transaction?.description, + isBase64: true, + apiVersion: 2, + withFee: true, + }, + ], + true + ); + + if (resPublish?.signature) { + transactionsDone.push(resPublish?.signature); + } + } + + return transactionsDone; +}; + +export const transferAssetRequest = async (data, isFromExtension) => { + const requiredFields = ['amount', 'assetId', 'recipient']; + requiredFields.forEach((field) => { + if (data[field] === undefined || data[field] === null) { + throw new Error( + i18n.t('question:message.error.missing_fields', { + fields: field, + postProcess: 'capitalizeFirstChar', + }) + ); + } + }); + const amount = data.amount; + const assetId = data.assetId; + const recipient = data.recipient; + + const { fee } = await getFee('TRANSFER_ASSET'); + const balance = await getBalanceInfo(); + + if (+balance < +fee) + throw new Error( + i18n.t('question:message.error.insufficient_balance_qort', { + postProcess: 'capitalizeFirstChar', + }) + ); + const assetBalance = await getAssetBalanceInfo(assetId); + if (assetBalance < amount) + throw new Error( + i18n.t('question:message.error.insufficient_balance', { + postProcess: 'capitalizeFirstChar', + }) + ); + const confirmReceiver = await getNameOrAddress(recipient); + if (confirmReceiver.error) { + throw new Error( + i18n.t('question:message.error.invalid_receiver', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const assetInfo = await getAssetInfo(assetId); + const resPermission = await getUserPermission( + { + text1: i18n.t('question:permission.transfer_asset', { + postProcess: 'capitalizeFirstChar', + }), + text2: i18n.t('question:asset_name', { + asset: assetInfo?.name, + postProcess: 'capitalizeFirstChar', + }), + highlightedText: i18n.t('question:amount_qty', { + quantity: amount, + postProcess: 'capitalizeFirstChar', + }), + fee: fee, + }, + isFromExtension + ); + + const { accepted } = resPermission; + if (!accepted) { + throw new Error( + i18n.t('question:message.generic.user_declined_request', { + postProcess: 'capitalizeFirstChar', + }) + ); + } + const res = await transferAsset({ + amount, + recipient: confirmReceiver, + assetId, + }); + return res; +}; diff --git a/src/qortalRequests/qortalRequests.ts b/src/qortal/qortal-requests.ts similarity index 100% rename from src/qortalRequests/qortalRequests.ts rename to src/qortal/qortal-requests.ts diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index 7957dc4..6819842 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -37,27 +37,18 @@ import { getAssetInfo, getPublicKey, transferAsset, -<<<<<<< HEAD -} from '../background'; -import { - getAllUserNames, - getNameInfo, - uint8ArrayToObject, -} from '../backgroundFunctions/encryption'; -======= } from '../background/background.ts'; import { getNameInfo, uint8ArrayToObject } from '../encryption/encryption.ts'; ->>>>>>> 2d01b3e (Create encryption folder and move files) -import { showSaveFilePicker } from '../hooks/useQortalMessageListener'; -import { getPublishesFromAdminsAdminSpace } from '../components/Chat/AdminSpaceInner'; -import { extractComponents } from '../components/Chat/MessageDisplay'; +import { showSaveFilePicker } from '../hooks/useQortalMessageListener.tsx'; +import { getPublishesFromAdminsAdminSpace } from '../components/Chat/AdminSpaceInner.tsx'; +import { extractComponents } from '../components/Chat/MessageDisplay.tsx'; import { decryptResource, getGroupAdmins, getPublishesFromAdmins, validateSecretKey, -} from '../components/Group/Group'; -import { QORT_DECIMALS } from '../constants/constants'; +} from '../components/Group/Group.tsx'; +import { QORT_DECIMALS } from '../constants/constants.ts'; import Base58 from '../encryption/Base58.ts'; import ed2curve from '../encryption/ed2curve.ts'; import nacl from '../encryption/nacl-fast.ts'; @@ -73,24 +64,24 @@ import { objectToBase64, uint8ArrayStartsWith, uint8ArrayToBase64, -} from '../qdn/encryption/group-encryption'; +} from '../qdn/encryption/group-encryption.ts'; import { publishData } from '../qdn/publish/publish.ts'; import { getPermission, isRunningGateway, setPermission, } from './qortalRequests.ts'; -import TradeBotCreateRequest from '../transactions/TradeBotCreateRequest'; -import DeleteTradeOffer from '../transactions/TradeBotDeleteRequest'; -import signTradeBotTransaction from '../transactions/signTradeBotTransaction'; -import { createTransaction } from '../transactions/transactions'; -import { executeEvent } from '../utils/events'; -import { fileToBase64 } from '../utils/fileReading'; -import { mimeToExtensionMap } from '../utils/memeTypes'; -import { RequestQueueWithPromise } from '../utils/queue/queue'; -import utils from '../utils/utils'; +import TradeBotCreateRequest from '../transactions/TradeBotCreateRequest.ts'; +import DeleteTradeOffer from '../transactions/TradeBotDeleteRequest.ts'; +import signTradeBotTransaction from '../transactions/signTradeBotTransaction.ts'; +import { createTransaction } from '../transactions/transactions.ts'; +import { executeEvent } from '../utils/events.ts'; +import { fileToBase64 } from '../utils/fileReading/index.ts'; +import { mimeToExtensionMap } from '../utils/memeTypes.ts'; +import { RequestQueueWithPromise } from '../utils/queue/queue.ts'; +import utils from '../utils/utils.ts'; import ShortUniqueId from 'short-unique-id'; -import { isValidBase64WithDecode } from '../utils/decode'; +import { isValidBase64WithDecode } from '../utils/decode.ts'; import i18n from 'i18next'; const uid = new ShortUniqueId({ length: 6 }); From a996015b2055672f92f1a3522369e6ef387ac2ae Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 12:12:25 +0200 Subject: [PATCH 472/717] Remove CustomSvg (unused) --- src/common/CustomSvg.tsx | 14 -------------- src/components/Embeds/AttachmentEmbed.tsx | 3 +-- src/components/Group/Forum/Thread.tsx | 1 + 3 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 src/common/CustomSvg.tsx diff --git a/src/common/CustomSvg.tsx b/src/common/CustomSvg.tsx deleted file mode 100644 index fc805a9..0000000 --- a/src/common/CustomSvg.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export const CustomSvg = ({ src, color = 'black', size = 24 }) => { - return ( - - {src} - - ); -}; diff --git a/src/components/Embeds/AttachmentEmbed.tsx b/src/components/Embeds/AttachmentEmbed.tsx index d7458a7..adbcd82 100644 --- a/src/components/Embeds/AttachmentEmbed.tsx +++ b/src/components/Embeds/AttachmentEmbed.tsx @@ -226,8 +226,7 @@ export const AttachmentCard = ({ width: '100%', }} > - {' '} - {' '} +
    )} {errorMsg && ( diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index bf8682f..a293d5f 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -931,6 +931,7 @@ export const Thread = ({ }} > + Date: Sat, 24 May 2025 12:14:06 +0200 Subject: [PATCH 473/717] List all namespaces in useTranslation --- src/App.tsx | 8 +++++++- src/Wallets.tsx | 16 ++++++++++++++-- src/components/Apps/AppInfo.tsx | 8 +++++++- src/components/Apps/AppInfoSnippet.tsx | 8 +++++++- src/components/Apps/AppPublish.tsx | 8 +++++++- src/components/Apps/AppRating.tsx | 8 +++++++- src/components/Apps/AppsCategoryDesktop.tsx | 8 +++++++- src/components/Apps/AppsDesktop.tsx | 8 +++++++- src/components/Apps/AppsDevMode.tsx | 8 +++++++- src/components/Apps/AppsDevModeHome.tsx | 8 +++++++- src/components/Apps/AppsHomeDesktop.tsx | 8 +++++++- src/components/Apps/AppsLibraryDesktop.tsx | 8 +++++++- src/components/Apps/AppsNavBarDesktop.tsx | 8 +++++++- src/components/Apps/AppsPrivate.tsx | 8 +++++++- src/components/BuyQortInformation.tsx | 8 +++++++- src/components/Chat/AdminSpace.tsx | 8 +++++++- src/components/Chat/AdminSpaceInner.tsx | 8 +++++++- src/components/Chat/AnnouncementDiscussion.tsx | 8 +++++++- src/components/Chat/AnnouncementItem.tsx | 8 +++++++- src/components/Chat/AnnouncementList.tsx | 8 +++++++- src/components/Chat/ChatDirect.tsx | 8 +++++++- src/components/Chat/ChatGroup.tsx | 8 +++++++- src/components/Chat/ChatList.tsx | 8 +++++++- src/components/Chat/ChatOptions.tsx | 8 +++++++- src/components/Chat/CreateCommonSecret.tsx | 8 +++++++- src/components/Chat/GroupAnnouncements.tsx | 8 +++++++- src/components/Chat/GroupAvatar.tsx | 16 ++++++++++++++-- src/components/Chat/MentionList.tsx | 8 +++++++- src/components/Chat/MessageItem.tsx | 16 ++++++++++++++-- src/components/Chat/TipTap.tsx | 8 +++++++- src/components/CoreSyncStatus.tsx | 8 +++++++- src/components/Desktop/DesktopFooter.tsx | 8 +++++++- src/components/Desktop/DesktopHeader.tsx | 8 +++++++- src/components/Desktop/DesktopSideBar.tsx | 8 +++++++- src/components/Embeds/AttachmentEmbed.tsx | 8 +++++++- src/components/Embeds/Embed.tsx | 8 +++++++- src/components/Embeds/ImageEmbed.tsx | 8 +++++++- src/components/Embeds/PollEmbed.tsx | 16 ++++++++++++++-- src/components/GeneralNotifications.tsx | 8 +++++++- src/components/GlobalActions/JoinGroup.tsx | 8 +++++++- src/components/Group/AddGroup.tsx | 8 +++++++- src/components/Group/AddGroupList.tsx | 8 +++++++- src/components/Group/BlockedUsersModal.tsx | 8 +++++++- src/components/Group/Forum/GroupMail.tsx | 8 +++++++- src/components/Group/Forum/NewThread.tsx | 8 +++++++- src/components/Group/Forum/Thread.tsx | 8 +++++++- src/components/Group/Group.tsx | 8 +++++++- src/components/Group/GroupInvites.tsx | 8 +++++++- src/components/Group/GroupJoinRequests.tsx | 8 +++++++- src/components/Group/GroupList.tsx | 8 +++++++- src/components/Group/HomeDesktop.tsx | 8 +++++++- src/components/Group/InviteMember.tsx | 8 +++++++- src/components/Group/ListOfBans.tsx | 8 +++++++- src/components/Group/ListOfGroupPromotions.tsx | 8 +++++++- src/components/Group/ListOfInvites.tsx | 8 +++++++- src/components/Group/ListOfJoinRequests.tsx | 8 +++++++- src/components/Group/ListOfMembers.tsx | 8 +++++++- .../Group/ListOfThreadPostsWatched.tsx | 8 +++++++- src/components/Group/ManageMembers.tsx | 8 +++++++- src/components/Group/QMailMessages.tsx | 8 +++++++- src/components/Group/Settings.tsx | 16 ++++++++++++++-- src/components/Group/UserListOfInvites.tsx | 8 +++++++- src/components/Group/WalletsAppWrapper.tsx | 8 +++++++- src/components/MainAvatar.tsx | 16 ++++++++++++++-- src/components/Minting/Minting.tsx | 8 +++++++- src/components/NewUsersCTA.tsx | 8 +++++++- src/components/QMailStatus.tsx | 8 +++++++- src/components/QortPayment.tsx | 8 +++++++- src/components/RegisterName.tsx | 8 +++++++- src/components/Save/Save.tsx | 8 +++++++- src/components/Theme/ThemeManager.tsx | 8 +++++++- src/components/Theme/ThemeSelector.tsx | 8 +++++++- src/components/UserLookup.tsx/UserLookup.tsx | 8 +++++++- src/components/WrapperUserAction.tsx | 16 ++++++++++++++-- src/hooks/useHandlePrivateApps.tsx | 8 +++++++- 75 files changed, 574 insertions(+), 82 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 4fd9057..f58c7c2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -308,7 +308,13 @@ function App() { const [isLoading, setIsLoading] = useState(false); const [isLoadingSendCoin, setIsLoadingSendCoin] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); const [ diff --git a/src/Wallets.tsx b/src/Wallets.tsx index e6e4906..c0aa098 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -53,7 +53,13 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { const [isOpenSeedModal, setIsOpenSeedModal] = useState(false); const [isLoadingEncryptSeed, setIsLoadingEncryptSeed] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const { isShow, onCancel, onOk, show } = useModal(); const { getRootProps, getInputProps } = useDropzone({ @@ -461,7 +467,13 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => { const [note, setNote] = useState(''); const [isEdit, setIsEdit] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); useEffect(() => { if (wallet?.name) { diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index 1c77d25..180dac4 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -37,7 +37,13 @@ export const AppInfo = ({ app, myName }) => { ); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const isSelectedAppPinned = !!sortablePinnedApps?.find( (item) => item?.name === app?.name && item?.service === app?.service diff --git a/src/components/Apps/AppInfoSnippet.tsx b/src/components/Apps/AppInfoSnippet.tsx index a0b8f72..41391dc 100644 --- a/src/components/Apps/AppInfoSnippet.tsx +++ b/src/components/Apps/AppInfoSnippet.tsx @@ -42,7 +42,13 @@ export const AppInfoSnippet = ({ ); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( { const [file, setFile] = useState(null); const { show } = useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [tag1, setTag1] = useState(''); const [tag2, setTag2] = useState(''); const [tag3, setTag3] = useState(''); diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index edd628a..b0f8258 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -20,7 +20,13 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { const [openSnack, setOpenSnack] = useState(false); const [infoSnack, setInfoSnack] = useState(null); const hasCalledRef = useRef(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getRating = useCallback(async (name, service) => { try { diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index 4c4a541..3752353 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -45,7 +45,13 @@ export const AppsCategoryDesktop = ({ const [searchValue, setSearchValue] = useState(''); const virtuosoRef = useRef(null); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const categoryList = useMemo(() => { if (category?.id === 'all') return availableQapps; diff --git a/src/components/Apps/AppsDesktop.tsx b/src/components/Apps/AppsDesktop.tsx index 71472db..369adc9 100644 --- a/src/components/Apps/AppsDesktop.tsx +++ b/src/components/Apps/AppsDesktop.tsx @@ -51,7 +51,13 @@ export const AppsDesktop = ({ const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); const { showTutorial } = useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const myApp = useMemo(() => { return availableQapps.find( diff --git a/src/components/Apps/AppsDevMode.tsx b/src/components/Apps/AppsDevMode.tsx index 89358f5..7d90e0b 100644 --- a/src/components/Apps/AppsDevMode.tsx +++ b/src/components/Apps/AppsDevMode.tsx @@ -47,7 +47,13 @@ export const AppsDevMode = ({ const [categories, setCategories] = useState([]); const iframeRefs = useRef({}); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); useEffect(() => { setTimeout(() => { diff --git a/src/components/Apps/AppsDevModeHome.tsx b/src/components/Apps/AppsDevModeHome.tsx index f9ade5d..88218ca 100644 --- a/src/components/Apps/AppsDevModeHome.tsx +++ b/src/components/Apps/AppsDevModeHome.tsx @@ -41,7 +41,13 @@ export const AppsDevModeHome = ({ const [domain, setDomain] = useState('127.0.0.1'); const [port, setPort] = useState(''); const [selectedPreviewFile, setSelectedPreviewFile] = useState(null); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const { isShow, onCancel, onOk, show, message } = useModal(); const { openSnackGlobal, diff --git a/src/components/Apps/AppsHomeDesktop.tsx b/src/components/Apps/AppsHomeDesktop.tsx index 9c7d0d3..137e6b3 100644 --- a/src/components/Apps/AppsHomeDesktop.tsx +++ b/src/components/Apps/AppsHomeDesktop.tsx @@ -28,7 +28,13 @@ export const AppsHomeDesktop = ({ }) => { const [qortalUrl, setQortalUrl] = useState(''); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const openQortalUrl = () => { try { diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index d953bff..d844dbf 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -105,7 +105,13 @@ export const AppsLibraryDesktop = ({ const [searchValue, setSearchValue] = useState(''); const virtuosoRef = useRef(null); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const officialApps = useMemo(() => { return availableQapps.filter( diff --git a/src/components/Apps/AppsNavBarDesktop.tsx b/src/components/Apps/AppsNavBarDesktop.tsx index e6d2ea2..616c09f 100644 --- a/src/components/Apps/AppsNavBarDesktop.tsx +++ b/src/components/Apps/AppsNavBarDesktop.tsx @@ -76,7 +76,13 @@ export const AppsNavBarDesktop = ({ disableBack }) => { ); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isNewTabWindow, setIsNewTabWindow] = useState(false); const tabsRef = useRef(null); const [anchorEl, setAnchorEl] = useState(null); diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index f24d15a..56b33d9 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -73,7 +73,13 @@ export const AppsPrivate = ({ myName, myAddress }) => { const [memberGroups] = useAtom(memberGroupsAtom); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const myGroupsPrivate = useMemo(() => { return memberGroups?.filter( diff --git a/src/components/BuyQortInformation.tsx b/src/components/BuyQortInformation.tsx index e10f535..c3519fa 100644 --- a/src/components/BuyQortInformation.tsx +++ b/src/components/BuyQortInformation.tsx @@ -27,7 +27,13 @@ import { useTranslation } from 'react-i18next'; export const BuyQortInformation = ({ balance }) => { const [isOpen, setIsOpen] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const openBuyQortInfoFunc = useCallback( (e) => { diff --git a/src/components/Chat/AdminSpace.tsx b/src/components/Chat/AdminSpace.tsx index 276369a..62a8832 100644 --- a/src/components/Chat/AdminSpace.tsx +++ b/src/components/Chat/AdminSpace.tsx @@ -19,7 +19,13 @@ export const AdminSpace = ({ isOwner, }) => { const [isMoved, setIsMoved] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); useEffect(() => { if (hide) { diff --git a/src/components/Chat/AdminSpaceInner.tsx b/src/components/Chat/AdminSpaceInner.tsx index 97e3fd1..7dbb705 100644 --- a/src/components/Chat/AdminSpaceInner.tsx +++ b/src/components/Chat/AdminSpaceInner.tsx @@ -74,7 +74,13 @@ export const AdminSpaceInner = ({ const [isLoadingPublishKey, setIsLoadingPublishKey] = useState(false); const { show, setInfoSnackCustom, setOpenSnackGlobal } = useContext(QORTAL_APP_CONTEXT); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getAdminGroupSecretKey = useCallback(async () => { try { diff --git a/src/components/Chat/AnnouncementDiscussion.tsx b/src/components/Chat/AnnouncementDiscussion.tsx index e0da144..b248735 100644 --- a/src/components/Chat/AnnouncementDiscussion.tsx +++ b/src/components/Chat/AnnouncementDiscussion.tsx @@ -40,7 +40,13 @@ export const AnnouncementDiscussion = ({ isPrivate, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isSending, setIsSending] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isFocusedParent, setIsFocusedParent] = useState(false); diff --git a/src/components/Chat/AnnouncementItem.tsx b/src/components/Chat/AnnouncementItem.tsx index 98ebe11..a20f58d 100644 --- a/src/components/Chat/AnnouncementItem.tsx +++ b/src/components/Chat/AnnouncementItem.tsx @@ -18,7 +18,13 @@ export const AnnouncementItem = ({ myName, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [commentLength, setCommentLength] = useState(0); const getNumberOfComments = useCallback(async () => { diff --git a/src/components/Chat/AnnouncementList.tsx b/src/components/Chat/AnnouncementList.tsx index ba2892e..eea6c83 100644 --- a/src/components/Chat/AnnouncementList.tsx +++ b/src/components/Chat/AnnouncementList.tsx @@ -20,7 +20,13 @@ export const AnnouncementList = ({ myName, }) => { const [messages, setMessages] = useState(initialMessages); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); useEffect(() => { cache.clearAll(); diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index 36681e9..0af54a6 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -42,7 +42,13 @@ export const ChatDirect = ({ setMobileViewModeKeepOpen, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue(); const [isFocusedParent, setIsFocusedParent] = useState(false); const [onEditMessage, setOnEditMessage] = useState(null); diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index e6b75d0..6fada40 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -96,7 +96,13 @@ export const ChatGroup = ({ const [, forceUpdate] = useReducer((x) => x + 1, 0); const lastReadTimestamp = useRef(null); const handleUpdateRef = useRef(null); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getTimestampEnterChat = async (selectedGroup) => { try { diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 96abf7a..1a47778 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -181,7 +181,13 @@ export const ChatList = ({ }, []); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( { const queryString = admins.map((name) => `name=${name}`).join('&'); diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index 18170cd..7e1e5f9 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -153,7 +153,13 @@ export const GroupAnnouncements = ({ editorRef.current = editorInstance; }; const [, forceUpdate] = useReducer((x) => x + 1, 0); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const triggerRerender = () => { forceUpdate(); // Trigger re-render by updating the state diff --git a/src/components/Chat/GroupAvatar.tsx b/src/components/Chat/GroupAvatar.tsx index ecc7c86..f7322bd 100644 --- a/src/components/Chat/GroupAvatar.tsx +++ b/src/components/Chat/GroupAvatar.tsx @@ -33,7 +33,13 @@ export const GroupAvatar = ({ const [avatarFile, setAvatarFile] = useState(null); const [tempAvatar, setTempAvatar] = useState(null); const { show } = useContext(QORTAL_APP_CONTEXT); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [anchorEl, setAnchorEl] = useState(null); const [isLoading, setIsLoading] = useState(false); // Handle child element click to open Popover @@ -263,7 +269,13 @@ const PopoverComp = ({ myName, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( { - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [selectedIndex, setSelectedIndex] = useState(0); diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index a429846..f570ba2 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -170,7 +170,13 @@ export const MessageItem = memo( }, [message?.id]); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( <> @@ -612,7 +618,13 @@ export const MessageItem = memo( export const ReplyPreview = ({ message, isEdit = false }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( { if (editor && setEditorRef) { diff --git a/src/components/CoreSyncStatus.tsx b/src/components/CoreSyncStatus.tsx index 09a50ed..345678f 100644 --- a/src/components/CoreSyncStatus.tsx +++ b/src/components/CoreSyncStatus.tsx @@ -13,7 +13,13 @@ export const CoreSyncStatus = () => { const [coreInfos, setCoreInfos] = useState({}); const [isUsingGateway, setIsUsingGateway] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); useEffect(() => { diff --git a/src/components/Desktop/DesktopFooter.tsx b/src/components/Desktop/DesktopFooter.tsx index ac567ec..3441cf6 100644 --- a/src/components/Desktop/DesktopFooter.tsx +++ b/src/components/Desktop/DesktopFooter.tsx @@ -67,7 +67,13 @@ export const DesktopFooter = ({ }) => { const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); if (hide) return; return ( diff --git a/src/components/Desktop/DesktopHeader.tsx b/src/components/Desktop/DesktopHeader.tsx index c82eae0..0428462 100644 --- a/src/components/Desktop/DesktopHeader.tsx +++ b/src/components/Desktop/DesktopHeader.tsx @@ -84,7 +84,13 @@ export const DesktopHeader = ({ }) => { const [value, setValue] = useState(0); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( { const { name, service, identifier } = resourceData; diff --git a/src/components/Embeds/Embed.tsx b/src/components/Embeds/Embed.tsx index 6be25f7..0038830 100644 --- a/src/components/Embeds/Embed.tsx +++ b/src/components/Embeds/Embed.tsx @@ -63,7 +63,13 @@ export const Embed = ({ embedLink }) => { const [parsedData, setParsedData] = useState(null); const setBlobs = useSetAtom(blobControllerAtom); const [selectedGroupId] = useAtom(selectedGroupIdAtom); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const resourceData = useMemo(() => { const parsedDataOnTheFly = parseQortalLink(embedLink); if ( diff --git a/src/components/Embeds/ImageEmbed.tsx b/src/components/Embeds/ImageEmbed.tsx index a7b6534..ab0548d 100644 --- a/src/components/Embeds/ImageEmbed.tsx +++ b/src/components/Embeds/ImageEmbed.tsx @@ -30,7 +30,13 @@ export const ImageCard = ({ encryptionType, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isOpen, setIsOpen] = useState(true); const [height, setHeight] = useState('400px'); diff --git a/src/components/Embeds/PollEmbed.tsx b/src/components/Embeds/PollEmbed.tsx index 6d7a510..eb3e304 100644 --- a/src/components/Embeds/PollEmbed.tsx +++ b/src/components/Embeds/PollEmbed.tsx @@ -40,7 +40,13 @@ export const PollCard = ({ const { show, userInfo } = useContext(QORTAL_APP_CONTEXT); const [isLoadingSubmit, setIsLoadingSubmit] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const handleVote = async () => { const fee = await getFee('VOTE_ON_POLL'); @@ -379,7 +385,13 @@ const PollResults = ({ votes }) => { ...votes?.voteCounts?.map((option) => option.voteCount) ); const options = votes?.voteCounts; - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( diff --git a/src/components/GeneralNotifications.tsx b/src/components/GeneralNotifications.tsx index 3e23637..53e85a8 100644 --- a/src/components/GeneralNotifications.tsx +++ b/src/components/GeneralNotifications.tsx @@ -32,7 +32,13 @@ export const GeneralNotifications = ({ address }) => { setAnchorEl(event.currentTarget); }; - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); return ( diff --git a/src/components/GlobalActions/JoinGroup.tsx b/src/components/GlobalActions/JoinGroup.tsx index 95e95e6..aca7e1f 100644 --- a/src/components/GlobalActions/JoinGroup.tsx +++ b/src/components/GlobalActions/JoinGroup.tsx @@ -29,7 +29,13 @@ export const JoinGroup = () => { const [isLoadingInfo, setIsLoadingInfo] = useState(false); const [isOpen, setIsOpen] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isLoadingJoinGroup, setIsLoadingJoinGroup] = useState(false); const handleJoinGroup = async (e) => { diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index ec01443..3815d0d 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -97,7 +97,13 @@ export const AddGroup = ({ address, open, setOpen }) => { setMaxBlock(event.target.value as string); }; - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); const handleCreateGroup = async () => { diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index 8cad787..db4460c 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -42,7 +42,13 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const { show } = useContext(QORTAL_APP_CONTEXT); const [memberGroups] = useAtom(memberGroupsAtom); const setTxList = useSetAtom(txListAtom); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [groups, setGroups] = useState([]); const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index c8145b3..8cdd358 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -28,7 +28,13 @@ import { useTranslation } from 'react-i18next'; export const BlockedUsersModal = () => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isOpenBlockedModal, setIsOpenBlockedModal] = useAtom( isOpenBlockedModalAtom ); diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index afa6e91..ba4b620 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -73,7 +73,13 @@ export const GroupMail = ({ const anchorElInstanceFilter = useRef(null); const [tempPublishedList, setTempPublishedList] = useState([]); const dataPublishes = useRef({}); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); const [isLoading, setIsLoading] = useState(false); const groupIdRef = useRef(null); diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index 9749480..f89e2aa 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -144,7 +144,13 @@ export const NewThread = ({ setPostReply, isPrivate, }: NewMessageProps) => { - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const { show } = useContext(QORTAL_APP_CONTEXT); const [isOpen, setIsOpen] = useState(false); const [value, setValue] = useState(''); diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index a293d5f..762db5d 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -115,7 +115,13 @@ export const Thread = ({ const [isLoading, setIsLoading] = useState(true); const [postReply, setPostReply] = useState(null); const [hasLastPage, setHasLastPage] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); // Update: Use a new ref for the scrollable container const threadContainerRef = useRef(null); diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 368dd15..8f312ec 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -444,7 +444,13 @@ export const Group = ({ const [isForceShowCreationKeyPopup, setIsForceShowCreationKeyPopup] = useState(false); const groupsOwnerNamesRef = useRef({}); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [groupsProperties, setGroupsProperties] = useAtom(groupsPropertiesAtom); const setGroupsOwnerNames = useSetAtom(groupsOwnerNamesAtom); diff --git a/src/components/Group/GroupInvites.tsx b/src/components/Group/GroupInvites.tsx index 96f375c..8bad077 100644 --- a/src/components/Group/GroupInvites.tsx +++ b/src/components/Group/GroupInvites.tsx @@ -18,7 +18,13 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { const [groupsWithJoinRequests, setGroupsWithJoinRequests] = useState([]); const [isExpanded, setIsExpanded] = useState(false); const [loading, setLoading] = useState(true); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); const getJoinRequests = async () => { diff --git a/src/components/Group/GroupJoinRequests.tsx b/src/components/Group/GroupJoinRequests.tsx index 8f617a1..1310174 100644 --- a/src/components/Group/GroupJoinRequests.tsx +++ b/src/components/Group/GroupJoinRequests.tsx @@ -28,7 +28,13 @@ export const GroupJoinRequests = ({ setDesktopViewMode, }) => { const [isExpanded, setIsExpanded] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [groupsWithJoinRequests, setGroupsWithJoinRequests] = useState([]); const [loading, setLoading] = useState(true); const [txList] = useAtom(txListAtom); diff --git a/src/components/Group/GroupList.tsx b/src/components/Group/GroupList.tsx index 156d744..e928331 100644 --- a/src/components/Group/GroupList.tsx +++ b/src/components/Group/GroupList.tsx @@ -49,7 +49,13 @@ export const GroupList = ({ myAddress, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isRunningPublicNode] = useAtom(isRunningPublicNodeAtom); return ( diff --git a/src/components/Group/HomeDesktop.tsx b/src/components/Group/HomeDesktop.tsx index a5cf85e..f148be1 100644 --- a/src/components/Group/HomeDesktop.tsx +++ b/src/components/Group/HomeDesktop.tsx @@ -31,7 +31,13 @@ export const HomeDesktop = ({ const [checked1, setChecked1] = useState(false); const [checked2, setChecked2] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); useEffect(() => { diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx index 8a562d0..8ac8d93 100644 --- a/src/components/Group/InviteMember.tsx +++ b/src/components/Group/InviteMember.tsx @@ -10,7 +10,13 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const [value, setValue] = useState(''); const [expiryTime, setExpiryTime] = useState('259200'); const [isLoadingInvite, setIsLoadingInvite] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const inviteMember = async () => { try { diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index 122ce27..fc84cc9 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -56,7 +56,13 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const listRef = useRef(null); const [isLoadingUnban, setIsLoadingUnban] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getInvites = async (groupId) => { try { diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index 01fbed3..c077475 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -91,7 +91,13 @@ export const ListOfGroupPromotions = () => { const { show } = useContext(QORTAL_APP_CONTEXT); const setTxList = useSetAtom(txListAtom); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const listRef = useRef(null); const rowVirtualizer = useVirtualizer({ count: promotions.length, diff --git a/src/components/Group/ListOfInvites.tsx b/src/components/Group/ListOfInvites.tsx index 037dddc..3758863 100644 --- a/src/components/Group/ListOfInvites.tsx +++ b/src/components/Group/ListOfInvites.tsx @@ -60,7 +60,13 @@ export const ListOfInvites = ({ const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const [isLoadingCancelInvite, setIsLoadingCancelInvite] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const listRef = useRef(null); const getInvites = async (groupId) => { diff --git a/src/components/Group/ListOfJoinRequests.tsx b/src/components/Group/ListOfJoinRequests.tsx index 5fbfe9e..2a65cfe 100644 --- a/src/components/Group/ListOfJoinRequests.tsx +++ b/src/components/Group/ListOfJoinRequests.tsx @@ -64,7 +64,13 @@ export const ListOfJoinRequests = ({ const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const listRef = useRef(null); const [isLoadingAccept, setIsLoadingAccept] = useState(false); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getInvites = async (groupId) => { try { diff --git a/src/components/Group/ListOfMembers.tsx b/src/components/Group/ListOfMembers.tsx index c57e501..1d23b44 100644 --- a/src/components/Group/ListOfMembers.tsx +++ b/src/components/Group/ListOfMembers.tsx @@ -42,7 +42,13 @@ const ListOfMembers = ({ const [isLoadingMakeAdmin, setIsLoadingMakeAdmin] = useState(false); const [isLoadingRemoveAdmin, setIsLoadingRemoveAdmin] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const listRef = useRef(null); const handlePopoverOpen = (event, index) => { diff --git a/src/components/Group/ListOfThreadPostsWatched.tsx b/src/components/Group/ListOfThreadPostsWatched.tsx index 0868a53..8a3c3d2 100644 --- a/src/components/Group/ListOfThreadPostsWatched.tsx +++ b/src/components/Group/ListOfThreadPostsWatched.tsx @@ -14,7 +14,13 @@ import { useTranslation } from 'react-i18next'; export const ListOfThreadPostsWatched = () => { const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getPosts = async () => { try { diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index e87b002..b8c7052 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -71,7 +71,13 @@ export const ManageMembers = ({ setValue(newValue); }; const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const { show } = useContext(QORTAL_APP_CONTEXT); const setTxList = useSetAtom(txListAtom); diff --git a/src/components/Group/QMailMessages.tsx b/src/components/Group/QMailMessages.tsx index f6490e0..93d96cb 100644 --- a/src/components/Group/QMailMessages.tsx +++ b/src/components/Group/QMailMessages.tsx @@ -54,7 +54,13 @@ export const QMailMessages = ({ userName, userAddress }) => { const [loading, setLoading] = useState(true); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const getMails = useCallback(async () => { try { diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index 78db9bc..ef6d0cb 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -87,7 +87,13 @@ export const Settings = ({ open, setOpen, rawWallet }) => { const [checked, setChecked] = useState(false); const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const handleChange = (event: ChangeEvent) => { setChecked(event.target.checked); @@ -235,7 +241,13 @@ const ExportPrivateKey = ({ rawWallet }) => { const [isOpen, setIsOpen] = useState(false); const { setOpenSnackGlobal, setInfoSnackCustom } = useContext(QORTAL_APP_CONTEXT); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const exportPrivateKeyFunc = async () => { try { diff --git a/src/components/Group/UserListOfInvites.tsx b/src/components/Group/UserListOfInvites.tsx index 2717af9..50cbb5b 100644 --- a/src/components/Group/UserListOfInvites.tsx +++ b/src/components/Group/UserListOfInvites.tsx @@ -61,7 +61,13 @@ export const UserListOfInvites = ({ const [invites, setInvites] = useState([]); const [isLoading, setIsLoading] = useState(false); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const listRef = useRef(null); diff --git a/src/components/Group/WalletsAppWrapper.tsx b/src/components/Group/WalletsAppWrapper.tsx index ee9885a..990544d 100644 --- a/src/components/Group/WalletsAppWrapper.tsx +++ b/src/components/Group/WalletsAppWrapper.tsx @@ -15,7 +15,13 @@ import { useAtom } from 'jotai'; import { useTranslation } from 'react-i18next'; export const WalletsAppWrapper = () => { - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const iframeRef = useRef(null); const [isOpen, setIsOpen] = useState(false); const [navigationController, setNavigationController] = useAtom( diff --git a/src/components/MainAvatar.tsx b/src/components/MainAvatar.tsx index b458145..9f6945e 100644 --- a/src/components/MainAvatar.tsx +++ b/src/components/MainAvatar.tsx @@ -45,7 +45,13 @@ export const MainAvatar = ({ myName, balance, setOpenSnack, setInfoSnack }) => { const open = Boolean(anchorEl); const id = open ? 'avatar-img' : undefined; - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const checkIfAvatarExists = async () => { try { @@ -258,7 +264,13 @@ const PopoverComp = ({ myName, }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); return ( { const { show: showKey, message } = useModal(); const { isShow: isShowNext, onOk, show: showNext } = useModal(); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [info, setInfo] = useState(null); const [names, setNames] = useState({}); const [accountInfos, setAccountInfos] = useState({}); diff --git a/src/components/NewUsersCTA.tsx b/src/components/NewUsersCTA.tsx index 94606d1..d444717 100644 --- a/src/components/NewUsersCTA.tsx +++ b/src/components/NewUsersCTA.tsx @@ -3,7 +3,13 @@ import { Spacer } from '../common/Spacer'; import { useTranslation } from 'react-i18next'; export const NewUsersCTA = ({ balance }) => { - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); if (balance === undefined || +balance > 0) return null; diff --git a/src/components/QMailStatus.tsx b/src/components/QMailStatus.tsx index 45e51c8..f0b51f3 100644 --- a/src/components/QMailStatus.tsx +++ b/src/components/QMailStatus.tsx @@ -8,7 +8,13 @@ import { useTranslation } from 'react-i18next'; import { useAtom } from 'jotai'; export const QMailStatus = () => { - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const theme = useTheme(); const [lastEnteredTimestamp, setLastEnteredTimestamp] = useAtom( diff --git a/src/components/QortPayment.tsx b/src/components/QortPayment.tsx index 1907686..f31c7c0 100644 --- a/src/components/QortPayment.tsx +++ b/src/components/QortPayment.tsx @@ -7,7 +7,13 @@ import { useTranslation } from 'react-i18next'; export const QortPayment = ({ balance, show, onSuccess, defaultPaymentTo }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [paymentTo, setPaymentTo] = useState(defaultPaymentTo); const [paymentAmount, setPaymentAmount] = useState(0); const [paymentPassword, setPaymentPassword] = useState(''); diff --git a/src/components/RegisterName.tsx b/src/components/RegisterName.tsx index 2b5b224..eb988ff 100644 --- a/src/components/RegisterName.tsx +++ b/src/components/RegisterName.tsx @@ -51,7 +51,13 @@ export const RegisterName = ({ ); const [nameFee, setNameFee] = useState(null); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const checkIfNameExisits = async (name) => { if (!name?.trim()) { setIsNameAvailable(Availability.NULL); diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index cebccf8..3c6a89b 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -84,7 +84,13 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { const [anchorEl, setAnchorEl] = useState(null); const { show } = useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const hasChanged = useMemo(() => { const newChanges = { diff --git a/src/components/Theme/ThemeManager.tsx b/src/components/Theme/ThemeManager.tsx index 69c63c9..f6b1551 100644 --- a/src/components/Theme/ThemeManager.tsx +++ b/src/components/Theme/ThemeManager.tsx @@ -82,7 +82,13 @@ export default function ThemeManager() { }); const [currentTab, setCurrentTab] = useState('light'); const nameInputRef = useRef(null); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); useEffect(() => { if (openEditor && nameInputRef.current) { diff --git a/src/components/Theme/ThemeSelector.tsx b/src/components/Theme/ThemeSelector.tsx index dd0dbba..6c25e3c 100644 --- a/src/components/Theme/ThemeSelector.tsx +++ b/src/components/Theme/ThemeSelector.tsx @@ -5,7 +5,13 @@ import DarkModeIcon from '@mui/icons-material/DarkMode'; import { useTranslation } from 'react-i18next'; const ThemeSelector = () => { - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const { themeMode, toggleTheme } = useThemeContext(); return ( diff --git a/src/components/UserLookup.tsx/UserLookup.tsx b/src/components/UserLookup.tsx/UserLookup.tsx index 3be4ef6..bd52b2a 100644 --- a/src/components/UserLookup.tsx/UserLookup.tsx +++ b/src/components/UserLookup.tsx/UserLookup.tsx @@ -49,7 +49,13 @@ function formatAddress(str) { export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [nameOrAddress, setNameOrAddress] = useState(''); const [inputValue, setInputValue] = useState(''); const { results, isLoading } = useNameSearch(inputValue); diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx index 490333d..caaee97 100644 --- a/src/components/WrapperUserAction.tsx +++ b/src/components/WrapperUserAction.tsx @@ -14,7 +14,13 @@ import { useTranslation } from 'react-i18next'; export const WrapperUserAction = ({ children, address, name, disabled }) => { const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const [isRunningPublicNode] = useAtom(isRunningPublicNodeAtom); const [anchorEl, setAnchorEl] = useState(null); @@ -177,7 +183,13 @@ const BlockUser = ({ address, name, handleClose }) => { const { isUserBlocked, addToBlockList, removeBlockFromList } = useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); useEffect(() => { if (!address) return; diff --git a/src/hooks/useHandlePrivateApps.tsx b/src/hooks/useHandlePrivateApps.tsx index 3876130..1065502 100644 --- a/src/hooks/useHandlePrivateApps.tsx +++ b/src/hooks/useHandlePrivateApps.tsx @@ -22,7 +22,13 @@ export const useHandlePrivateApps = () => { } = useContext(QORTAL_APP_CONTEXT); const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom); const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom); - const { t } = useTranslation(['auth', 'core', 'group']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const openApp = async ( privateAppProperties, From 702e18a33476c9304c5111818a943b653f5762a6 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 12:19:11 +0200 Subject: [PATCH 474/717] Rename files and folders (uniform the case) --- src/App.tsx | 2 +- src/Wallets.tsx | 2 +- src/components/Chat/ChatDirect.tsx | 2 +- src/components/Chat/ChatGroup.tsx | 2 +- src/components/Group/Group.tsx | 2 +- src/main.tsx | 4 +- src/{ => messaging}/MessageQueueContext.tsx | 0 ...ackground.tsx => MessagesToBackground.tsx} | 44 ++++++++++++++----- ...enerator.ts => randomSentenceGenerator.ts} | 0 src/utils/{Size => size}/index.ts | 0 10 files changed, 40 insertions(+), 18 deletions(-) rename src/{ => messaging}/MessageQueueContext.tsx (100%) rename src/messaging/{messagesToBackground.tsx => MessagesToBackground.tsx} (71%) rename src/utils/seedPhrase/{RandomSentenceGenerator.ts => randomSentenceGenerator.ts} (100%) rename src/utils/{Size => size}/index.ts (100%) diff --git a/src/App.tsx b/src/App.tsx index f58c7c2..9e1332b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -35,7 +35,7 @@ import PersonSearchIcon from '@mui/icons-material/PersonSearch'; import qortLogo from './assets/qort.png'; import { Return } from './assets/Icons/Return.tsx'; import WarningIcon from '@mui/icons-material/Warning'; -import './utils/seedPhrase/RandomSentenceGenerator'; +import './utils/seedPhrase/randomSentenceGenerator.ts'; import EngineeringIcon from '@mui/icons-material/Engineering'; import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; import PriorityHighIcon from '@mui/icons-material/PriorityHigh'; diff --git a/src/Wallets.tsx b/src/Wallets.tsx index c0aa098..1fe7583 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -75,7 +75,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { const fileContents = await new Promise((resolve, reject) => { const reader = new FileReader(); - reader.onabort = () => reject('File reading was aborted'); + reader.onabort = () => reject('File reading was aborted'); // TODO translate reader.onerror = () => reject('File reading has failed'); reader.onload = () => { // Resolve the promise with the reader result when reading completes diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index 0af54a6..6f95eee 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -15,7 +15,7 @@ import { resumeAllQueues, } from '../../App'; import { getPublicKey } from '../../background/background.ts'; -import { useMessageQueue } from '../../MessageQueueContext'; +import { useMessageQueue } from '../../messaging/MessageQueueContext.tsx'; import { executeEvent, subscribeToEvent, diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 6fada40..9fc5e9a 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -25,7 +25,7 @@ import { } from '../../App'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../../constants/constants'; -import { useMessageQueue } from '../../MessageQueueContext'; +import { useMessageQueue } from '../../messaging/MessageQueueContext.tsx'; import { executeEvent, subscribeToEvent, diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 8f312ec..fa534f9 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -43,7 +43,7 @@ import { } from '../../utils/events'; import { RequestQueueWithPromise } from '../../utils/queue/queue'; import { WebSocketActive } from './WebsocketActive'; -import { useMessageQueue } from '../../MessageQueueContext'; +import { useMessageQueue } from '../../messaging/MessageQueueContext'; import { HomeDesktop } from './HomeDesktop'; import { IconWrapper } from '../Desktop/DesktopFooter'; import { DesktopHeader } from '../Desktop/DesktopHeader'; diff --git a/src/main.tsx b/src/main.tsx index d6da0d2..2b224c8 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,8 +1,8 @@ import { createRoot } from 'react-dom/client'; import App from './App.tsx'; import '../src/styles/index.css'; -import './messaging/messagesToBackground'; -import { MessageQueueProvider } from './MessageQueueContext.tsx'; +import './messaging/MessagesToBackground.tsx'; +import { MessageQueueProvider } from './messaging/MessageQueueContext.tsx'; import { ThemeProvider } from './components/Theme/ThemeContext.tsx'; import { CssBaseline } from '@mui/material'; import './i18n/i18n.js'; diff --git a/src/MessageQueueContext.tsx b/src/messaging/MessageQueueContext.tsx similarity index 100% rename from src/MessageQueueContext.tsx rename to src/messaging/MessageQueueContext.tsx diff --git a/src/messaging/messagesToBackground.tsx b/src/messaging/MessagesToBackground.tsx similarity index 71% rename from src/messaging/messagesToBackground.tsx rename to src/messaging/MessagesToBackground.tsx index 4eebc5e..a325352 100644 --- a/src/messaging/messagesToBackground.tsx +++ b/src/messaging/MessagesToBackground.tsx @@ -1,5 +1,3 @@ - - // Utility to generate unique request IDs function generateRequestId() { return `msg-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; @@ -9,34 +7,55 @@ function generateRequestId() { const callbackMap = new Map(); // Global listener for handling message responses -window.addEventListener("message", (event) => { +window.addEventListener('message', (event) => { const { type, requestId, payload, error, message } = event.data; // Only process messages of type `backgroundMessageResponse` - if (type !== "backgroundMessageResponse") return; + if (type !== 'backgroundMessageResponse') return; // Check if there’s a callback stored for this requestId if (callbackMap.has(requestId)) { const { resolve, reject } = callbackMap.get(requestId); callbackMap.delete(requestId); // Remove callback after use - resolve(event.data) + resolve(event.data); } }); -export const sendMessageBackground = (action, data = {}, timeout = 240000, isExtension, appInfo, skipAuth) => { +export const sendMessageBackground = ( + action, + data = {}, + timeout = 240000, + isExtension, + appInfo, + skipAuth +) => { return new Promise((resolve, reject) => { const requestId = generateRequestId(); // Unique ID for each request callbackMap.set(requestId, { resolve, reject }); // Store both resolve and reject callbacks - const targetOrigin = window.location.origin + const targetOrigin = window.location.origin; // Send the message with `backgroundMessage` type - window.postMessage({ type: "backgroundMessage", action, requestId, payload: data, isExtension, appInfo, skipAuth }, targetOrigin); + window.postMessage( + { + type: 'backgroundMessage', + action, + requestId, + payload: data, + isExtension, + appInfo, + skipAuth, + }, + targetOrigin + ); // Set up a timeout to automatically reject if no response is received const timeoutId = setTimeout(() => { // Remove the callback to prevent memory leaks callbackMap.delete(requestId); - reject({ error: "timeout", message: `Request timed out after ${timeout} ms` }); + reject({ + error: 'timeout', + message: `Request timed out after ${timeout} ms`, + }); }, timeout); // Adjust resolve/reject to clear the timeout when a response arrives @@ -48,14 +67,17 @@ export const sendMessageBackground = (action, data = {}, timeout = 240000, isExt reject: (error) => { clearTimeout(timeoutId); // Clear the timeout if an error occurs reject(error); - } + }, }); }).then((response) => { // Return payload or error based on response content if (response?.payload !== null && response?.payload !== undefined) { return response.payload; } else if (response?.error) { - return { error: response.error, message: response?.message || "An error occurred" }; + return { + error: response.error, + message: response?.message || 'An error occurred', + }; } }); }; diff --git a/src/utils/seedPhrase/RandomSentenceGenerator.ts b/src/utils/seedPhrase/randomSentenceGenerator.ts similarity index 100% rename from src/utils/seedPhrase/RandomSentenceGenerator.ts rename to src/utils/seedPhrase/randomSentenceGenerator.ts diff --git a/src/utils/Size/index.ts b/src/utils/size/index.ts similarity index 100% rename from src/utils/Size/index.ts rename to src/utils/size/index.ts From e017d5d50d9743946d9e39c3c81158c658de377c Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 12:21:37 +0200 Subject: [PATCH 475/717] Move file into components --- src/App.tsx | 2 +- src/{ => components}/Wallets.tsx | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) rename src/{ => components}/Wallets.tsx (96%) diff --git a/src/App.tsx b/src/App.tsx index 9e1332b..ed70e64 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -118,7 +118,7 @@ import { } from './atoms/global'; import { NotAuthenticated } from './components/NotAuthenticated.tsx'; import { handleGetFileFromIndexedDB } from './utils/indexedDB'; -import { Wallets } from './Wallets'; +import { Wallets } from './components/Wallets.tsx'; import { useFetchResources } from './common/useFetchResources'; import { Tutorials } from './components/Tutorials/Tutorials'; import { useHandleTutorials } from './hooks/useHandleTutorials.tsx'; diff --git a/src/Wallets.tsx b/src/components/Wallets.tsx similarity index 96% rename from src/Wallets.tsx rename to src/components/Wallets.tsx index 1fe7583..24aa4705 100644 --- a/src/Wallets.tsx +++ b/src/components/Wallets.tsx @@ -18,24 +18,24 @@ import { Input, useTheme, } from '@mui/material'; -import { CustomButton } from './styles/App-styles'; +import { CustomButton } from '../styles/App-styles.ts'; import { useDropzone } from 'react-dropzone'; import EditIcon from '@mui/icons-material/Edit'; -import { Label } from './components/Group/AddGroup'; -import { Spacer } from './common/Spacer'; +import { Label } from './Group/AddGroup.tsx'; +import { Spacer } from '../common/Spacer.tsx'; import { getWallets, storeWallets, walletVersion, -} from './background/background.ts'; -import { useModal } from './common/useModal'; -import PhraseWallet from './utils/generateWallet/phrase-wallet'; -import { decryptStoredWalletFromSeedPhrase } from './utils/decryptWallet'; -import { crypto } from './constants/decryptWallet'; +} from '../background/background.ts'; +import { useModal } from '../common/useModal.tsx'; +import PhraseWallet from '../utils/generateWallet/phrase-wallet.ts'; +import { decryptStoredWalletFromSeedPhrase } from '../utils/decryptWallet.ts'; +import { crypto } from '../constants/decryptWallet.ts'; import { LoadingButton } from '@mui/lab'; -import { PasswordField } from './components'; -import { HtmlTooltip } from './components/NotAuthenticated'; -import { QORTAL_APP_CONTEXT } from './App'; +import { PasswordField } from './index.ts'; +import { HtmlTooltip } from './NotAuthenticated.tsx'; +import { QORTAL_APP_CONTEXT } from '../App.tsx'; import { useTranslation } from 'react-i18next'; const parsefilenameQortal = (filename) => { From 593252cce334f332009a63f5d3d5e954b2da3bed Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 12:26:21 +0200 Subject: [PATCH 476/717] Move hooks into proper folder --- src/App.tsx | 4 ++-- src/components/Apps/AppsDevModeHome.tsx | 2 +- src/components/Group/BlockedUsersModal.tsx | 2 +- src/components/Minting/Minting.tsx | 2 +- src/components/Wallets.tsx | 2 +- src/{common => hooks}/useFetchResources.tsx | 0 src/{common => hooks}/useModal.tsx | 0 7 files changed, 6 insertions(+), 6 deletions(-) rename src/{common => hooks}/useFetchResources.tsx (100%) rename src/{common => hooks}/useModal.tsx (100%) diff --git a/src/App.tsx b/src/App.tsx index ed70e64..4aad404 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -64,7 +64,7 @@ import { Loader } from './components/Loader'; import { PasswordField, ErrorText } from './components'; import { Group, requestQueueMemberNames } from './components/Group/Group'; import { TaskManager } from './components/TaskManager/TaskManager.tsx'; -import { useModal } from './common/useModal'; +import { useModal } from './hooks/useModal.tsx'; import { CustomizedSnackbars } from './components/Snackbar/Snackbar'; import SettingsIcon from '@mui/icons-material/Settings'; import LogoutIcon from '@mui/icons-material/Logout'; @@ -119,7 +119,7 @@ import { import { NotAuthenticated } from './components/NotAuthenticated.tsx'; import { handleGetFileFromIndexedDB } from './utils/indexedDB'; import { Wallets } from './components/Wallets.tsx'; -import { useFetchResources } from './common/useFetchResources'; +import { useFetchResources } from './hooks/useFetchResources.tsx'; import { Tutorials } from './components/Tutorials/Tutorials'; import { useHandleTutorials } from './hooks/useHandleTutorials.tsx'; import { useHandleUserInfo } from './hooks/useHandleUserInfo.tsx'; diff --git a/src/components/Apps/AppsDevModeHome.tsx b/src/components/Apps/AppsDevModeHome.tsx index 88218ca..db32248 100644 --- a/src/components/Apps/AppsDevModeHome.tsx +++ b/src/components/Apps/AppsDevModeHome.tsx @@ -22,7 +22,7 @@ import { Add } from '@mui/icons-material'; import { QORTAL_APP_CONTEXT, getBaseApiReact } from '../../App'; import { executeEvent } from '../../utils/events'; import { Spacer } from '../../common/Spacer'; -import { useModal } from '../../common/useModal'; +import { useModal } from '../../hooks/useModal.tsx'; import { createEndpoint, isUsingLocal } from '../../background/background.ts'; import { Label } from '../Group/AddGroup'; import ShortUniqueId from 'short-unique-id'; diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index 8cdd358..487711e 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -20,7 +20,7 @@ import { } from '../../utils/events'; import { validateAddress } from '../../utils/validateAddress'; import { getNameInfo, requestQueueMemberNames } from './Group'; -import { useModal } from '../../common/useModal'; +import { useModal } from '../../hooks/useModal'; import { isOpenBlockedModalAtom } from '../../atoms/global'; import InfoIcon from '@mui/icons-material/Info'; import { useAtom } from 'jotai'; diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index ce3846b..cddd67c 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -24,7 +24,7 @@ import { import { getFee } from '../../background/background.ts'; import { Spacer } from '../../common/Spacer'; import { FidgetSpinner } from 'react-loader-spinner'; -import { useModal } from '../../common/useModal'; +import { useModal } from '../../hooks/useModal.tsx'; import { useAtom, useSetAtom } from 'jotai'; import { memberGroupsAtom, txListAtom } from '../../atoms/global'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/Wallets.tsx b/src/components/Wallets.tsx index 24aa4705..ae749d3 100644 --- a/src/components/Wallets.tsx +++ b/src/components/Wallets.tsx @@ -28,7 +28,7 @@ import { storeWallets, walletVersion, } from '../background/background.ts'; -import { useModal } from '../common/useModal.tsx'; +import { useModal } from '../hooks/useModal.tsx'; import PhraseWallet from '../utils/generateWallet/phrase-wallet.ts'; import { decryptStoredWalletFromSeedPhrase } from '../utils/decryptWallet.ts'; import { crypto } from '../constants/decryptWallet.ts'; diff --git a/src/common/useFetchResources.tsx b/src/hooks/useFetchResources.tsx similarity index 100% rename from src/common/useFetchResources.tsx rename to src/hooks/useFetchResources.tsx diff --git a/src/common/useModal.tsx b/src/hooks/useModal.tsx similarity index 100% rename from src/common/useModal.tsx rename to src/hooks/useModal.tsx From a957c6fe04b73cc9690a9040823618dd3c4a8163 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 14:07:22 +0200 Subject: [PATCH 477/717] Remove duplicated uploadData function --- src/qdn/publish/publish.ts | 145 +++---------------------------------- 1 file changed, 11 insertions(+), 134 deletions(-) diff --git a/src/qdn/publish/publish.ts b/src/qdn/publish/publish.ts index f1f75ad..c73efd2 100644 --- a/src/qdn/publish/publish.ts +++ b/src/qdn/publish/publish.ts @@ -30,7 +30,6 @@ async function reusablePost(endpoint, _body) { data = await response.clone().json(); } catch (e) { data = await response.text(); -<<<<<<< HEAD } return data; } @@ -84,27 +83,23 @@ async function getKeyPair() { } export const publishData = async ({ - registeredName, - data, - service, - identifier, - uploadType, - file, - service, - identifier, - uploadType, - isBase64, - filename, - withFee, - title, - description, category, + data, + description, + feeAmount, + filename, + identifier, + isBase64, + registeredName, + service, tag1, tag2, tag3, tag4, tag5, - feeAmount, + title, + uploadType, + withFee, }: any) => { console.log('data', data); const validateName = async (receiverName: string) => { @@ -252,123 +247,6 @@ export const publishData = async ({ return signAndProcessRes; }; -<<<<<<< HEAD - const uploadData = async (registeredName: string, data: any, fee: number) => { - console.log('data', uploadType, data); - let postBody = ''; - let urlSuffix = ''; - - if (data != null) { - if (uploadType === 'base64') { - urlSuffix = '/base64'; - } - - if (uploadType === 'base64') { - postBody = data; - } - } else { - throw new Error('No data provided'); - } - - let uploadDataUrl = `/arbitrary/${service}/${registeredName}`; - let paramQueries = ''; - if (identifier?.trim().length > 0) { - uploadDataUrl = `/arbitrary/${service}/${registeredName}/${identifier}`; - } - - paramQueries = paramQueries + `?fee=${fee}`; - - if (filename != null && filename != 'undefined') { - paramQueries = paramQueries + '&filename=' + encodeURIComponent(filename); - } - - if (title != null && title != 'undefined') { - paramQueries = paramQueries + '&title=' + encodeURIComponent(title); - } - - if (description != null && description != 'undefined') { - paramQueries = - paramQueries + '&description=' + encodeURIComponent(description); - } - - if (category != null && category != 'undefined') { - paramQueries = paramQueries + '&category=' + encodeURIComponent(category); - } - - if (tag1 != null && tag1 != 'undefined') { - paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag1); - } - - if (tag2 != null && tag2 != 'undefined') { - paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag2); - } - - if (tag3 != null && tag3 != 'undefined') { - paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag3); - } - - if (tag4 != null && tag4 != 'undefined') { - paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag4); - } - - if (tag5 != null && tag5 != 'undefined') { - paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag5); - } - if (uploadType === 'zip') { - paramQueries = paramQueries + '&isZip=' + true; - } - - if (uploadType === 'base64') { - if (urlSuffix) { - uploadDataUrl = uploadDataUrl + urlSuffix; - } - uploadDataUrl = uploadDataUrl + paramQueries; - return await reusablePost(uploadDataUrl, postBody); - } - - const file = data; - const urlCheck = `/arbitrary/check-tmp-space?totalSize=${file.size}`; - - const checkEndpoint = await createEndpoint(urlCheck); - const checkRes = await fetch(checkEndpoint); - if (!checkRes.ok) { - throw new Error('Not enough space on your hard drive'); - } - - const chunkUrl = uploadDataUrl + `/chunk`; - const chunkSize = 5 * 1024 * 1024; // 5MB - - const totalChunks = Math.ceil(file.size / chunkSize); - - for (let index = 0; index < totalChunks; index++) { - const start = index * chunkSize; - const end = Math.min(start + chunkSize, file.size); - const chunk = file.slice(start, end); - - const formData = new FormData(); - formData.append('chunk', chunk, file.name); // Optional: include filename - formData.append('index', index); - - await uploadChunkWithRetry(chunkUrl, formData, index); - } - const finalizeUrl = uploadDataUrl + `/finalize` + paramQueries; - - const finalizeEndpoint = await createEndpoint(finalizeUrl); - - const response = await fetch(finalizeEndpoint, { - method: 'POST', - headers: {}, - }); - - if (!response.ok) { - const errorText = await response.text(); - throw new Error(`Finalize failed: ${errorText}`); - } - - const result = await response.text(); // Base58-encoded unsigned transaction - return result; -} - const uploadData = async (registeredName: string, file: any, fee: number) => { let postBody = ''; let urlSuffix = ''; @@ -442,7 +320,6 @@ export const publishData = async ({ } return await reusablePost(uploadDataUrl, postBody); ->>>>>>> 2d01b3e (Create encryption folder and move files) }; try { From c9a678368333b81190bce403c064d2acf777c34b Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 14:16:31 +0200 Subject: [PATCH 478/717] Fix conflicts --- src/encryption/encryption.ts | 3 --- src/qdn/publish/publish.ts | 16 ++++++---------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/encryption/encryption.ts b/src/encryption/encryption.ts index 09af412..cf96e89 100644 --- a/src/encryption/encryption.ts +++ b/src/encryption/encryption.ts @@ -294,9 +294,6 @@ export const publishOnQDN = async ({ }) => { if (data && service) { const registeredName = name || (await getNameInfo()); -}) => { - if (data && service) { - const registeredName = await getNameInfo(); if (!registeredName) throw new Error( i18n.t('core:message.generic.name_publish', { diff --git a/src/qdn/publish/publish.ts b/src/qdn/publish/publish.ts index b105a1a..43ca630 100644 --- a/src/qdn/publish/publish.ts +++ b/src/qdn/publish/publish.ts @@ -83,29 +83,25 @@ async function getKeyPair() { } export const publishData = async ({ + category, data, + description, feeAmount, filename, identifier, isBase64, registeredName, - file, service, - identifier, - uploadType, - withFee, - title, - description, - category, tag1, tag2, tag3, tag4, tag5, + title, + uploadType, + withFee, }: any) => { console.log('data', data); - feeAmount, -}: any) => { const validateName = async (receiverName: string) => { return await reusableGet(`/names/${receiverName}`); }; @@ -232,7 +228,7 @@ export const publishData = async ({ let transactionBytes = await uploadData(registeredName, data, fee); console.log('transactionBytes length', transactionBytes?.length); - + if (!transactionBytes || transactionBytes.error) { throw new Error(transactionBytes?.message || 'Error when uploading'); } else if (transactionBytes.includes('Error 500 Internal Server Error')) { From 7deb2501e2664b6ea96789013fe480dd69f746f0 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 14:18:40 +0200 Subject: [PATCH 479/717] Remove duplicated --- src/encryption/encryption.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/encryption/encryption.ts b/src/encryption/encryption.ts index cf96e89..e0d068e 100644 --- a/src/encryption/encryption.ts +++ b/src/encryption/encryption.ts @@ -143,16 +143,13 @@ export const encryptAndPublishSymmetricKeyGroupChat = async ({ if (encryptedData) { const registeredName = await getNameInfo(); const data = await publishData({ - registeredName, data: encryptedData, - service: 'DOCUMENT_PRIVATE', - identifier: `symmetric-qchat-group-${groupId}`, - uploadType: 'base64', file: encryptedData, - service: 'DOCUMENT_PRIVATE', identifier: `symmetric-qchat-group-${groupId}`, - uploadType: 'file', isBase64: true, + registeredName, + service: 'DOCUMENT_PRIVATE', + uploadType: 'base64', withFee: true, }); return { From a778aa7a0f05f123e8e9a6f65d453c3c79958ec8 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 14:32:51 +0200 Subject: [PATCH 480/717] Set correct type (base64), remove isBase64 --- src/encryption/encryption.ts | 33 +++++------ src/qdn/publish/publish.ts | 108 ++++++++++++++++++++++++----------- 2 files changed, 88 insertions(+), 53 deletions(-) diff --git a/src/encryption/encryption.ts b/src/encryption/encryption.ts index e0d068e..ab0a36d 100644 --- a/src/encryption/encryption.ts +++ b/src/encryption/encryption.ts @@ -146,7 +146,6 @@ export const encryptAndPublishSymmetricKeyGroupChat = async ({ data: encryptedData, file: encryptedData, identifier: `symmetric-qchat-group-${groupId}`, - isBase64: true, registeredName, service: 'DOCUMENT_PRIVATE', uploadType: 'base64', @@ -212,14 +211,12 @@ export const encryptAndPublishSymmetricKeyGroupChatForAdmins = async ({ if (encryptedData) { const registeredName = await getNameInfo(); const data = await publishData({ - registeredName, data: encryptedData, - service: 'DOCUMENT_PRIVATE', - identifier: `admins-symmetric-qchat-group-${groupId}`, - uploadType: 'base64', file: encryptedData, - uploadType: 'file', - isBase64: true, + identifier: `admins-symmetric-qchat-group-${groupId}`, + registeredName, + service: 'DOCUMENT_PRIVATE', + uploadType: 'base64', withFee: true, }); return { @@ -252,13 +249,12 @@ export const publishGroupEncryptedResource = async ({ }) ); const data = await publishData({ - registeredName, data: encryptedData, - uploadType: 'base64', file: encryptedData, - service: 'DOCUMENT', identifier, - isBase64: true, + registeredName, + service: 'DOCUMENT', + uploadType: 'base64', withFee: true, }); return data; @@ -275,19 +271,19 @@ export const publishGroupEncryptedResource = async ({ }; export const publishOnQDN = async ({ - data, - identifier, - service, - title, - description, category, + data, + description, + identifier, + name, + service, tag1, tag2, tag3, tag4, tag5, - uploadType = 'file', - name, + title, + uploadType = 'base64', }) => { if (data && service) { const registeredName = name || (await getNameInfo()); @@ -305,7 +301,6 @@ export const publishOnQDN = async ({ service, identifier, uploadType, - isBase64: true, withFee: true, title, description, diff --git a/src/qdn/publish/publish.ts b/src/qdn/publish/publish.ts index 43ca630..dfe682f 100644 --- a/src/qdn/publish/publish.ts +++ b/src/qdn/publish/publish.ts @@ -70,7 +70,6 @@ async function uploadChunkWithRetry(endpoint, formData, index, maxRetries = 3) { await new Promise((res) => setTimeout(res, 10_000)); } } - return data; } async function getKeyPair() { @@ -248,79 +247,120 @@ export const publishData = async ({ return signAndProcessRes; }; - const uploadData = async (registeredName: string, file: any, fee: number) => { + const uploadData = async (registeredName: string, data: any, fee: number) => { + console.log('data', uploadType, data); let postBody = ''; let urlSuffix = ''; - if (file != null) { - // If we're sending zipped data, make sure to use the /zip version of the POST /arbitrary/* API - if (uploadType === 'zip') { - urlSuffix = '/zip'; - } - - // If we're sending file data, use the /base64 version of the POST /arbitrary/* API - else if (uploadType === 'file') { + if (data != null) { + if (uploadType === 'base64') { urlSuffix = '/base64'; } - // Base64 encode the file to work around compatibility issues between javascript and java byte arrays - if (isBase64) { - postBody = file; - } - - if (!isBase64) { - let fileBuffer = new Uint8Array(await file.arrayBuffer()); - postBody = Buffer.from(fileBuffer).toString('base64'); + if (uploadType === 'base64') { + postBody = data; } + } else { + throw new Error('No data provided'); } - let uploadDataUrl = `/arbitrary/${service}/${registeredName}${urlSuffix}`; + let uploadDataUrl = `/arbitrary/${service}/${registeredName}`; + let paramQueries = ''; if (identifier?.trim().length > 0) { - uploadDataUrl = `/arbitrary/${service}/${registeredName}/${identifier}${urlSuffix}`; + uploadDataUrl = `/arbitrary/${service}/${registeredName}/${identifier}`; } - uploadDataUrl = uploadDataUrl + `?fee=${fee}`; + paramQueries = paramQueries + `?fee=${fee}`; if (filename != null && filename != 'undefined') { - uploadDataUrl = - uploadDataUrl + '&filename=' + encodeURIComponent(filename); + paramQueries = paramQueries + '&filename=' + encodeURIComponent(filename); } if (title != null && title != 'undefined') { - uploadDataUrl = uploadDataUrl + '&title=' + encodeURIComponent(title); + paramQueries = paramQueries + '&title=' + encodeURIComponent(title); } if (description != null && description != 'undefined') { - uploadDataUrl = - uploadDataUrl + '&description=' + encodeURIComponent(description); + paramQueries = + paramQueries + '&description=' + encodeURIComponent(description); } if (category != null && category != 'undefined') { - uploadDataUrl = - uploadDataUrl + '&category=' + encodeURIComponent(category); + paramQueries = paramQueries + '&category=' + encodeURIComponent(category); } if (tag1 != null && tag1 != 'undefined') { - uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag1); + paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag1); } if (tag2 != null && tag2 != 'undefined') { - uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag2); + paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag2); } if (tag3 != null && tag3 != 'undefined') { - uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag3); + paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag3); } if (tag4 != null && tag4 != 'undefined') { - uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag4); + paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag4); } if (tag5 != null && tag5 != 'undefined') { - uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag5); + paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag5); + } + if (uploadType === 'zip') { + paramQueries = paramQueries + '&isZip=' + true; } - return await reusablePost(uploadDataUrl, postBody); + if (uploadType === 'base64') { + if (urlSuffix) { + uploadDataUrl = uploadDataUrl + urlSuffix; + } + uploadDataUrl = uploadDataUrl + paramQueries; + return await reusablePost(uploadDataUrl, postBody); + } + + const file = data; + const urlCheck = `/arbitrary/check-tmp-space?totalSize=${file.size}`; + + const checkEndpoint = await createEndpoint(urlCheck); + const checkRes = await fetch(checkEndpoint); + if (!checkRes.ok) { + throw new Error('Not enough space on your hard drive'); + } + + const chunkUrl = uploadDataUrl + `/chunk`; + const chunkSize = 5 * 1024 * 1024; // 5MB + + const totalChunks = Math.ceil(file.size / chunkSize); + + for (let index = 0; index < totalChunks; index++) { + const start = index * chunkSize; + const end = Math.min(start + chunkSize, file.size); + const chunk = file.slice(start, end); + + const formData = new FormData(); + formData.append('chunk', chunk, file.name); // Optional: include filename + formData.append('index', index); + + await uploadChunkWithRetry(chunkUrl, formData, index); + } + const finalizeUrl = uploadDataUrl + `/finalize` + paramQueries; + + const finalizeEndpoint = await createEndpoint(finalizeUrl); + + const response = await fetch(finalizeEndpoint, { + method: 'POST', + headers: {}, + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`Finalize failed: ${errorText}`); + } + + const result = await response.text(); // Base58-encoded unsigned transaction + return result; }; try { From 506d65dc0879fbfb4e6b3e8f6ee11a19c017c295 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 14:53:46 +0200 Subject: [PATCH 481/717] Manage conflicts --- src/qortal/get.ts | 151 +- src/qortalRequests/get.ts | 7077 ------------------------------------- 2 files changed, 114 insertions(+), 7114 deletions(-) delete mode 100644 src/qortalRequests/get.ts diff --git a/src/qortal/get.ts b/src/qortal/get.ts index 102a9bf..dad01f5 100644 --- a/src/qortal/get.ts +++ b/src/qortal/get.ts @@ -38,7 +38,11 @@ import { getPublicKey, transferAsset, } from '../background/background.ts'; -import { getNameInfo, uint8ArrayToObject } from '../encryption/encryption.ts'; +import { + getAllUserNames, + getNameInfo, + uint8ArrayToObject, +} from '../encryption/encryption.ts'; import { showSaveFilePicker } from '../hooks/useQortalMessageListener.tsx'; import { getPublishesFromAdminsAdminSpace } from '../components/Chat/AdminSpaceInner.tsx'; import { extractComponents } from '../components/Chat/MessageDisplay.tsx'; @@ -1315,7 +1319,7 @@ export const publishQDNResource = async ( if (appFee && appFee > 0 && appFeeRecipient) { hasAppFee = true; } - const registeredName = await getNameInfo(); + const registeredName = data?.name || (await getNameInfo()); const name = registeredName; if (!name) { throw new Error( @@ -1331,7 +1335,7 @@ export const publishQDNResource = async ( const title = data.title; const description = data.description; const category = data.category; - + const file = data?.file || data?.blob; const tags = data?.tags || []; const result = {}; @@ -1346,9 +1350,7 @@ export const publishQDNResource = async ( if (data.identifier == null) { identifier = 'default'; } - if (data?.file || data?.blob) { - data64 = await fileToBase64(data?.file || data?.blob); - } + if ( data.encrypt && (!data.publicKeys || @@ -1367,6 +1369,9 @@ export const publishQDNResource = async ( const parsedData = resKeyPair; const privateKey = parsedData.privateKey; const userPublicKey = parsedData.publicKey; + if (data?.file || data?.blob) { + data64 = await fileToBase64(data?.file || data?.blob); + } const encryptDataResponse = encryptDataGroup({ data64, publicKeys: data.publicKeys, @@ -1410,6 +1415,7 @@ export const publishQDNResource = async ( }), text2: `service: ${service}`, text3: `identifier: ${identifier || null}`, + text4: `name: ${registeredName}`, fee: fee.fee, ...handleDynamicValues, }, @@ -1420,11 +1426,10 @@ export const publishQDNResource = async ( try { const resPublish = await publishData({ registeredName: encodeURIComponent(name), - file: data64, + data: data64 ? data64 : file, service: service, identifier: encodeURIComponent(identifier), - uploadType: 'file', - isBase64: true, + uploadType: data64 ? 'base64' : 'file', filename: filename, title, description, @@ -1558,6 +1563,7 @@ export const publishMultipleQDNResources = async ( const fee = await getFee('ARBITRARY'); const registeredName = await getNameInfo(); + const name = registeredName; if (!name) { @@ -1568,6 +1574,14 @@ export const publishMultipleQDNResources = async ( ); } + const userNames = await getAllUserNames(); + data.resources?.forEach((item) => { + if (item?.name && !userNames?.includes(item.name)) + throw new Error( + `The name ${item.name}, does not belong to the publisher.` + ); + }); + const appFee = data?.appFee ? +data.appFee : undefined; const appFeeRecipient = data?.appFeeRecipient; let hasAppFee = false; @@ -1638,7 +1652,7 @@ export const publishMultipleQDNResources = async (
    Service: ${ resource.service }
    -
    Name: ${name}
    +
    Name: ${resource?.name || name}
    Identifier: ${ resource.identifier }
    @@ -1695,6 +1709,7 @@ export const publishMultipleQDNResources = async ( reason: errorMsg, identifier: resource.identifier, service: resource.service, + name: resource?.name || name, }); continue; } @@ -1709,12 +1724,13 @@ export const publishMultipleQDNResources = async ( reason: errorMsg, identifier: resource.identifier, service: resource.service, + name: resource?.name || name, }); continue; } const service = resource.service; let identifier = resource.identifier; - let data64 = resource?.data64 || resource?.base64; + let rawData = resource?.data64 || resource?.base64; const filename = resource.filename; const title = resource.title; const description = resource.description; @@ -1741,26 +1757,31 @@ export const publishMultipleQDNResources = async ( reason: errorMsg, identifier: resource.identifier, service: resource.service, + name: resource?.name || name, }); continue; } if (resource.file) { - data64 = await fileToBase64(resource.file); + rawData = resource.file; } + if (resourceEncrypt) { try { + if (resource?.file) { + rawData = await fileToBase64(resource.file); + } const resKeyPair = await getKeyPair(); const parsedData = resKeyPair; const privateKey = parsedData.privateKey; const userPublicKey = parsedData.publicKey; const encryptDataResponse = encryptDataGroup({ - data64, + data64: rawData, publicKeys: data.publicKeys, privateKey, userPublicKey, }); if (encryptDataResponse) { - data64 = encryptDataResponse; + rawData = encryptDataResponse; } } catch (error) { const errorMsg = @@ -1772,32 +1793,37 @@ export const publishMultipleQDNResources = async ( reason: errorMsg, identifier: resource.identifier, service: resource.service, + name: resource?.name || name, }); continue; } } try { + const dataType = + resource?.base64 || resource?.data64 || resourceEncrypt + ? 'base64' + : 'file'; + console.log('dataType', dataType); await retryTransaction( publishData, [ { - registeredName: encodeURIComponent(name), - file: data64, - service: service, - identifier: encodeURIComponent(identifier), - uploadType: 'file', - isBase64: true, - filename: filename, - title, - description, + apiVersion: 2, category, + data: rawData, + description, + filename: filename, + identifier: encodeURIComponent(identifier), + registeredName: encodeURIComponent(resource?.name || name), + service: service, tag1, tag2, tag3, tag4, tag5, - apiVersion: 2, + title, + uploadType: dataType, withFee: true, }, ], @@ -1818,6 +1844,7 @@ export const publishMultipleQDNResources = async ( reason: errorMsg, identifier: resource.identifier, service: resource.service, + name: resource?.name || name, }); } } catch (error) { @@ -1829,6 +1856,7 @@ export const publishMultipleQDNResources = async ( }), identifier: resource.identifier, service: resource.service, + name: resource?.name || name, }); } } @@ -2324,6 +2352,44 @@ export const joinGroup = async (data, isFromExtension) => { export const saveFile = async (data, sender, isFromExtension, snackMethods) => { try { + if (!data?.filename) throw new Error('Missing filename'); + if (data?.location) { + const requiredFieldsLocation = ['service', 'name']; + const missingFieldsLocation: string[] = []; + requiredFieldsLocation.forEach((field) => { + if (!data?.location[field]) { + missingFieldsLocation.push(field); + } + }); + if (missingFieldsLocation.length > 0) { + const missingFieldsString = missingFieldsLocation.join(', '); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } + const resPermission = await getUserPermission( + { + text1: 'Would you like to download:', + highlightedText: `${data?.filename}`, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (!accepted) throw new Error('User declined to save file'); + const a = document.createElement('a'); + let locationUrl = `/arbitrary/${data.location.service}/${data.location.name}`; + if (data.location.identifier) { + locationUrl = locationUrl + `/${data.location.identifier}`; + } + const endpoint = await createEndpoint( + locationUrl + `?attachment=true&attachmentFilename=${data?.filename}` + ); + a.href = endpoint; + a.download = data.filename; + document.body.appendChild(a); + a.click(); + a.remove(); + return true; + } const requiredFields = ['filename', 'blob']; const missingFields: string[] = []; requiredFields.forEach((field) => { @@ -2341,6 +2407,8 @@ export const saveFile = async (data, sender, isFromExtension, snackMethods) => { } const filename = data.filename; const blob = data.blob; + + const mimeType = blob.type || data.mimeType; const resPermission = await getUserPermission( { text1: i18n.t('question:download_file', { @@ -2351,6 +2419,17 @@ export const saveFile = async (data, sender, isFromExtension, snackMethods) => { isFromExtension ); const { accepted } = resPermission; + if (!accepted) throw new Error('User declined to save file'); // TODO translate + showSaveFilePicker( + { + filename, + mimeType, + blob, + }, + snackMethods + ); + + return true; if (accepted) { const mimeType = blob.type || data.mimeType; @@ -5396,11 +5475,10 @@ export const updateNameRequest = async (data, isFromExtension) => { const fee = await getFee('UPDATE_NAME'); const resPermission = await getUserPermission( { - text1: i18n.t('question:permission.register_name', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: data.newName, - text2: data?.description, + text1: `Do you give this application permission to update this name?`, // TODO translate + text2: `previous name: ${oldName}`, + text3: `new name: ${newName}`, + text4: data?.description, fee: fee.fee, }, isFromExtension @@ -6012,7 +6090,7 @@ export const createGroupRequest = async (data, isFromExtension) => { ]; const missingFields: string[] = []; requiredFields.forEach((field) => { - if (data[field] !== undefined && data[field] !== null) { + if (data[field] === undefined || data[field] === null) { missingFields.push(field); } }); @@ -6076,7 +6154,7 @@ export const updateGroupRequest = async (data, isFromExtension) => { ]; const missingFields: string[] = []; requiredFields.forEach((field) => { - if (data[field] !== undefined && data[field] !== null) { + if (data[field] === undefined || data[field] === null) { missingFields.push(field); } }); @@ -6250,7 +6328,7 @@ export const sellNameRequest = async (data, isFromExtension) => { const requiredFields = ['salePrice', 'nameForSale']; const missingFields: string[] = []; requiredFields.forEach((field) => { - if (data[field] !== undefined && data[field] !== null) { + if (data[field] === undefined || data[field] === null) { missingFields.push(field); } }); @@ -6320,7 +6398,7 @@ export const cancelSellNameRequest = async (data, isFromExtension) => { const requiredFields = ['nameForSale']; const missingFields: string[] = []; requiredFields.forEach((field) => { - if (data[field] !== undefined && data[field] !== null) { + if (data[field] === undefined || data[field] === null) { missingFields.push(field); } }); @@ -6377,7 +6455,7 @@ export const buyNameRequest = async (data, isFromExtension) => { const requiredFields = ['nameForSale']; const missingFields: string[] = []; requiredFields.forEach((field) => { - if (data[field] !== undefined && data[field] !== null) { + if (data[field] === undefined || data[field] === null) { missingFields.push(field); } }); @@ -6908,12 +6986,11 @@ export const multiPaymentWithPrivateData = async (data, isFromExtension) => { [ { registeredName: encodeURIComponent(name), - file: encryptDataResponse, + data: encryptDataResponse, service: transaction.service, identifier: encodeURIComponent(transaction.identifier), - uploadType: 'file', + uploadType: 'base64', description: transaction?.description, - isBase64: true, apiVersion: 2, withFee: true, }, diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts deleted file mode 100644 index 6819842..0000000 --- a/src/qortalRequests/get.ts +++ /dev/null @@ -1,7077 +0,0 @@ -import { Sha256 } from 'asmcrypto.js'; -import { - createEndpoint, - getBalanceInfo, - getFee, - getKeyPair, - getLastRef, - getSaveWallet, - processTransactionVersion2, - signChatFunc, - joinGroup as joinGroupFunc, - sendQortFee, - sendCoin as sendCoinFunc, - createBuyOrderTx, - performPowTask, - parseErrorResponse, - groupSecretkeys, - registerName, - updateName, - leaveGroup, - inviteToGroup, - getNameInfoForOthers, - kickFromGroup, - banFromGroup, - cancelBan, - makeAdmin, - removeAdmin, - cancelInvitationToGroup, - createGroup, - updateGroup, - sellName, - cancelSellName, - buyName, - getBaseApi, - getAssetBalanceInfo, - getNameOrAddress, - getAssetInfo, - getPublicKey, - transferAsset, -} from '../background/background.ts'; -import { getNameInfo, uint8ArrayToObject } from '../encryption/encryption.ts'; -import { showSaveFilePicker } from '../hooks/useQortalMessageListener.tsx'; -import { getPublishesFromAdminsAdminSpace } from '../components/Chat/AdminSpaceInner.tsx'; -import { extractComponents } from '../components/Chat/MessageDisplay.tsx'; -import { - decryptResource, - getGroupAdmins, - getPublishesFromAdmins, - validateSecretKey, -} from '../components/Group/Group.tsx'; -import { QORT_DECIMALS } from '../constants/constants.ts'; -import Base58 from '../encryption/Base58.ts'; -import ed2curve from '../encryption/ed2curve.ts'; -import nacl from '../encryption/nacl-fast.ts'; -import { - base64ToUint8Array, - createSymmetricKeyAndNonce, - decryptDeprecatedSingle, - decryptGroupDataQortalRequest, - decryptGroupEncryptionWithSharingKey, - decryptSingle, - encryptDataGroup, - encryptSingle, - objectToBase64, - uint8ArrayStartsWith, - uint8ArrayToBase64, -} from '../qdn/encryption/group-encryption.ts'; -import { publishData } from '../qdn/publish/publish.ts'; -import { - getPermission, - isRunningGateway, - setPermission, -} from './qortalRequests.ts'; -import TradeBotCreateRequest from '../transactions/TradeBotCreateRequest.ts'; -import DeleteTradeOffer from '../transactions/TradeBotDeleteRequest.ts'; -import signTradeBotTransaction from '../transactions/signTradeBotTransaction.ts'; -import { createTransaction } from '../transactions/transactions.ts'; -import { executeEvent } from '../utils/events.ts'; -import { fileToBase64 } from '../utils/fileReading/index.ts'; -import { mimeToExtensionMap } from '../utils/memeTypes.ts'; -import { RequestQueueWithPromise } from '../utils/queue/queue.ts'; -import utils from '../utils/utils.ts'; -import ShortUniqueId from 'short-unique-id'; -import { isValidBase64WithDecode } from '../utils/decode.ts'; -import i18n from 'i18next'; - -const uid = new ShortUniqueId({ length: 6 }); - -export const requestQueueGetAtAddresses = new RequestQueueWithPromise(10); - -const sellerForeignFee = { - LITECOIN: { - value: '~0.00005', - ticker: 'LTC', - }, - DOGECOIN: { - value: '~0.005', - ticker: 'DOGE', - }, - BITCOIN: { - value: '~0.0001', - ticker: 'BTC', - }, - DIGIBYTE: { - value: '~0.0005', - ticker: 'DGB', - }, - RAVENCOIN: { - value: '~0.006', - ticker: 'RVN', - }, - PIRATECHAIN: { - value: '~0.0002', - ticker: 'ARRR', - }, -}; - -const btcFeePerByte = 0.000001; -const ltcFeePerByte = 0.0000003; -const dogeFeePerByte = 0.00001; -const dgbFeePerByte = 0.0000001; -const rvnFeePerByte = 0.00001125; - -const MAX_RETRIES = 3; // Set max number of retries - -export async function retryTransaction( - fn, - args, - throwError, - retries = MAX_RETRIES -) { - let attempt = 0; - while (attempt < retries) { - try { - return await fn(...args); - } catch (error) { - console.error(`Attempt ${attempt + 1} failed: ${error.message}`); - attempt++; - if (attempt === retries) { - console.error( - i18n.t('question:message.generic.max_retry_transaction', { - postProcess: 'capitalizeFirstChar', - }) - ); - if (throwError) { - throw new Error( - error?.message || - i18n.t('question:message.error.process_transaction', { - postProcess: 'capitalizeFirstChar', - }) - ); - } else { - throw new Error( - error?.message || - i18n.t('question:message.error.process_transaction', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } - await new Promise((res) => setTimeout(res, 10000)); - } - } -} - -function roundUpToDecimals(number, decimals = 8) { - const factor = Math.pow(10, decimals); // Create a factor based on the number of decimals - return Math.ceil(+number * factor) / factor; -} - -export const _createPoll = async ( - { pollName, pollDescription, options }, - isFromExtension, - skipPermission -) => { - const fee = await getFee('CREATE_POLL'); - let resPermission = {}; - if (!skipPermission) { - resPermission = await getUserPermission( - { - text1: i18n.t('question:request_create_poll', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:poll', { - name: pollName, - postProcess: 'capitalizeFirstChar', - }), - text3: i18n.t('question:description', { - description: pollDescription, - postProcess: 'capitalizeFirstChar', - }), - text4: i18n.t('question:options', { - optionList: options?.join(', '), - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - } - - const { accepted = false } = resPermission; - - if (accepted || skipPermission) { - const wallet = await getSaveWallet(); - const address = wallet.address0; - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const uint8PrivateKey = Base58.decode(parsedData.privateKey); - const uint8PublicKey = Base58.decode(parsedData.publicKey); - const keyPair = { - privateKey: uint8PrivateKey, - publicKey: uint8PublicKey, - }; - let lastRef = await getLastRef(); - - const tx = await createTransaction(8, keyPair, { - fee: fee.fee, - ownerAddress: address, - rPollName: pollName, - rPollDesc: pollDescription, - rOptions: options, - lastReference: lastRef, - }); - const signedBytes = Base58.encode(tx.signedBytes); - const res = await processTransactionVersion2(signedBytes); - if (!res?.signature) - throw new Error( - res?.message || - i18n.t('question:message.error.process_transaction', { - postProcess: 'capitalizeFirstChar', - }) - ); - return res; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -const _deployAt = async ( - { name, description, tags, creationBytes, amount, assetId, atType }, - isFromExtension -) => { - const fee = await getFee('DEPLOY_AT'); - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:deploy_at', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:name', { - name: name, - postProcess: 'capitalizeFirstChar', - }), - text3: i18n.t('question:description', { - description: description, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - - const { accepted } = resPermission; - - if (accepted) { - const wallet = await getSaveWallet(); - const address = wallet.address0; - const lastReference = await getLastRef(); - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const uint8PrivateKey = Base58.decode(parsedData.privateKey); - const uint8PublicKey = Base58.decode(parsedData.publicKey); - const keyPair = { - privateKey: uint8PrivateKey, - publicKey: uint8PublicKey, - }; - - const tx = await createTransaction(16, keyPair, { - fee: fee.fee, - rName: name, - rDescription: description, - rTags: tags, - rAmount: amount, - rAssetId: assetId, - rCreationBytes: creationBytes, - atType: atType, - lastReference: lastReference, - }); - - const signedBytes = Base58.encode(tx.signedBytes); - - const res = await processTransactionVersion2(signedBytes); - if (!res?.signature) - throw new Error( - res?.message || - i18n.t('question:message.error.process_transaction', { - postProcess: 'capitalizeFirstChar', - }) - ); - return res; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const _voteOnPoll = async ( - { pollName, optionIndex, optionName }, - isFromExtension, - skipPermission -) => { - const fee = await getFee('VOTE_ON_POLL'); - let resPermission = {}; - - if (!skipPermission) { - resPermission = await getUserPermission( - { - text1: i18n.t('question:request_vote_poll', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:poll', { - name: pollName, - postProcess: 'capitalizeFirstChar', - }), - text3: i18n.t('question:option', { - option: optionName, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - } - - const { accepted = false } = resPermission; - - if (accepted || skipPermission) { - const wallet = await getSaveWallet(); - const address = wallet.address0; - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const uint8PrivateKey = Base58.decode(parsedData.privateKey); - const uint8PublicKey = Base58.decode(parsedData.publicKey); - const keyPair = { - privateKey: uint8PrivateKey, - publicKey: uint8PublicKey, - }; - let lastRef = await getLastRef(); - - const tx = await createTransaction(9, keyPair, { - fee: fee.fee, - voterAddress: address, - rPollName: pollName, - rOptionIndex: optionIndex, - lastReference: lastRef, - }); - const signedBytes = Base58.encode(tx.signedBytes); - const res = await processTransactionVersion2(signedBytes); - if (!res?.signature) - throw new Error( - res?.message || - i18n.t('question:message.error.process_transaction', { - postProcess: 'capitalizeFirstChar', - }) - ); - return res; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -// Map to store resolvers and rejectors by requestId -const fileRequestResolvers = new Map(); - -const handleFileMessage = (event) => { - const { action, requestId, result, error } = event.data; - - if ( - action === 'getFileFromIndexedDBResponse' && - fileRequestResolvers.has(requestId) - ) { - const { resolve, reject } = fileRequestResolvers.get(requestId); - fileRequestResolvers.delete(requestId); // Clean up after resolving - - if (result) { - resolve(result); - } else { - reject( - error || - i18n.t('question:message.error.retrieve_file', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } -}; - -window.addEventListener('message', handleFileMessage); - -function getFileFromContentScript(fileId) { - return new Promise((resolve, reject) => { - const requestId = `getFile_${fileId}_${Date.now()}`; - - fileRequestResolvers.set(requestId, { resolve, reject }); // Store resolvers by requestId - const targetOrigin = window.location.origin; - - // Send the request message - window.postMessage( - { action: 'getFileFromIndexedDB', fileId, requestId }, - targetOrigin - ); - - // Timeout to handle no response scenario - setTimeout(() => { - if (fileRequestResolvers.has(requestId)) { - fileRequestResolvers.get(requestId).reject( - i18n.t('question:message.error.timeout_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - fileRequestResolvers.delete(requestId); // Clean up on timeout - } - }, 10000); // 10-second timeout - }); -} - -const responseResolvers = new Map(); - -const handleMessage = (event) => { - const { action, requestId, result } = event.data; - - // Check if this is the expected response action and if we have a stored resolver - if ( - action === 'QORTAL_REQUEST_PERMISSION_RESPONSE' && - responseResolvers.has(requestId) - ) { - // Resolve the stored promise with the result - responseResolvers.get(requestId)(result || false); - responseResolvers.delete(requestId); // Clean up after resolving - } -}; - -window.addEventListener('message', handleMessage); - -async function getUserPermission(payload, isFromExtension) { - return new Promise((resolve) => { - const requestId = `qortalRequest_${Date.now()}`; - responseResolvers.set(requestId, resolve); // Store resolver by requestId - const targetOrigin = window.location.origin; - - // Send the request message - window.postMessage( - { - action: 'QORTAL_REQUEST_PERMISSION', - payload, - requestId, - isFromExtension, - }, - targetOrigin - ); - - // Optional timeout to handle no response scenario - setTimeout(() => { - if (responseResolvers.has(requestId)) { - responseResolvers.get(requestId)(false); // Resolve with `false` if no response - responseResolvers.delete(requestId); - } - }, 60000); // 30-second timeout - }); -} - -export const getUserAccount = async ({ - isFromExtension, - appInfo, - skipAuth, -}) => { - try { - const value = - (await getPermission(`qAPPAutoAuth-${appInfo?.name}`)) || false; - let skip = false; - if (value) { - skip = true; - } - if (skipAuth) { - skip = true; - } - let resPermission; - if (!skip) { - resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.authenticate', { - postProcess: 'capitalizeFirstChar', - }), - checkbox1: { - value: false, - label: i18n.t('question:always_authenticate', { - postProcess: 'capitalizeFirstChar', - }), - }, - }, - isFromExtension - ); - } - - const { accepted = false, checkbox1 = false } = resPermission || {}; - if (resPermission) { - setPermission(`qAPPAutoAuth-${appInfo?.name}`, checkbox1); - } - if (accepted || skip) { - const wallet = await getSaveWallet(); - const address = wallet.address0; - const publicKey = wallet.publicKey; - return { - address, - publicKey, - }; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } catch (error) { - throw new Error( - i18n.t('auth:message.error.fetch_user_account', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const encryptData = async (data, sender) => { - let data64 = data.data64 || data.base64; - let publicKeys = data.publicKeys || []; - if (data?.file || data?.blob) { - data64 = await fileToBase64(data?.file || data?.blob); - } - if (!data64) { - throw new Error( - i18n.t('question:message.generic.include_data_encrypt', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const privateKey = parsedData.privateKey; - const userPublicKey = parsedData.publicKey; - - const encryptDataResponse = encryptDataGroup({ - data64, - publicKeys: publicKeys, - privateKey, - userPublicKey, - }); - if (encryptDataResponse) { - return encryptDataResponse; - } else { - throw new Error( - i18n.t('question:message.error.encrypt', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const encryptQortalGroupData = async (data, sender) => { - let data64 = data?.data64 || data?.base64; - let groupId = data?.groupId; - let isAdmins = data?.isAdmins; - if (!groupId) { - throw new Error( - i18n.t('question:message.generic.provide_group_id', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - if (data?.file || data?.blob) { - data64 = await fileToBase64(data?.file || data?.blob); - } - if (!data64) { - throw new Error( - i18n.t('question:message.generic.include_data_encrypt', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - let secretKeyObject; - if (!isAdmins) { - if ( - groupSecretkeys[groupId] && - groupSecretkeys[groupId].secretKeyObject && - groupSecretkeys[groupId]?.timestamp && - Date.now() - groupSecretkeys[groupId]?.timestamp < 1200000 - ) { - secretKeyObject = groupSecretkeys[groupId].secretKeyObject; - } - - if (!secretKeyObject) { - const { names } = await getGroupAdmins(groupId); - - const publish = await getPublishesFromAdmins(names, groupId); - if (publish === false) - throw new Error( - i18n.t('question:message.error.no_group_key', { - postProcess: 'capitalizeFirstChar', - }) - ); - const url = await createEndpoint( - `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ - publish.identifier - }?encoding=base64&rebuild=true` - ); - - const res = await fetch(url); - const resData = await res.text(); - - const decryptedKey: any = await decryptResource(resData, true); - - const dataint8Array = base64ToUint8Array(decryptedKey.data); - const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); - - if (!validateSecretKey(decryptedKeyToObject)) - throw new Error( - i18n.t('auth:message.error.invalid_secret_key', { - postProcess: 'capitalizeFirstChar', - }) - ); - secretKeyObject = decryptedKeyToObject; - groupSecretkeys[groupId] = { - secretKeyObject, - timestamp: Date.now(), - }; - } - } else { - if ( - groupSecretkeys[`admins-${groupId}`] && - groupSecretkeys[`admins-${groupId}`].secretKeyObject && - groupSecretkeys[`admins-${groupId}`]?.timestamp && - Date.now() - groupSecretkeys[`admins-${groupId}`]?.timestamp < 1200000 - ) { - secretKeyObject = groupSecretkeys[`admins-${groupId}`].secretKeyObject; - } - - if (!secretKeyObject) { - const { names } = await getGroupAdmins(groupId); - - const publish = await getPublishesFromAdminsAdminSpace(names, groupId); - if (publish === false) - throw new Error( - i18n.t('question:message.error.no_group_key', { - postProcess: 'capitalizeFirstChar', - }) - ); - const url = await createEndpoint( - `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ - publish.identifier - }?encoding=base64&rebuild=true` - ); - - const res = await fetch(url); - const resData = await res.text(); - const decryptedKey: any = await decryptResource(resData, true); - const dataint8Array = base64ToUint8Array(decryptedKey.data); - const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); - - if (!validateSecretKey(decryptedKeyToObject)) - throw new Error( - i18n.t('auth:message.error.invalid_secret_key', { - postProcess: 'capitalizeFirstChar', - }) - ); - secretKeyObject = decryptedKeyToObject; - groupSecretkeys[`admins-${groupId}`] = { - secretKeyObject, - timestamp: Date.now(), - }; - } - } - - const resGroupEncryptedResource = encryptSingle({ - data64, - secretKeyObject: secretKeyObject, - }); - - if (resGroupEncryptedResource) { - return resGroupEncryptedResource; - } else { - throw new Error( - i18n.t('question:message.error.encrypt', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const decryptQortalGroupData = async (data, sender) => { - let data64 = data?.data64 || data?.base64; - let groupId = data?.groupId; - let isAdmins = data?.isAdmins; - if (!groupId) { - throw new Error( - i18n.t('question:message.generic.provide_group_id', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - if (!data64) { - throw new Error( - i18n.t('question:message.generic.include_data_encrypt', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - let secretKeyObject; - if (!isAdmins) { - if ( - groupSecretkeys[groupId] && - groupSecretkeys[groupId].secretKeyObject && - groupSecretkeys[groupId]?.timestamp && - Date.now() - groupSecretkeys[groupId]?.timestamp < 1200000 - ) { - secretKeyObject = groupSecretkeys[groupId].secretKeyObject; - } - if (!secretKeyObject) { - const { names } = await getGroupAdmins(groupId); - - const publish = await getPublishesFromAdmins(names, groupId); - if (publish === false) - throw new Error( - i18n.t('question:message.error.no_group_key', { - postProcess: 'capitalizeFirstChar', - }) - ); - const url = await createEndpoint( - `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ - publish.identifier - }?encoding=base64&rebuild=true` - ); - - const res = await fetch(url); - const resData = await res.text(); - const decryptedKey: any = await decryptResource(resData, true); - - const dataint8Array = base64ToUint8Array(decryptedKey.data); - const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); - if (!validateSecretKey(decryptedKeyToObject)) - throw new Error( - i18n.t('auth:message.error.invalid_secret_key', { - postProcess: 'capitalizeFirstChar', - }) - ); - secretKeyObject = decryptedKeyToObject; - groupSecretkeys[groupId] = { - secretKeyObject, - timestamp: Date.now(), - }; - } - } else { - if ( - groupSecretkeys[`admins-${groupId}`] && - groupSecretkeys[`admins-${groupId}`].secretKeyObject && - groupSecretkeys[`admins-${groupId}`]?.timestamp && - Date.now() - groupSecretkeys[`admins-${groupId}`]?.timestamp < 1200000 - ) { - secretKeyObject = groupSecretkeys[`admins-${groupId}`].secretKeyObject; - } - if (!secretKeyObject) { - const { names } = await getGroupAdmins(groupId); - - const publish = await getPublishesFromAdminsAdminSpace(names, groupId); - if (publish === false) - throw new Error( - i18n.t('question:message.error.no_group_key', { - postProcess: 'capitalizeFirstChar', - }) - ); - const url = await createEndpoint( - `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ - publish.identifier - }?encoding=base64&rebuild=true` - ); - - const res = await fetch(url); - const resData = await res.text(); - const decryptedKey: any = await decryptResource(resData, true); - - const dataint8Array = base64ToUint8Array(decryptedKey.data); - const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); - if (!validateSecretKey(decryptedKeyToObject)) - throw new Error( - i18n.t('auth:message.error.invalid_secret_key', { - postProcess: 'capitalizeFirstChar', - }) - ); - secretKeyObject = decryptedKeyToObject; - groupSecretkeys[`admins-${groupId}`] = { - secretKeyObject, - timestamp: Date.now(), - }; - } - } - - const resGroupDecryptResource = decryptSingle({ - data64, - secretKeyObject: secretKeyObject, - skipDecodeBase64: true, - }); - if (resGroupDecryptResource) { - return resGroupDecryptResource; - } else { - throw new Error( - i18n.t('question:message.error.encrypt', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const encryptDataWithSharingKey = async (data, sender) => { - let data64 = data?.data64 || data?.base64; - let publicKeys = data.publicKeys || []; - if (data?.file || data?.blob) { - data64 = await fileToBase64(data?.file || data?.blob); - } - if (!data64) { - throw new Error( - i18n.t('question:message.generic.include_data_encrypt', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const symmetricKey = createSymmetricKeyAndNonce(); - const dataObject = { - data: data64, - key: symmetricKey.messageKey, - }; - const dataObjectBase64 = await objectToBase64(dataObject); - - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const privateKey = parsedData.privateKey; - const userPublicKey = parsedData.publicKey; - - const encryptDataResponse = encryptDataGroup({ - data64: dataObjectBase64, - publicKeys: publicKeys, - privateKey, - userPublicKey, - customSymmetricKey: symmetricKey.messageKey, - }); - if (encryptDataResponse) { - return encryptDataResponse; - } else { - throw new Error( - i18n.t('question:message.error.encrypt', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const decryptDataWithSharingKey = async (data, sender) => { - const { encryptedData, key } = data; - - if (!encryptedData) { - throw new Error( - i18n.t('question:message.generic.include_data_decrypt', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - const decryptedData = await decryptGroupEncryptionWithSharingKey({ - data64EncryptedData: encryptedData, - key, - }); - const base64ToObject = JSON.parse(atob(decryptedData)); - - if (!base64ToObject.data) - throw new Error( - i18n.t('question:message.error.no_data_encrypted_resource', { - postProcess: 'capitalizeFirstChar', - }) - ); - return base64ToObject.data; -}; - -export const getHostedData = async (data, isFromExtension) => { - const isGateway = await isRunningGateway(); - if (isGateway) { - throw new Error( - i18n.t('question:message.generic.no_action_public_node', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const resPermission = await getUserPermission( - { - text1: i18n.t('question:message.error.submit_sell_order', { - postProcess: 'capitalizeFirstChar', - }), - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { - const limit = data?.limit ? data?.limit : 20; - const query = data?.query ? data?.query : ''; - const offset = data?.offset ? data?.offset : 0; - - let urlPath = `/arbitrary/hosted/resources/?limit=${limit}&offset=${offset}`; - if (query) { - urlPath = urlPath + `&query=${query}`; - } - - const url = await createEndpoint(urlPath); - const response = await fetch(url); - const dataResponse = await response.json(); - return dataResponse; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_list', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const deleteHostedData = async (data, isFromExtension) => { - const isGateway = await isRunningGateway(); - if (isGateway) { - throw new Error( - i18n.t('question:message.generic.no_action_public_node', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const requiredFields = ['hostedData']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.delete_hosts_resources', { - size: data?.hostedData?.length, - postProcess: 'capitalizeFirstChar', - }), - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { - const { hostedData } = data; - - for (const hostedDataItem of hostedData) { - try { - const url = await createEndpoint( - `/arbitrary/resource/${hostedDataItem.service}/${hostedDataItem.name}/${hostedDataItem.identifier}` - ); - await fetch(url, { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, - }); - } catch (error) { - console.log(error); - } - } - - return true; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_delete_hosted_resources', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; -export const decryptData = async (data) => { - const { encryptedData, publicKey } = data; - - if (!encryptedData) { - throw new Error(`Missing fields: encryptedData`); - } - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const uint8PrivateKey = Base58.decode(parsedData.privateKey); - const uint8Array = base64ToUint8Array(encryptedData); - const startsWithQortalEncryptedData = uint8ArrayStartsWith( - uint8Array, - 'qortalEncryptedData' - ); - if (startsWithQortalEncryptedData) { - if (!publicKey) { - throw new Error(`Missing fields: publicKey`); - } - - const decryptedDataToBase64 = decryptDeprecatedSingle( - uint8Array, - publicKey, - uint8PrivateKey - ); - return decryptedDataToBase64; - } - const startsWithQortalGroupEncryptedData = uint8ArrayStartsWith( - uint8Array, - 'qortalGroupEncryptedData' - ); - if (startsWithQortalGroupEncryptedData) { - const decryptedData = decryptGroupDataQortalRequest( - encryptedData, - parsedData.privateKey - ); - const decryptedDataToBase64 = uint8ArrayToBase64(decryptedData); - return decryptedDataToBase64; - } - throw new Error( - i18n.t('question:message.error.encrypt', { - postProcess: 'capitalizeFirstChar', - }) - ); -}; - -export const getListItems = async (data, isFromExtension) => { - const isGateway = await isRunningGateway(); - if (isGateway) { - throw new Error( - i18n.t('question:message.generic.no_action_public_node', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const requiredFields = ['list_name']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const value = (await getPermission('qAPPAutoLists')) || false; - - let skip = false; - if (value) { - skip = true; - } - let resPermission; - let acceptedVar; - let checkbox1Var; - if (!skip) { - resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.access_list', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: data.list_name, - checkbox1: { - value: value, - label: i18n.t('question:always_retrieve_list', { - postProcess: 'capitalizeFirstChar', - }), - }, - }, - isFromExtension - ); - const { accepted, checkbox1 } = resPermission; - acceptedVar = accepted; - checkbox1Var = checkbox1; - setPermission('qAPPAutoLists', checkbox1); - } - - if (acceptedVar || skip) { - const url = await createEndpoint(`/lists/${data.list_name}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_list', { - postProcess: 'capitalizeFirstChar', - }) - ); - - const list = await response.json(); - return list; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_share_list', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const addListItems = async (data, isFromExtension) => { - const isGateway = await isRunningGateway(); - if (isGateway) { - throw new Error( - i18n.t('question:message.generic.no_action_public_node', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const requiredFields = ['list_name', 'items']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const items = data.items; - const list_name = data.list_name; - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.all_item_list', { - name: list_name, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: items.join(', '), - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { - const url = await createEndpoint(`/lists/${list_name}`); - const body = { - items: items, - }; - const bodyToString = JSON.stringify(body); - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: bodyToString, - }); - - if (!response.ok) - throw new Error( - i18n.t('question:message.error.add_to_list', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - return res; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_add_list', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const deleteListItems = async (data, isFromExtension) => { - const isGateway = await isRunningGateway(); - if (isGateway) { - throw new Error( - i18n.t('question:message.generic.no_action_public_node', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const requiredFields = ['list_name']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - if (!data?.item && !data?.items) { - throw new Error( - i18n.t('question:message.error.missing_fields', { - fields: 'items', - postProcess: 'capitalizeFirstChar', - }) - ); - } - const item = data?.item; - const items = data?.items; - const list_name = data.list_name; - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.remove_from_list', { - name: list_name, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: items ? JSON.stringify(items) : item, - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { - const url = await createEndpoint(`/lists/${list_name}`); - const body = { - items: items || [item], - }; - const bodyToString = JSON.stringify(body); - const response = await fetch(url, { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, - body: bodyToString, - }); - - if (!response.ok) - throw new Error( - i18n.t('question:message.error.add_to_list', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - return res; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_delete_from_list', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const publishQDNResource = async ( - data: any, - sender, - isFromExtension -) => { - const requiredFields = ['service']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - if (!data.file && !data.data64 && !data.base64) { - throw new Error( - i18n.t('question:message.error.no_data_file_submitted', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - // Use "default" if user hasn't specified an identifier - const service = data.service; - const appFee = data?.appFee ? +data.appFee : undefined; - const appFeeRecipient = data?.appFeeRecipient; - let hasAppFee = false; - if (appFee && appFee > 0 && appFeeRecipient) { - hasAppFee = true; - } - - const registeredName = data?.name || (await getNameInfo()); - const name = registeredName; - if (!name) { - throw new Error( - i18n.t('question:message.error.user_qortal_name', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - let identifier = data.identifier; - let data64 = data.data64 || data.base64; - const filename = data.filename; - const title = data.title; - const description = data.description; - const category = data.category; - const file = data?.file || data?.blob; - const tags = data?.tags || []; - const result = {}; - - // Fill tags dynamically while maintaining backward compatibility - for (let i = 0; i < 5; i++) { - result[`tag${i + 1}`] = tags[i] || data[`tag${i + 1}`] || undefined; - } - - // Access tag1 to tag5 from result - const { tag1, tag2, tag3, tag4, tag5 } = result; - - if (data.identifier == null) { - identifier = 'default'; - } - - if ( - data.encrypt && - (!data.publicKeys || - (Array.isArray(data.publicKeys) && data.publicKeys.length === 0)) - ) { - throw new Error( - i18n.t('question:message.error.encryption_requires_public_key', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - if (data.encrypt) { - try { - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const privateKey = parsedData.privateKey; - const userPublicKey = parsedData.publicKey; - if (data?.file || data?.blob) { - data64 = await fileToBase64(data?.file || data?.blob); - } - const encryptDataResponse = encryptDataGroup({ - data64, - publicKeys: data.publicKeys, - privateKey, - userPublicKey, - }); - if (encryptDataResponse) { - data64 = encryptDataResponse; - } - } catch (error) { - throw new Error( - error.message || - i18n.t('question:message.error.upload_encryption', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } - - const fee = await getFee('ARBITRARY'); - - const handleDynamicValues = {}; - if (hasAppFee) { - const feePayment = await getFee('PAYMENT'); - - (handleDynamicValues['appFee'] = +appFee + +feePayment.fee), - (handleDynamicValues['checkbox1'] = { - value: true, - label: i18n.t('question:accept_app_fee', { - postProcess: 'capitalizeFirstChar', - }), - }); - } - if (!!data?.encrypt) { - handleDynamicValues['highlightedText'] = `isEncrypted: ${!!data.encrypt}`; - } - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.publish_qdn', { - postProcess: 'capitalizeFirstChar', - }), - text2: `service: ${service}`, - text3: `identifier: ${identifier || null}`, - text4: `name: ${registeredName}`, - fee: fee.fee, - ...handleDynamicValues, - }, - isFromExtension - ); - const { accepted, checkbox1 = false } = resPermission; - if (accepted) { - try { - const resPublish = await publishData({ - registeredName: encodeURIComponent(name), - data: data64 ? data64 : file, - service: service, - identifier: encodeURIComponent(identifier), - uploadType: data64 ? 'base64' : 'file', - filename: filename, - title, - description, - category, - tag1, - tag2, - tag3, - tag4, - tag5, - apiVersion: 2, - withFee: true, - }); - if (resPublish?.signature && hasAppFee && checkbox1) { - sendCoinFunc( - { - amount: appFee, - receiver: appFeeRecipient, - }, - true - ); - } - return resPublish; - } catch (error) { - throw new Error(error?.message || 'Upload failed'); - } - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const checkArrrSyncStatus = async (seed) => { - const _url = await createEndpoint(`/crosschain/arrr/syncstatus`); - let tries = 0; // Track the number of attempts - - while (tries < 36) { - const response = await fetch(_url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: seed, - }); - - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - - if (res.indexOf('<') > -1 || res !== 'Synchronized') { - // Wait 2 seconds before trying again - await new Promise((resolve) => setTimeout(resolve, 2000)); - tries += 1; - } else { - // If the response doesn't meet the two conditions, exit the function - return; - } - } - - // If we exceed N tries, throw an error - throw new Error( - i18n.t('question:message.error.synchronization_attempts', { - quantity: 36, - postProcess: 'capitalizeFirstChar', - }) - ); -}; - -export const publishMultipleQDNResources = async ( - data: any, - sender, - isFromExtension -) => { - const requiredFields = ['resources']; - const missingFields: string[] = []; - - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const resources = data.resources; - if (!Array.isArray(resources)) { - throw new Error( - i18n.t('group:message.generic.invalid_data', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - if (resources.length === 0) { - throw new Error( - i18n.t('question:message.error.no_resources_publish', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - const encrypt = data?.encrypt; - - for (const resource of resources) { - const resourceEncrypt = encrypt && resource?.disableEncrypt !== true; - - if (!resourceEncrypt && resource?.service.endsWith('_PRIVATE')) { - const errorMsg = i18n.t('question:message.error.only_encrypted_data', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } else if (resourceEncrypt && !resource?.service.endsWith('_PRIVATE')) { - const errorMsg = i18n.t('question:message.error.use_private_service', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - } - - const fee = await getFee('ARBITRARY'); - const registeredName = await getNameInfo(); - - const name = registeredName; - - if (!name) { - throw new Error( - i18n.t('question:message.error.registered_name', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - const userNames = await getAllUserNames(); - data.resources?.forEach((item) => { - if (item?.name && !userNames?.includes(item.name)) - throw new Error( - `The name ${item.name}, does not belong to the publisher.` - ); - }); - - const appFee = data?.appFee ? +data.appFee : undefined; - const appFeeRecipient = data?.appFeeRecipient; - let hasAppFee = false; - - if (appFee && appFee > 0 && appFeeRecipient) { - hasAppFee = true; - } - - const handleDynamicValues = {}; - if (hasAppFee) { - const feePayment = await getFee('PAYMENT'); - - (handleDynamicValues['appFee'] = +appFee + +feePayment.fee), - (handleDynamicValues['checkbox1'] = { - value: true, - label: i18n.t('question:accept_app_fee', { - postProcess: 'capitalizeFirstChar', - }), - }); - } - if (data?.encrypt) { - handleDynamicValues['highlightedText'] = `isEncrypted: ${!!data.encrypt}`; - } - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.publish_qdn', { - postProcess: 'capitalizeFirstChar', - }), - html: ` -
    - - - ${data.resources - .map( - (resource) => ` -
    -
    Service: ${ - resource.service - }
    -
    Name: ${resource?.name || name}
    -
    Identifier: ${ - resource.identifier - }
    - ${ - resource.filename - ? `
    Filename: ${resource.filename}
    ` - : '' - } -
    ` - ) - .join('')} -
    - - `, - fee: +fee.fee * resources.length, - ...handleDynamicValues, - }, - isFromExtension - ); - - const { accepted, checkbox1 = false } = resPermission; - if (!accepted) { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - type FailedPublish = { - reason: string; - identifier: any; - service: any; - }; - - const failedPublishesIdentifiers: FailedPublish[] = []; - - for (const resource of resources) { - try { - const requiredFields = ['service']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!resource[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - failedPublishesIdentifiers.push({ - reason: errorMsg, - identifier: resource.identifier, - service: resource.service, - name: resource?.name || name, - }); - continue; - } - if (!resource.file && !resource.data64 && !resource?.base64) { - const errorMsg = i18n.t( - 'question:message.error.no_data_file_submitted', - { - postProcess: 'capitalizeFirstChar', - } - ); - failedPublishesIdentifiers.push({ - reason: errorMsg, - identifier: resource.identifier, - service: resource.service, - name: resource?.name || name, - }); - continue; - } - const service = resource.service; - let identifier = resource.identifier; - let rawData = resource?.data64 || resource?.base64; - const filename = resource.filename; - const title = resource.title; - const description = resource.description; - const category = resource.category; - const tags = resource?.tags || []; - const result = {}; - // Fill tags dynamically while maintaining backward compatibility - for (let i = 0; i < 5; i++) { - result[`tag${i + 1}`] = tags[i] || resource[`tag${i + 1}`] || undefined; - } - - // Access tag1 to tag5 from result - const { tag1, tag2, tag3, tag4, tag5 } = result; - const resourceEncrypt = encrypt && resource?.disableEncrypt !== true; - if (resource.identifier == null) { - identifier = 'default'; - } - if (!resourceEncrypt && service.endsWith('_PRIVATE')) { - const errorMsg = i18n.t('question:message.error.only_encrypted_data', { - postProcess: 'capitalizeFirstChar', - }); - failedPublishesIdentifiers.push({ - reason: errorMsg, - identifier: resource.identifier, - service: resource.service, - name: resource?.name || name, - }); - continue; - } - if (resource.file) { - rawData = resource.file; - } - - if (resourceEncrypt) { - try { - if (resource?.file) { - rawData = await fileToBase64(resource.file); - } - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const privateKey = parsedData.privateKey; - const userPublicKey = parsedData.publicKey; - const encryptDataResponse = encryptDataGroup({ - data64: rawData, - publicKeys: data.publicKeys, - privateKey, - userPublicKey, - }); - if (encryptDataResponse) { - rawData = encryptDataResponse; - } - } catch (error) { - const errorMsg = - error?.message || - i18n.t('question:message.error.upload_encryption', { - postProcess: 'capitalizeFirstChar', - }); - failedPublishesIdentifiers.push({ - reason: errorMsg, - identifier: resource.identifier, - service: resource.service, - name: resource?.name || name, - }); - continue; - } - } - - try { - const dataType = - resource?.base64 || resource?.data64 || resourceEncrypt - ? 'base64' - : 'file'; - console.log('dataType', dataType); - await retryTransaction( - publishData, - [ - { - data: rawData, - registeredName: encodeURIComponent(resource?.name || name), - service: service, - identifier: encodeURIComponent(identifier), - uploadType: dataType, - filename: filename, - title, - description, - category, - tag1, - tag2, - tag3, - tag4, - tag5, - apiVersion: 2, - withFee: true, - }, - ], - true - ); - await new Promise((res) => { - setTimeout(() => { - res(); - }, 1000); - }); - } catch (error) { - const errorMsg = - error.message || - i18n.t('question:message.error.upload', { - postProcess: 'capitalizeFirstChar', - }); - failedPublishesIdentifiers.push({ - reason: errorMsg, - identifier: resource.identifier, - service: resource.service, - name: resource?.name || name, - }); - } - } catch (error) { - failedPublishesIdentifiers.push({ - reason: - error?.message || - i18n.t('question:message.error.unknown_error', { - postProcess: 'capitalizeFirstChar', - }), - identifier: resource.identifier, - service: resource.service, - name: resource?.name || name, - }); - } - } - if (failedPublishesIdentifiers.length > 0) { - const obj = { - message: i18n.t('question:message.error.resources_publish', { - postProcess: 'capitalizeFirstChar', - }), - }; - obj['error'] = { - unsuccessfulPublishes: failedPublishesIdentifiers, - }; - return obj; - } - if (hasAppFee && checkbox1) { - sendCoinFunc( - { - amount: appFee, - receiver: appFeeRecipient, - }, - true - ); - } - return true; -}; - -export const voteOnPoll = async (data, isFromExtension) => { - const requiredFields = ['pollName', 'optionIndex']; - const missingFields: string[] = []; - - requiredFields.forEach((field) => { - if (!data[field] && data[field] !== 0) { - missingFields.push(field); - } - }); - - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const pollName = data.pollName; - const optionIndex = data.optionIndex; - let pollInfo = null; - try { - const url = await createEndpoint(`/polls/${encodeURIComponent(pollName)}`); - const response = await fetch(url); - if (!response.ok) { - const errorMessage = await parseErrorResponse( - response, - i18n.t('question:message.error.fetch_poll', { - postProcess: 'capitalizeFirstChar', - }) - ); - throw new Error(errorMessage); - } - - pollInfo = await response.json(); - } catch (error) { - const errorMsg = - (error && error.message) || - i18n.t('question:message.error.no_poll', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - if (!pollInfo || pollInfo.error) { - const errorMsg = - (pollInfo && pollInfo.message) || - i18n.t('question:message.error.no_poll', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - try { - const optionName = pollInfo.pollOptions[optionIndex].optionName; - const resVoteOnPoll = await _voteOnPoll( - { pollName, optionIndex, optionName }, - isFromExtension - ); - return resVoteOnPoll; - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.poll_vote', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const createPoll = async (data, isFromExtension) => { - const requiredFields = [ - 'pollName', - 'pollDescription', - 'pollOptions', - 'pollOwnerAddress', - ]; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const pollName = data.pollName; - const pollDescription = data.pollDescription; - const pollOptions = data.pollOptions; - try { - const resCreatePoll = await _createPoll( - { - pollName, - pollDescription, - options: pollOptions, - }, - isFromExtension - ); - return resCreatePoll; - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.poll_create', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -function isBase64(str) { - const base64Regex = - /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; - return base64Regex.test(str) && str.length % 4 === 0; -} - -function checkValue(value) { - if (typeof value === 'string') { - if (isBase64(value)) { - return 'string'; - } else { - return 'string'; - } - } else if (typeof value === 'object' && value !== null) { - return 'object'; - } else { - throw new Error( - i18n.t('question:message.error.invalid_fullcontent', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -} - -export const sendChatMessage = async (data, isFromExtension, appInfo) => { - const message = data?.message; - const fullMessageObject = data?.fullMessageObject || data?.fullContent; - const recipient = data?.destinationAddress || data.recipient; - const groupId = data.groupId; - const isRecipient = groupId === undefined; - const chatReference = data?.chatReference; - if (groupId === undefined && recipient === undefined) { - throw new Error( - i18n.t('question:provide_recipient_group_id', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - let fullMessageObjectType; - if (fullMessageObject) { - fullMessageObjectType = checkValue(fullMessageObject); - } - const value = - (await getPermission(`qAPPSendChatMessage-${appInfo?.name}`)) || false; - let skip = false; - if (value) { - skip = true; - } - let resPermission; - if (!skip) { - resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.send_chat_message', { - postProcess: 'capitalizeFirstChar', - }), - text2: isRecipient - ? i18n.t('question:to_recipient', { - recipient: recipient, - postProcess: 'capitalizeFirstChar', - }) - : i18n.t('question:to_group', { - group_id: groupId, - postProcess: 'capitalizeFirstChar', - }), - text3: fullMessageObject - ? fullMessageObjectType === 'string' - ? `${fullMessageObject?.slice(0, 25)}${fullMessageObject?.length > 25 ? '...' : ''}` - : `${JSON.stringify(fullMessageObject)?.slice(0, 25)}${JSON.stringify(fullMessageObject)?.length > 25 ? '...' : ''}` - : `${message?.slice(0, 25)}${message?.length > 25 ? '...' : ''}`, - checkbox1: { - value: false, - label: i18n.t('question:always_chat_messages', { - postProcess: 'capitalizeFirstChar', - }), - }, - }, - isFromExtension - ); - } - const { accepted = false, checkbox1 = false } = resPermission || {}; - if (resPermission && accepted) { - setPermission(`qAPPSendChatMessage-${appInfo?.name}`, checkbox1); - } - if (accepted || skip) { - const tiptapJson = { - type: 'doc', - content: [ - { - type: 'paragraph', - content: [ - { - type: 'text', - text: message, - }, - ], - }, - ], - }; - const messageObject = fullMessageObject - ? fullMessageObject - : { - messageText: tiptapJson, - images: [], - repliedTo: '', - version: 3, - }; - - let stringifyMessageObject = JSON.stringify(messageObject); - if (fullMessageObjectType === 'string') { - stringifyMessageObject = messageObject; - } - - const balance = await getBalanceInfo(); - const hasEnoughBalance = +balance < 4 ? false : true; - if (!hasEnoughBalance) { - throw new Error( - i18n.t('group:message.error.qortals_required', { - quantity: 4, - postProcess: 'capitalizeFirstChar', - }) - ); - } - if (isRecipient && recipient) { - const url = await createEndpoint(`/addresses/publickey/${recipient}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_recipient_public_key', { - postProcess: 'capitalizeFirstChar', - }) - ); - - let key; - let hasPublicKey; - let res; - const contentType = response.headers.get('content-type'); - - // If the response is JSON, parse it as JSON - if (contentType && contentType.includes('application/json')) { - res = await response.json(); - } else { - // Otherwise, treat it as plain text - res = await response.text(); - } - if (res?.error === 102) { - key = ''; - hasPublicKey = false; - } else if (res !== false) { - key = res; - hasPublicKey = true; - } else { - key = ''; - hasPublicKey = false; - } - - if (!hasPublicKey && isRecipient) { - throw new Error( - 'Cannot send an encrypted message to this user since they do not have their publickey on chain.' - ); - } - let _reference = new Uint8Array(64); - self.crypto.getRandomValues(_reference); - - let sendTimestamp = Date.now(); - - let reference = Base58.encode(_reference); - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const uint8PrivateKey = Base58.decode(parsedData.privateKey); - const uint8PublicKey = Base58.decode(parsedData.publicKey); - const keyPair = { - privateKey: uint8PrivateKey, - publicKey: uint8PublicKey, - }; - - let handleDynamicValues = {}; - if (chatReference) { - handleDynamicValues['chatReference'] = chatReference; - } - - const tx = await createTransaction(18, keyPair, { - timestamp: sendTimestamp, - recipient: recipient, - recipientPublicKey: key, - hasChatReference: chatReference ? 1 : 0, - message: stringifyMessageObject, - lastReference: reference, - proofOfWorkNonce: 0, - isEncrypted: 1, - isText: 1, - ...handleDynamicValues, - }); - - const chatBytes = tx.chatBytes; - const difficulty = 8; - const { nonce, chatBytesArray } = await performPowTask( - chatBytes, - difficulty - ); - - let _response = await signChatFunc(chatBytesArray, nonce, null, keyPair); - if (_response?.error) { - throw new Error(_response?.message); - } - return _response; - } else if (!isRecipient && groupId) { - let _reference = new Uint8Array(64); - self.crypto.getRandomValues(_reference); - - let reference = Base58.encode(_reference); - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const uint8PrivateKey = Base58.decode(parsedData.privateKey); - const uint8PublicKey = Base58.decode(parsedData.publicKey); - const keyPair = { - privateKey: uint8PrivateKey, - publicKey: uint8PublicKey, - }; - - let handleDynamicValues = {}; - if (chatReference) { - handleDynamicValues['chatReference'] = chatReference; - } - - const txBody = { - timestamp: Date.now(), - groupID: Number(groupId), - hasReceipient: 0, - hasChatReference: chatReference ? 1 : 0, - message: stringifyMessageObject, - lastReference: reference, - proofOfWorkNonce: 0, - isEncrypted: 0, // Set default to not encrypted for groups - isText: 1, - ...handleDynamicValues, - }; - - const tx = await createTransaction(181, keyPair, txBody); - - // if (!hasEnoughBalance) { - // throw new Error("Must have at least 4 QORT to send a chat message"); - // } - - const chatBytes = tx.chatBytes; - const difficulty = 8; - const { nonce, chatBytesArray } = await performPowTask( - chatBytes, - difficulty - ); - - let _response = await signChatFunc(chatBytesArray, nonce, null, keyPair); - if (_response?.error) { - throw new Error(_response?.message); - } - return _response; - } else { - throw new Error( - i18n.t('question:provide_recipient_group_id', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_send_message', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const joinGroup = async (data, isFromExtension) => { - const requiredFields = ['groupId']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - let groupInfo = null; - try { - const url = await createEndpoint(`/groups/${data.groupId}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_group', { - postProcess: 'capitalizeFirstChar', - }) - ); - - groupInfo = await response.json(); - } catch (error) { - const errorMsg = - (error && error.message) || - i18n.t('question:message.error.no_group_found', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const fee = await getFee('JOIN_GROUP'); - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:message.generic.confirm_join_group', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: `${groupInfo.groupName}`, - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { - const groupId = data.groupId; - - if (!groupInfo || groupInfo.error) { - const errorMsg = - (groupInfo && groupInfo.message) || - i18n.t('question:message.error.no_group_found', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - try { - const resJoinGroup = await joinGroupFunc({ groupId }); - return resJoinGroup; - } catch (error) { - throw new Error( - error?.message || - i18n.t('group:message.error.group_join', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_join', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const saveFile = async (data, sender, isFromExtension, snackMethods) => { - try { - if (!data?.filename) throw new Error('Missing filename'); - if (data?.location) { - const requiredFieldsLocation = ['service', 'name']; - const missingFieldsLocation: string[] = []; - requiredFieldsLocation.forEach((field) => { - if (!data?.location[field]) { - missingFieldsLocation.push(field); - } - }); - if (missingFieldsLocation.length > 0) { - const missingFieldsString = missingFieldsLocation.join(', '); - const errorMsg = `Missing fields: ${missingFieldsString}`; - throw new Error(errorMsg); - } - const resPermission = await getUserPermission( - { - text1: 'Would you like to download:', - highlightedText: `${data?.filename}`, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (!accepted) throw new Error('User declined to save file'); - const a = document.createElement('a'); - let locationUrl = `/arbitrary/${data.location.service}/${data.location.name}`; - if (data.location.identifier) { - locationUrl = locationUrl + `/${data.location.identifier}`; - } - const endpoint = await createEndpoint( - locationUrl + `?attachment=true&attachmentFilename=${data?.filename}` - ); - a.href = endpoint; - a.download = data.filename; - document.body.appendChild(a); - a.click(); - a.remove(); - return true; - } - const requiredFields = ['filename', 'blob']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const filename = data.filename; - const blob = data.blob; - - const mimeType = blob.type || data.mimeType; - const resPermission = await getUserPermission( - { - text1: i18n.t('question:download_file', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: `${filename}`, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (!accepted) throw new Error('User declined to save file'); - showSaveFilePicker( - { - filename, - mimeType, - blob, - }, - snackMethods - ); - - return true; - - if (accepted) { - const mimeType = blob.type || data.mimeType; - let backupExention = filename.split('.').pop(); - if (backupExention) { - backupExention = '.' + backupExention; - } - const fileExtension = mimeToExtensionMap[mimeType] || backupExention; - let fileHandleOptions = {}; - if (!mimeType) { - throw new Error( - i18n.t('question:message.error.mime_type', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - if (!fileExtension) { - throw new Error( - i18n.t('question:message.error.file_extension', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - if (fileExtension && mimeType) { - fileHandleOptions = { - accept: { - [mimeType]: [fileExtension], - }, - }; - } - - showSaveFilePicker( - { - filename, - mimeType, - blob, - }, - snackMethods - ); - return true; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_save_file', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } catch (error) { - throw new Error( - error?.message || - i18n.t('core:message.error.initiate_download', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const deployAt = async (data, isFromExtension) => { - const requiredFields = [ - 'name', - 'description', - 'tags', - 'creationBytes', - 'amount', - 'assetId', - 'type', - ]; - - const missingFields: string[] = []; - - requiredFields.forEach((field) => { - if (!data[field] && data[field] !== 0) { - missingFields.push(field); - } - }); - - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - try { - const resDeployAt = await _deployAt( - { - name: data.name, - description: data.description, - tags: data.tags, - creationBytes: data.creationBytes, - amount: data.amount, - assetId: data.assetId, - atType: data.type, - }, - isFromExtension - ); - return resDeployAt; - } catch (error) { - throw new Error( - error?.message || - i18n.t('group:message.error.group_join', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const getUserWallet = async (data, isFromExtension, appInfo) => { - const requiredFields = ['coin']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const isGateway = await isRunningGateway(); - - if (data?.coin === 'ARRR' && isGateway) - throw new Error( - i18n.t('question:message.error.gateway_wallet_local_node', { - token: 'ARRR', - postProcess: 'capitalizeFirstChar', - }) - ); - - const value = - (await getPermission( - `qAPPAutoGetUserWallet-${appInfo?.name}-${data.coin}` - )) || false; - let skip = false; - if (value) { - skip = true; - } - - let resPermission; - - if (!skip) { - resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.get_wallet_info', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: `coin: ${data.coin}`, - checkbox1: { - value: true, - label: i18n.t('question:always_retrieve_wallet', { - postProcess: 'capitalizeFirstChar', - }), - }, - }, - isFromExtension - ); - } - const { accepted = false, checkbox1 = false } = resPermission || {}; - - if (resPermission) { - setPermission( - `qAPPAutoGetUserWallet-${appInfo?.name}-${data.coin}`, - checkbox1 - ); - } - - if (accepted || skip) { - let coin = data.coin; - let userWallet = {}; - let arrrAddress = ''; - const wallet = await getSaveWallet(); - const address = wallet.address0; - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const arrrSeed58 = parsedData.arrrSeed58; - if (coin === 'ARRR') { - const bodyToString = arrrSeed58; - const url = await createEndpoint(`/crosschain/arrr/walletaddress`); - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: bodyToString, - }); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - if (res?.error && res?.message) { - throw new Error(res.message); - } - arrrAddress = res; - } - switch (coin) { - case 'QORT': - userWallet['address'] = address; - userWallet['publickey'] = parsedData.publicKey; - break; - case 'BTC': - userWallet['address'] = parsedData.btcAddress; - userWallet['publickey'] = parsedData.btcPublicKey; - break; - case 'LTC': - userWallet['address'] = parsedData.ltcAddress; - userWallet['publickey'] = parsedData.ltcPublicKey; - break; - case 'DOGE': - userWallet['address'] = parsedData.dogeAddress; - userWallet['publickey'] = parsedData.dogePublicKey; - break; - case 'DGB': - userWallet['address'] = parsedData.dgbAddress; - userWallet['publickey'] = parsedData.dgbPublicKey; - break; - case 'RVN': - userWallet['address'] = parsedData.rvnAddress; - userWallet['publickey'] = parsedData.rvnPublicKey; - break; - case 'ARRR': - await checkArrrSyncStatus(parsedData.arrrSeed58); - userWallet['address'] = arrrAddress; - break; - default: - break; - } - return userWallet; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const getWalletBalance = async ( - data, - bypassPermission?: boolean, - isFromExtension?: boolean, - appInfo?: any -) => { - const requiredFields = ['coin']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const isGateway = await isRunningGateway(); - - if (data?.coin === 'ARRR' && isGateway) - throw new Error( - i18n.t('question:message.error.gateway_balance_local_node', { - token: 'ARRR', - postProcess: 'capitalizeFirstChar', - }) - ); - - const value = - (await getPermission( - `qAPPAutoWalletBalance-${appInfo?.name}-${data.coin}` - )) || false; - let skip = false; - if (value) { - skip = true; - } - let resPermission; - - if (!bypassPermission && !skip) { - resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.fetch_balance', { - coin: data.coin, // TODO highlight coin in the modal - postProcess: 'capitalizeFirstChar', - }), - checkbox1: { - value: true, - label: i18n.t('question:always_retrieve_balance', { - postProcess: 'capitalizeFirstChar', - }), - }, - }, - isFromExtension - ); - } - const { accepted = false, checkbox1 = false } = resPermission || {}; - if (resPermission) { - setPermission( - `qAPPAutoWalletBalance-${appInfo?.name}-${data.coin}`, - checkbox1 - ); - } - if (accepted || bypassPermission || skip) { - let coin = data.coin; - const wallet = await getSaveWallet(); - const address = wallet.address0; - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - if (coin === 'QORT') { - let qortAddress = address; - try { - const url = await createEndpoint(`/addresses/balance/${qortAddress}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_balance', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - return res; - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.fetch_wallet', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } else { - let _url = ``; - let _body = null; - switch (coin) { - case 'BTC': - _url = await createEndpoint(`/crosschain/btc/walletbalance`); - - _body = parsedData.btcPublicKey; - break; - case 'LTC': - _url = await createEndpoint(`/crosschain/ltc/walletbalance`); - _body = parsedData.ltcPublicKey; - break; - case 'DOGE': - _url = await createEndpoint(`/crosschain/doge/walletbalance`); - _body = parsedData.dogePublicKey; - break; - case 'DGB': - _url = await createEndpoint(`/crosschain/dgb/walletbalance`); - _body = parsedData.dgbPublicKey; - break; - case 'RVN': - _url = await createEndpoint(`/crosschain/rvn/walletbalance`); - _body = parsedData.rvnPublicKey; - break; - case 'ARRR': - await checkArrrSyncStatus(parsedData.arrrSeed58); - _url = await createEndpoint(`/crosschain/arrr/walletbalance`); - _body = parsedData.arrrSeed58; - break; - default: - break; - } - try { - const response = await fetch(_url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: _body, - }); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - if (res?.error && res?.message) { - throw new Error(res.message); - } - if (isNaN(Number(res))) { - throw new Error( - i18n.t('question:message.error.fetch_balance', { - postProcess: 'capitalizeFirstChar', - }) - ); - } else { - return (Number(res) / 1e8).toFixed(8); - } - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.fetch_balance', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -const getPirateWallet = async (arrrSeed58) => { - const isGateway = await isRunningGateway(); - if (isGateway) { - throw new Error( - i18n.t('question:message.error.gateway_retrieve_balance', { - token: 'PIRATECHAIN', - postProcess: 'capitalizeFirstChar', - }) - ); - } - const bodyToString = arrrSeed58; - await checkArrrSyncStatus(bodyToString); - const url = await createEndpoint(`/crosschain/arrr/walletaddress`); - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: bodyToString, - }); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - if (res?.error && res?.message) { - throw new Error(res.message); - } - return res; -}; - -export const getUserWalletFunc = async (coin) => { - let userWallet = {}; - const wallet = await getSaveWallet(); - const address = wallet.address0; - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - switch (coin) { - case 'QORT': - userWallet['address'] = address; - userWallet['publickey'] = parsedData.publicKey; - break; - case 'BTC': - case 'BITCOIN': - userWallet['address'] = parsedData.btcAddress; - userWallet['publickey'] = parsedData.btcPublicKey; - break; - case 'LTC': - case 'LITECOIN': - userWallet['address'] = parsedData.ltcAddress; - userWallet['publickey'] = parsedData.ltcPublicKey; - break; - case 'DOGE': - case 'DOGECOIN': - userWallet['address'] = parsedData.dogeAddress; - userWallet['publickey'] = parsedData.dogePublicKey; - break; - case 'DGB': - case 'DIGIBYTE': - userWallet['address'] = parsedData.dgbAddress; - userWallet['publickey'] = parsedData.dgbPublicKey; - break; - case 'RVN': - case 'RAVENCOIN': - userWallet['address'] = parsedData.rvnAddress; - userWallet['publickey'] = parsedData.rvnPublicKey; - break; - case 'ARRR': - case 'PIRATECHAIN': - const arrrAddress = await getPirateWallet(parsedData.arrrSeed58); - userWallet['address'] = arrrAddress; - break; - default: - break; - } - return userWallet; -}; - -export const getUserWalletInfo = async (data, isFromExtension, appInfo) => { - const requiredFields = ['coin']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - if (data?.coin === 'ARRR') { - throw new Error( - i18n.t('question:message.error.token_not_supported', { - token: 'ARRR', - postProcess: 'capitalizeFirstChar', - }) - ); - } - const value = - (await getPermission(`getUserWalletInfo-${appInfo?.name}-${data.coin}`)) || - false; - let skip = false; - if (value) { - skip = true; - } - let resPermission; - - if (!skip) { - resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.get_wallet_info', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: `coin: ${data.coin}`, - checkbox1: { - value: true, - label: i18n.t('question:always_retrieve_wallet', { - postProcess: 'capitalizeFirstChar', - }), - }, - }, - isFromExtension - ); - } - const { accepted = false, checkbox1 = false } = resPermission || {}; - - if (resPermission) { - setPermission(`getUserWalletInfo-${appInfo?.name}-${data.coin}`, checkbox1); - } - - if (accepted || skip) { - let coin = data.coin; - let walletKeys = await getUserWalletFunc(coin); - - const _url = await createEndpoint( - `/crosschain/` + data.coin.toLowerCase() + `/addressinfos` - ); - let _body = { xpub58: walletKeys['publickey'] }; - try { - const response = await fetch(_url, { - method: 'POST', - headers: { - Accept: '*/*', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(_body), - }); - if (!response?.ok) - throw new Error( - i18n.t('question:message.error.fetch_wallet_info', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - if (res?.error && res?.message) { - throw new Error(res.message); - } - - return res; - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.fetch_wallet', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const getUserWalletTransactions = async ( - data, - isFromExtension, - appInfo -) => { - const requiredFields = ['coin']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const value = - (await getPermission( - `getUserWalletTransactions-${appInfo?.name}-${data.coin}` - )) || false; - let skip = false; - if (value) { - skip = true; - } - let resPermission; - - if (!skip) { - resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.get_wallet_transactions', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: `coin: ${data.coin}`, - checkbox1: { - value: true, - label: i18n.t('question:always_retrieve_wallet_transactions', { - postProcess: 'capitalizeFirstChar', - }), - }, - }, - isFromExtension - ); - } - const { accepted = false, checkbox1 = false } = resPermission || {}; - - if (resPermission) { - setPermission( - `getUserWalletTransactions-${appInfo?.name}-${data.coin}`, - checkbox1 - ); - } - - if (accepted || skip) { - const coin = data.coin; - const walletKeys = await getUserWalletFunc(coin); - let publicKey; - if (data?.coin === 'ARRR') { - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - publicKey = parsedData.arrrSeed58; - } else { - publicKey = walletKeys['publickey']; - } - - const _url = await createEndpoint( - `/crosschain/` + data.coin.toLowerCase() + `/wallettransactions` - ); - const _body = publicKey; - try { - const response = await fetch(_url, { - method: 'POST', - headers: { - Accept: '*/*', - 'Content-Type': 'application/json', - }, - body: _body, - }); - if (!response?.ok) - throw new Error( - i18n.t('question:message.error.fetch_wallet_transactions', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - if (res?.error && res?.message) { - throw new Error(res.message); - } - - return res; - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.fetch_wallet_transactions', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const getCrossChainServerInfo = async (data) => { - const requiredFields = ['coin']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const _url = `/crosschain/` + data.coin.toLowerCase() + `/serverinfos`; - try { - const url = await createEndpoint(_url); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_generic', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - if (res?.error && res?.message) { - throw new Error(res.message); - } - return res.servers; - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.server_info', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const getTxActivitySummary = async (data) => { - const requiredFields = ['coin']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const coin = data.coin; - const url = `/crosschain/txactivity?foreignBlockchain=${coin}`; // No apiKey here - - try { - const endpoint = await createEndpoint(url); - const response = await fetch(endpoint, { - method: 'POST', - headers: { - Accept: '*/*', - 'Content-Type': 'application/json', - }, - }); - - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_generic', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - if (res?.error && res?.message) { - throw new Error(res.message); - } - return res; // Return full response here - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.transaction_activity_summary', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const getForeignFee = async (data) => { - const requiredFields = ['coin', 'type']; - const missingFields: string[] = []; - - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const { coin, type } = data; - const url = `/crosschain/${coin.toLowerCase()}/${type}`; - - try { - const endpoint = await createEndpoint(url); - const response = await fetch(endpoint, { - method: 'GET', - headers: { - Accept: '*/*', - 'Content-Type': 'application/json', - }, - }); - - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_generic', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - if (res?.error && res?.message) { - throw new Error(res.message); - } - return res; // Return full response here - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.get_foreign_fee', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -function calculateRateFromFee(totalFee, sizeInBytes) { - const fee = (totalFee / sizeInBytes) * 1000; - return fee.toFixed(0); -} - -export const updateForeignFee = async (data, isFromExtension) => { - const isGateway = await isRunningGateway(); - if (isGateway) { - throw new Error( - i18n.t('question:message.generic.no_action_public_node', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const requiredFields = ['coin', 'type', 'value']; - const missingFields: string[] = []; - - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const { coin, type, value } = data; - - const text3 = - type === 'feerequired' - ? i18n.t('question:sats', { - amount: value, - postProcess: 'capitalizeFirstChar', - }) - : i18n.t('question:sats_per_kb', { - amount: value, - postProcess: 'capitalizeFirstChar', - }); - const text4 = - type === 'feerequired' - ? i18n.t('question:message.generic.calculate_fee', { - amount: value, - rate: calculateRateFromFee(value, 300), - postProcess: 'capitalizeFirstChar', - }) - : ''; - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.update_foreign_fee', { - postProcess: 'capitalizeFirstChar', - }), - text2: `type: ${type === 'feerequired' ? 'unlocking' : 'locking'}`, - text3: i18n.t('question:value', { - value: text3, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('question:coin', { - coin: coin, - postProcess: 'capitalizeFirstChar', - }), - }, - isFromExtension - ); - - const { accepted } = resPermission; - if (!accepted) { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const url = `/crosschain/${coin.toLowerCase()}/update${type}`; - const valueStringified = JSON.stringify(+value); - - const endpoint = await createEndpoint(url); - const response = await fetch(endpoint, { - method: 'POST', - headers: { - Accept: '*/*', - 'Content-Type': 'application/json', - }, - body: valueStringified, - }); - - if (!response.ok) - throw new Error( - i18n.t('question:message.error.update_foreign_fee', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - if (res?.error && res?.message) { - throw new Error(res.message); - } - return res; // Return full response here -}; - -export const getServerConnectionHistory = async (data) => { - const requiredFields = ['coin']; - const missingFields: string[] = []; - - // Validate required fields - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const coin = data.coin.toLowerCase(); - const url = `/crosschain/${coin.toLowerCase()}/serverconnectionhistory`; - - try { - const endpoint = await createEndpoint(url); // Assuming createEndpoint is available - const response = await fetch(endpoint, { - method: 'GET', - headers: { - Accept: '*/*', - 'Content-Type': 'application/json', - }, - }); - - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_connection_history', { - postProcess: 'capitalizeFirstChar', - }) - ); - - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - - if (res?.error && res?.message) { - throw new Error(res.message); - } - - return res; // Return full response here - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.fetch_connection_history', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const setCurrentForeignServer = async (data, isFromExtension) => { - const isGateway = await isRunningGateway(); - if (isGateway) { - throw new Error( - i18n.t('question:message.generic.no_action_public_node', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const requiredFields = ['coin']; - const missingFields: string[] = []; - - // Validate required fields - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const { coin, host, port, type } = data; - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.set_current_server', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:server_type', { - type: type, - postProcess: 'capitalizeFirstChar', - }), - text3: i18n.t('question:server_host', { - host: host, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('question:coin', { - coin: coin, - postProcess: 'capitalizeFirstChar', - }), - }, - isFromExtension - ); - - const { accepted } = resPermission; - if (!accepted) { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const body = { - hostName: host, - port: port, - connectionType: type, - }; - - const url = `/crosschain/${coin.toLowerCase()}/setcurrentserver`; - - const endpoint = await createEndpoint(url); // Assuming createEndpoint is available - const response = await fetch(endpoint, { - method: 'POST', - headers: { - Accept: '*/*', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), - }); - - if (!response.ok) - throw new Error( - i18n.t('question:message.error.server_current_set', { - postProcess: 'capitalizeFirstChar', - }) - ); - - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - - if (res?.error && res?.message) { - throw new Error(res.message); - } - - return res; // Return the full response -}; - -export const addForeignServer = async (data, isFromExtension) => { - const isGateway = await isRunningGateway(); - if (isGateway) { - throw new Error( - i18n.t('question:message.generic.no_action_public_node', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const requiredFields = ['coin']; - const missingFields: string[] = []; - - // Validate required fields - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const { coin, host, port, type } = data; - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.server_add', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:server_type', { - type: type, - postProcess: 'capitalizeFirstChar', - }), - text3: i18n.t('question:server_host', { - host: host, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('question:coin', { - coin: coin, - postProcess: 'capitalizeFirstChar', - }), - }, - isFromExtension - ); - - const { accepted } = resPermission; - if (!accepted) { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const body = { - hostName: host, - port: port, - connectionType: type, - }; - - const url = `/crosschain/${coin.toLowerCase()}/addserver`; - - const endpoint = await createEndpoint(url); // Assuming createEndpoint is available - const response = await fetch(endpoint, { - method: 'POST', - headers: { - Accept: '*/*', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), - }); - - if (!response.ok) - throw new Error( - i18n.t('question:message.error.server_current_add', { - postProcess: 'capitalizeFirstChar', - }) - ); - - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - - if (res?.error && res?.message) { - throw new Error(res.message); - } - - return res; // Return the full response -}; - -export const removeForeignServer = async (data, isFromExtension) => { - const isGateway = await isRunningGateway(); - if (isGateway) { - throw new Error( - i18n.t('question:message.generic.no_action_public_node', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const requiredFields = ['coin']; - const missingFields: string[] = []; - - // Validate required fields - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const { coin, host, port, type } = data; - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.server_remove', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:server_type', { - type: type, - postProcess: 'capitalizeFirstChar', - }), - text3: i18n.t('question:server_host', { - host: host, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('question:coin', { - coin: coin, - postProcess: 'capitalizeFirstChar', - }), - }, - isFromExtension - ); - - const { accepted } = resPermission; - if (!accepted) { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const body = { - hostName: host, - port: port, - connectionType: type, - }; - - const url = `/crosschain/${coin.toLowerCase()}/removeserver`; - - const endpoint = await createEndpoint(url); // Assuming createEndpoint is available - const response = await fetch(endpoint, { - method: 'POST', - headers: { - Accept: '*/*', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), - }); - - if (!response.ok) - throw new Error( - i18n.t('question:message.error.server_remove', { - postProcess: 'capitalizeFirstChar', - }) - ); - - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - - if (res?.error && res?.message) { - throw new Error(res.message); - } - - return res; // Return the full response -}; - -export const getDaySummary = async () => { - const url = `/admin/summary`; // Simplified endpoint URL - - try { - const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL - const response = await fetch(endpoint, { - method: 'GET', - headers: { - Accept: '*/*', - }, - }); - - if (!response.ok) - throw new Error( - i18n.t('question:message.error.retrieve_summary', { - postProcess: 'capitalizeFirstChar', - }) - ); - - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - - if (res?.error && res?.message) { - throw new Error(res.message); - } - - return res; // Return the full response - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.retrieve_summary', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const getNodeInfo = async () => { - const url = `/admin/info`; // Simplified endpoint URL - - try { - const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL - const response = await fetch(endpoint, { - method: 'GET', - headers: { - Accept: '*/*', - }, - }); - - if (!response.ok) - throw new Error( - i18n.t('question:message.error.node_info', { - postProcess: 'capitalizeFirstChar', - }) - ); - - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - - if (res?.error && res?.message) { - throw new Error(res.message); - } - - return res; // Return the full response - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.node_info', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const getNodeStatus = async () => { - const url = `/admin/status`; // Simplified endpoint URL - - try { - const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL - const response = await fetch(endpoint, { - method: 'GET', - headers: { - Accept: '*/*', - }, - }); - - if (!response.ok) - throw new Error( - i18n.t('question:message.error.node_status', { - postProcess: 'capitalizeFirstChar', - }) - ); - - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - - if (res?.error && res?.message) { - throw new Error(res.message); - } - - return res; // Return the full response - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.node_status', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const getArrrSyncStatus = async () => { - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const arrrSeed = parsedData.arrrSeed58; - const url = `/crosschain/arrr/syncstatus`; // Simplified endpoint URL - - try { - const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL - const response = await fetch(endpoint, { - method: 'POST', - headers: { - Accept: '*/*', - }, - body: arrrSeed, - }); - - let res; - - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - - return res; // Return the full response - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.retrieve_sync_status', { - token: 'ARRR', - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const sendCoin = async (data, isFromExtension) => { - const requiredFields = ['coin', 'amount']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - if (!data?.destinationAddress && !data?.recipient) { - throw new Error( - i18n.t('question:message.error.missing_fields', { - fields: 'recipient', - postProcess: 'capitalizeFirstChar', - }) - ); - } - let checkCoin = data.coin; - const wallet = await getSaveWallet(); - const address = wallet.address0; - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const isGateway = await isRunningGateway(); - - if (checkCoin !== 'QORT' && isGateway) - throw new Error( - i18n.t('question:message.error.gateway_non_qort_local_node', { - postProcess: 'capitalizeFirstChar', - }) - ); - if (checkCoin === 'QORT') { - // Params: data.coin, data.recipient, data.amount, data.fee - // TODO: prompt user to send. If they confirm, call `POST /crosschain/:coin/send`, or for QORT, broadcast a PAYMENT transaction - // then set the response string from the core to the `response` variable (defined above) - // If they decline, send back JSON that includes an `error` key, such as `{"error": "User declined request"}` - const amount = Number(data.amount); - const recipient = data?.recipient || data.destinationAddress; - - const url = await createEndpoint(`/addresses/balance/${address}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_balance', { - postProcess: 'capitalizeFirstChar', - }) - ); - let walletBalance; - try { - walletBalance = await response.clone().json(); - } catch (e) { - walletBalance = await response.text(); - } - if (isNaN(Number(walletBalance))) { - const errorMsg = i18n.t('question:message.error.fetch_balance_token', { - token: 'QORT', - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const transformDecimals = (Number(walletBalance) * QORT_DECIMALS).toFixed( - 0 - ); - const walletBalanceDecimals = Number(transformDecimals); - const amountDecimals = Number(amount) * QORT_DECIMALS; - const fee: number = await sendQortFee(); - if (amountDecimals + fee * QORT_DECIMALS > walletBalanceDecimals) { - const errorMsg = i18n.t('question:message.error.insufficient_funds', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - if (amount <= 0) { - const errorMsg = i18n.t('core:message.error.invalid_amount', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - if (recipient.length === 0) { - const errorMsg = i18n.t('question:message.error.empty_receiver', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.send_coins', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:to_recipient', { - recipient: recipient, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: `${amount} ${checkCoin}`, - fee: fee, - confirmCheckbox: true, - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { - const makePayment = await sendCoinFunc( - { amount, password: null, receiver: recipient }, - true - ); - return makePayment.res?.data; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } else if (checkCoin === 'BTC') { - const amount = Number(data.amount); - const recipient = data?.recipient || data.destinationAddress; - const xprv58 = parsedData.btcPrivateKey; - const feePerByte = data.fee ? data.fee : btcFeePerByte; - - const btcWalletBalance = await getWalletBalance({ coin: checkCoin }, true); - - if (isNaN(Number(btcWalletBalance))) { - throw new Error( - i18n.t('question:message.error.fetch_balance_token', { - token: 'BTC', - postProcess: 'capitalizeFirstChar', - }) - ); - } - const btcWalletBalanceDecimals = Number(btcWalletBalance); - const btcAmountDecimals = Number(amount); - const fee = feePerByte * 500; // default 0.00050000 - if (btcAmountDecimals + fee > btcWalletBalanceDecimals) { - throw new Error( - i18n.t('question:message.error.insufficient_funds', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.send_coins', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:to_recipient', { - recipient: recipient, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: `${amount} ${checkCoin}`, - foreignFee: `${fee} BTC`, - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { - const opts = { - xprv58: xprv58, - receivingAddress: recipient, - bitcoinAmount: amount, - feePerByte: feePerByte, - }; - const url = await createEndpoint(`/crosschain/btc/send`); - - const response = await fetch(url, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(opts), - }); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.send', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - return res; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } else if (checkCoin === 'LTC') { - const amount = Number(data.amount); - const recipient = data?.recipient || data.destinationAddress; - const xprv58 = parsedData.ltcPrivateKey; - const feePerByte = data.fee ? data.fee : ltcFeePerByte; - const ltcWalletBalance = await getWalletBalance({ coin: checkCoin }, true); - - if (isNaN(Number(ltcWalletBalance))) { - const errorMsg = i18n.t('question:message.error.fetch_balance_token', { - token: 'LTC', - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const ltcWalletBalanceDecimals = Number(ltcWalletBalance); - const ltcAmountDecimals = Number(amount); - const fee = feePerByte * 1000; // default 0.00030000 - if (ltcAmountDecimals + fee > ltcWalletBalanceDecimals) { - throw new Error( - i18n.t('question:message.error.insufficient_funds', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.send_coins', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:to_recipient', { - recipient: recipient, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: `${amount} ${checkCoin}`, - foreignFee: `${fee} LTC`, - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { - const url = await createEndpoint(`/crosschain/ltc/send`); - const opts = { - xprv58: xprv58, - receivingAddress: recipient, - litecoinAmount: amount, - feePerByte: feePerByte, - }; - const response = await fetch(url, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(opts), - }); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.send', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - return res; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } else if (checkCoin === 'DOGE') { - const amount = Number(data.amount); - const recipient = data?.recipient || data.destinationAddress; - const xprv58 = parsedData.dogePrivateKey; - const feePerByte = data.fee ? data.fee : dogeFeePerByte; - const dogeWalletBalance = await getWalletBalance({ coin: checkCoin }, true); - if (isNaN(Number(dogeWalletBalance))) { - const errorMsg = i18n.t('question:message.error.fetch_balance_token', { - token: 'DOGE', - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const dogeWalletBalanceDecimals = Number(dogeWalletBalance); - const dogeAmountDecimals = Number(amount); - const fee = feePerByte * 5000; // default 0.05000000 - if (dogeAmountDecimals + fee > dogeWalletBalanceDecimals) { - const errorMsg = i18n.t('question:message.error.insufficient_funds', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.send_coins', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:to_recipient', { - recipient: recipient, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: `${amount} ${checkCoin}`, - foreignFee: `${fee} DOGE`, - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { - const opts = { - xprv58: xprv58, - receivingAddress: recipient, - dogecoinAmount: amount, - feePerByte: feePerByte, - }; - const url = await createEndpoint(`/crosschain/doge/send`); - - const response = await fetch(url, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(opts), - }); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.send', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - return res; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } else if (checkCoin === 'DGB') { - const amount = Number(data.amount); - const recipient = data?.recipient || data.destinationAddress; - const xprv58 = parsedData.dbgPrivateKey; - const feePerByte = data.fee ? data.fee : dgbFeePerByte; - const dgbWalletBalance = await getWalletBalance({ coin: checkCoin }, true); - if (isNaN(Number(dgbWalletBalance))) { - const errorMsg = i18n.t('question:message.error.fetch_balance_token', { - token: 'DGB', - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const dgbWalletBalanceDecimals = Number(dgbWalletBalance); - const dgbAmountDecimals = Number(amount); - const fee = feePerByte * 500; // default 0.00005000 - if (dgbAmountDecimals + fee > dgbWalletBalanceDecimals) { - const errorMsg = i18n.t('question:message.error.insufficient_funds', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.send_coins', { - postProcess: 'capitalizeFirstChar', - }), - text2: `To: ${recipient}`, - highlightedText: `${amount} ${checkCoin}`, - foreignFee: `${fee} DGB`, - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { - const opts = { - xprv58: xprv58, - receivingAddress: recipient, - digibyteAmount: amount, - feePerByte: feePerByte, - }; - const url = await createEndpoint(`/crosschain/dgb/send`); - - const response = await fetch(url, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(opts), - }); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.send', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - return res; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } else if (checkCoin === 'RVN') { - const amount = Number(data.amount); - const recipient = data?.recipient || data.destinationAddress; - const xprv58 = parsedData.rvnPrivateKey; - const feePerByte = data.fee ? data.fee : rvnFeePerByte; - const rvnWalletBalance = await getWalletBalance({ coin: checkCoin }, true); - if (isNaN(Number(rvnWalletBalance))) { - const errorMsg = i18n.t('question:message.error.fetch_balance_token', { - token: 'RVN', - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const rvnWalletBalanceDecimals = Number(rvnWalletBalance); - const rvnAmountDecimals = Number(amount); - const fee = feePerByte * 500; // default 0.00562500 - if (rvnAmountDecimals + fee > rvnWalletBalanceDecimals) { - const errorMsg = i18n.t('question:message.error.insufficient_funds', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.send_coins', { - postProcess: 'capitalizeFirstChar', - }), - text2: `To: ${recipient}`, - highlightedText: `${amount} ${checkCoin}`, - foreignFee: `${fee} RVN`, - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { - const opts = { - xprv58: xprv58, - receivingAddress: recipient, - ravencoinAmount: amount, - feePerByte: feePerByte, - }; - const url = await createEndpoint(`/crosschain/rvn/send`); - - const response = await fetch(url, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(opts), - }); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.send', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - return res; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } else if (checkCoin === 'ARRR') { - const amount = Number(data.amount); - const recipient = data?.recipient || data.destinationAddress; - const memo = data?.memo; - const arrrWalletBalance = await getWalletBalance({ coin: checkCoin }, true); - - if (isNaN(Number(arrrWalletBalance))) { - const errorMsg = i18n.t('question:message.error.fetch_balance_token', { - token: 'ARR', - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const arrrWalletBalanceDecimals = Number(arrrWalletBalance); - const arrrAmountDecimals = Number(amount); - const fee = 0.0001; - if (arrrAmountDecimals + fee > arrrWalletBalanceDecimals) { - const errorMsg = i18n.t('question:message.error.insufficient_funds', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.send_coins', { - postProcess: 'capitalizeFirstChar', - }), - text2: `To: ${recipient}`, - highlightedText: `${amount} ${checkCoin}`, - foreignFee: `${fee} ARRR`, - }, - isFromExtension - ); - const { accepted } = resPermission; - - if (accepted) { - const opts = { - entropy58: parsedData.arrrSeed58, - receivingAddress: recipient, - arrrAmount: amount, - memo: memo, - }; - const url = await createEndpoint(`/crosschain/arrr/send`); - - const response = await fetch(url, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(opts), - }); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.send', { - postProcess: 'capitalizeFirstChar', - }) - ); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - return res; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } -}; - -function calculateFeeFromRate(feePerKb, sizeInBytes) { - return (feePerKb / 1000) * sizeInBytes; -} - -const getBuyingFees = async (foreignBlockchain) => { - const ticker = sellerForeignFee[foreignBlockchain].ticker; - if (!ticker) throw new Error('invalid foreign blockchain'); - const unlockFee = await getForeignFee({ - coin: ticker, - type: 'feerequired', - }); - const lockFee = await getForeignFee({ - coin: ticker, - type: 'feekb', - }); - return { - ticker: ticker, - lock: { - sats: lockFee, - fee: lockFee / QORT_DECIMALS, - }, - unlock: { - sats: unlockFee, - fee: unlockFee / QORT_DECIMALS, - feePerKb: +calculateRateFromFee(+unlockFee, 300) / QORT_DECIMALS, - }, - }; -}; - -export const createBuyOrder = async (data, isFromExtension) => { - const requiredFields = ['crosschainAtInfo', 'foreignBlockchain']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const isGateway = await isRunningGateway(); - const foreignBlockchain = data.foreignBlockchain; - const atAddresses = data.crosschainAtInfo?.map( - (order) => order.qortalAtAddress - ); - - const atPromises = atAddresses.map((atAddress) => - requestQueueGetAtAddresses.enqueue(async () => { - const url = await createEndpoint(`/crosschain/trade/${atAddress}`); - const resAddress = await fetch(url); - const resData = await resAddress.json(); - if (foreignBlockchain !== resData?.foreignBlockchain) { - throw new Error( - i18n.t('core:message.error.same_foreign_blockchain', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - return resData; - }) - ); - - const crosschainAtInfo = await Promise.all(atPromises); - - try { - const buyingFees = await getBuyingFees(foreignBlockchain); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.buy_order', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:permission.buy_order_quantity', { - quantity: atAddresses?.length, - postProcess: 'capitalizeFirstChar', - }), - text3: i18n.t('question:permission.buy_order_ticker', { - qort_amount: crosschainAtInfo?.reduce((latest, cur) => { - return latest + +cur?.qortAmount; - }, 0), - foreign_amount: roundUpToDecimals( - crosschainAtInfo?.reduce((latest, cur) => { - return latest + +cur?.expectedForeignAmount; - }, 0) - ), - ticker: buyingFees.ticker, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('auth:node.using_public_gateway', { - gateway: isGateway, - postProcess: 'capitalizeFirstChar', - }), - fee: '', - html: ` -
    - - -
    -
    ${i18n.t('question:total_unlocking_fee', { - postProcess: 'capitalizeFirstChar', - })}
    -
    ${(+buyingFees?.unlock?.fee * atAddresses?.length)?.toFixed(8)} ${buyingFees.ticker}
    -
    - ${i18n.t('question:permission.buy_order_fee_estimation', { - quantity: atAddresses?.length, - fee: buyingFees?.unlock?.feePerKb?.toFixed(8), - ticker: buyingFees.ticker, - postProcess: 'capitalizeFirstChar', - })} -
    -
    ${i18n.t('question:total_locking_fee', { - postProcess: 'capitalizeFirstChar', - })}
    -
    ${i18n.t('question:permission.buy_order_per_kb', { - fee: +buyingFees?.lock.fee.toFixed(8), - ticker: buyingFees.ticker, - postProcess: 'capitalizeFirstChar', - })} -
    -
    -
    -`, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const resBuyOrder = await createBuyOrderTx({ - crosschainAtInfo, - isGateway, - foreignBlockchain, - }); - return resBuyOrder; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.buy_order', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -const cancelTradeOfferTradeBot = async (body, keyPair) => { - const txn = new DeleteTradeOffer().createTransaction(body); - const url = await createEndpoint(`/crosschain/tradeoffer`); - const bodyToString = JSON.stringify(txn); - - const deleteTradeBotResponse = await fetch(url, { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, - body: bodyToString, - }); - - if (!deleteTradeBotResponse.ok) { - throw new Error( - i18n.t('question:message.error.update_tradebot', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - const unsignedTxn = await deleteTradeBotResponse.text(); - const signedTxnBytes = await signTradeBotTransaction(unsignedTxn, keyPair); - const signedBytes = Base58.encode(signedTxnBytes); - - let res; - try { - res = await processTransactionVersion2(signedBytes); - } catch (error) { - return { - error: i18n.t('question:message.error.cancel_sell_order', { - postProcess: 'capitalizeFirstChar', - }), - failedTradeBot: { - atAddress: body.atAddress, - creatorAddress: body.creatorAddress, - }, - }; - } - if (res?.error) { - return { - error: i18n.t('question:message.error.cancel_sell_order', { - postProcess: 'capitalizeFirstChar', - }), - failedTradeBot: { - atAddress: body.atAddress, - creatorAddress: body.creatorAddress, - }, - }; - } - if (res?.signature) { - return res; - } else { - throw new Error( - i18n.t('question:message.error.cancel_sell_order', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; -const findFailedTradebot = async (createBotCreationTimestamp, body) => { - //wait 5 secs - const wallet = await getSaveWallet(); - const address = wallet.address0; - await new Promise((res) => { - setTimeout(() => { - res(null); - }, 5000); - }); - const url = await createEndpoint( - `/crosschain/tradebot?foreignBlockchain=LITECOIN` - ); - - const tradeBotsReponse = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - const data = await tradeBotsReponse.json(); - const latestItem2 = data - .filter((item) => item.creatorAddress === address) - .sort((a, b) => b.timestamp - a.timestamp)[0]; - const latestItem = data - .filter( - (item) => - item.creatorAddress === address && - +item.foreignAmount === +body.foreignAmount - ) - .sort((a, b) => b.timestamp - a.timestamp)[0]; - if ( - latestItem && - createBotCreationTimestamp - latestItem.timestamp <= 5000 && - createBotCreationTimestamp > latestItem.timestamp // Ensure latestItem's timestamp is before createBotCreationTimestamp - ) { - return latestItem; - } else { - return null; - } -}; -const tradeBotCreateRequest = async (body, keyPair) => { - const txn = new TradeBotCreateRequest().createTransaction(body); - const url = await createEndpoint(`/crosschain/tradebot/create`); - const bodyToString = JSON.stringify(txn); - - const unsignedTxnResponse = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: bodyToString, - }); - if (!unsignedTxnResponse.ok) - throw new Error( - i18n.t('question:message.error.create_tradebot', { - postProcess: 'capitalizeFirstChar', - }) - ); - const createBotCreationTimestamp = Date.now(); - const unsignedTxn = await unsignedTxnResponse.text(); - const signedTxnBytes = await signTradeBotTransaction(unsignedTxn, keyPair); - const signedBytes = Base58.encode(signedTxnBytes); - - let res; - try { - res = await processTransactionVersion2(signedBytes); - } catch (error) { - const findFailedTradeBot = await findFailedTradebot( - createBotCreationTimestamp, - body - ); - return { - error: i18n.t('question:message.error.create_sell_order', { - postProcess: 'capitalizeFirstChar', - }), - failedTradeBot: findFailedTradeBot, - }; - } - - if (res?.signature) { - return res; - } else { - throw new Error( - i18n.t('question:message.error.create_sell_order', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const createSellOrder = async (data, isFromExtension) => { - const requiredFields = ['qortAmount', 'foreignBlockchain', 'foreignAmount']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const parsedForeignAmount = Number(data.foreignAmount)?.toFixed(8); - - const receivingAddress = await getUserWalletFunc(data.foreignBlockchain); - try { - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.sell_order', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:permission.order_detail', { - qort_amount: data.qortAmount, - foreign_amount: parsedForeignAmount, - ticker: data.foreignBlockchain, - postProcess: 'capitalizeFirstChar', - }), - fee: '0.02', - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const userPublicKey = parsedData.publicKey; - const uint8PrivateKey = Base58.decode(parsedData.privateKey); - const uint8PublicKey = Base58.decode(parsedData.publicKey); - const keyPair = { - privateKey: uint8PrivateKey, - publicKey: uint8PublicKey, - }; - const response = await tradeBotCreateRequest( - { - creatorPublicKey: userPublicKey, - qortAmount: parseFloat(data.qortAmount), - fundingQortAmount: parseFloat(data.qortAmount) + 0.01, - foreignBlockchain: data.foreignBlockchain, - foreignAmount: parseFloat(parsedForeignAmount), - tradeTimeout: 120, - receivingAddress: receivingAddress.address, - }, - keyPair - ); - - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.submit_sell_order', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const cancelSellOrder = async (data, isFromExtension) => { - const requiredFields = ['atAddress']; - const missingFields: string[] = []; - - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const url = await createEndpoint(`/crosschain/trade/${data.atAddress}`); - const resAddress = await fetch(url); - const resData = await resAddress.json(); - - if (!resData?.qortalAtAddress) - throw new Error( - i18n.t('question:message.error.at_info', { - postProcess: 'capitalizeFirstChar', - }) - ); - - try { - const fee = await getFee('MESSAGE'); - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.cancel_sell_order', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:permission.order_detail', { - qort_amount: resData.qortAmount, - foreign_amount: resData.expectedForeignAmount, - ticker: resData.foreignBlockchain, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const userPublicKey = parsedData.publicKey; - const uint8PrivateKey = Base58.decode(parsedData.privateKey); - const uint8PublicKey = Base58.decode(parsedData.publicKey); - const keyPair = { - privateKey: uint8PrivateKey, - publicKey: uint8PublicKey, - }; - const response = await cancelTradeOfferTradeBot( - { - creatorPublicKey: userPublicKey, - atAddress: data.atAddress, - }, - keyPair - ); - - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - } catch (error) { - throw new Error( - error?.message || - i18n.t('question:message.error.submit_sell_order', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const openNewTab = async (data, isFromExtension) => { - const requiredFields = ['qortalLink']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const res = extractComponents(data.qortalLink); - if (res) { - const { service, name, identifier, path } = res; - if (!service && !name) - throw new Error( - i18n.t('auth:message.error.invalid_qortal_link', { - postProcess: 'capitalizeFirstChar', - }) - ); - executeEvent('addTab', { data: { service, name, identifier, path } }); - executeEvent('open-apps-mode', {}); - return true; - } else { - throw new Error( - i18n.t('auth:message.error.invalid_qortal_link', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const adminAction = async (data, isFromExtension) => { - const requiredFields = ['type']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - // For actions that require a value, check for 'value' field - const actionsRequiringValue = [ - 'addpeer', - 'removepeer', - 'forcesync', - 'addmintingaccount', - 'removemintingaccount', - ]; - if (actionsRequiringValue.includes(data.type.toLowerCase()) && !data.value) { - missingFields.push('value'); - } - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const isGateway = await isRunningGateway(); - if (isGateway) { - throw new Error( - i18n.t('question:message.generic.no_action_public_node', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - let apiEndpoint = ''; - let method = 'GET'; // Default method - let includeValueInBody = false; - switch (data.type.toLowerCase()) { - case 'stop': - apiEndpoint = await createEndpoint('/admin/stop'); - break; - case 'restart': - apiEndpoint = await createEndpoint('/admin/restart'); - break; - case 'bootstrap': - apiEndpoint = await createEndpoint('/admin/bootstrap'); - break; - case 'addmintingaccount': - apiEndpoint = await createEndpoint('/admin/mintingaccounts'); - method = 'POST'; - includeValueInBody = true; - break; - case 'removemintingaccount': - apiEndpoint = await createEndpoint('/admin/mintingaccounts'); - method = 'DELETE'; - includeValueInBody = true; - break; - case 'forcesync': - apiEndpoint = await createEndpoint('/admin/forcesync'); - method = 'POST'; - includeValueInBody = true; - break; - case 'addpeer': - apiEndpoint = await createEndpoint('/peers'); - method = 'POST'; - includeValueInBody = true; - break; - case 'removepeer': - apiEndpoint = await createEndpoint('/peers'); - method = 'DELETE'; - includeValueInBody = true; - break; - default: - throw new Error( - i18n.t('question:message.error.unknown_admin_action_type', { - type: data.type, - postProcess: 'capitalizeFirstChar', - }) - ); - } - // Prepare the permission prompt text - let permissionText = i18n.t('question:permission.perform_admin_action', { - type: data.type, - postProcess: 'capitalizeFirstChar', - }); - - if (data.value) { - permissionText += - ' ' + - i18n.t('question:permission.perform_admin_action_with_value', { - value: data.value, - postProcess: 'capitalizeFirstChar', - }); - } - - const resPermission = await getUserPermission( - { - text1: permissionText, - }, - isFromExtension - ); - - const { accepted } = resPermission; - - if (accepted) { - // Set up options for the API call - const options: RequestInit = { - method: method, - headers: {}, - }; - if (includeValueInBody) { - options.headers['Content-Type'] = 'text/plain'; - options.body = data.value; - } - const response = await fetch(apiEndpoint, options); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.perform_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - return res; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const signTransaction = async (data, isFromExtension) => { - const requiredFields = ['unsignedBytes']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const shouldProcess = data?.process || false; - const _url = await createEndpoint( - '/transactions/decode?ignoreValidityChecks=false' - ); - - const _body = data.unsignedBytes; - const response = await fetch(_url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: _body, - }); - - if (!response.ok) - throw new Error( - i18n.t('question:message.error.decode_transaction', { - postProcess: 'capitalizeFirstChar', - }) - ); - const decodedData = await response.json(); - const resPermission = await getUserPermission( - { - text1: shouldProcess - ? i18n.t('question:permission.sign_process_transaction', { - postProcess: 'capitalizeFirstChar', - }) - : i18n.t('question:permission.sign_transaction', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t( - 'question:message.generic.read_transaction_carefully', - { postProcess: 'capitalizeFirstChar' } - ), - text2: `Tx type: ${decodedData.type}`, - json: decodedData, - }, - isFromExtension - ); - - const { accepted } = resPermission; - if (accepted) { - let urlConverted = await createEndpoint('/transactions/convert'); - - const responseConverted = await fetch(urlConverted, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: data.unsignedBytes, - }); - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const uint8PrivateKey = Base58.decode(parsedData.privateKey); - const uint8PublicKey = Base58.decode(parsedData.publicKey); - const keyPair = { - privateKey: uint8PrivateKey, - publicKey: uint8PublicKey, - }; - const convertedBytes = await responseConverted.text(); - const txBytes = Base58.decode(data.unsignedBytes); - const _arbitraryBytesBuffer = Object.keys(txBytes).map(function (key) { - return txBytes[key]; - }); - const arbitraryBytesBuffer = new Uint8Array(_arbitraryBytesBuffer); - const txByteSigned = Base58.decode(convertedBytes); - const _bytesForSigningBuffer = Object.keys(txByteSigned).map( - function (key) { - return txByteSigned[key]; - } - ); - const bytesForSigningBuffer = new Uint8Array(_bytesForSigningBuffer); - const signature = nacl.sign.detached( - bytesForSigningBuffer, - keyPair.privateKey - ); - const signedBytes = utils.appendBuffer(arbitraryBytesBuffer, signature); - const signedBytesToBase58 = Base58.encode(signedBytes); - if (!shouldProcess) { - return signedBytesToBase58; - } - const res = await processTransactionVersion2(signedBytesToBase58); - if (!res?.signature) - throw new Error( - res?.message || - i18n.t('question:message.error.process_transaction', { - postProcess: 'capitalizeFirstChar', - }) - ); - return res; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -const missingFieldsFunc = (data, requiredFields) => { - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } -}; - -const encode = (value) => encodeURIComponent(value.trim()); // Helper to encode values -const buildQueryParams = (data) => { - const allowedParams = [ - 'name', - 'service', - 'identifier', - 'mimeType', - 'fileName', - 'encryptionType', - 'key', - ]; - return Object.entries(data) - .map(([key, value]) => { - if ( - value === undefined || - value === null || - value === false || - !allowedParams.includes(key) - ) - return null; // Skip null, undefined, or false - if (typeof value === 'boolean') return `${key}=${value}`; // Handle boolean values - return `${key}=${encode(value)}`; // Encode other values - }) - .filter(Boolean) // Remove null values - .join('&'); // Join with `&` -}; -export const createAndCopyEmbedLink = async (data, isFromExtension) => { - const requiredFields = ['type']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - switch (data.type) { - case 'POLL': { - missingFieldsFunc(data, ['type', 'name']); - - const queryParams = [ - `name=${encode(data.name)}`, - data.ref ? `ref=${encode(data.ref)}` : null, // Add only if ref exists - ] - .filter(Boolean) // Remove null values - .join('&'); // Join with `&` - const link = `qortal://use-embed/POLL?${queryParams}`; - try { - await navigator.clipboard.writeText(link); - } catch (error) { - throw new Error( - i18n.t('question:message.error.copy_clipboard', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - return link; - } - case 'IMAGE': - case 'ATTACHMENT': { - missingFieldsFunc(data, ['type', 'name', 'service', 'identifier']); - if (data?.encryptionType === 'private' && !data?.key) { - throw new Error( - i18n.t('question:message.generic.provide_key_shared_link', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const queryParams = buildQueryParams(data); - - const link = `qortal://use-embed/${data.type}?${queryParams}`; - - try { - await navigator.clipboard.writeText(link); - } catch (error) { - throw new Error( - i18n.t('question:message.error.copy_clipboard', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - return link; - } - - default: - throw new Error( - i18n.t('question:message.error.invalid_type', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const registerNameRequest = async (data, isFromExtension) => { - const requiredFields = ['name']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const fee = await getFee('REGISTER_NAME'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.register_name', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: data.name, - text2: data?.description, - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const name = data.name; - const description = data?.description || ''; - const response = await registerName({ name, description }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const updateNameRequest = async (data, isFromExtension) => { - const requiredFields = ['newName', 'oldName']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const oldName = data.oldName; - const newName = data.newName; - const description = data?.description || ''; - const fee = await getFee('UPDATE_NAME'); - const resPermission = await getUserPermission( - { - text1: `Do you give this application permission to update this name?`, - text2: `previous name: ${oldName}`, - text3: `new name: ${newName}`, - text4: data?.description, - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const response = await updateName({ oldName, newName, description }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const leaveGroupRequest = async (data, isFromExtension) => { - const requiredFields = ['groupId']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const groupId = data.groupId; - let groupInfo = null; - try { - const url = await createEndpoint(`/groups/${groupId}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_group', { - postProcess: 'capitalizeFirstChar', - }) - ); - - groupInfo = await response.json(); - } catch (error) { - const errorMsg = - (error && error.message) || - i18n.t('question:message.error.no_group_found', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const fee = await getFee('LEAVE_GROUP'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.leave_group', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: `${groupInfo.groupName}`, - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const response = await leaveGroup({ groupId }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const inviteToGroupRequest = async (data, isFromExtension) => { - const requiredFields = ['groupId', 'inviteTime', 'inviteeAddress']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const groupId = data.groupId; - const qortalAddress = data?.inviteeAddress; - const inviteTime = data?.inviteTime; - - let groupInfo = null; - try { - const url = await createEndpoint(`/groups/${groupId}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_group', { - postProcess: 'capitalizeFirstChar', - }) - ); - - groupInfo = await response.json(); - } catch (error) { - const errorMsg = - (error && error.message) || - i18n.t('question:message.error.no_group_found', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const displayInvitee = await getNameInfoForOthers(qortalAddress); - - const fee = await getFee('GROUP_INVITE'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.invite', { - invitee: displayInvitee || qortalAddress, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('group:group.group_name', { - name: groupInfo?.groupName, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const response = await inviteToGroup({ - groupId, - qortalAddress, - inviteTime, - }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const kickFromGroupRequest = async (data, isFromExtension) => { - const requiredFields = ['groupId', 'qortalAddress']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const groupId = data.groupId; - const qortalAddress = data?.qortalAddress; - const reason = data?.reason; - - let groupInfo = null; - try { - const url = await createEndpoint(`/groups/${groupId}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_group', { - postProcess: 'capitalizeFirstChar', - }) - ); - - groupInfo = await response.json(); - } catch (error) { - const errorMsg = - (error && error.message) || - i18n.t('question:message.error.no_group_found', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const displayInvitee = await getNameInfoForOthers(qortalAddress); - - const fee = await getFee('GROUP_KICK'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.kick', { - partecipant: displayInvitee || qortalAddress, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('group:group.group_name', { - name: groupInfo?.groupName, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const response = await kickFromGroup({ - groupId, - qortalAddress, - rBanReason: reason, - }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const banFromGroupRequest = async (data, isFromExtension) => { - const requiredFields = ['groupId', 'qortalAddress']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const groupId = data.groupId; - const qortalAddress = data?.qortalAddress; - const rBanTime = data?.banTime; - const reason = data?.reason; - let groupInfo = null; - try { - const url = await createEndpoint(`/groups/${groupId}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_group', { - postProcess: 'capitalizeFirstChar', - }) - ); - - groupInfo = await response.json(); - } catch (error) { - const errorMsg = - (error && error.message) || - i18n.t('question:message.error.no_group_found', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const displayInvitee = await getNameInfoForOthers(qortalAddress); - - const fee = await getFee('GROUP_BAN'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.ban', { - partecipant: displayInvitee || qortalAddress, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('group:group.group_name', { - name: groupInfo?.groupName, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const response = await banFromGroup({ - groupId, - qortalAddress, - rBanTime, - rBanReason: reason, - }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const cancelGroupBanRequest = async (data, isFromExtension) => { - const requiredFields = ['groupId', 'qortalAddress']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const groupId = data.groupId; - const qortalAddress = data?.qortalAddress; - - let groupInfo = null; - try { - const url = await createEndpoint(`/groups/${groupId}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_group', { - postProcess: 'capitalizeFirstChar', - }) - ); - - groupInfo = await response.json(); - } catch (error) { - const errorMsg = - (error && error.message) || - i18n.t('question:message.error.no_group_found', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const displayInvitee = await getNameInfoForOthers(qortalAddress); - - const fee = await getFee('CANCEL_GROUP_BAN'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.cancel_ban', { - partecipant: displayInvitee || qortalAddress, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('group:group.group_name', { - name: groupInfo?.groupName, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const response = await cancelBan({ - groupId, - qortalAddress, - }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const addGroupAdminRequest = async (data, isFromExtension) => { - const requiredFields = ['groupId', 'qortalAddress']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const groupId = data.groupId; - const qortalAddress = data?.qortalAddress; - - let groupInfo = null; - try { - const url = await createEndpoint(`/groups/${groupId}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_group', { - postProcess: 'capitalizeFirstChar', - }) - ); - - groupInfo = await response.json(); - } catch (error) { - const errorMsg = - (error && error.message) || - i18n.t('question:message.error.no_group_found', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const displayInvitee = await getNameInfoForOthers(qortalAddress); - - const fee = await getFee('ADD_GROUP_ADMIN'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.add_admin', { - invitee: displayInvitee || qortalAddress, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('group:group.group_name', { - name: groupInfo?.groupName, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const response = await makeAdmin({ - groupId, - qortalAddress, - }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const removeGroupAdminRequest = async (data, isFromExtension) => { - const requiredFields = ['groupId', 'qortalAddress']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const groupId = data.groupId; - const qortalAddress = data?.qortalAddress; - - let groupInfo = null; - try { - const url = await createEndpoint(`/groups/${groupId}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_group', { - postProcess: 'capitalizeFirstChar', - }) - ); - - groupInfo = await response.json(); - } catch (error) { - const errorMsg = - (error && error.message) || - i18n.t('question:message.error.no_group_found', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const displayInvitee = await getNameInfoForOthers(qortalAddress); - - const fee = await getFee('REMOVE_GROUP_ADMIN'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.remove_admin', { - partecipant: displayInvitee || qortalAddress, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('group:group.group_name', { - name: groupInfo?.groupName, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const response = await removeAdmin({ - groupId, - qortalAddress, - }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const cancelGroupInviteRequest = async (data, isFromExtension) => { - const requiredFields = ['groupId', 'qortalAddress']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (!data[field]) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const groupId = data.groupId; - const qortalAddress = data?.qortalAddress; - - let groupInfo = null; - try { - const url = await createEndpoint(`/groups/${groupId}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_group', { - postProcess: 'capitalizeFirstChar', - }) - ); - - groupInfo = await response.json(); - } catch (error) { - const errorMsg = - (error && error.message) || - i18n.t('question:message.error.no_group_found', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const displayInvitee = await getNameInfoForOthers(qortalAddress); - - const fee = await getFee('CANCEL_GROUP_INVITE'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.cancel_group_invite', { - invitee: displayInvitee || qortalAddress, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('group:group.group_name', { - name: groupInfo?.groupName, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - - const { accepted } = resPermission; - - if (accepted) { - const response = await cancelInvitationToGroup({ - groupId, - qortalAddress, - }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const createGroupRequest = async (data, isFromExtension) => { - const requiredFields = [ - 'approvalThreshold', - 'groupId', - 'groupName', - 'maxBlock', - 'minBlock', - 'qortalAddress', - 'type', - ]; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (data[field] === undefined || data[field] === null) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const groupName = data.groupName; - const description = data?.description || ''; - const type = +data.type; - const approvalThreshold = +data?.approvalThreshold; - const minBlock = +data?.minBlock; - const maxBlock = +data.maxBlock; - - const fee = await getFee('CREATE_GROUP'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.create_group', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('group:group.group_name', { - name: groupName, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const response = await createGroup({ - groupName, - groupDescription: description, - groupType: type, - groupApprovalThreshold: approvalThreshold, - minBlock, - maxBlock, - }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const updateGroupRequest = async (data, isFromExtension) => { - const requiredFields = [ - 'groupId', - 'newOwner', - 'type', - 'approvalThreshold', - 'minBlock', - 'maxBlock', - ]; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (data[field] === undefined || data[field] === null) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const groupId = +data.groupId; - const newOwner = data.newOwner; - const description = data?.description || ''; - const type = +data.type; - const approvalThreshold = +data?.approvalThreshold; - const minBlock = +data?.minBlock; - const maxBlock = +data.maxBlock; - - let groupInfo = null; - try { - const url = await createEndpoint(`/groups/${groupId}`); - const response = await fetch(url); - if (!response.ok) - throw new Error( - i18n.t('question:message.error.fetch_group', { - postProcess: 'capitalizeFirstChar', - }) - ); - - groupInfo = await response.json(); - } catch (error) { - const errorMsg = - (error && error.message) || - i18n.t('question:message.error.no_group_found', { - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const displayInvitee = await getNameInfoForOthers(newOwner); - - const fee = await getFee('CREATE_GROUP'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.update_group', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:permission.update_group_detail', { - owner: displayInvitee || newOwner, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('group:group.group_name', { - name: groupInfo?.groupName, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const response = await updateGroup({ - groupId, - newOwner, - newIsOpen: type, - newDescription: description, - newApprovalThreshold: approvalThreshold, - newMinimumBlockDelay: minBlock, - newMaximumBlockDelay: maxBlock, - }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const decryptAESGCMRequest = async (data, isFromExtension) => { - const requiredFields = ['encryptedData', 'iv', 'senderPublicKey']; - requiredFields.forEach((field) => { - if (!data[field]) { - throw new Error( - i18n.t('question:message.error.missing_fields', { - fields: field, - postProcess: 'capitalizeFirstChar', - }) - ); - } - }); - - const encryptedData = data.encryptedData; - const iv = data.iv; - const senderPublicKeyBase58 = data.senderPublicKey; - - // Decode keys and IV - const senderPublicKey = Base58.decode(senderPublicKeyBase58); - const resKeyPair = await getKeyPair(); // Assume this retrieves the current user's keypair - const uint8PrivateKey = Base58.decode(resKeyPair.privateKey); - - // Convert ed25519 keys to Curve25519 - const convertedPrivateKey = ed2curve.convertSecretKey(uint8PrivateKey); - const convertedPublicKey = ed2curve.convertPublicKey(senderPublicKey); - - // Generate shared secret - const sharedSecret = new Uint8Array(32); - nacl.lowlevel.crypto_scalarmult( - sharedSecret, - convertedPrivateKey, - convertedPublicKey - ); - - // Derive encryption key - const encryptionKey: Uint8Array = new Sha256() - .process(sharedSecret) - .finish().result; - - // Convert IV and ciphertext from Base64 - const base64ToUint8Array = (base64) => - Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)); - const ivUint8Array = base64ToUint8Array(iv); - const ciphertext = base64ToUint8Array(encryptedData); - // Validate IV and key lengths - if (ivUint8Array.length !== 12) { - throw new Error( - i18n.t('question:message.error.invalid_encryption_iv', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - if (encryptionKey.length !== 32) { - throw new Error( - i18n.t('question:message.error.invalid_encryption_key', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - try { - // Decrypt data - const algorithm = { name: 'AES-GCM', iv: ivUint8Array }; - const cryptoKey = await crypto.subtle.importKey( - 'raw', - encryptionKey, - algorithm, - false, - ['decrypt'] - ); - const decryptedArrayBuffer = await crypto.subtle.decrypt( - algorithm, - cryptoKey, - ciphertext - ); - - // Return decrypted data as Base64 - return uint8ArrayToBase64(new Uint8Array(decryptedArrayBuffer)); - } catch (error) { - console.error('Decryption failed:', error); - throw new Error( - i18n.t('question:message.error.decrypt_message', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const sellNameRequest = async (data, isFromExtension) => { - const requiredFields = ['salePrice', 'nameForSale']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (data[field] === undefined || data[field] === null) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const name = data.nameForSale; - const sellPrice = +data.salePrice; - - const validApi = await getBaseApi(); - - const response = await fetch(validApi + '/names/' + name); - const nameData = await response.json(); - if (!nameData) - throw new Error( - i18n.t('auth:message.error.name_not_existing', { - postProcess: 'capitalizeFirstChar', - }) - ); - - if (nameData?.isForSale) - throw new Error( - i18n.t('question:message.error.name_already_for_sale', { - postProcess: 'capitalizeFirstChar', - }) - ); - const fee = await getFee('SELL_NAME'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.sell_name_transaction', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t( - 'question:permission.sell_name_transaction_detail', - { - name: name, - price: sellPrice, - postProcess: 'capitalizeFirstChar', - } - ), - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const response = await sellName({ - name, - sellPrice, - }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const cancelSellNameRequest = async (data, isFromExtension) => { - const requiredFields = ['nameForSale']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (data[field] === undefined || data[field] === null) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - const name = data.nameForSale; - const validApi = await getBaseApi(); - - const response = await fetch(validApi + '/names/' + name); - const nameData = await response.json(); - if (!nameData?.isForSale) - throw new Error( - i18n.t('question:message.error.name_not_for_sale', { - postProcess: 'capitalizeFirstChar', - }) - ); - - const fee = await getFee('CANCEL_SELL_NAME'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.sell_name_cancel', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('question:name', { - name: name, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const response = await cancelSellName({ - name, - }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const buyNameRequest = async (data, isFromExtension) => { - const requiredFields = ['nameForSale']; - const missingFields: string[] = []; - requiredFields.forEach((field) => { - if (data[field] === undefined || data[field] === null) { - missingFields.push(field); - } - }); - if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(', '); - const errorMsg = i18n.t('question:message.error.missing_fields', { - fields: missingFieldsString, - postProcess: 'capitalizeFirstChar', - }); - throw new Error(errorMsg); - } - - const name = data.nameForSale; - const validApi = await getBaseApi(); - const response = await fetch(validApi + '/names/' + name); - const nameData = await response.json(); - - if (!nameData?.isForSale) - throw new Error( - i18n.t('question:message.error.name_not_for_sale', { - postProcess: 'capitalizeFirstChar', - }) - ); - - const sellerAddress = nameData.owner; - const sellPrice = +nameData.salePrice; - - const fee = await getFee('BUY_NAME'); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.buy_name', { - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('question:permission.buy_name_detail', { - name: name, - price: sellPrice, - postProcess: 'capitalizeFirstChar', - }), - fee: fee.fee, - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const response = await buyName({ - name, - sellerAddress, - sellPrice, - }); - return response; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; - -export const signForeignFees = async (data, isFromExtension) => { - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.sign_fee', { - postProcess: 'capitalizeFirstChar', - }), - }, - isFromExtension - ); - const { accepted } = resPermission; - if (accepted) { - const wallet = await getSaveWallet(); - const address = wallet.address0; - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const uint8PrivateKey = Base58.decode(parsedData.privateKey); - const uint8PublicKey = Base58.decode(parsedData.publicKey); - const keyPair = { - privateKey: uint8PrivateKey, - publicKey: uint8PublicKey, - }; - - const unsignedFeesUrl = await createEndpoint( - `/crosschain/unsignedfees/${address}` - ); - - const unsignedFeesResponse = await fetch(unsignedFeesUrl); - - const unsignedFees = await unsignedFeesResponse.json(); - - const signedFees = []; - - unsignedFees.forEach((unsignedFee) => { - const unsignedDataDecoded = Base58.decode(unsignedFee.data); - - const signature = nacl.sign.detached( - unsignedDataDecoded, - keyPair.privateKey - ); - - const signedFee = { - timestamp: unsignedFee.timestamp, - data: `${Base58.encode(signature)}`, - atAddress: unsignedFee.atAddress, - fee: unsignedFee.fee, - }; - - signedFees.push(signedFee); - }); - - const signedFeesUrl = await createEndpoint(`/crosschain/signedfees`); - - await fetch(signedFeesUrl, { - method: 'POST', - headers: { - Accept: '*/*', - 'Content-Type': 'application/json', - }, - body: `${JSON.stringify(signedFees)}`, - }); - - return true; - } else { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } -}; -export const multiPaymentWithPrivateData = async (data, isFromExtension) => { - const requiredFields = ['payments', 'assetId']; - requiredFields.forEach((field) => { - if (data[field] === undefined || data[field] === null) { - throw new Error( - i18n.t('question:message.error.missing_fields', { - fields: field, - postProcess: 'capitalizeFirstChar', - }) - ); - } - }); - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const privateKey = parsedData.privateKey; - const userPublicKey = parsedData.publicKey; - const { fee: paymentFee } = await getFee('TRANSFER_ASSET'); - const { fee: arbitraryFee } = await getFee('ARBITRARY'); - - let name = null; - const payments = data.payments; - const assetId = data.assetId; - const pendingTransactions = []; - const pendingAdditionalArbitraryTxs = []; - const additionalArbitraryTxsWithoutPayment = - data?.additionalArbitraryTxsWithoutPayment || []; - let totalAmount = 0; - let fee = 0; - for (const payment of payments) { - const paymentRefId = uid.rnd(); - const requiredFieldsPayment = ['recipient', 'amount']; - - for (const field of requiredFieldsPayment) { - if (!payment[field]) { - throw new Error( - i18n.t('question:message.error.missing_fields', { - fields: field, - postProcess: 'capitalizeFirstChar', - }) - ); - } - } - - const confirmReceiver = await getNameOrAddress(payment.recipient); - if (confirmReceiver.error) { - throw new Error( - i18n.t('question:message.error.invalid_receiver', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const receiverPublicKey = await getPublicKey(confirmReceiver); - - const amount = +payment.amount.toFixed(8); - - pendingTransactions.push({ - type: 'PAYMENT', - recipientAddress: confirmReceiver, - amount: amount, - paymentRefId, - }); - - fee = fee + +paymentFee; - totalAmount = totalAmount + amount; - - if (payment.arbitraryTxs && payment.arbitraryTxs.length > 0) { - for (const arbitraryTx of payment.arbitraryTxs) { - const requiredFieldsArbitraryTx = ['service', 'identifier', 'base64']; - - for (const field of requiredFieldsArbitraryTx) { - if (!arbitraryTx[field]) { - throw new Error( - i18n.t('question:message.error.missing_fields', { - fields: field, - postProcess: 'capitalizeFirstChar', - }) - ); - } - } - - if (!name) { - const getName = await getNameInfo(); - if (!getName) - throw new Error( - i18n.t('question:message.error.registered_name', { - postProcess: 'capitalizeFirstChar', - }) - ); - name = getName; - } - - const isValid = isValidBase64WithDecode(arbitraryTx.base64); - if (!isValid) - throw new Error( - i18n.t('core:message.error.invalid_base64', { - postProcess: 'capitalizeFirstChar', - }) - ); - if (!arbitraryTx?.service?.includes('_PRIVATE')) - throw new Error( - i18n.t('question:message.generic.private_service', { - postProcess: 'capitalizeFirstChar', - }) - ); - const additionalPublicKeys = arbitraryTx?.additionalPublicKeys || []; - pendingTransactions.push({ - type: 'ARBITRARY', - identifier: arbitraryTx.identifier, - service: arbitraryTx.service, - base64: arbitraryTx.base64, - description: arbitraryTx?.description || '', - paymentRefId, - publicKeys: [receiverPublicKey, ...additionalPublicKeys], - }); - - fee = fee + +arbitraryFee; - } - } - } - - if ( - additionalArbitraryTxsWithoutPayment && - additionalArbitraryTxsWithoutPayment.length > 0 - ) { - for (const arbitraryTx of additionalArbitraryTxsWithoutPayment) { - const requiredFieldsArbitraryTx = ['service', 'identifier', 'base64']; - - for (const field of requiredFieldsArbitraryTx) { - if (!arbitraryTx[field]) { - throw new Error( - i18n.t('question:message.error.missing_fields', { - fields: field, - postProcess: 'capitalizeFirstChar', - }) - ); - } - } - - if (!name) { - const getName = await getNameInfo(); - if (!getName) - throw new Error( - i18n.t('question:message.error.registered_name', { - postProcess: 'capitalizeFirstChar', - }) - ); - name = getName; - } - - const isValid = isValidBase64WithDecode(arbitraryTx.base64); - if (!isValid) - throw new Error( - i18n.t('core:message.error.invalid_base64', { - postProcess: 'capitalizeFirstChar', - }) - ); - if (!arbitraryTx?.service?.includes('_PRIVATE')) - throw new Error( - i18n.t('question:message.generic.private_service', { - postProcess: 'capitalizeFirstChar', - }) - ); - const additionalPublicKeys = arbitraryTx?.additionalPublicKeys || []; - pendingAdditionalArbitraryTxs.push({ - type: 'ARBITRARY', - identifier: arbitraryTx.identifier, - service: arbitraryTx.service, - base64: arbitraryTx.base64, - description: arbitraryTx?.description || '', - publicKeys: additionalPublicKeys, - }); - - fee = fee + +arbitraryFee; - } - } - - if (!name) - throw new Error( - i18n.t('question:message.error.registered_name', { - postProcess: 'capitalizeFirstChar', - }) - ); - const balance = await getBalanceInfo(); - - if (+balance < fee) - throw new Error( - i18n.t('question:message.error.insufficient_balance_qort', { - postProcess: 'capitalizeFirstChar', - }) - ); - const assetBalance = await getAssetBalanceInfo(assetId); - const assetInfo = await getAssetInfo(assetId); - if (assetBalance < totalAmount) - throw new Error( - i18n.t('question:message.error.insufficient_balance', { - postProcess: 'capitalizeFirstChar', - }) - ); - - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.pay_publish', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:assets_used_pay', { - asset: assetInfo.name, - postProcess: 'capitalizeFirstChar', - }), - html: ` -
    - - - ${pendingTransactions - .filter((item) => item.type === 'PAYMENT') - .map( - (payment) => ` -
    -
    Recipient: ${ - payment.recipientAddress - }
    -
    Amount: ${payment.amount}
    -
    ` - ) - .join('')} - ${[...pendingTransactions, ...pendingAdditionalArbitraryTxs] - .filter((item) => item.type === 'ARBITRARY') - .map( - (arbitraryTx) => ` -
    -
    Service: ${ - arbitraryTx.service - }
    -
    Name: ${name}
    -
    Identifier: ${ - arbitraryTx.identifier - }
    -
    ` - ) - .join('')} -
    - - `, - highlightedText: `Total Amount: ${totalAmount}`, - fee: fee, - }, - isFromExtension - ); - - const { accepted, checkbox1 = false } = resPermission; - if (!accepted) { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - - // const failedTxs = [] - const paymentsDone = {}; - - const transactionsDone = []; - - for (const transaction of pendingTransactions) { - const type = transaction.type; - - if (type === 'PAYMENT') { - const makePayment = await retryTransaction( - transferAsset, - [ - { - amount: transaction.amount, - assetId, - recipient: transaction.recipientAddress, - }, - ], - true - ); - if (makePayment) { - transactionsDone.push(makePayment?.signature); - if (transaction.paymentRefId) { - paymentsDone[transaction.paymentRefId] = makePayment; - } - } - } else if (type === 'ARBITRARY' && paymentsDone[transaction.paymentRefId]) { - const objectToEncrypt = { - data: transaction.base64, - payment: paymentsDone[transaction.paymentRefId], - }; - - const toBase64 = await retryTransaction( - objectToBase64, - [objectToEncrypt], - true - ); - - if (!toBase64) continue; // Skip if encryption fails - - const encryptDataResponse = await retryTransaction( - encryptDataGroup, - [ - { - data64: toBase64, - publicKeys: transaction.publicKeys, - privateKey, - userPublicKey, - }, - ], - true - ); - - if (!encryptDataResponse) continue; // Skip if encryption fails - - const resPublish = await retryTransaction( - publishData, - [ - { - registeredName: encodeURIComponent(name), - data: encryptDataResponse, - service: transaction.service, - identifier: encodeURIComponent(transaction.identifier), - uploadType: 'base64', - description: transaction?.description, - apiVersion: 2, - withFee: true, - }, - ], - true - ); - - if (resPublish?.signature) { - transactionsDone.push(resPublish?.signature); - } - } - } - - for (const transaction of pendingAdditionalArbitraryTxs) { - const objectToEncrypt = { - data: transaction.base64, - }; - - const toBase64 = await retryTransaction( - objectToBase64, - [objectToEncrypt], - true - ); - - if (!toBase64) continue; // Skip if encryption fails - - const encryptDataResponse = await retryTransaction( - encryptDataGroup, - [ - { - data64: toBase64, - publicKeys: transaction.publicKeys, - privateKey, - userPublicKey, - }, - ], - true - ); - - if (!encryptDataResponse) continue; // Skip if encryption fails - - const resPublish = await retryTransaction( - publishData, - [ - { - registeredName: encodeURIComponent(name), - data: encryptDataResponse, - service: transaction.service, - identifier: encodeURIComponent(transaction.identifier), - uploadType: 'base64', - description: transaction?.description, - apiVersion: 2, - withFee: true, - }, - ], - true - ); - - if (resPublish?.signature) { - transactionsDone.push(resPublish?.signature); - } - } - - return transactionsDone; -}; - -export const transferAssetRequest = async (data, isFromExtension) => { - const requiredFields = ['amount', 'assetId', 'recipient']; - requiredFields.forEach((field) => { - if (data[field] === undefined || data[field] === null) { - throw new Error( - i18n.t('question:message.error.missing_fields', { - fields: field, - postProcess: 'capitalizeFirstChar', - }) - ); - } - }); - const amount = data.amount; - const assetId = data.assetId; - const recipient = data.recipient; - - const { fee } = await getFee('TRANSFER_ASSET'); - const balance = await getBalanceInfo(); - - if (+balance < +fee) - throw new Error( - i18n.t('question:message.error.insufficient_balance_qort', { - postProcess: 'capitalizeFirstChar', - }) - ); - const assetBalance = await getAssetBalanceInfo(assetId); - if (assetBalance < amount) - throw new Error( - i18n.t('question:message.error.insufficient_balance', { - postProcess: 'capitalizeFirstChar', - }) - ); - const confirmReceiver = await getNameOrAddress(recipient); - if (confirmReceiver.error) { - throw new Error( - i18n.t('question:message.error.invalid_receiver', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const assetInfo = await getAssetInfo(assetId); - const resPermission = await getUserPermission( - { - text1: i18n.t('question:permission.transfer_asset', { - postProcess: 'capitalizeFirstChar', - }), - text2: i18n.t('question:asset_name', { - asset: assetInfo?.name, - postProcess: 'capitalizeFirstChar', - }), - highlightedText: i18n.t('question:amount_qty', { - quantity: amount, - postProcess: 'capitalizeFirstChar', - }), - fee: fee, - }, - isFromExtension - ); - - const { accepted } = resPermission; - if (!accepted) { - throw new Error( - i18n.t('question:message.generic.user_declined_request', { - postProcess: 'capitalizeFirstChar', - }) - ); - } - const res = await transferAsset({ - amount, - recipient: confirmReceiver, - assetId, - }); - return res; -}; From d06a66c59147420c90749999c5ddd09856d489d3 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 24 May 2025 16:01:44 +0300 Subject: [PATCH 482/717] fix missing imports --- src/components/Apps/AppsDevModeNavBar.tsx | 3 ++- src/components/Apps/AppsPrivate.tsx | 2 +- src/qdn/publish/publish.ts | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Apps/AppsDevModeNavBar.tsx b/src/components/Apps/AppsDevModeNavBar.tsx index 798929b..edce516 100644 --- a/src/components/Apps/AppsDevModeNavBar.tsx +++ b/src/components/Apps/AppsDevModeNavBar.tsx @@ -16,6 +16,7 @@ import RefreshIcon from '@mui/icons-material/Refresh'; import { navigationControllerAtom } from '../../atoms/global'; import { AppsDevModeTabComponent } from './AppsDevModeTabComponent'; import { useAtom } from 'jotai'; +import { useTranslation } from 'react-i18next'; export const AppsDevModeNavBar = () => { const [tabs, setTabs] = useState([]); @@ -28,7 +29,7 @@ export const AppsDevModeNavBar = () => { const tabsRef = useRef(null); const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); - + const { t } = useTranslation(); const handleClick = (event) => { setAnchorEl(event.currentTarget); }; diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index 56b33d9..143ad0e 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -37,7 +37,7 @@ import { } from './Apps-styles'; import AddIcon from '@mui/icons-material/Add'; import ImageUploader from '../../common/ImageUploader'; -import { QORTAL_APP_CONTEXT } from '../../App'; +import { getBaseApiReact, QORTAL_APP_CONTEXT } from '../../App'; import { fileToBase64 } from '../../utils/fileReading'; import { objectToBase64 } from '../../qdn/encryption/group-encryption'; import { getFee } from '../../background/background.ts'; diff --git a/src/qdn/publish/publish.ts b/src/qdn/publish/publish.ts index dfe682f..848ddc9 100644 --- a/src/qdn/publish/publish.ts +++ b/src/qdn/publish/publish.ts @@ -88,7 +88,6 @@ export const publishData = async ({ feeAmount, filename, identifier, - isBase64, registeredName, service, tag1, From 388e44e4e773ad6769727a4c107eb62172c374dc Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 24 May 2025 16:03:42 +0300 Subject: [PATCH 483/717] remove logs --- src/qdn/publish/publish.ts | 3 --- src/qortal/get.ts | 1 - 2 files changed, 4 deletions(-) diff --git a/src/qdn/publish/publish.ts b/src/qdn/publish/publish.ts index 848ddc9..3bb3307 100644 --- a/src/qdn/publish/publish.ts +++ b/src/qdn/publish/publish.ts @@ -99,7 +99,6 @@ export const publishData = async ({ uploadType, withFee, }: any) => { - console.log('data', data); const validateName = async (receiverName: string) => { return await reusableGet(`/names/${receiverName}`); }; @@ -225,7 +224,6 @@ export const publishData = async ({ } let transactionBytes = await uploadData(registeredName, data, fee); - console.log('transactionBytes length', transactionBytes?.length); if (!transactionBytes || transactionBytes.error) { throw new Error(transactionBytes?.message || 'Error when uploading'); @@ -247,7 +245,6 @@ export const publishData = async ({ }; const uploadData = async (registeredName: string, data: any, fee: number) => { - console.log('data', uploadType, data); let postBody = ''; let urlSuffix = ''; diff --git a/src/qortal/get.ts b/src/qortal/get.ts index dad01f5..7bd9bee 100644 --- a/src/qortal/get.ts +++ b/src/qortal/get.ts @@ -1804,7 +1804,6 @@ export const publishMultipleQDNResources = async ( resource?.base64 || resource?.data64 || resourceEncrypt ? 'base64' : 'file'; - console.log('dataType', dataType); await retryTransaction( publishData, [ From 1a549be89b8d16f225e5ceb90eaecd448d46f8fa Mon Sep 17 00:00:00 2001 From: "nico.benaz" <52411515+nbenaglia@users.noreply.github.com> Date: Sat, 24 May 2025 15:05:43 +0200 Subject: [PATCH 484/717] Set namespaces --- src/components/Apps/AppsDevModeNavBar.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Apps/AppsDevModeNavBar.tsx b/src/components/Apps/AppsDevModeNavBar.tsx index edce516..b5f0593 100644 --- a/src/components/Apps/AppsDevModeNavBar.tsx +++ b/src/components/Apps/AppsDevModeNavBar.tsx @@ -25,11 +25,12 @@ export const AppsDevModeNavBar = () => { navigationControllerAtom ); const theme = useTheme(); + const { t } = useTranslation(['auth', 'core', 'group', 'question', 'tutorial']); const [isNewTabWindow, setIsNewTabWindow] = useState(false); const tabsRef = useRef(null); const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); - const { t } = useTranslation(); + const handleClick = (event) => { setAnchorEl(event.currentTarget); }; From c6fb7f2025d73ad83cb0fe8cd46a3b5510ea43cb Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 20:25:54 +0200 Subject: [PATCH 485/717] Refactor label in translation --- src/background/background.ts | 2 +- src/components/Group/InviteMember.tsx | 2 +- src/components/Group/Settings.tsx | 2 +- src/i18n/locales/de/group.json | 6 +- src/i18n/locales/en/group.json | 4 +- src/i18n/locales/es/group.json | 6 +- src/i18n/locales/fr/group.json | 6 +- src/i18n/locales/it/group.json | 256 +++++++++++++------------- src/i18n/locales/ja/group.json | 6 +- src/i18n/locales/ru/group.json | 6 +- src/i18n/locales/zh/group.json | 6 +- 11 files changed, 151 insertions(+), 151 deletions(-) diff --git a/src/background/background.ts b/src/background/background.ts index 2a7b158..2709a8c 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -430,7 +430,7 @@ export async function performPowTask(chatBytes, difficulty) { // Send the task to the worker worker.postMessage({ chatBytes, - path: `${import.meta.env.BASE_URL}memory-pow.wasm.full`, + path: `${import.meta.env.BASE_URL}memory-pow.wasm.full`, // TODO move into ./wasm/ folder difficulty, }); }); diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx index 8ac8d93..e4dd93d 100644 --- a/src/components/Group/InviteMember.tsx +++ b/src/components/Group/InviteMember.tsx @@ -45,7 +45,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { setInfoSnack({ type: 'success', message: t('group:message.success.group_invite', { - value: value, + invitee: value, postProcess: 'capitalizeFirstChar', }), }); diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index ef6d0cb..7a3b5e9 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -277,7 +277,7 @@ const ExportPrivateKey = ({ rawWallet }) => { type: 'error', message: error?.message ? t('group:message.error.decrypt_wallet', { - errorMessage: error?.message, + message: error?.message, postProcess: 'capitalizeFirstChar', }) : t('group:message.error.descrypt_wallet', { diff --git a/src/i18n/locales/de/group.json b/src/i18n/locales/de/group.json index 75b172c..8572281 100644 --- a/src/i18n/locales/de/group.json +++ b/src/i18n/locales/de/group.json @@ -110,7 +110,7 @@ }, "error": { "access_name": "Eine Nachricht kann nicht ohne Zugriff auf Ihren Namen gesendet werden", - "descrypt_wallet": "error decrypting wallet {{ :errorMessage }}", + "descrypt_wallet": "error decrypting wallet {{ message }}", "description_required": "Bitte geben Sie eine Beschreibung an", "group_info": "kann nicht auf Gruppeninformationen zugreifen", "group_join": "versäumte es, sich der Gruppe anzuschließen", @@ -129,7 +129,7 @@ "group_creation": "erfolgreich erstellte Gruppe. Es kann ein paar Minuten dauern, bis sich die Änderungen der Verbreitung verbreiten", "group_creation_name": "created group {{group_name}}: awaiting confirmation", "group_creation_label": "created group {{name}}: success!", - "group_invite": "successfully invited {{value}}. It may take a couple of minutes for the changes to propagate", + "group_invite": "successfully invited {{ invitee }}. It may take a couple of minutes for the changes to propagate", "group_join": "erfolgreich gebeten, sich der Gruppe anzuschließen. Es kann ein paar Minuten dauern, bis sich die Änderungen der Verbreitung verbreiten", "group_join_name": "joined group {{group_name}}: awaiting confirmation", "group_join_label": "joined group {{name}}: success!", @@ -163,4 +163,4 @@ } }, "thread_posts": "Neue Thread -Beiträge" -} \ No newline at end of file +} diff --git a/src/i18n/locales/en/group.json b/src/i18n/locales/en/group.json index 8cbcff6..d30122a 100644 --- a/src/i18n/locales/en/group.json +++ b/src/i18n/locales/en/group.json @@ -110,7 +110,7 @@ }, "error": { "access_name": "cannot send a message without a access to your name", - "descrypt_wallet": "error decrypting wallet {{ :errorMessage }}", + "descrypt_wallet": "error decrypting wallet {{ message }}", "description_required": "please provide a description", "group_info": "cannot access group information", "group_join": "failed to join the group", @@ -129,7 +129,7 @@ "group_creation": "successfully created group. It may take a couple of minutes for the changes to propagate", "group_creation_name": "created group {{group_name}}: awaiting confirmation", "group_creation_label": "created group {{name}}: success!", - "group_invite": "successfully invited {{value}}. It may take a couple of minutes for the changes to propagate", + "group_invite": "successfully invited {{invitee}}. It may take a couple of minutes for the changes to propagate", "group_join": "successfully requested to join group. It may take a couple of minutes for the changes to propagate", "group_join_name": "joined group {{group_name}}: awaiting confirmation", "group_join_label": "joined group {{name}}: success!", diff --git a/src/i18n/locales/es/group.json b/src/i18n/locales/es/group.json index 24d97d3..f9efe13 100644 --- a/src/i18n/locales/es/group.json +++ b/src/i18n/locales/es/group.json @@ -110,7 +110,7 @@ }, "error": { "access_name": "No se puede enviar un mensaje sin acceso a su nombre", - "descrypt_wallet": "error decrypting wallet {{ :errorMessage }}", + "descrypt_wallet": "error decrypting wallet {{ message }}", "description_required": "Proporcione una descripción", "group_info": "No se puede acceder a la información del grupo", "group_join": "No se unió al grupo", @@ -129,7 +129,7 @@ "group_creation": "Grupo creado con éxito. Puede tomar un par de minutos para que los cambios se propagen", "group_creation_name": "created group {{group_name}}: awaiting confirmation", "group_creation_label": "created group {{name}}: success!", - "group_invite": "successfully invited {{value}}. It may take a couple of minutes for the changes to propagate", + "group_invite": "successfully invited {{invitee}}. It may take a couple of minutes for the changes to propagate", "group_join": "solicitó con éxito unirse al grupo. Puede tomar un par de minutos para que los cambios se propagen", "group_join_name": "joined group {{group_name}}: awaiting confirmation", "group_join_label": "joined group {{name}}: success!", @@ -163,4 +163,4 @@ } }, "thread_posts": "Nuevas publicaciones de hilo" -} \ No newline at end of file +} diff --git a/src/i18n/locales/fr/group.json b/src/i18n/locales/fr/group.json index 27ffbb0..4236cf4 100644 --- a/src/i18n/locales/fr/group.json +++ b/src/i18n/locales/fr/group.json @@ -110,7 +110,7 @@ }, "error": { "access_name": "Impossible d'envoyer un message sans accès à votre nom", - "descrypt_wallet": "error decrypting wallet {{ :errorMessage }}", + "descrypt_wallet": "error decrypting wallet {{ message }}", "description_required": "Veuillez fournir une description", "group_info": "Impossible d'accéder aux informations du groupe", "group_join": "n'a pas réussi à rejoindre le groupe", @@ -129,7 +129,7 @@ "group_creation": "Groupe créé avec succès. Il peut prendre quelques minutes pour que les modifications se propagent", "group_creation_name": "created group {{group_name}}: awaiting confirmation", "group_creation_label": "created group {{name}}: success!", - "group_invite": "successfully invited {{value}}. It may take a couple of minutes for the changes to propagate", + "group_invite": "successfully invited {{invitee}}. It may take a couple of minutes for the changes to propagate", "group_join": "demandé avec succès à rejoindre le groupe. Il peut prendre quelques minutes pour que les modifications se propagent", "group_join_name": "joined group {{group_name}}: awaiting confirmation", "group_join_label": "joined group {{name}}: success!", @@ -163,4 +163,4 @@ } }, "thread_posts": "Nouveaux messages de fil" -} \ No newline at end of file +} diff --git a/src/i18n/locales/it/group.json b/src/i18n/locales/it/group.json index 947a31a..f7380c6 100644 --- a/src/i18n/locales/it/group.json +++ b/src/i18n/locales/it/group.json @@ -1,166 +1,166 @@ { "action": { - "add_promotion": "Aggiungi promozione", - "ban": "Ban membro del gruppo", - "cancel_ban": "Annulla divieto", - "copy_private_key": "Copia chiave privata", - "create_group": "Crea gruppo", - "disable_push_notifications": "Disabilita tutte le notifiche push", + "add_promotion": "aggiungi promozione", + "ban": "bandisci membro del gruppo", + "cancel_ban": "annulla divieto", + "copy_private_key": "copia chiave privata", + "create_group": "crea gruppo", + "disable_push_notifications": "disabilita tutte le notifiche push", "export_password": "password di esportazione", "export_private_key": "esporta una chiave privata", - "find_group": "Trova il gruppo", - "join_group": "Unisciti al gruppo", - "kick_member": "Kick membro dal gruppo", - "invite_member": "Invita membro", - "leave_group": "Lascia il gruppo", - "load_members": "Carica i membri con i nomi", + "find_group": "trova il gruppo", + "join_group": "unisciti al gruppo", + "kick_member": "togli membro dal gruppo", + "invite_member": "invita membro", + "leave_group": "lascia il gruppo", + "load_members": "carica i membri con i nomi", "make_admin": "fare un amministratore", - "manage_members": "Gestisci i membri", - "promote_group": "Promuovi il tuo gruppo ai non membri", - "publish_announcement": "Pubblica annuncio", - "publish_avatar": "Pubblica avatar", - "refetch_page": "Pagina REPPETCHE", - "remove_admin": "rimuovere come amministratore", - "remove_minting_account": "Rimuovere l'account di minting", - "return_to_thread": "Torna ai thread", - "scroll_bottom": "Scorri sul fondo", - "scroll_unread_messages": "Scorri verso i messaggi non letto", - "select_group": "Seleziona un gruppo", - "visit_q_mintership": "Visita Q-Mintership" + "manage_members": "gestisci i membri", + "promote_group": "promuovi il tuo gruppo ai non membri", + "publish_announcement": "pubblica annuncio", + "publish_avatar": "pubblica avatar", + "refetch_page": "ricarica pagina", + "remove_admin": "rimuovi da amministratore", + "remove_minting_account": "rimuovi l'account di minting", + "return_to_thread": "torna ai thread", + "scroll_bottom": "scorri sul fondo", + "scroll_unread_messages": "scorri verso i messaggi non letto", + "select_group": "seleziona un gruppo", + "visit_q_mintership": "visita Q-Mintership" }, "advanced_options": "opzioni avanzate", - "ban_list": "Elenco di divieto", + "ban_list": "elenco di divieto", "block_delay": { - "minimum": "Ritardo del blocco minimo", - "maximum": "Ritardo massimo del blocco" + "minimum": "ritardo del blocco minimo", + "maximum": "ritardo massimo del blocco" }, "group": { - "approval_threshold": "Soglia di approvazione del gruppo", - "avatar": "Avatar di gruppo", + "approval_threshold": "soglia di approvazione del gruppo", + "avatar": "avatar di gruppo", "closed": "chiuso (privato) - Gli utenti necessitano dell'autorizzazione per partecipare", - "description": "Descrizione del gruppo", - "id": "Gruppo ID", - "invites": "Inviti di gruppo", + "description": "descrizione del gruppo", + "id": "gruppo ID", + "invites": "inviti di gruppo", "group": "gruppo", "group_name": "group: {{ name }}", "group_other": "gruppi", "groups_admin": "gruppi in cui sei un amministratore", - "management": "Gestione del gruppo", - "member_number": "Numero di membri", + "management": "gestione del gruppo", + "member_number": "numero di membri", "messaging": "messaggistica", - "name": "Nome del gruppo", + "name": "nome del gruppo", "open": "aperto (pubblico)", "private": "gruppo privato", - "promotions": "Promozioni di gruppo", + "promotions": "promozioni di gruppo", "public": "gruppo pubblico", - "type": "Tipo di gruppo" + "type": "tipo di gruppo" }, - "invitation_expiry": "Tempo di scadenza dell'invito", - "invitees_list": "Elenco degli inviti", - "join_link": "Unisciti al link di gruppo", - "join_requests": "Unisciti alle richieste", - "last_message": "Ultimo messaggio", + "invitation_expiry": "tempo di scadenza dell'invito", + "invitees_list": "elenco degli inviti", + "join_link": "unisciti al link di gruppo", + "join_requests": "unisciti alle richieste", + "last_message": "ultimo messaggio", "last_message_date": "last message: {{date }}", - "latest_mails": "Ultimi Q-Mails", + "latest_mails": "ultimi Q-Mails", "message": { "generic": { "avatar_publish_fee": "publishing an Avatar requires {{ fee }}", "avatar_registered_name": "È necessario un nome registrato per impostare un avatar", - "admin_only": "Verranno mostrati solo gruppi in cui sei un amministratore", - "already_in_group": "Sei già in questo gruppo!", - "block_delay_minimum": "Ritardo minimo del blocco per le approvazioni delle transazioni di gruppo", - "block_delay_maximum": "Ritardo massimo del blocco per le approvazioni delle transazioni di gruppo", - "closed_group": "Questo è un gruppo chiuso/privato, quindi dovrai attendere fino a quando un amministratore accetta la tua richiesta", - "descrypt_wallet": "Portafoglio decrypting ...", - "encryption_key": "La prima chiave di crittografia comune del gruppo è in procinto di creare. Si prega di attendere qualche minuto per essere recuperato dalla rete. Controllo ogni 2 minuti ...", - "group_announcement": "Annunci di gruppo", - "group_approval_threshold": "Soglia di approvazione del gruppo (numero / percentuale di amministratori che devono approvare una transazione)", + "admin_only": "verranno mostrati solo gruppi in cui sei un amministratore", + "already_in_group": "sei già in questo gruppo!", + "block_delay_minimum": "ritardo minimo del blocco per le approvazioni delle transazioni di gruppo", + "block_delay_maximum": "ritardo massimo del blocco per le approvazioni delle transazioni di gruppo", + "closed_group": "questo è un gruppo chiuso/privato, quindi dovrai attendere fino a quando un amministratore accetta la tua richiesta", + "descrypt_wallet": "portafoglio decrypting ...", + "encryption_key": "la prima chiave di crittografia comune del gruppo è in procinto di creare. Si prega di attendere qualche minuto per essere recuperato dalla rete. Controllo ogni 2 minuti ...", + "group_announcement": "annunci di gruppo", + "group_approval_threshold": "soglia di approvazione del gruppo (numero / percentuale di amministratori che devono approvare una transazione)", "group_encrypted": "gruppo crittografato", "group_invited_you": "{{group}} has invited you", - "group_key_created": "Primo tasto di gruppo creato.", - "group_member_list_changed": "L'elenco dei membri del gruppo è cambiato. Si prega di rivivere nuovamente la chiave segreta.", - "group_no_secret_key": "Non esiste una chiave segreta di gruppo. Sii il primo amministratore a pubblicarne uno!", - "group_secret_key_no_owner": "L'ultima chiave segreta del gruppo è stata pubblicata da un non proprietario. Come proprietario del gruppo si prega di rivivere la chiave come salvaguardia.", + "group_key_created": "primo tasto di gruppo creato.", + "group_member_list_changed": "l'elenco dei membri del gruppo è cambiato. Si prega di rivivere nuovamente la chiave segreta.", + "group_no_secret_key": "non esiste una chiave segreta di gruppo. Sii il primo amministratore a pubblicarne uno!", + "group_secret_key_no_owner": "l'ultima chiave segreta del gruppo è stata pubblicata da un non proprietario. Come proprietario del gruppo si prega di rivivere la chiave come salvaguardia.", "invalid_content": "contenuto non valido, mittente o timestamp nei dati di reazione", - "invalid_data": "Contenuto di caricamento degli errori: dati non validi", - "latest_promotion": "Verrà mostrata solo l'ultima promozione della settimana per il tuo gruppo.", - "loading_members": "Caricamento dell'elenco dei membri con nomi ... Attendi.", - "max_chars": "Max 200 caratteri. Pubblica tassa", - "manage_minting": "Gestisci il tuo minuto", - "minter_group": "Al momento non fai parte del gruppo Minter", - "mintership_app": "Visita l'app Q-Mintership per fare domanda per essere un minter", - "minting_account": "Account di minting:", - "minting_keys_per_node": "Sono ammessi solo 2 chiavi di minting per nodo. Rimuovi uno se si desidera menta con questo account.", - "minting_keys_per_node_different": "Sono ammessi solo 2 chiavi di minting per nodo. Rimuovi uno se desideri aggiungere un account diverso.", - "next_level": "Blocca restanti fino al livello successivo:", - "node_minting": "Questo nodo sta estraendo:", - "node_minting_account": "Account di minting di Node", - "node_minting_key": "Attualmente hai una chiave di estrazione per questo account allegato a questo nodo", - "no_announcement": "Nessun annuncio", - "no_display": "Niente da visualizzare", - "no_selection": "Nessun gruppo selezionato", - "not_part_group": "Non fai parte del gruppo crittografato di membri. Aspetta fino a quando un amministratore ri-crittografa le chiavi.", - "only_encrypted": "Verranno visualizzati solo messaggi non crittografati.", - "only_private_groups": "Verranno mostrati solo gruppi privati", - "pending_join_requests": "{{ group }} has {{ count }} pending join requests", - "private_key_copied": "Copiata a chiave privata", - "provide_message": "Si prega di fornire un primo messaggio al thread", - "secure_place": "Mantieni la chiave privata in un luogo sicuro. Non condividere!", - "setting_group": "Impostazione del gruppo ... per favore aspetta." + "invalid_data": "contenuto di caricamento degli errori: dati non validi", + "latest_promotion": "verrà mostrata solo l'ultima promozione della settimana per il tuo gruppo.", + "loading_members": "caricamento dell'elenco dei membri con nomi ... Attendi.", + "max_chars": "max 200 caratteri. Pubblica tassa", + "manage_minting": "gestisci il minting", + "minter_group": "al momento non fai parte del gruppo Minter", + "mintership_app": "visita l'app Q-Mintership per chiedere di diventare un minter", + "minting_account": "account di minting:", + "minting_keys_per_node": "sono ammessi solo 2 chiavi di minting per nodo. Rimuovi uno se si desidera menta con questo account.", + "minting_keys_per_node_different": "sono ammessi solo 2 chiavi di minting per nodo. Rimuovi uno se desideri aggiungere un account diverso.", + "next_level": "blocchi mancanti al livello successivo:", + "node_minting": "questo nodo sta coniando:", + "node_minting_account": "account minting del nodo", + "node_minting_key": "attualmente hai una chiave di minting per questo account collegata al nodo", + "no_announcement": "nessun annuncio", + "no_display": "niente da visualizzare", + "no_selection": "nessun gruppo selezionato", + "not_part_group": "non fai parte del gruppo crittografato di membri. Attendi che un amministratore ricifri le chiavi.", + "only_encrypted": "verranno visualizzati solo messaggi non crittografati.", + "only_private_groups": "verranno mostrati solo gruppi privati", + "pending_join_requests": "{{ group }} ha {{ count }} richieste pendenti di join", + "private_key_copied": "copiata a chiave privata", + "provide_message": "si prega di fornire un primo messaggio al thread", + "secure_place": "mantieni la chiave privata in un luogo sicuro. Non condividerla!", + "setting_group": "impostazione del gruppo ... per favore aspetta." }, "error": { - "access_name": "Impossibile inviare un messaggio senza accesso al tuo nome", - "descrypt_wallet": "error decrypting wallet {{ :errorMessage }}", - "description_required": "Si prega di fornire una descrizione", - "group_info": "Impossibile accedere alle informazioni del gruppo", - "group_join": "Impossibile aderire al gruppo", - "group_promotion": "Errore che pubblica la promozione. Per favore riprova", - "group_secret_key": "Impossibile ottenere la chiave segreta del gruppo", - "name_required": "Si prega di fornire un nome", - "notify_admins": "Prova a avvisare un amministratore dall'elenco degli amministratori di seguito:", - "qortals_required": "you need at least {{ quantity }} QORT to send a message", + "access_name": "impossibile inviare un messaggio senza accesso al tuo nome", + "descrypt_wallet": "errore di decrifrazione del wallet {{ message }}", + "description_required": "si prega di fornire una descrizione", + "group_info": "impossibile accedere alle informazioni del gruppo", + "group_join": "impossibile aderire al gruppo", + "group_promotion": "errore che pubblica la promozione. Per favore riprova", + "group_secret_key": "impossibile ottenere la chiave segreta del gruppo", + "name_required": "si prega di fornire un nome", + "notify_admins": "prova a avvisare un amministratore dall'elenco degli amministratori di seguito:", + "qortals_required": "occorrono almeno {{ quantity }} QORT per inviare un messaggio", "timeout_reward": "timeout in attesa di conferma della condivisione della ricompensa", - "thread_id": "Impossibile individuare ID thread", - "unable_determine_group_private": "Impossibile determinare se il gruppo è privato", - "unable_minting": "Impossibile iniziare a misire" + "thread_id": "impossibile individuare il thread ID", + "unable_determine_group_private": "impossibile determinare se il gruppo è privato", + "unable_minting": "impossibile iniziare a coniare" }, "success": { - "group_ban": "membro vietato con successo dal gruppo. Potrebbero essere necessari un paio di minuti per le modifiche da propagare", - "group_creation": "Gruppo creato correttamente. Potrebbero essere necessari un paio di minuti per le modifiche da propagare", - "group_creation_name": "created group {{group_name}}: awaiting confirmation", - "group_creation_label": "created group {{name}}: success!", - "group_invite": "successfully invited {{value}}. It may take a couple of minutes for the changes to propagate", - "group_join": "richiesto con successo di unirsi al gruppo. Potrebbero essere necessari un paio di minuti per le modifiche da propagare", - "group_join_name": "joined group {{group_name}}: awaiting confirmation", - "group_join_label": "joined group {{name}}: success!", - "group_join_request": "requested to join Group {{group_name}}: awaiting confirmation", - "group_join_outcome": "requested to join Group {{group_name}}: success!", - "group_kick": "ha calciato con successo il membro dal gruppo. Potrebbero essere necessari un paio di minuti per le modifiche da propagare", - "group_leave": "richiesto con successo di lasciare il gruppo. Potrebbero essere necessari un paio di minuti per le modifiche da propagare", - "group_leave_name": "left group {{group_name}}: awaiting confirmation", - "group_leave_label": "left group {{name}}: success!", - "group_member_admin": "ha reso il membro con successo un amministratore. Potrebbero essere necessari un paio di minuti per le modifiche da propagare", - "group_promotion": "Promozione pubblicata con successo. Potrebbero essere necessari un paio di minuti per la promozione", - "group_remove_member": "Rimosso con successo il membro come amministratore. Potrebbero essere necessari un paio di minuti per le modifiche da propagare", - "invitation_cancellation": "Invito annullato con successo. Potrebbero essere necessari un paio di minuti per le modifiche da propagare", - "invitation_request": "Richiesta di join accettata: in attesa di conferma", - "loading_threads": "Caricamento dei thread ... Attendi.", - "post_creation": "Post creato correttamente. Potrebbe essere necessario del tempo per la propagazione della pubblicazione", - "published_secret_key": "published secret key for group {{ group_id }}: awaiting confirmation", - "published_secret_key_label": "published secret key for group {{ group_id }}: success!", - "registered_name": "registrato con successo. Potrebbero essere necessari un paio di minuti per le modifiche da propagare", - "registered_name_label": "Nome registrato: in attesa di conferma. Questo potrebbe richiedere un paio di minuti.", - "registered_name_success": "Nome registrato: successo!", - "rewardshare_add": "Aggiungi ricompensa: in attesa di conferma", - "rewardshare_add_label": "Aggiungi ricompensa: successo!", - "rewardshare_creation": "Confermare la creazione di ricompensa sulla catena. Si prega di essere paziente, potrebbe richiedere fino a 90 secondi.", + "group_ban": "membro escluso con successo dal gruppo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "group_creation": "gruppo creato correttamente. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "group_creation_name": "creato il gruppo {{group_name}}: attendere la conferma", + "group_creation_label": "creato il grupp {{name}}: successo!", + "group_invite": "invitato con successo {{invitee}}. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "group_join": "richiesto con successo di unirsi al gruppo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "group_join_name": "adesione al gruppo {{group_name}}: attendere la conferma", + "group_join_label": "adesione al gruppo {{name}}: success!", + "group_join_request": "richiesta di adesione al gruppo {{group_name}}: attendere la conferma", + "group_join_outcome": "richiesta di adesione al gruppo {{group_name}}: successo!", + "group_kick": "il membro è stato escludo dal gruppo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "group_leave": "richiesto con successo di lasciare il gruppo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "group_leave_name": "abbandonato il gruppo {{group_name}}: attendere la conferma", + "group_leave_label": "abbandonato il gruppo {{name}}: success!", + "group_member_admin": "il membro è ora amministratore. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "group_promotion": "promozione pubblicata con successo. Potrebbero essere necessari un paio di minuti per la promozione", + "group_remove_member": "rimosso con successo il membro come amministratore. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "invitation_cancellation": "invito annullato con successo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "invitation_request": "richiesta di join accettata: in attesa di conferma", + "loading_threads": "caricamento dei thread ... Attendi.", + "post_creation": "post creato correttamente. Potrebbe essere necessario del tempo per la propagazione della pubblicazione", + "published_secret_key": "pubblicata la secret key per il gruppo {{ group_id }}: attendere la conferma", + "published_secret_key_label": "pubblicata la secret key per il gruppo {{ group_id }}: successo!", + "registered_name": "registrato con successo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "registered_name_label": "nome registrato: in attesa di conferma. Questo potrebbe richiedere un paio di minuti.", + "registered_name_success": "nome registrato: successo!", + "rewardshare_add": "aggiungi ricompensa: in attesa di conferma", + "rewardshare_add_label": "aggiungi ricompensa: successo!", + "rewardshare_creation": "confermare la creazione di ricompensa sulla catena. Si prega di essere paziente, potrebbe richiedere fino a 90 secondi.", "rewardshare_confirmed": "ricompensa confermata. Fare clic su Avanti.", - "rewardshare_remove": "Rimuovi la ricompensa: in attesa di conferma", - "rewardshare_remove_label": "Rimuovi la ricompensa: successo!", + "rewardshare_remove": "rimuovi la ricompensa: in attesa di conferma", + "rewardshare_remove_label": "rimuovi la ricompensa: successo!", "thread_creation": "thread creato correttamente. Potrebbe essere necessario del tempo per la propagazione della pubblicazione", - "unbanned_user": "Utente non suscitato con successo. Potrebbero essere necessari un paio di minuti per le modifiche da propagare", - "user_joined": "L'utente si è unita con successo!" + "unbanned_user": "utente riammesso con successo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "user_joined": "l'utente si è unito con successo!" } }, - "thread_posts": "Nuovi post di thread" + "thread_posts": "nuovi post di thread" } diff --git a/src/i18n/locales/ja/group.json b/src/i18n/locales/ja/group.json index 1f645ce..9ca8c8a 100644 --- a/src/i18n/locales/ja/group.json +++ b/src/i18n/locales/ja/group.json @@ -110,7 +110,7 @@ }, "error": { "access_name": "あなたの名前にアクセスせずにメッセージを送信できません", - "descrypt_wallet": "error decrypting wallet {{ :errorMessage }}", + "descrypt_wallet": "error decrypting wallet {{ message }}", "description_required": "説明を提供してください", "group_info": "グループ情報にアクセスできません", "group_join": "グループに参加できませんでした", @@ -129,7 +129,7 @@ "group_creation": "GROUPを正常に作成しました。変更が伝播するまでに数分かかる場合があります", "group_creation_name": "created group {{group_name}}: awaiting confirmation", "group_creation_label": "created group {{name}}: success!", - "group_invite": "successfully invited {{value}}. It may take a couple of minutes for the changes to propagate", + "group_invite": "successfully invited {{invitee}}. It may take a couple of minutes for the changes to propagate", "group_join": "グループへの参加を正常にリクエストしました。変更が伝播するまでに数分かかる場合があります", "group_join_name": "joined group {{group_name}}: awaiting confirmation", "group_join_label": "joined group {{name}}: success!", @@ -163,4 +163,4 @@ } }, "thread_posts": "新しいスレッド投稿" -} \ No newline at end of file +} diff --git a/src/i18n/locales/ru/group.json b/src/i18n/locales/ru/group.json index 7085940..4630593 100644 --- a/src/i18n/locales/ru/group.json +++ b/src/i18n/locales/ru/group.json @@ -110,7 +110,7 @@ }, "error": { "access_name": "Не могу отправить сообщение без доступа к вашему имени", - "descrypt_wallet": "error decrypting wallet {{ :errorMessage }}", + "descrypt_wallet": "error decrypting wallet {{ message }}", "description_required": "Пожалуйста, предоставьте описание", "group_info": "Невозможно получить доступ к группе информации", "group_join": "Не удалось присоединиться к группе", @@ -129,7 +129,7 @@ "group_creation": "успешно созданная группа. Это может занять пару минут для изменений в распространении", "group_creation_name": "created group {{group_name}}: awaiting confirmation", "group_creation_label": "created group {{name}}: success!", - "group_invite": "successfully invited {{value}}. It may take a couple of minutes for the changes to propagate", + "group_invite": "successfully invited {{invitee}}. It may take a couple of minutes for the changes to propagate", "group_join": "успешно попросил присоединиться к группе. Это может занять пару минут для изменений в распространении", "group_join_name": "joined group {{group_name}}: awaiting confirmation", "group_join_label": "joined group {{name}}: success!", @@ -163,4 +163,4 @@ } }, "thread_posts": "Новые посты ветки" -} \ No newline at end of file +} diff --git a/src/i18n/locales/zh/group.json b/src/i18n/locales/zh/group.json index 6200efa..bb92a44 100644 --- a/src/i18n/locales/zh/group.json +++ b/src/i18n/locales/zh/group.json @@ -110,7 +110,7 @@ }, "error": { "access_name": "如果没有访问您的名字,就无法发送消息", - "descrypt_wallet": "error decrypting wallet {{ :errorMessage }}", + "descrypt_wallet": "error decrypting wallet {{ message }}", "description_required": "请提供描述", "group_info": "无法访问组信息", "group_join": "未能加入小组", @@ -129,7 +129,7 @@ "group_creation": "成功创建了组。更改可能需要几分钟才能传播", "group_creation_name": "created group {{group_name}}: awaiting confirmation", "group_creation_label": "created group {{name}}: success!", - "group_invite": "successfully invited {{value}}. It may take a couple of minutes for the changes to propagate", + "group_invite": "successfully invited {{invitee}}. It may take a couple of minutes for the changes to propagate", "group_join": "成功要求加入组。更改可能需要几分钟才能传播", "group_join_name": "joined group {{group_name}}: awaiting confirmation", "group_join_label": "joined group {{name}}: success!", @@ -163,4 +163,4 @@ } }, "thread_posts": "新线程帖子" -} \ No newline at end of file +} From ae296dda6062e848201b51dfa2d39ba094ef2055 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 20:26:14 +0200 Subject: [PATCH 486/717] Improve italian --- src/i18n/locales/it/question.json | 356 +++++++++++++++--------------- src/i18n/locales/it/tutorial.json | 8 +- 2 files changed, 182 insertions(+), 182 deletions(-) diff --git a/src/i18n/locales/it/question.json b/src/i18n/locales/it/question.json index 3ef124c..834932d 100644 --- a/src/i18n/locales/it/question.json +++ b/src/i18n/locales/it/question.json @@ -1,192 +1,192 @@ { - "accept_app_fee": "accept app fee", - "always_authenticate": "always authenticate automatically", - "always_chat_messages": "always allow chat messages from this app", - "always_retrieve_balance": "always allow balance to be retrieved automatically", - "always_retrieve_list": "always allow lists to be retrieved automatically", - "always_retrieve_wallet": "always allow wallet to be retrieved automatically", - "always_retrieve_wallet_transactions": "always allow wallet transactions to be retrieved automatically", - "amount_qty": "amount: {{ quantity }}", + "accept_app_fee": "accetta la commissione dell'app", + "always_authenticate": "autentica sempre automaticamente", + "always_chat_messages": "consenti sempre i messaggi chat da questa app", + "always_retrieve_balance": "consenti sempre il recupero automatico del saldo", + "always_retrieve_list": "consenti sempre il recupero automatico delle liste", + "always_retrieve_wallet": "consenti sempre il recupero automatico del portafoglio", + "always_retrieve_wallet_transactions": "consenti sempre il recupero automatico delle transazioni del portafoglio", + "amount_qty": "quantità: {{ quantity }}", "asset_name": "asset: {{ asset }}", - "assets_used_pay": "asset used in payments: {{ asset }}", - "coin": "coin: {{ coin }}", - "description": "description: {{ description }}", - "deploy_at": "would you like to deploy this AT?", - "download_file": "would you like to download:", + "assets_used_pay": "asset usato nei pagamenti: {{ asset }}", + "coin": "moneta: {{ coin }}", + "description": "descrizione: {{ description }}", + "deploy_at": "vuoi distribuire questo AT?", + "download_file": "vuoi scaricare:", "message": { "error": { - "add_to_list": "failed to add to list", - "at_info": "cannot find AT info.", - "buy_order": "failed to submit trade order", - "cancel_sell_order": "failed to Cancel Sell Order. Try again!", - "copy_clipboard": "failed to copy to clipboard", - "create_sell_order": "failed to Create Sell Order. Try again!", - "create_tradebot": "unable to create tradebot", - "decode_transaction": "failed to decode transaction", - "decrypt": "unable to decrypt", - "decrypt_message": "failed to decrypt the message. Ensure the data and keys are correct", - "decryption_failed": "decryption failed", - "empty_receiver": "receiver cannot be empty!", - "encrypt": "unable to encrypt", - "encryption_failed": "encryption failed", - "encryption_requires_public_key": "encrypting data requires public keys", - "fetch_balance_token": "failed to fetch {{ token }} Balance. Try again!", - "fetch_balance": "unable to fetch balance", - "fetch_connection_history": "failed to fetch server connection history", - "fetch_generic": "unable to fetch", - "fetch_group": "failed to fetch the group", - "fetch_list": "failed to fetch the list", - "fetch_poll": "failed to fetch poll", - "fetch_recipient_public_key": "failed to fetch recipient's public key", - "fetch_wallet_info": "unable to fetch wallet information", - "fetch_wallet_transactions": "unable to fetch wallet transactions", - "fetch_wallet": "fetch Wallet Failed. Please try again", - "file_extension": "a file extension could not be derived", - "gateway_balance_local_node": "cannot view {{ token }} balance through the gateway. Please use your local node.", - "gateway_non_qort_local_node": "cannot send a non-QORT coin through the gateway. Please use your local node.", - "gateway_retrieve_balance": "retrieving {{ token }} balance is not allowed through a gateway", - "gateway_wallet_local_node": "cannot view {{ token }} wallet through the gateway. Please use your local node.", - "get_foreign_fee": "error in get foreign fee", - "insufficient_balance_qort": "your QORT balance is insufficient", - "insufficient_balance": "your asset balance is insufficient", - "insufficient_funds": "insufficient funds", - "invalid_encryption_iv": "invalid IV: AES-GCM requires a 12-byte IV", - "invalid_encryption_key": "invalid key: AES-GCM requires a 256-bit key.", - "invalid_fullcontent": "field fullContent is in an invalid format. Either use a string, base64 or an object", - "invalid_receiver": "invalid receiver address or name", - "invalid_type": "invalid type", - "mime_type": "a mimeType could not be derived", - "missing_fields": "missing fields: {{ fields }}", - "name_already_for_sale": "this name is already for sale", - "name_not_for_sale": "this name is not for sale", - "no_api_found": "no usable API found", - "no_data_encrypted_resource": "no data in the encrypted resource", - "no_data_file_submitted": "no data or file was submitted", - "no_group_found": "group not found", - "no_group_key": "no group key found", - "no_poll": "poll not found", - "no_resources_publish": "no resources to publish", - "node_info": "failed to retrieve node info", - "node_status": "failed to retrieve node status", - "only_encrypted_data": "only encrypted data can go into private services", - "perform_request": "failed to perform request", - "poll_create": "failed to create poll", - "poll_vote": "failed to vote on the poll", - "process_transaction": "unable to process transaction", - "provide_key_shared_link": "for an encrypted resource, you must provide the key to create the shared link", - "registered_name": "a registered name is needed to publish", - "resources_publish": "some resources have failed to publish", - "retrieve_file": "failed to retrieve file", - "retrieve_keys": "unable to retrieve keys", - "retrieve_summary": "failed to retrieve summary", - "retrieve_sync_status": "error in retrieving {{ token }} sync status", - "same_foreign_blockchain": "all requested ATs need to be of the same foreign Blockchain.", - "send": "failed to send", - "server_current_add": "failed to add current server", - "server_current_set": "failed to set current server", - "server_info": "error in retrieving server info", - "server_remove": "failed to remove server", - "submit_sell_order": "failed to submit sell order", - "synchronization_attempts": "failed to synchronize after {{ quantity }} attempts", - "timeout_request": "request timed out", - "token_not_supported": "{{ token }} is not supported for this call", - "transaction_activity_summary": "error in transaction activity summary", - "unknown_error": "unknown error", - "unknown_admin_action_type": "unknown admin action type: {{ type }}", - "update_foreign_fee": "failed to update foreign fee", - "update_tradebot": "unable to update tradebot", - "upload_encryption": "upload failed due to failed encryption", - "upload": "upload failed", - "use_private_service": "for an encrypted publish please use a service that ends with _PRIVATE", - "user_qortal_name": "user has no Qortal name" + "add_to_list": "impossibile aggiungere alla lista", + "at_info": "impossibile trovare informazioni sull'AT.", + "buy_order": "invio ordine di scambio fallito", + "cancel_sell_order": "annullamento ordine di vendita fallito. Riprova!", + "copy_clipboard": "copia negli appunti fallita", + "create_sell_order": "creazione ordine di vendita fallita. Riprova!", + "create_tradebot": "impossibile creare tradebot", + "decode_transaction": "decodifica della transazione fallita", + "decrypt": "impossibile decriptare", + "decrypt_message": "decriptazione del messaggio fallita. Verifica dati e chiavi", + "decryption_failed": "decriptazione fallita", + "empty_receiver": "il destinatario non può essere vuoto!", + "encrypt": "impossibile criptare", + "encryption_failed": "criptazione fallita", + "encryption_requires_public_key": "la criptazione richiede chiavi pubbliche", + "fetch_balance_token": "impossibile recuperare il saldo di {{ token }}. Riprova!", + "fetch_balance": "impossibile recuperare il saldo", + "fetch_connection_history": "recupero cronologia connessioni fallito", + "fetch_generic": "impossibile recuperare", + "fetch_group": "recupero gruppo fallito", + "fetch_list": "recupero lista fallito", + "fetch_poll": "recupero sondaggio fallito", + "fetch_recipient_public_key": "recupero chiave pubblica del destinatario fallito", + "fetch_wallet_info": "impossibile recuperare informazioni sul portafoglio", + "fetch_wallet_transactions": "impossibile recuperare transazioni del portafoglio", + "fetch_wallet": "recupero portafoglio fallito. Riprova", + "file_extension": "impossibile determinare l'estensione del file", + "gateway_balance_local_node": "non è possibile visualizzare il saldo {{ token }} tramite il gateway. Usa il tuo nodo locale.", + "gateway_non_qort_local_node": "non è possibile inviare monete non-QORT tramite il gateway. Usa il tuo nodo locale.", + "gateway_retrieve_balance": "il recupero del saldo {{ token }} non è consentito tramite un gateway", + "gateway_wallet_local_node": "non è possibile visualizzare il portafoglio {{ token }} tramite il gateway. Usa il tuo nodo locale.", + "get_foreign_fee": "errore nel recupero delle commissioni estere", + "insufficient_balance_qort": "saldo QORT insufficiente", + "insufficient_balance": "saldo asset insufficiente", + "insufficient_funds": "fondi insufficienti", + "invalid_encryption_iv": "IV non valido: AES-GCM richiede un IV di 12 byte", + "invalid_encryption_key": "chiave non valida: AES-GCM richiede una chiave di 256 bit", + "invalid_fullcontent": "campo fullContent in formato non valido. Usa stringa, base64 o oggetto", + "invalid_receiver": "indirizzo o nome destinatario non valido", + "invalid_type": "tipo non valido", + "mime_type": "impossibile determinare il mimeType", + "missing_fields": "campi mancanti: {{ fields }}", + "name_already_for_sale": "questo nome è già in vendita", + "name_not_for_sale": "questo nome non è in vendita", + "no_api_found": "nessuna API disponibile trovata", + "no_data_encrypted_resource": "nessun dato nella risorsa criptata", + "no_data_file_submitted": "nessun dato o file inviato", + "no_group_found": "gruppo non trovato", + "no_group_key": "chiave del gruppo non trovata", + "no_poll": "sondaggio non trovato", + "no_resources_publish": "nessuna risorsa da pubblicare", + "node_info": "recupero info nodo fallito", + "node_status": "recupero stato nodo fallito", + "only_encrypted_data": "solo dati criptati possono essere usati nei servizi privati", + "perform_request": "richiesta fallita", + "poll_create": "creazione sondaggio fallita", + "poll_vote": "voto al sondaggio fallito", + "process_transaction": "impossibile elaborare la transazione", + "provide_key_shared_link": "per una risorsa criptata, devi fornire la chiave per creare il link condiviso", + "registered_name": "serve un nome registrato per pubblicare", + "resources_publish": "alcune risorse non sono state pubblicate", + "retrieve_file": "recupero file fallito", + "retrieve_keys": "impossibile recuperare le chiavi", + "retrieve_summary": "recupero sommario fallito", + "retrieve_sync_status": "errore nel recupero dello stato di sincronizzazione di {{ token }}", + "same_foreign_blockchain": "tutti gli AT richiesti devono essere della stessa blockchain estera.", + "send": "invio fallito", + "server_current_add": "aggiunta server corrente fallita", + "server_current_set": "impostazione server corrente fallita", + "server_info": "errore nel recupero informazioni del server", + "server_remove": "rimozione server fallita", + "submit_sell_order": "invio ordine di vendita fallito", + "synchronization_attempts": "sincronizzazione fallita dopo {{ quantity }} tentativi", + "timeout_request": "richiesta scaduta", + "token_not_supported": "{{ token }} non è supportato per questa operazione", + "transaction_activity_summary": "errore nel riepilogo attività transazioni", + "unknown_error": "errore sconosciuto", + "unknown_admin_action_type": "tipo di azione amministrativa sconosciuto: {{ type }}", + "update_foreign_fee": "aggiornamento commissione estera fallito", + "update_tradebot": "impossibile aggiornare tradebot", + "upload_encryption": "caricamento fallito a causa della criptazione", + "upload": "caricamento fallito", + "use_private_service": "per pubblicare criptato, usa un servizio che termina con _PRIVATE", + "user_qortal_name": "l'utente non ha un nome Qortal" }, "generic": { - "calculate_fee": "*the {{ amount }} sats fee is derived from {{ rate }} sats per kb, for a transaction that is approximately 300 bytes in size.", - "confirm_join_group": "confirm joining the group:", - "include_data_decrypt": "please include data to decrypt", - "include_data_encrypt": "please include data to encrypt", - "max_retry_transaction": "max retries reached. Skipping transaction.", - "no_action_public_node": "this action cannot be done through a public node", - "private_service": "please use a private service", - "provide_group_id": "please provide a groupId", - "read_transaction_carefully": "read the transaction carefully before accepting!", - "user_declined_add_list": "user declined add to list", - "user_declined_delete_from_list": "User declined delete from list", - "user_declined_delete_hosted_resources": "user declined delete hosted resources", - "user_declined_join": "user declined to join group", - "user_declined_list": "user declined to get list of hosted resources", - "user_declined_request": "user declined request", - "user_declined_save_file": "user declined to save file", - "user_declined_send_message": "user declined to send message", - "user_declined_share_list": "user declined to share list" + "calculate_fee": "*la commissione di {{ amount }} sats è calcolata su una tariffa di {{ rate }} sats per kb, per una transazione di circa 300 byte.", + "confirm_join_group": "conferma partecipazione al gruppo:", + "include_data_decrypt": "includi dati da decriptare", + "include_data_encrypt": "includi dati da criptare", + "max_retry_transaction": "numero massimo di tentativi raggiunto. Transazione saltata.", + "no_action_public_node": "questa azione non può essere eseguita tramite un nodo pubblico", + "private_service": "usa un servizio privato", + "provide_group_id": "fornisci un groupId", + "read_transaction_carefully": "leggi attentamente la transazione prima di accettare!", + "user_declined_add_list": "utente ha rifiutato l'aggiunta alla lista", + "user_declined_delete_from_list": "utente ha rifiutato l'eliminazione dalla lista", + "user_declined_delete_hosted_resources": "utente ha rifiutato l'eliminazione delle risorse ospitate", + "user_declined_join": "utente ha rifiutato di unirsi al gruppo", + "user_declined_list": "utente ha rifiutato di ottenere la lista delle risorse ospitate", + "user_declined_request": "utente ha rifiutato la richiesta", + "user_declined_save_file": "utente ha rifiutato di salvare il file", + "user_declined_send_message": "utente ha rifiutato di inviare il messaggio", + "user_declined_share_list": "utente ha rifiutato di condividere la lista" } }, - "name": "name: {{ name }}", - "option": "option: {{ option }}", - "options": "options: {{ optionList }}", + "name": "nome: {{ name }}", + "option": "opzione: {{ option }}", + "options": "opzioni: {{ optionList }}", "permission": { - "access_list": "do you give this application permission to access the list", - "add_admin": "do you give this application permission to add user {{ invitee }} as an admin?", - "all_item_list": "do you give this application permission to add the following to the list {{ name }}:", - "authenticate": "do you give this application permission to authenticate?", - "ban": "do you give this application permission to ban {{ partecipant }} from the group?", - "buy_name_detail": "buying {{ name }} for {{ price }} QORT", - "buy_name": "do you give this application permission to buy a name?", - "buy_order_fee_estimation_one": "this fee is an estimate based on {{ quantity }} order, assuming a 300-byte size at a rate of {{ fee }} {{ ticker }} per KB.", - "buy_order_fee_estimation_other": "this fee is an estimate based on {{ quantity }} orders, assuming a 300-byte size at a rate of {{ fee }} {{ ticker }} per KB.", - "buy_order_per_kb": "{{ fee }} {{ ticker }} per kb", - "buy_order_quantity_one": "{{ quantity }} buy order", - "buy_order_quantity_other": "{{ quantity }} buy orders", - "buy_order_ticker": "{{ qort_amount }} QORT for {{ foreign_amount }} {{ ticker }}", - "buy_order": "do you give this application permission to perform a buy order?", - "cancel_ban": "do you give this application permission to cancel the group ban for user {{ partecipant }}?", - "cancel_group_invite": "do you give this application permission to cancel the group invite for {{ invitee }}?", - "cancel_sell_order": "do you give this application permission to perform: cancel a sell order?", - "create_group": "do you give this application permission to create a group?", - "delete_hosts_resources": "do you give this application permission to delete {{ size }} hosted resources?", - "fetch_balance": "do you give this application permission to fetch your {{ coin }} balance", - "get_wallet_info": "do you give this application permission to get your wallet information?", - "get_wallet_transactions": "do you give this application permission to retrieve your wallet transactions", - "invite": "do you give this application permission to invite {{ invitee }}?", - "kick": "do you give this application permission to kick {{ partecipant }} from the group?", - "leave_group": "do you give this application permission to leave the following group?", - "list_hosted_data": "do you give this application permission to get a list of your hosted data?", - "order_detail": "{{ qort_amount }} QORT for {{ foreign_amount }} {{ ticker }}", - "pay_publish": "do you give this application permission to make the following payments and publishes?", - "perform_admin_action_with_value": "with value: {{ value }}", - "perform_admin_action": "do you give this application permission to perform the admin action: {{ type }}", - "publish_qdn": "do you give this application permission to publish to QDN?", - "register_name": "do you give this application permission to register this name?", - "remove_admin": "do you give this application permission to remove user {{ partecipant }} as an admin?", - "remove_from_list": "do you give this application permission to remove the following from the list {{ name }}:", - "sell_name_cancel": "do you give this application permission to cancel the selling of a name?", - "sell_name_transaction_detail": "sell {{ name }} for {{ price }} QORT", - "sell_name_transaction": "do you give this application permission to create a sell name transaction?", - "sell_order": "do you give this application permission to perform a sell order?", - "send_chat_message": "do you give this application permission to send this chat message?", - "send_coins": "do you give this application permission to send coins?", - "server_add": "do you give this application permission to add a server?", - "server_remove": "do you give this application permission to remove a server?", - "set_current_server": "do you give this application permission to set the current server?", - "sign_fee": "do you give this application permission to sign the required fees for all your trade offers?", - "sign_process_transaction": "do you give this application permission to sign and process a transaction?", - "sign_transaction": "do you give this application permission to sign a transaction?", - "transfer_asset": "do you give this application permission to transfer the following asset?", - "update_foreign_fee": "do you give this application permission to update foreign fees on your node?", - "update_group_detail": "new owner: {{ owner }}", - "update_group": "do you give this application permission to update this group?" + "access_list": "consenti a questa applicazione di accedere alla lista?", + "add_admin": "consenti a questa applicazione di aggiungere l'utente {{ invitee }} come amministratore?", + "all_item_list": "consenti a questa applicazione di aggiungere i seguenti elementi alla lista {{ name }}:", + "authenticate": "consenti a questa applicazione di autenticarti?", + "ban": "consenti a questa applicazione di bannare {{ partecipant }} dal gruppo?", + "buy_name_detail": "acquisto di {{ name }} per {{ price }} QORT", + "buy_name": "consenti a questa applicazione di acquistare un nome?", + "buy_order_fee_estimation_one": "questa commissione è una stima basata su {{ quantity }} ordine, assumendo una dimensione di 300 byte e una tariffa di {{ fee }} {{ ticker }} per KB.", + "buy_order_fee_estimation_other": "questa commissione è una stima basata su {{ quantity }} ordini, assumendo una dimensione di 300 byte e una tariffa di {{ fee }} {{ ticker }} per KB.", + "buy_order_per_kb": "{{ fee }} {{ ticker }} per KB", + "buy_order_quantity_one": "{{ quantity }} ordine di acquisto", + "buy_order_quantity_other": "{{ quantity }} ordini di acquisto", + "buy_order_ticker": "{{ qort_amount }} QORT per {{ foreign_amount }} {{ ticker }}", + "buy_order": "consenti a questa applicazione di eseguire un ordine di acquisto?", + "cancel_ban": "consenti a questa applicazione di annullare il ban del gruppo per l'utente {{ partecipant }}?", + "cancel_group_invite": "consenti a questa applicazione di annullare l'invito al gruppo per {{ invitee }}?", + "cancel_sell_order": "consenti a questa applicazione di annullare un ordine di vendita?", + "create_group": "consenti a questa applicazione di creare un gruppo?", + "delete_hosts_resources": "consenti a questa applicazione di eliminare {{ size }} risorse ospitate?", + "fetch_balance": "consenti a questa applicazione di recuperare il saldo {{ coin }}?", + "get_wallet_info": "consenti a questa applicazione di ottenere le informazioni del tuo portafoglio?", + "get_wallet_transactions": "consenti a questa applicazione di recuperare le transazioni del tuo portafoglio?", + "invite": "consenti a questa applicazione di invitare {{ invitee }}?", + "kick": "consenti a questa applicazione di espellere {{ partecipant }} dal gruppo?", + "leave_group": "consenti a questa applicazione di uscire dal seguente gruppo?", + "list_hosted_data": "consenti a questa applicazione di ottenere l'elenco dei tuoi dati ospitati?", + "order_detail": "{{ qort_amount }} QORT per {{ foreign_amount }} {{ ticker }}", + "pay_publish": "consenti a questa applicazione di effettuare i seguenti pagamenti e pubblicazioni?", + "perform_admin_action_with_value": "con valore: {{ value }}", + "perform_admin_action": "consenti a questa applicazione di eseguire l'azione amministrativa: {{ type }}", + "publish_qdn": "consenti a questa applicazione di pubblicare su QDN?", + "register_name": "consenti a questa applicazione di registrare questo nome?", + "remove_admin": "consenti a questa applicazione di rimuovere l'utente {{ partecipant }} come amministratore?", + "remove_from_list": "consenti a questa applicazione di rimuovere i seguenti elementi dalla lista {{ name }}?", + "sell_name_cancel": "consenti a questa applicazione di annullare la vendita di un nome?", + "sell_name_transaction_detail": "vendi {{ name }} per {{ price }} QORT", + "sell_name_transaction": "consenti a questa applicazione di creare una transazione di vendita nome?", + "sell_order": "consenti a questa applicazione di eseguire un ordine di vendita?", + "send_chat_message": "consenti a questa applicazione di inviare questo messaggio chat?", + "send_coins": "consenti a questa applicazione di inviare monete?", + "server_add": "consenti a questa applicazione di aggiungere un server?", + "server_remove": "consenti a questa applicazione di rimuovere un server?", + "set_current_server": "consenti a questa applicazione di impostare il server corrente?", + "sign_fee": "consenti a questa applicazione di firmare le commissioni richieste per tutte le tue offerte di scambio?", + "sign_process_transaction": "consenti a questa applicazione di firmare ed elaborare una transazione?", + "sign_transaction": "consenti a questa applicazione di firmare una transazione?", + "transfer_asset": "consenti a questa applicazione di trasferire il seguente asset?", + "update_foreign_fee": "consenti a questa applicazione di aggiornare le commissioni estere sul tuo nodo?", + "update_group_detail": "nuovo proprietario: {{ owner }}", + "update_group": "consenti a questa applicazione di aggiornare questo gruppo?" }, - "poll": "poll: {{ name }}", - "provide_recipient_group_id": "please provide a recipient or groupId", - "request_create_poll": "you are requesting to create the poll below:", - "request_vote_poll": "you are being requested to vote on the poll below:", + "poll": "sondaggio: {{ name }}", + "provide_recipient_group_id": "fornisci un destinatario o un groupId", + "request_create_poll": "stai richiedendo di creare il seguente sondaggio:", + "request_vote_poll": "ti viene richiesto di votare nel seguente sondaggio:", "sats_per_kb": "{{ amount }} sats per KB", "sats": "{{ amount }} sats", "server_host": "host: {{ host }}", - "server_type": "type: {{ type }}", - "to_group": "to: group {{ group_id }}", - "to_recipient": "to: {{ recipient }}", - "total_locking_fee": "total Locking Fee:", - "total_unlocking_fee": "total Unlocking Fee:", - "value": "value: {{ value }}" + "server_type": "tipo: {{ type }}", + "to_group": "a: gruppo {{ group_id }}", + "to_recipient": "a: {{ recipient }}", + "total_locking_fee": "commissione totale di blocco:", + "total_unlocking_fee": "commissione totale di sblocco:", + "value": "valore: {{ value }}" } diff --git a/src/i18n/locales/it/tutorial.json b/src/i18n/locales/it/tutorial.json index c807da8..6018e62 100644 --- a/src/i18n/locales/it/tutorial.json +++ b/src/i18n/locales/it/tutorial.json @@ -3,8 +3,8 @@ "2_overview": "2. Panoramica", "3_groups": "3. Gruppi Qortali", "4_obtain_qort": "4. Ottenimento di Qort", - "account_creation": "Creazione dell'account", - "important_info": "Informazioni importanti!", + "account_creation": "creazione dell'account", + "important_info": "informazioni importanti!", "apps": { "dashboard": "1. Dashboard di app", "navigation": "2. Navigazione delle app" @@ -14,8 +14,8 @@ "explore": "esplorare", "general_chat": "chat generale", "getting_started": "iniziare", - "register_name": "Registra un nome", + "register_name": "registra un nome", "see_apps": "Vedi le app", "trade_qort": "commercio qort" } -} \ No newline at end of file +} From 5daa6a7547188572fe56e77252a148d4f3b743e6 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 20:34:21 +0200 Subject: [PATCH 487/717] Add authentication key --- src/App.tsx | 2 +- src/i18n/locales/de/auth.json | 3 ++- src/i18n/locales/en/auth.json | 1 + src/i18n/locales/es/auth.json | 3 ++- src/i18n/locales/fr/auth.json | 3 ++- src/i18n/locales/it/auth.json | 37 ++++++++++++++++--------------- src/i18n/locales/it/core.json | 12 +++++----- src/i18n/locales/it/tutorial.json | 14 ++++++------ src/i18n/locales/ja/auth.json | 3 ++- src/i18n/locales/ru/auth.json | 3 ++- src/i18n/locales/zh/auth.json | 11 ++++----- 11 files changed, 50 insertions(+), 42 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 4aad404..0e82a0e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2754,7 +2754,7 @@ function App() { fontWeight: 600, }} > - {t('auth:action.authenticate', { + {t('auth:authentication', { postProcess: 'capitalizeFirstChar', })} diff --git a/src/i18n/locales/de/auth.json b/src/i18n/locales/de/auth.json index 842b6be..7f5ce8d 100644 --- a/src/i18n/locales/de/auth.json +++ b/src/i18n/locales/de/auth.json @@ -46,6 +46,7 @@ "key": "API -Schlüssel", "select_valid": "Wählen Sie einen gültigen Apikey aus" }, + "authentication": "Authentifizierung", "blocked_users": "Blockierte Benutzer", "build_version": "Version erstellen", "message": { @@ -132,4 +133,4 @@ } }, "welcome": "Willkommen zu" -} \ No newline at end of file +} diff --git a/src/i18n/locales/en/auth.json b/src/i18n/locales/en/auth.json index 747b3ca..ee9b004 100644 --- a/src/i18n/locales/en/auth.json +++ b/src/i18n/locales/en/auth.json @@ -46,6 +46,7 @@ "key": "API key", "select_valid": "select a valid apikey" }, + "authentication": "authentication", "blocked_users": "blocked users", "build_version": "build version", "message": { diff --git a/src/i18n/locales/es/auth.json b/src/i18n/locales/es/auth.json index 7014d99..73ab68a 100644 --- a/src/i18n/locales/es/auth.json +++ b/src/i18n/locales/es/auth.json @@ -46,6 +46,7 @@ "key": "Llave de API", "select_valid": "Seleccione un apikey válido" }, + "authentication": "autenticación", "blocked_users": "usuarios bloqueados", "build_version": "versión de compilación", "message": { @@ -132,4 +133,4 @@ } }, "welcome": "bienvenido" -} \ No newline at end of file +} diff --git a/src/i18n/locales/fr/auth.json b/src/i18n/locales/fr/auth.json index 444b11c..74313eb 100644 --- a/src/i18n/locales/fr/auth.json +++ b/src/i18n/locales/fr/auth.json @@ -46,6 +46,7 @@ "key": "Clé API", "select_valid": "Sélectionnez un apikey valide" }, + "authentication": "authentification", "blocked_users": "utilisateurs bloqués", "build_version": "version de construction", "message": { @@ -132,4 +133,4 @@ } }, "welcome": "bienvenue" -} \ No newline at end of file +} diff --git a/src/i18n/locales/it/auth.json b/src/i18n/locales/it/auth.json index 508ae83..d42452c 100644 --- a/src/i18n/locales/it/auth.json +++ b/src/i18n/locales/it/auth.json @@ -10,16 +10,16 @@ "account": "aggiungi account", "seed_phrase": "aggiungi seme-frase" }, - "authenticate": "autenticazione", - "block": "bloccare", + "authenticate": "autentica", + "block": "blocca", "block_all": "blocca tutto", "block_data": "blocca i dati QDN", "block_name": "nome del blocco", "block_txs": "blocca TSX", "fetch_names": "nomi di recupero", "copy_address": "indirizzo di copia", - "create_account": "creare un account", - "create_qortal_account": "create your Qortal account by clicking NEXT below.", + "create_account": "crea un account", + "create_qortal_account": "crea your Qortal account by clicking NEXT below.", "choose_password": "scegli nuova password", "download_account": "scarica account", "enter_amount": "si prega di inserire un importo maggiore di 0", @@ -31,9 +31,9 @@ "publish_group_secret_key": "pubblica Key Secret Group", "reencrypt_key": "chiave di ri-crittografia", "return_to_list": "torna all'elenco", - "setup_qortal_account": "Imposta il tuo account Qortal", - "unblock": "sbloccare", - "unblock_name": "nome sblocco" + "setup_qortal_account": "imposta il tuo account Qortal", + "unblock": "sblocca", + "unblock_name": "sblocca nome" }, "address": "indirizzo", "address_name": "indirizzo o nome", @@ -46,6 +46,7 @@ "key": "chiave API", "select_valid": "seleziona un apikey valido" }, + "authentication": "autenticazione", "blocked_users": "utenti bloccati", "build_version": "versione build", "message": { @@ -74,7 +75,7 @@ "blocked_addresses": "indirizzi bloccati: l'elaborazione dei blocchi di TXS", "blocked_names": "nomi bloccati per QDN", "blocking": "blocking {{ name }}", - "choose_block": "scegli \"Block TXS\" o \"All\" per bloccare i messaggi di chat", + "choose_block": "scegli 'Blocca TXS' o 'Tutti' per bloccare i messaggi di chat", "congrats_setup": "congratulazioni, sei tutto impostato!", "decide_block": "decidi cosa bloccare", "name_address": "nome o indirizzo", @@ -85,11 +86,11 @@ "fetching_group_secret_key": "recupero Group Secret Key pubblica", "last_encryption_date": "last encryption date: {{ date }} by {{ name }}", "keep_secure": "mantieni il tuo file account sicuro", - "publishing_key": "attenzione: dopo aver pubblicato la chiave, ci vorranno un paio di minuti per apparire. Per favore aspetta.", - "seedphrase_notice": "a SEEDPHRASE has been randomly generated in the background.", - "turn_local_node": "Si prega di attivare il nodo locale", - "type_seed": "digita o incolla nella frase di semi", - "your_accounts": "I tuoi conti salvati" + "publishing_key": "attenzione: dopo aver pubblicato la chiave, ci vorranno un paio di minuti per apparire. Attendere, per favore.", + "seedphrase_notice": "È stato generato un SEEDPHRASE in background.", + "turn_local_node": "si prega di attivare il nodo locale", + "type_seed": "digita o incolla la seedphrase", + "your_accounts": "i tuoi conti salvati" }, "success": { "reencrypted_secret_key": "chiave segreta recriptata con successo. Potrebbero essere necessari un paio di minuti per propagare le modifiche. Aggiorna il gruppo in 5 minuti." @@ -106,16 +107,16 @@ }, "note": "nota", "password": "password", - "password_confirmation": "Conferma password", - "seed_phrase": "frase di semi", + "password_confirmation": "conferma password", + "seed_phrase": "seedphrase", "seed_your": "la tua seedphrase", "tips": { - "additional_wallet": "usa questa opzione per collegare ulteriori portafogli Qortali che hai già realizzato, per accedere con loro in seguito. Avrai bisogno di accedere al tuo file JSON di backup per farlo.", - "digital_id": "il tuo portafoglio è come il tuo ID digitale su Qortal ed è come accederai all'interfaccia utente Qortal. Contiene il tuo indirizzo pubblico e il nome Qortal che alla fine sceglierai. Ogni transazione che fai è collegata al tuo ID, ed è qui che gestisci tutte le tue criptovalute Qort e altre criptovalute negoziabili su Qortal.", + "additional_wallet": "usa quest'opzione per collegare ulteriori portafogli Qortal che hai già realizzato, per accedere con loro in seguito. Avrai bisogno di accedere al tuo file JSON di backup.", + "digital_id": "il tuo portafoglio è come il tuo ID digitale su Qortal ed è come accederai all'interfaccia utente Qortal. Contiene il tuo indirizzo pubblico e il nome Qortal che alla fine sceglierai. Ogni transazione che fai è collegata al tuo ID, nel quale puoi gestire tutte le tue criptovalute Qort e altre criptovalute negoziabili su Qortal.", "existing_account": "hai già un account Qortal? Inserisci la tua frase di backup segreta qui per accedervi. Questa frase è uno dei modi per recuperare il tuo account.", "key_encrypt_admin": "questa chiave è crittografare i contenuti relativi ad amministrazione. Solo gli amministratori vedrebbero il contenuto crittografato con esso.", "key_encrypt_group": "questa chiave è crittografare i contenuti relativi al gruppo. Questo è l'unico usato in questa interfaccia utente al momento. Tutti i membri del gruppo saranno in grado di vedere i contenuti crittografati con questa chiave.", - "new_account": "La creazione di un account significa creare un nuovo portafoglio e un ID digitale per iniziare a utilizzare Qortal. Una volta che hai realizzato il tuo account, puoi iniziare a fare cose come ottenere un po 'di Qort, acquistare un nome e Avatar, pubblicare video e blog e molto altro.", + "new_account": "la creazione di un account significa creare un nuovo portafoglio e un ID digitale per iniziare a utilizzare Qortal. Una volta che hai realizzato il tuo account, puoi iniziare a fare cose come ottenere un po 'di Qort, acquistare un nome e Avatar, pubblicare video e blog e molto altro.", "new_users": "i nuovi utenti iniziano qui!", "safe_place": "salva il tuo account in un posto in cui lo ricorderai!", "view_seedphrase": "se si desidera visualizzare la seedphrase, fai clic sulla parola \"seedphrase\" in questo testo. Le seedphrasi vengono utilizzate per generare la chiave privata per il tuo account Qortal. Per la sicurezza per impostazione predefinita, le semina non vengono visualizzate se non specificamente scelte.", diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 33a84d0..061e3ed 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -29,7 +29,7 @@ "create_file": "crea file", "create_transaction": "crea transazioni sulla blockchain Qortal", "create_thread": "crea thread", - "decline": "declina", + "decline": "rifiuta", "decrypt": "decripta", "disable_enter": "disabilita inserire", "download": "scarica", @@ -176,7 +176,7 @@ "message": { "error": { "address_not_found": "il tuo indirizzo non è stato trovato", - "app_need_name": "La tua app ha bisogno di un nome", + "app_need_name": "la tua app ha bisogno di un nome", "build_app": "impossibile creare app private", "decrypt_app": "impossibile decrittografare l'app privata '", "download_image": "impossibile scaricare l'immagine. Riprova più tardi facendo clic sul pulsante Aggiorna", @@ -193,7 +193,7 @@ "invalid_image_embed_link_name": "iMMAGINE IMMAGINE INCONTRO IN ENTRARE. Param mancante.", "invalid_poll_embed_link_name": "sondaggio non valido Incorporare il collegamento. Nome mancante.", "invalid_signature": "firma non valida", - "invalid_theme_format": "Formato tema non valido", + "invalid_theme_format": "formato tema non valido", "invalid_zip": "zip non valido", "message_loading": "errore di caricamento del messaggio.", "message_size": "your message size is of {{ size }} bytes out of a maximum of {{ maximum }}", @@ -267,8 +267,8 @@ "rating": "rating for {{ service }} {{ name }}", "register_name": "hai bisogno di un nome Qortal registrato per salvare le app appuntate a QDN.", "replied_to": "replied to {{ person }}", - "revert_default": "Ritorna a predefinito", - "revert_qdn": "Ritorna a QDN", + "revert_default": "ritorna a predefinito", + "revert_qdn": "ritorna a QDN", "save_qdn": "salva su QDN", "secure_ownership": "proprietà sicura dei dati pubblicati con il tuo nome. Puoi anche vendere il tuo nome, insieme ai tuoi dati a una terza parte.", "select_file": "seleziona un file", @@ -293,7 +293,7 @@ "publish_app": "vorresti pubblicare questa app?", "publish_avatar": "vorresti pubblicare un avatar?", "publish_qdn": "vorresti pubblicare le tue impostazioni su QDN (crittografato)?", - "overwrite_changes": "L'app non è stata in grado di scaricare le app appuntate a QDN esistenti. Vorresti sovrascrivere quei cambiamenti?", + "overwrite_changes": "l'app non è stata in grado di scaricare le app appuntate a QDN esistenti. Vorresti sovrascrivere quei cambiamenti?", "rate_app": "vorresti dare il voto {{ rate }} a quest'app?. Questo creerà una transazione POLL.", "register_name": "vorresti registrare questo nome?", "reset_pinned": "non ti piacciono le tue attuali modifiche locali? Vorresti ripristinare le app appuntate predefinite?", diff --git a/src/i18n/locales/it/tutorial.json b/src/i18n/locales/it/tutorial.json index 6018e62..7c35460 100644 --- a/src/i18n/locales/it/tutorial.json +++ b/src/i18n/locales/it/tutorial.json @@ -1,8 +1,8 @@ { "1_getting_started": "1. Iniziare", "2_overview": "2. Panoramica", - "3_groups": "3. Gruppi Qortali", - "4_obtain_qort": "4. Ottenimento di Qort", + "3_groups": "3. Gruppi Qortal", + "4_obtain_qort": "4. Ottenere i Qort", "account_creation": "creazione dell'account", "important_info": "informazioni importanti!", "apps": { @@ -10,12 +10,12 @@ "navigation": "2. Navigazione delle app" }, "initial": { - "recommended_qort_qty": "have at least {{ quantity }} QORT in your wallet", - "explore": "esplorare", + "recommended_qort_qty": "avere almeno {{ quantity }} QORT nel tuo wallet", + "explore": "esplora", "general_chat": "chat generale", - "getting_started": "iniziare", + "getting_started": "come iniziare", "register_name": "registra un nome", - "see_apps": "Vedi le app", - "trade_qort": "commercio qort" + "see_apps": "vedi le app", + "trade_qort": "scambia qort" } } diff --git a/src/i18n/locales/ja/auth.json b/src/i18n/locales/ja/auth.json index 4fbcd28..3399514 100644 --- a/src/i18n/locales/ja/auth.json +++ b/src/i18n/locales/ja/auth.json @@ -46,6 +46,7 @@ "key": "APIキー", "select_valid": "有効なApikeyを選択します" }, + "authentication": "認証", "blocked_users": "ブロックされたユーザー", "build_version": "ビルドバージョン", "message": { @@ -132,4 +133,4 @@ } }, "welcome": "ようこそ" -} \ No newline at end of file +} diff --git a/src/i18n/locales/ru/auth.json b/src/i18n/locales/ru/auth.json index 5068f1c..74b374b 100644 --- a/src/i18n/locales/ru/auth.json +++ b/src/i18n/locales/ru/auth.json @@ -46,6 +46,7 @@ "key": "API -ключ", "select_valid": "Выберите действительный apikey" }, + "authentication": "идентификация", "blocked_users": "Заблокировали пользователей", "build_version": "Построить версию", "message": { @@ -132,4 +133,4 @@ } }, "welcome": "Добро пожаловать в" -} \ No newline at end of file +} diff --git a/src/i18n/locales/zh/auth.json b/src/i18n/locales/zh/auth.json index 4a2004a..3e5b864 100644 --- a/src/i18n/locales/zh/auth.json +++ b/src/i18n/locales/zh/auth.json @@ -19,7 +19,7 @@ "fetch_names": "获取名称", "copy_address": "复制地址", "create_account": "创建账户", - "create_qortal_account": "create your Qortal account by clicking NEXT below.", + "create_qortal_account": "通过单击下面的NEXT创建您的珊瑚帐户。", "choose_password": "选择新密码", "download_account": "下载帐户", "enter_amount": "请输入大于0的金额", @@ -46,6 +46,7 @@ "key": "API键", "select_valid": "选择有效的apikey" }, + "authentication": "身份验证", "blocked_users": "阻止用户", "build_version": "构建版本", "message": { @@ -83,10 +84,10 @@ "no_secret_key_published": "尚未发布秘密密钥", "fetching_admin_secret_key": "获取管理员秘密钥匙", "fetching_group_secret_key": "获取组秘密密钥发布", - "last_encryption_date": "last encryption date: {{ date }} by {{ name }}", + "last_encryption_date": "最后加密日期:{{date}}by{{name}}", "keep_secure": "确保您的帐户文件安全", "publishing_key": "提醒:发布钥匙后,出现需要几分钟才能出现。请等待。", - "seedphrase_notice": "a SEEDPHRASE has been randomly generated in the background.", + "seedphrase_notice": "在后台随机生成了一个种子短语。", "turn_local_node": "请打开您的本地节点", "type_seed": "在您的种子角度中输入或粘贴", "your_accounts": "您保存的帐户" @@ -102,7 +103,7 @@ "use_local": "使用本地节点", "using": "使用节点", "using_public": "使用公共节点", - "using_public_gateway": "using public node: {{ gateway }}" + "using_public_gateway": "使用公共节点: {{ gateway }}" }, "note": "笔记", "password": "密码", @@ -132,4 +133,4 @@ } }, "welcome": "欢迎来" -} \ No newline at end of file +} From 81a562454a9c308fbe4a733161a75edda23724af Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 20:43:06 +0200 Subject: [PATCH 488/717] Improve translation --- src/i18n/locales/it/auth.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/locales/it/auth.json b/src/i18n/locales/it/auth.json index d42452c..f2c8e03 100644 --- a/src/i18n/locales/it/auth.json +++ b/src/i18n/locales/it/auth.json @@ -83,7 +83,7 @@ "no_minimum_length": "non esiste un requisito di lunghezza minima", "no_secret_key_published": "nessuna chiave segreta ancora pubblicata", "fetching_admin_secret_key": "recupero chiave segreta admin", - "fetching_group_secret_key": "recupero Group Secret Key pubblica", + "fetching_group_secret_key": "recupero chiavi segreta di gruppo pubblicate", "last_encryption_date": "last encryption date: {{ date }} by {{ name }}", "keep_secure": "mantieni il tuo file account sicuro", "publishing_key": "attenzione: dopo aver pubblicato la chiave, ci vorranno un paio di minuti per apparire. Attendere, per favore.", @@ -113,7 +113,7 @@ "tips": { "additional_wallet": "usa quest'opzione per collegare ulteriori portafogli Qortal che hai già realizzato, per accedere con loro in seguito. Avrai bisogno di accedere al tuo file JSON di backup.", "digital_id": "il tuo portafoglio è come il tuo ID digitale su Qortal ed è come accederai all'interfaccia utente Qortal. Contiene il tuo indirizzo pubblico e il nome Qortal che alla fine sceglierai. Ogni transazione che fai è collegata al tuo ID, nel quale puoi gestire tutte le tue criptovalute Qort e altre criptovalute negoziabili su Qortal.", - "existing_account": "hai già un account Qortal? Inserisci la tua frase di backup segreta qui per accedervi. Questa frase è uno dei modi per recuperare il tuo account.", + "existing_account": "hai già un account Qortal? Inserisci qui la tua frase di backup segreta per accedervi. Questa frase è uno dei modi per recuperare il tuo account.", "key_encrypt_admin": "questa chiave è crittografare i contenuti relativi ad amministrazione. Solo gli amministratori vedrebbero il contenuto crittografato con esso.", "key_encrypt_group": "questa chiave è crittografare i contenuti relativi al gruppo. Questo è l'unico usato in questa interfaccia utente al momento. Tutti i membri del gruppo saranno in grado di vedere i contenuti crittografati con questa chiave.", "new_account": "la creazione di un account significa creare un nuovo portafoglio e un ID digitale per iniziare a utilizzare Qortal. Una volta che hai realizzato il tuo account, puoi iniziare a fare cose come ottenere un po 'di Qort, acquistare un nome e Avatar, pubblicare video e blog e molto altro.", From bbbf1513e7d6858f11ab7e154dc886f202a03c2d Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 20:48:40 +0200 Subject: [PATCH 489/717] Improve translations --- src/i18n/locales/it/auth.json | 20 ++++++++++---------- src/i18n/locales/it/group.json | 2 +- src/i18n/locales/it/question.json | 16 ++++++++-------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/i18n/locales/it/auth.json b/src/i18n/locales/it/auth.json index f2c8e03..cd5fa7f 100644 --- a/src/i18n/locales/it/auth.json +++ b/src/i18n/locales/it/auth.json @@ -24,7 +24,7 @@ "download_account": "scarica account", "enter_amount": "si prega di inserire un importo maggiore di 0", "enter_recipient": "inserisci un destinatario", - "enter_wallet_password": "inserisci la password del tuo portafoglio", + "enter_wallet_password": "inserisci la password del tuo wallet", "export_seedphrase": "export Seedphrase", "insert_name_address": "si prega di inserire un nome o un indirizzo", "publish_admin_secret_key": "pubblica la chiave segreta dell'amministratore", @@ -44,7 +44,7 @@ "enter": "inserisci Apikey", "import": "importa apikey", "key": "chiave API", - "select_valid": "seleziona un apikey valido" + "select_valid": "seleziona una apikey valida" }, "authentication": "autenticazione", "blocked_users": "utenti bloccati", @@ -62,13 +62,13 @@ "field_not_found_json": "{{ field }} not found in JSON", "find_secret_key": "impossibile trovare secretkey corretta", "incorrect_password": "password errata", - "invalid_qortal_link": "collegamento Qortale non valido", + "invalid_qortal_link": "collegamento Qortal non valido", "invalid_secret_key": "la secretKey non è valida", "invalid_uint8": "l'uint8arraydata che hai inviato non è valido", "name_not_existing": "il nome non esiste", "name_not_registered": "nome non registrato", "read_blob_base64": "impossibile leggere il BLOB come stringa codificata da base64", - "reencrypt_secret_key": "incapace di rivivere nuovamente la chiave segreta", + "reencrypt_secret_key": "impossibile recriptare la chiave segreta", "set_apikey": "impossibile impostare la chiave API:" }, "generic": { @@ -76,7 +76,7 @@ "blocked_names": "nomi bloccati per QDN", "blocking": "blocking {{ name }}", "choose_block": "scegli 'Blocca TXS' o 'Tutti' per bloccare i messaggi di chat", - "congrats_setup": "congratulazioni, sei tutto impostato!", + "congrats_setup": "congratulazioni, tutto è stato impostato!", "decide_block": "decidi cosa bloccare", "name_address": "nome o indirizzo", "no_account": "nessun account salvato", @@ -112,19 +112,19 @@ "seed_your": "la tua seedphrase", "tips": { "additional_wallet": "usa quest'opzione per collegare ulteriori portafogli Qortal che hai già realizzato, per accedere con loro in seguito. Avrai bisogno di accedere al tuo file JSON di backup.", - "digital_id": "il tuo portafoglio è come il tuo ID digitale su Qortal ed è come accederai all'interfaccia utente Qortal. Contiene il tuo indirizzo pubblico e il nome Qortal che alla fine sceglierai. Ogni transazione che fai è collegata al tuo ID, nel quale puoi gestire tutte le tue criptovalute Qort e altre criptovalute negoziabili su Qortal.", + "digital_id": "il tuo wallet è come il tuo ID digitale su Qortal ed è come accederai all'interfaccia utente Qortal. Contiene il tuo indirizzo pubblico e il nome Qortal che alla fine sceglierai. Ogni transazione che fai è collegata al tuo ID, nel quale puoi gestire tutte le tue criptovalute Qort e altre criptovalute negoziabili su Qortal.", "existing_account": "hai già un account Qortal? Inserisci qui la tua frase di backup segreta per accedervi. Questa frase è uno dei modi per recuperare il tuo account.", "key_encrypt_admin": "questa chiave è crittografare i contenuti relativi ad amministrazione. Solo gli amministratori vedrebbero il contenuto crittografato con esso.", "key_encrypt_group": "questa chiave è crittografare i contenuti relativi al gruppo. Questo è l'unico usato in questa interfaccia utente al momento. Tutti i membri del gruppo saranno in grado di vedere i contenuti crittografati con questa chiave.", - "new_account": "la creazione di un account significa creare un nuovo portafoglio e un ID digitale per iniziare a utilizzare Qortal. Una volta che hai realizzato il tuo account, puoi iniziare a fare cose come ottenere un po 'di Qort, acquistare un nome e Avatar, pubblicare video e blog e molto altro.", + "new_account": "la creazione di un account significa creare un nuovo wallet e un ID digitale per iniziare a utilizzare Qortal. Una volta che hai realizzato il tuo account, puoi iniziare a fare cose come ottenere un po 'di Qort, acquistare un nome e Avatar, pubblicare video e blog e molto altro.", "new_users": "i nuovi utenti iniziano qui!", "safe_place": "salva il tuo account in un posto in cui lo ricorderai!", "view_seedphrase": "se si desidera visualizzare la seedphrase, fai clic sulla parola \"seedphrase\" in questo testo. Le seedphrasi vengono utilizzate per generare la chiave privata per il tuo account Qortal. Per la sicurezza per impostazione predefinita, le semina non vengono visualizzate se non specificamente scelte.", - "wallet_secure": "mantieni il tuo file di portafoglio sicuro." + "wallet_secure": "mantieni il tuo file di wallet sicuro." }, "wallet": { - "password_confirmation": "conferma la password del portafoglio", - "password": "password del portafoglio", + "password_confirmation": "conferma la password del wallet", + "password": "password del wallet", "keep_password": "mantieni la password corrente", "new_password": "nuova password", "error": { diff --git a/src/i18n/locales/it/group.json b/src/i18n/locales/it/group.json index f7380c6..ad77095 100644 --- a/src/i18n/locales/it/group.json +++ b/src/i18n/locales/it/group.json @@ -71,7 +71,7 @@ "block_delay_minimum": "ritardo minimo del blocco per le approvazioni delle transazioni di gruppo", "block_delay_maximum": "ritardo massimo del blocco per le approvazioni delle transazioni di gruppo", "closed_group": "questo è un gruppo chiuso/privato, quindi dovrai attendere fino a quando un amministratore accetta la tua richiesta", - "descrypt_wallet": "portafoglio decrypting ...", + "descrypt_wallet": "wallet decrypting ...", "encryption_key": "la prima chiave di crittografia comune del gruppo è in procinto di creare. Si prega di attendere qualche minuto per essere recuperato dalla rete. Controllo ogni 2 minuti ...", "group_announcement": "annunci di gruppo", "group_approval_threshold": "soglia di approvazione del gruppo (numero / percentuale di amministratori che devono approvare una transazione)", diff --git a/src/i18n/locales/it/question.json b/src/i18n/locales/it/question.json index 834932d..df04d0a 100644 --- a/src/i18n/locales/it/question.json +++ b/src/i18n/locales/it/question.json @@ -4,8 +4,8 @@ "always_chat_messages": "consenti sempre i messaggi chat da questa app", "always_retrieve_balance": "consenti sempre il recupero automatico del saldo", "always_retrieve_list": "consenti sempre il recupero automatico delle liste", - "always_retrieve_wallet": "consenti sempre il recupero automatico del portafoglio", - "always_retrieve_wallet_transactions": "consenti sempre il recupero automatico delle transazioni del portafoglio", + "always_retrieve_wallet": "consenti sempre il recupero automatico del wallet", + "always_retrieve_wallet_transactions": "consenti sempre il recupero automatico delle transazioni del wallet", "amount_qty": "quantità: {{ quantity }}", "asset_name": "asset: {{ asset }}", "assets_used_pay": "asset usato nei pagamenti: {{ asset }}", @@ -38,14 +38,14 @@ "fetch_list": "recupero lista fallito", "fetch_poll": "recupero sondaggio fallito", "fetch_recipient_public_key": "recupero chiave pubblica del destinatario fallito", - "fetch_wallet_info": "impossibile recuperare informazioni sul portafoglio", - "fetch_wallet_transactions": "impossibile recuperare transazioni del portafoglio", - "fetch_wallet": "recupero portafoglio fallito. Riprova", + "fetch_wallet_info": "impossibile recuperare informazioni sul wallet", + "fetch_wallet_transactions": "impossibile recuperare transazioni del wallet", + "fetch_wallet": "recupero wallet fallito. Riprova", "file_extension": "impossibile determinare l'estensione del file", "gateway_balance_local_node": "non è possibile visualizzare il saldo {{ token }} tramite il gateway. Usa il tuo nodo locale.", "gateway_non_qort_local_node": "non è possibile inviare monete non-QORT tramite il gateway. Usa il tuo nodo locale.", "gateway_retrieve_balance": "il recupero del saldo {{ token }} non è consentito tramite un gateway", - "gateway_wallet_local_node": "non è possibile visualizzare il portafoglio {{ token }} tramite il gateway. Usa il tuo nodo locale.", + "gateway_wallet_local_node": "non è possibile visualizzare il wallet {{ token }} tramite il gateway. Usa il tuo nodo locale.", "get_foreign_fee": "errore nel recupero delle commissioni estere", "insufficient_balance_qort": "saldo QORT insufficiente", "insufficient_balance": "saldo asset insufficiente", @@ -145,8 +145,8 @@ "create_group": "consenti a questa applicazione di creare un gruppo?", "delete_hosts_resources": "consenti a questa applicazione di eliminare {{ size }} risorse ospitate?", "fetch_balance": "consenti a questa applicazione di recuperare il saldo {{ coin }}?", - "get_wallet_info": "consenti a questa applicazione di ottenere le informazioni del tuo portafoglio?", - "get_wallet_transactions": "consenti a questa applicazione di recuperare le transazioni del tuo portafoglio?", + "get_wallet_info": "consenti a questa applicazione di ottenere le informazioni del tuo wallet?", + "get_wallet_transactions": "consenti a questa applicazione di recuperare le transazioni del tuo wallet?", "invite": "consenti a questa applicazione di invitare {{ invitee }}?", "kick": "consenti a questa applicazione di espellere {{ partecipant }} dal gruppo?", "leave_group": "consenti a questa applicazione di uscire dal seguente gruppo?", From 8e57aef29a138e198ef0737443859e05d727fbf5 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 20:51:07 +0200 Subject: [PATCH 490/717] Improvement --- src/i18n/locales/it/question.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/i18n/locales/it/question.json b/src/i18n/locales/it/question.json index df04d0a..bd113e8 100644 --- a/src/i18n/locales/it/question.json +++ b/src/i18n/locales/it/question.json @@ -110,15 +110,15 @@ "private_service": "usa un servizio privato", "provide_group_id": "fornisci un groupId", "read_transaction_carefully": "leggi attentamente la transazione prima di accettare!", - "user_declined_add_list": "utente ha rifiutato l'aggiunta alla lista", - "user_declined_delete_from_list": "utente ha rifiutato l'eliminazione dalla lista", - "user_declined_delete_hosted_resources": "utente ha rifiutato l'eliminazione delle risorse ospitate", - "user_declined_join": "utente ha rifiutato di unirsi al gruppo", - "user_declined_list": "utente ha rifiutato di ottenere la lista delle risorse ospitate", - "user_declined_request": "utente ha rifiutato la richiesta", - "user_declined_save_file": "utente ha rifiutato di salvare il file", - "user_declined_send_message": "utente ha rifiutato di inviare il messaggio", - "user_declined_share_list": "utente ha rifiutato di condividere la lista" + "user_declined_add_list": "l'utente ha rifiutato l'aggiunta alla lista", + "user_declined_delete_from_list": "l'utente ha rifiutato l'eliminazione dalla lista", + "user_declined_delete_hosted_resources": "l'utente ha rifiutato l'eliminazione delle risorse ospitate", + "user_declined_join": "l'utente ha rifiutato di unirsi al gruppo", + "user_declined_list": "l'utente ha rifiutato di ottenere la lista delle risorse ospitate", + "user_declined_request": "l'utente ha rifiutato la richiesta", + "user_declined_save_file": "l'utente ha rifiutato di salvare il file", + "user_declined_send_message": "l'utente ha rifiutato di inviare il messaggio", + "user_declined_share_list": "l'utente ha rifiutato di condividere la lista" } }, "name": "nome: {{ name }}", From 8fefd415f0d11c8ef7f38c2f30bd05ba0c3494fd Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 20:57:34 +0200 Subject: [PATCH 491/717] Improvement --- src/i18n/locales/it/auth.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/i18n/locales/it/auth.json b/src/i18n/locales/it/auth.json index cd5fa7f..bc48c13 100644 --- a/src/i18n/locales/it/auth.json +++ b/src/i18n/locales/it/auth.json @@ -112,13 +112,13 @@ "seed_your": "la tua seedphrase", "tips": { "additional_wallet": "usa quest'opzione per collegare ulteriori portafogli Qortal che hai già realizzato, per accedere con loro in seguito. Avrai bisogno di accedere al tuo file JSON di backup.", - "digital_id": "il tuo wallet è come il tuo ID digitale su Qortal ed è come accederai all'interfaccia utente Qortal. Contiene il tuo indirizzo pubblico e il nome Qortal che alla fine sceglierai. Ogni transazione che fai è collegata al tuo ID, nel quale puoi gestire tutte le tue criptovalute Qort e altre criptovalute negoziabili su Qortal.", + "digital_id": "il tuo wallet è come il tuo ID digitale su Qortal ed e verrà usato per accedere a Qortal. Contiene il tuo indirizzo pubblico e il nome Qortal che alla fine sceglierai. Ogni transazione che fai è collegata al tuo ID, nel quale potrai gestire tutte le tue criptovalute Qort e altre criptovalute negoziabili su Qortal.", "existing_account": "hai già un account Qortal? Inserisci qui la tua frase di backup segreta per accedervi. Questa frase è uno dei modi per recuperare il tuo account.", - "key_encrypt_admin": "questa chiave è crittografare i contenuti relativi ad amministrazione. Solo gli amministratori vedrebbero il contenuto crittografato con esso.", - "key_encrypt_group": "questa chiave è crittografare i contenuti relativi al gruppo. Questo è l'unico usato in questa interfaccia utente al momento. Tutti i membri del gruppo saranno in grado di vedere i contenuti crittografati con questa chiave.", - "new_account": "la creazione di un account significa creare un nuovo wallet e un ID digitale per iniziare a utilizzare Qortal. Una volta che hai realizzato il tuo account, puoi iniziare a fare cose come ottenere un po 'di Qort, acquistare un nome e Avatar, pubblicare video e blog e molto altro.", + "key_encrypt_admin": "questa chiave crittografa i contenuti relativi ad amministratore. Solo gli amministratori vedrebbero il contenuto crittografato.", + "key_encrypt_group": "questa chiave crittografa i contenuti relativi al gruppo. Questo è l'unico usato in questa interfaccia utente al momento. Tutti i membri del gruppo saranno in grado di vedere i contenuti crittografati con questa chiave.", + "new_account": "la creazione di un account consiste nella creazione di un wallet e di un ID digitale per iniziare a utilizzare Qortal. Una volta creato l'account, potrai iniziare a fare cose come ottenere dei Qort, acquistare un nome e un Avatar, pubblicare video e blog e molto altro.", "new_users": "i nuovi utenti iniziano qui!", - "safe_place": "salva il tuo account in un posto in cui lo ricorderai!", + "safe_place": "salva il tuo account in un posto da ricordare!", "view_seedphrase": "se si desidera visualizzare la seedphrase, fai clic sulla parola \"seedphrase\" in questo testo. Le seedphrasi vengono utilizzate per generare la chiave privata per il tuo account Qortal. Per la sicurezza per impostazione predefinita, le semina non vengono visualizzate se non specificamente scelte.", "wallet_secure": "mantieni il tuo file di wallet sicuro." }, From 9e41ca7d331241e7584eea14041c06c0502f75a2 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 21:05:32 +0200 Subject: [PATCH 492/717] Update --- src/i18n/locales/it/core.json | 14 +++++++------- src/i18n/locales/it/group.json | 12 ++++++------ src/i18n/locales/it/tutorial.json | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 061e3ed..b16a799 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -89,8 +89,8 @@ "show_poll": "mostra il sondaggio", "start_minting": "inizia a mellire", "start_typing": "inizia a digitare qui ...", - "trade_qort": "commercio qort", - "transfer_qort": "scambia Qort", + "trade_qort": "scambia qort", + "transfer_qort": "trasferisci Qort", "unpin": "rimuovi pin", "unpin_app": "rimuovi pin app", "unpin_from_dashboard": "rimuovi dalla dashboard", @@ -232,10 +232,10 @@ "fee_qort": "fee: {{ message }} QORT", "fetching_data": "recupero dei dati dell'app", "foreign_fee": "foreign fee: {{ message }}", - "get_qort_trade_portal": "ottieni Qort usando il portale commerciale Crosschain di Qortal", - "minimal_qort_balance": "having at least {{ quantity }} QORT in your balance (4 qort balance for chat, 1.25 for name, 0.75 for some transactions)", + "get_qort_trade_portal": "ottieni Qort con il portale di trade crosschain di Qortal", + "minimal_qort_balance": "avere almeno {{ quantity }} QORT a bilancio (4 qort per la chat, 1.25 per il nome, 0.75 per alcune transazioni)", "mentioned": "menzionato", - "message_with_image": "Questo messaggio ha già un'immagine", + "message_with_image": "questo messaggio ha già un'immagine", "most_recent_payment": "{{ count }} most recent payment", "name_available": "{{ name }} is available", "name_benefits": "vantaggi di un nome", @@ -301,8 +301,8 @@ "transfer_qort": "vuoi trasferire {{ amount }} QORT?" }, "status": { - "minting": "(Minting)", - "not_minting": "(non minante)", + "minting": "(minting)", + "not_minting": "(non minting)", "synchronized": "sincronizzato", "synchronizing": "sincronizzazione" }, diff --git a/src/i18n/locales/it/group.json b/src/i18n/locales/it/group.json index ad77095..78fb153 100644 --- a/src/i18n/locales/it/group.json +++ b/src/i18n/locales/it/group.json @@ -1,7 +1,7 @@ { "action": { "add_promotion": "aggiungi promozione", - "ban": "bandisci membro del gruppo", + "ban": "escludi membro del gruppo", "cancel_ban": "annulla divieto", "copy_private_key": "copia chiave privata", "create_group": "crea gruppo", @@ -47,7 +47,7 @@ "groups_admin": "gruppi in cui sei un amministratore", "management": "gestione del gruppo", "member_number": "numero di membri", - "messaging": "messaggistica", + "messaging": "chat", "name": "nome del gruppo", "open": "aperto (pubblico)", "private": "gruppo privato", @@ -60,18 +60,18 @@ "join_link": "unisciti al link di gruppo", "join_requests": "unisciti alle richieste", "last_message": "ultimo messaggio", - "last_message_date": "last message: {{date }}", - "latest_mails": "ultimi Q-Mails", + "last_message_date": "ultimo messaggio: {{date }}", + "latest_mails": "ultimi Q-Mail", "message": { "generic": { - "avatar_publish_fee": "publishing an Avatar requires {{ fee }}", + "avatar_publish_fee": "la pubblicazione di un Avatar richiede {{ fee }}", "avatar_registered_name": "È necessario un nome registrato per impostare un avatar", "admin_only": "verranno mostrati solo gruppi in cui sei un amministratore", "already_in_group": "sei già in questo gruppo!", "block_delay_minimum": "ritardo minimo del blocco per le approvazioni delle transazioni di gruppo", "block_delay_maximum": "ritardo massimo del blocco per le approvazioni delle transazioni di gruppo", "closed_group": "questo è un gruppo chiuso/privato, quindi dovrai attendere fino a quando un amministratore accetta la tua richiesta", - "descrypt_wallet": "wallet decrypting ...", + "descrypt_wallet": "decrittazione del wallet ...", "encryption_key": "la prima chiave di crittografia comune del gruppo è in procinto di creare. Si prega di attendere qualche minuto per essere recuperato dalla rete. Controllo ogni 2 minuti ...", "group_announcement": "annunci di gruppo", "group_approval_threshold": "soglia di approvazione del gruppo (numero / percentuale di amministratori che devono approvare una transazione)", diff --git a/src/i18n/locales/it/tutorial.json b/src/i18n/locales/it/tutorial.json index 7c35460..300c71a 100644 --- a/src/i18n/locales/it/tutorial.json +++ b/src/i18n/locales/it/tutorial.json @@ -14,7 +14,7 @@ "explore": "esplora", "general_chat": "chat generale", "getting_started": "come iniziare", - "register_name": "registra un nome", + "register_name": "registrare un nome", "see_apps": "vedi le app", "trade_qort": "scambia qort" } From 88d624c003d2e14a3e665e820db0abb76d5ed754 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 24 May 2025 22:24:14 +0300 Subject: [PATCH 493/717] fetch primary name --- src/background/background.ts | 11 +++++---- src/components/Group/Group.tsx | 6 ++--- src/components/Minting/Minting.tsx | 6 ++--- src/encryption/encryption.ts | 6 ++--- src/hooks/useQortalMessageListener.tsx | 2 ++ src/qdn/publish/publish.ts | 2 +- src/qortal/qortal-requests.ts | 33 +++++++++++++++++++++++++- 7 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/background/background.ts b/src/background/background.ts index 2a7b158..c95c7c7 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -805,9 +805,9 @@ export async function getNameInfo() { const wallet = await getSaveWallet(); const address = wallet.address0; const validApi = await getBaseApi(); - const response = await fetch(validApi + '/names/address/' + address); + const response = await fetch(validApi + '/names/primary/' + address); const nameData = await response.json(); - if (nameData?.length > 0) { + if (nameData?.name) { return nameData[0].name; } else { return ''; @@ -815,11 +815,12 @@ export async function getNameInfo() { } export async function getNameInfoForOthers(address) { + if (!address) return ''; const validApi = await getBaseApi(); - const response = await fetch(validApi + '/names/address/' + address); + const response = await fetch(validApi + '/names/primary/' + address); const nameData = await response.json(); - if (nameData?.length > 0) { - return nameData[0].name; + if (nameData?.name) { + return nameData?.name; } else { return ''; } diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index fa534f9..694d35d 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -265,11 +265,11 @@ export const getDataPublishesFunc = async (groupId, type) => { }; export async function getNameInfo(address: string) { - const response = await fetch(`${getBaseApiReact()}/names/address/` + address); + const response = await fetch(`${getBaseApiReact()}/names/primary/` + address); const nameData = await response.json(); - if (nameData?.length > 0) { - return nameData[0]?.name; + if (nameData?.name) { + return nameData?.name; } else { return ''; } diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index cddd67c..b812a66 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -85,14 +85,14 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { const getName = async (address) => { try { const response = await fetch( - `${getBaseApiReact()}/names/address/${address}` + `${getBaseApiReact()}/names/primary/${address}` ); const nameData = await response.json(); - if (nameData?.length > 0) { + if (nameData?.name) { setNames((prev) => { return { ...prev, - [address]: nameData[0].name, + [address]: nameData?.name, }; }); } else { diff --git a/src/encryption/encryption.ts b/src/encryption/encryption.ts index ab0a36d..2bf82f6 100644 --- a/src/encryption/encryption.ts +++ b/src/encryption/encryption.ts @@ -26,10 +26,10 @@ export async function getNameInfo() { const wallet = await getSaveWallet(); const address = wallet.address0; const validApi = await getBaseApi(); - const response = await fetch(validApi + '/names/address/' + address); + const response = await fetch(validApi + '/names/primary/' + address); const nameData = await response.json(); - if (nameData?.length > 0) { - return nameData[0].name; + if (nameData?.name) { + return nameData?.name; } else { return ''; } diff --git a/src/hooks/useQortalMessageListener.tsx b/src/hooks/useQortalMessageListener.tsx index 29549dc..d2a54c4 100644 --- a/src/hooks/useQortalMessageListener.tsx +++ b/src/hooks/useQortalMessageListener.tsx @@ -256,6 +256,7 @@ export const listOfAllQortalRequests = [ 'UPDATE_GROUP', 'UPDATE_NAME', 'VOTE_ON_POLL', + 'GET_PRIMARY_NAME', ]; export const UIQortalRequests = [ @@ -319,6 +320,7 @@ export const UIQortalRequests = [ 'UPDATE_GROUP', 'UPDATE_NAME', 'VOTE_ON_POLL', + 'GET_PRIMARY_NAME', ]; async function retrieveFileFromIndexedDB(fileId) { diff --git a/src/qdn/publish/publish.ts b/src/qdn/publish/publish.ts index 3bb3307..782ecdd 100644 --- a/src/qdn/publish/publish.ts +++ b/src/qdn/publish/publish.ts @@ -317,7 +317,7 @@ export const publishData = async ({ } const file = data; - const urlCheck = `/arbitrary/check-tmp-space?totalSize=${file.size}`; + const urlCheck = `/arbitrary/check/tmp?totalSize=${file.size}`; const checkEndpoint = await createEndpoint(urlCheck); const checkRes = await fetch(checkEndpoint); diff --git a/src/qortal/qortal-requests.ts b/src/qortal/qortal-requests.ts index 9681790..32a4186 100644 --- a/src/qortal/qortal-requests.ts +++ b/src/qortal/qortal-requests.ts @@ -1,4 +1,8 @@ -import { gateways, getApiKeyFromStorage } from '../background/background.ts'; +import { + gateways, + getApiKeyFromStorage, + getNameInfoForOthers, +} from '../background/background.ts'; import { listOfAllQortalRequests } from '../hooks/useQortalMessageListener.tsx'; import { addForeignServer, @@ -1932,6 +1936,33 @@ function setupMessageListenerQortalRequest() { break; } + case 'GET_PRIMARY_NAME': { + try { + const res = await getNameInfoForOthers(request.payload?.address); + const resData = res ? res : null; + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + payload: resData, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: request.action, + error: error.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } + break; + } + default: break; } From a9fe9254f3f11a97cf61fa2e20fb3f1dd7ed5acf Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 24 May 2025 21:59:00 +0200 Subject: [PATCH 494/717] Fix wrong I18N keys --- src/components/NotAuthenticated.tsx | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/components/NotAuthenticated.tsx b/src/components/NotAuthenticated.tsx index 0d2949c..506116c 100644 --- a/src/components/NotAuthenticated.tsx +++ b/src/components/NotAuthenticated.tsx @@ -360,7 +360,7 @@ export const NotAuthenticated = ({ }) .catch((error) => { console.error( - it('auth:message.error.set_apikey', { + t('auth:message.error.set_apikey', { postProcess: 'capitalizeFirstChar', }), error.message || @@ -399,7 +399,7 @@ export const NotAuthenticated = ({ }) .catch((error) => { console.error( - it('auth:message.error.set_apikey', { + t('auth:message.error.set_apikey', { postProcess: 'capitalizeFirstChar', }), error.message || @@ -683,7 +683,7 @@ export const NotAuthenticated = ({ }) .catch((error) => { console.error( - it('auth:message.error.set_apikey', { + t('auth:message.error.set_apikey', { postProcess: 'capitalizeFirstChar', }), error.message || @@ -772,7 +772,6 @@ export const NotAuthenticated = ({ fullWidth > - {' '} {t('auth:node.custom_many', { postProcess: 'capitalizeFirstChar' })} : @@ -837,7 +836,7 @@ export const NotAuthenticated = ({ }) .catch((error) => { console.error( - it('auth:message.error.set_apikey', { + t('auth:message.error.set_apikey', { postProcess: 'capitalizeFirstChar', }), error.message || @@ -873,6 +872,7 @@ export const NotAuthenticated = ({ > {node?.url}
    + { console.error( - it('auth:message.error.set_apikey', { + t('auth:message.error.set_apikey', { postProcess: 'capitalizeFirstChar', }), error.message || @@ -945,7 +945,7 @@ export const NotAuthenticated = ({ }} variant="contained" > - {t('core:remove', { + {t('core:action.remove', { postProcess: 'capitalizeFirstChar', })} @@ -955,6 +955,7 @@ export const NotAuthenticated = ({ })} )} + {mode === 'add-node' && ( + saveCustomNodes(customNodes)} autoFocus > - {t('core:save', { postProcess: 'capitalizeFirstChar' })} + {t('core:action.save', { + postProcess: 'capitalizeFirstChar', + })} )} @@ -1124,7 +1128,7 @@ export const NotAuthenticated = ({ }} autoFocus > - {t('core:save', { postProcess: 'capitalizeFirstChar' })} + {t('core:action.save', { postProcess: 'capitalizeFirstChar' })} @@ -474,6 +474,7 @@ export const ListOfGroupPromotions = () => { + { color: 'rgba(255, 255, 255, 0.2)', }} > - {t('group.message.generic.no_display', { + {t('group:message.generic.no_display', { postProcess: 'capitalizeFirstChar', })}
    @@ -927,7 +928,7 @@ export const ListOfGroupPromotions = () => { { const theme = useTheme(); diff --git a/src/components/RegisterName.tsx b/src/components/RegisterName.tsx index eb988ff..9bc5f5c 100644 --- a/src/components/RegisterName.tsx +++ b/src/components/RegisterName.tsx @@ -215,7 +215,11 @@ export const RegisterName = ({ aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" > - {'Register name'} + + {t('core:action.register_name', { + postProcess: 'capitalizeAll', + })} + + Date: Sat, 24 May 2025 23:43:56 +0200 Subject: [PATCH 497/717] Update --- src/i18n/locales/it/auth.json | 8 ++--- src/i18n/locales/it/core.json | 66 +++++++++++++++++----------------- src/i18n/locales/it/group.json | 6 ++-- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/i18n/locales/it/auth.json b/src/i18n/locales/it/auth.json index 78d8499..c1045e5 100644 --- a/src/i18n/locales/it/auth.json +++ b/src/i18n/locales/it/auth.json @@ -19,7 +19,7 @@ "fetch_names": "nomi di recupero", "copy_address": "indirizzo di copia", "create_account": "crea un account", - "create_qortal_account": "crea your Qortal account by clicking NEXT below.", + "create_qortal_account": "crea il tuo account Qortal cliccando NEXT sotto.", "choose_password": "scegli nuova password", "download_account": "scarica account", "enter_amount": "si prega di inserire un importo maggiore di 0", @@ -28,7 +28,7 @@ "export_seedphrase": "esporta seed phrase", "insert_name_address": "si prega di inserire un nome o un indirizzo", "publish_admin_secret_key": "pubblica la chiave segreta dell'amministratore", - "publish_group_secret_key": "pubblica Key Secret Group", + "publish_group_secret_key": "pubblica chiave segreta di gruppo", "reencrypt_key": "chiave di ri-crittografia", "return_to_list": "torna all'elenco", "setup_qortal_account": "imposta il tuo account Qortal", @@ -63,7 +63,7 @@ "find_secret_key": "impossibile trovare secretkey corretta", "incorrect_password": "password errata", "invalid_qortal_link": "collegamento Qortal non valido", - "invalid_secret_key": "la secretKey non è valida", + "invalid_secret_key": "la chiave segreta non è valida", "invalid_uint8": "l'uint8arraydata che hai inviato non è valido", "name_not_existing": "il nome non esiste", "name_not_registered": "nome non registrato", @@ -120,7 +120,7 @@ "new_users": "i nuovi utenti iniziano qui!", "safe_place": "salva il tuo account in un posto da ricordare!", "view_seedphrase": "se si desidera visualizzare la seed phrase, fai clic sulla parola \"seed phrase\" in questo testo. Le seed phrase vengono utilizzate per generare la chiave privata per il tuo account Qortal. Per la sicurezza per impostazione predefinita, le seed phrase non vengono visualizzate se non specificamente scelte.", - "wallet_secure": "mantieni il tuo file di wallet sicuro." + "wallet_secure": "mantieni al sicuro il tuo file di wallet." }, "wallet": { "password_confirmation": "conferma la password del wallet", diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 3b0b4f9..7cc0c1b 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -39,8 +39,8 @@ "enable_dev_mode": "abilita la modalità Dev", "enter_name": "immetti un nome", "export": "esporta", - "get_qort": "ottieni Qort", - "get_qort_trade": "ottieni Qort a Q-Trade", + "get_qort": "ottieni QORT", + "get_qort_trade": "ottieni Qort in Q-Trade", "hide": "nascondi", "hide_qr_code": "nascondi QR code", "import": "importare", @@ -83,11 +83,11 @@ "select_category": "seleziona categoria", "select_name_app": "seleziona nome/app", "send": "invia", - "send_qort": "i Qort", + "send_qort": "i QORT", "set_avatar": "imposta Avatar", "show": "mostra", "show_poll": "mostra il sondaggio", - "start_minting": "inizia a mellire", + "start_minting": "inizia a coniare", "start_typing": "inizia a digitare qui ...", "trade_qort": "scambia qort", "transfer_qort": "trasferisci QORT", @@ -114,7 +114,7 @@ "apps_official": "app ufficiali", "attachment": "allegato", "balance": "bilancia:", - "basic_tabs_example": "esempio di schede di base", + "basic_tabs_example": "esempio di schede base", "category": "categoria", "category_other": "categorie", "chat": "chat", @@ -166,9 +166,9 @@ "loading": { "announcements": "caricamento di annunci", "generic": "caricamento...", - "chat": "caricamento della chat ... per favore aspetta.", - "comments": "caricamento dei commenti ... per favore aspetta.", - "posts": "caricamento di post ... per favore aspetta." + "chat": "caricamento della chat. Attendere, per favore.", + "comments": "caricamento dei commenti. Attendere, per favore.", + "posts": "caricamento di post. Attendere, per favore." }, "member": "membro", "member_other": "membri", @@ -202,11 +202,11 @@ "missing_fields": "missing: {{ fields }}", "navigation_timeout": "timeout di navigazione", "network_generic": "errore di rete", - "password_not_matching": "i campi di password non corrispondono!", + "password_not_matching": "i campi della password non corrispondono!", "password_wrong": "impossibile autenticare. Password sbagliata", "publish_app": "impossibile pubblicare l'app", "publish_image": "impossibile pubblicare l'immagine", - "rate": "incapace di valutare", + "rate": "impossibile valutare", "rating_option": "impossibile trovare l'opzione di valutazione", "save_qdn": "impossibile salvare a QDN", "send_failed": "impossibile inviare", @@ -215,13 +215,13 @@ }, "generic": { "already_voted": "hai già votato.", - "avatar_size": "{{ size }} KB max. for GIFS", - "benefits_qort": "vantaggi di avere Qort", - "building": "edificio", - "building_app": "app di costruzione", - "created_by": "created by {{ owner }}", - "buy_order_request": "the Application
    {{hostname}}
    is requesting {{count}} buy order", - "buy_order_request_other": "the Application
    {{hostname}}
    is requesting {{count}} buy orders", + "avatar_size": "{{ size }} KB max. per GIFS", + "benefits_qort": "vantaggi di avere QORT", + "building": "creazione", + "building_app": "creazione app", + "created_by": "creato da {{ owner }}", + "buy_order_request": "l'applicazione
    {{hostname}}
    sta effettuando {{count}} ordine d'acquisto", + "buy_order_request_other": "l'applicazione
    {{hostname}}
    sta effettuando {{count}} ordini d'acquisto", "devmode_local_node": "si prega di utilizzare il tuo nodo locale per la modalità Dev! Logout e usa il nodo locale.", "downloading": "download", "downloading_decrypting_app": "download e decritting di app private.", @@ -229,21 +229,21 @@ "editing_message": "messaggio di modifica", "encrypted": "crittografato", "encrypted_not": "non crittografato", - "fee_qort": "fee: {{ message }} QORT", + "fee_qort": "commissione: {{ message }} QORT", "fetching_data": "recupero dei dati dell'app", - "foreign_fee": "foreign fee: {{ message }}", + "foreign_fee": "commissione esterna: {{ message }}", "get_qort_trade_portal": "ottieni Qort con il portale di trade crosschain di Qortal", "minimal_qort_balance": "avere almeno {{ quantity }} QORT a bilancio (4 qort per la chat, 1.25 per il nome, 0.75 per alcune transazioni)", "mentioned": "menzionato", "message_with_image": "questo messaggio ha già un'immagine", - "most_recent_payment": "{{ count }} most recent payment", - "name_available": "{{ name }} is available", + "most_recent_payment": "{{ count }} pagamenti più recenti", + "name_available": "{{ name }} è disponibile", "name_benefits": "vantaggi di un nome", "name_checking": "verifica se esiste già il nome", "name_preview": "hai bisogno di un nome per utilizzare l'anteprima", "name_publish": "hai bisogno di un nome Qortal per pubblicare", "name_rate": "hai bisogno di un nome da valutare.", - "name_registration": "your balance is {{ balance }} QORT. A name registration requires a {{ fee }} QORT fee", + "name_registration": "il tuo saldo è {{ balance }} QORT. La registrazione di un nome richiede una commissione di {{ fee }} QORT", "name_unavailable": "{{ name }} is unavailable", "no_data_image": "nessun dato per l'immagine", "no_description": "nessuna descrizione", @@ -251,7 +251,7 @@ "no_minting_details": "impossibile visualizzare i dettagli di minire sul gateway", "no_notifications": "nessuna nuova notifica", "no_payments": "nessun pagamento", - "no_pinned_changes": "attualmente non hai modifiche alle tue app appuntate", + "no_pinned_changes": "attualmente non hai modifiche alle tue app bloccate", "no_results": "nessun risultato", "one_app_per_name": "nota: attualmente, sono consentiti solo un'app e un sito Web per nome.", "opened": "aperto", @@ -262,10 +262,10 @@ "people_reaction": "persone che hanno reagito con {{ reaction }}", "processing_transaction": "elaborazione della transazione, per favore aspetta ...", "publish_data": "pubblica dati su Qortal: qualsiasi cosa, dalle app ai video. Completamente decentralizzato!", - "publishing": "publishing ... per favore aspetta.", + "publishing": "publishing. Attendere, per favore.", "qdn": "usa il salvataggio QDN", "rating": "rating for {{ service }} {{ name }}", - "register_name": "hai bisogno di un nome Qortal registrato per salvare le app appuntate a QDN.", + "register_name": "hai bisogno di un nome Qortal registrato per salvare le app bloccate a QDN.", "replied_to": "replied to {{ person }}", "revert_default": "ritorna a predefinito", "revert_qdn": "ritorna a QDN", @@ -278,7 +278,7 @@ "settings": "si utilizza il modo di esportazione/importazione per salvare le impostazioni.", "space_for_admins": "mi dispiace, questo spazio è solo per gli amministratori.", "unread_messages": "messaggi non letto di seguito", - "unsaved_changes": "hai cambiato modifiche alle app appuntate. Salvali su QDN.", + "unsaved_changes": "hai cambiato modifiche alle app bloccate. Salvali su QDN.", "updating": "aggiornamento" }, "message": "messaggio", @@ -293,10 +293,10 @@ "publish_app": "vorresti pubblicare questa app?", "publish_avatar": "vorresti pubblicare un avatar?", "publish_qdn": "vorresti pubblicare le tue impostazioni su QDN (crittografato)?", - "overwrite_changes": "l'app non è stata in grado di scaricare le app appuntate a QDN esistenti. Vorresti sovrascrivere quei cambiamenti?", + "overwrite_changes": "l'app non è stata in grado di scaricare le app bloccate a QDN esistenti. Vorresti sovrascrivere quei cambiamenti?", "rate_app": "vorresti dare il voto {{ rate }} a quest'app?. Questo creerà una transazione POLL.", "register_name": "vorresti registrare questo nome?", - "reset_pinned": "non ti piacciono le tue attuali modifiche locali? Vorresti ripristinare le app appuntate predefinite?", + "reset_pinned": "non ti piacciono le tue attuali modifiche locali? Vorresti ripristinare le app bloccate predefinite?", "reset_qdn": "non ti piacciono le tue attuali modifiche locali? Vorresti ripristinare le app QDN salvate?", "transfer_qort": "vuoi trasferire {{ amount }} QORT?" }, @@ -308,18 +308,18 @@ }, "success": { "order_submitted": "il tuo ordine di acquisto è stato inviato", - "published": "pubblicato con successo. Si prega di attendere un paio di minuti affinché la rete propogerasse le modifiche.", + "published": "pubblicato con successo. Si prega di attendere un paio di minuti affinché la rete propaghi le modifiche.", "published_qdn": "pubblicato con successo su QDN", - "rated_app": "valutato con successo. Si prega di attendere un paio di minuti affinché la rete propogerasse le modifiche.", + "rated_app": "valutato con successo. Si prega di attendere un paio di minuti affinché la rete propaghi le modifiche.", "request_read": "ho letto questa richiesta", "transfer": "il trasferimento è stato di successo!", - "voted": "votato con successo. Si prega di attendere un paio di minuti affinché la rete propogerasse le modifiche." + "voted": "votato con successo. Si prega di attendere un paio di minuti affinché la rete propaghi le modifiche." } }, "minting_status": "stato di minting", "name": "nome", "name_app": "nome/app", - "new_post_in": "new post in {{ title }}", + "new_post_in": "nuovo post in {{ title }}", "none": "nessuno", "note": "nota", "option": "opzione", @@ -380,7 +380,7 @@ "vote_other": "{{ count }} votes", "zip": "zip", "wallet": { - "litecoin": "wallet litecoin", + "litecoin": "wallet Litecoin", "qortal": "wallet Qortal", "wallet": "wallet", "wallet_other": "wallet" diff --git a/src/i18n/locales/it/group.json b/src/i18n/locales/it/group.json index cae8cbb..786ed18 100644 --- a/src/i18n/locales/it/group.json +++ b/src/i18n/locales/it/group.json @@ -14,7 +14,7 @@ "invite_member": "invita membro", "leave_group": "lascia il gruppo", "load_members": "carica i membri con i nomi", - "make_admin": "fare un amministratore", + "make_admin": "rendere amministratore", "manage_members": "gestisci i membri", "promote_group": "promuovi il tuo gruppo ai non membri", "publish_announcement": "pubblica annuncio", @@ -24,7 +24,7 @@ "remove_minting_account": "rimuovi l'account di minting", "return_to_thread": "torna ai thread", "scroll_bottom": "scorri sul fondo", - "scroll_unread_messages": "scorri verso i messaggi non letto", + "scroll_unread_messages": "scendi ai messaggi non letti", "select_group": "seleziona un gruppo", "visit_q_mintership": "visita Q-Mintership" }, @@ -90,7 +90,7 @@ "minter_group": "al momento non fai parte del gruppo Minter", "mintership_app": "visita l'app Q-Mintership per chiedere di diventare un minter", "minting_account": "account di minting:", - "minting_keys_per_node": "sono ammessi solo 2 chiavi di minting per nodo. Rimuovi uno se si desidera menta con questo account.", + "minting_keys_per_node": "sono ammessi solo 2 chiavi di minting per nodo. Rimuovine una se si desidera fare minting con questo account.", "minting_keys_per_node_different": "sono ammessi solo 2 chiavi di minting per nodo. Rimuovi uno se desideri aggiungere un account diverso.", "next_level": "blocchi mancanti al livello successivo:", "node_minting": "questo nodo sta coniando:", From 84f7a31f226b541fec16b4dc2a469c921d7e5778 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 25 May 2025 00:15:06 +0200 Subject: [PATCH 498/717] Add new keys --- src/App.tsx | 10 +++- src/common/ImageUploader.tsx | 88 ++++++++++++++++++----------------- src/common/customloader.css | 18 +++---- src/i18n/locales/en/core.json | 2 + src/i18n/locales/es/core.json | 12 +++-- src/i18n/locales/fr/core.json | 2 + src/i18n/locales/it/core.json | 4 +- src/i18n/locales/ja/core.json | 4 +- src/i18n/locales/ru/core.json | 4 +- src/i18n/locales/zh/core.json | 4 +- 10 files changed, 87 insertions(+), 61 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 0e82a0e..5c9e8a5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3338,13 +3338,20 @@ function App() { }} > - {message.paymentFee ? 'Payment' : 'Publish'} + {message.paymentFee + ? t('core:payment', { + postProcess: 'capitalizeFirstChar', + }) + : t('core:publish', { + postProcess: 'capitalizeFirstChar', + })} {message.message} + {message?.paymentFee && ( {t('core:fee.payment', { @@ -3353,6 +3360,7 @@ function App() { : {message.paymentFee} )} + {message?.publishFee && ( {t('core:fee.publish', { diff --git a/src/common/ImageUploader.tsx b/src/common/ImageUploader.tsx index f21c17f..e820a53 100644 --- a/src/common/ImageUploader.tsx +++ b/src/common/ImageUploader.tsx @@ -1,44 +1,48 @@ -import React, { useCallback } from 'react' -import { Box } from '@mui/material' -import { useDropzone, DropzoneRootProps, DropzoneInputProps } from 'react-dropzone' -import Compressor from 'compressorjs' +import React, { useCallback } from 'react'; +import { Box } from '@mui/material'; +import { + useDropzone, + DropzoneRootProps, + DropzoneInputProps, +} from 'react-dropzone'; +import Compressor from 'compressorjs'; const toBase64 = (file: File): Promise => new Promise((resolve, reject) => { - const reader = new FileReader() - reader.readAsDataURL(file) - reader.onload = () => resolve(reader.result) + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = () => resolve(reader.result); reader.onerror = (error) => { - reject(error) - } - }) + reject(error); + }; + }); // TODO toBase64 seems unused. Remove? interface ImageUploaderProps { - children: React.ReactNode - onPick: (file: File) => void + children: React.ReactNode; + onPick: (file: File) => void; } const ImageUploader: React.FC = ({ children, onPick }) => { const onDrop = useCallback( async (acceptedFiles: File[]) => { if (acceptedFiles.length > 1) { - return + return; } - const image = acceptedFiles[0] - let compressedFile: File | undefined + const image = acceptedFiles[0]; + let compressedFile: File | undefined; try { // Check if the file is a GIF if (image.type === 'image/gif') { // Check if the GIF is larger than 500 KB if (image.size > 500 * 1024) { - console.error('GIF file size exceeds 500KB limit.') - return + console.error('GIF file size exceeds 500KB limit.'); + return; } // No compression for GIF, pass the original file - compressedFile = image + compressedFile = image; } else { // For non-GIF files, compress them await new Promise((resolve) => { @@ -48,55 +52,55 @@ const ImageUploader: React.FC = ({ children, onPick }) => { mimeType: 'image/webp', success(result) { const file = new File([result], image.name, { - type: 'image/webp' - }) - compressedFile = file - resolve() + type: 'image/webp', + }); + compressedFile = file; + resolve(); }, error(err) { - console.error('Compression error:', err) - resolve() // Proceed even if there's an error - } - }) - }) + console.error('Compression error:', err); + resolve(); // Proceed even if there's an error + }, + }); + }); } - if (!compressedFile) return + if (!compressedFile) return; - onPick(compressedFile) + onPick(compressedFile); } catch (error) { - console.error('File processing error:', error) + console.error('File processing error:', error); } }, [onPick] - ) + ); const { getRootProps, getInputProps, - isDragActive + isDragActive, }: { - getRootProps: () => DropzoneRootProps - getInputProps: () => DropzoneInputProps - isDragActive: boolean + getRootProps: () => DropzoneRootProps; + getInputProps: () => DropzoneInputProps; + isDragActive: boolean; } = useDropzone({ onDrop, accept: { - 'image/*': [] - } - }) + 'image/*': [], + }, + }); return ( {children} - ) -} + ); +}; -export default ImageUploader +export default ImageUploader; diff --git a/src/common/customloader.css b/src/common/customloader.css index c16d21d..d35f2b4 100644 --- a/src/common/customloader.css +++ b/src/common/customloader.css @@ -7,34 +7,34 @@ } .lds-ellipsis { display: inline-block; + height: 80px; position: relative; width: 80px; - height: 80px; } .lds-ellipsis div { + animation-timing-function: cubic-bezier(0, 1, 1, 0); + background: currentColor; + border-radius: 50%; + height: 13.33333px; position: absolute; top: 33.33333px; width: 13.33333px; - height: 13.33333px; - border-radius: 50%; - background: currentColor; - animation-timing-function: cubic-bezier(0, 1, 1, 0); } .lds-ellipsis div:nth-child(1) { - left: 8px; animation: lds-ellipsis1 0.6s infinite; + left: 8px; } .lds-ellipsis div:nth-child(2) { - left: 8px; animation: lds-ellipsis2 0.6s infinite; + left: 8px; } .lds-ellipsis div:nth-child(3) { - left: 32px; animation: lds-ellipsis2 0.6s infinite; + left: 32px; } .lds-ellipsis div:nth-child(4) { - left: 56px; animation: lds-ellipsis3 0.6s infinite; + left: 56px; } @keyframes lds-ellipsis1 { diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index c742aca..ffcee9a 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -331,9 +331,11 @@ "previous": "previous" }, "payment_notification": "payment notification", + "payment": "payment", "poll_embed": "poll embed", "port": "port", "price": "price", + "publish": "publish", "q_apps": { "about": "about this Q-App", "q_mail": "q-mail", diff --git a/src/i18n/locales/es/core.json b/src/i18n/locales/es/core.json index 375a172..d6310f0 100644 --- a/src/i18n/locales/es/core.json +++ b/src/i18n/locales/es/core.json @@ -243,13 +243,13 @@ "name_registration": "your balance is {{ balance }} QORT. A name registration requires a {{ fee }} QORT fee", "name_unavailable": "{{ name }} is unavailable", "no_data_image": "No hay datos para la imagen", - "no_description": "Sin descripción", - "no_messages": "Sin mensajes", + "no_description": "sin descripción", + "no_messages": "sin mensajes", "no_minting_details": "No se puede ver los detalles de acuñado en la puerta de enlace", "no_notifications": "No hay nuevas notificaciones", - "no_payments": "Sin pagos", + "no_payments": "sin pagos", "no_pinned_changes": "Actualmente no tiene ningún cambio en sus aplicaciones fijadas", - "no_results": "Sin resultados", + "no_results": "sin resultados", "one_app_per_name": "Nota: Actualmente, solo se permite una aplicación y un sitio web por nombre.", "opened": "abierto", "overwrite_qdn": "sobrescribir a QDN", @@ -328,9 +328,11 @@ "previous": "anterior" }, "payment_notification": "notificación de pago", + "payment": "pago", "poll_embed": "encuesta", "port": "puerto", "price": "precio", + "publish": "publicación", "q_apps": { "about": "Sobre este Q-App", "q_mail": "QAIL", @@ -384,4 +386,4 @@ }, "website": "sitio web", "welcome": "bienvenido" -} \ No newline at end of file +} diff --git a/src/i18n/locales/fr/core.json b/src/i18n/locales/fr/core.json index 670184c..cd74696 100644 --- a/src/i18n/locales/fr/core.json +++ b/src/i18n/locales/fr/core.json @@ -329,9 +329,11 @@ "previous": "précédent" }, "payment_notification": "Notification de paiement", + "payment": "paiement", "poll_embed": "sondage", "port": "port", "price": "prix", + "publish": "publication", "q_apps": { "about": "À propos de ce Q-App", "q_mail": "Q-mail", diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 7cc0c1b..79d1e0e 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -143,7 +143,7 @@ "downloading_qdn": "download da QDN", "fee": { "payment": "commissione di pagamento", - "publish": "commissione per pubblicare" + "publish": "commissione di pubblicazione" }, "for": "per", "general": "generale", @@ -330,10 +330,12 @@ "next": "prossimo", "previous": "precedente" }, + "payment": "pagamento", "payment_notification": "notifica di pagamento", "poll_embed": "sondaggio incorporato", "port": "porta", "price": "prezzo", + "publish": "pubblicazione", "q_apps": { "about": "su questo Q-app", "q_mail": "Q-mail", diff --git a/src/i18n/locales/ja/core.json b/src/i18n/locales/ja/core.json index 95f7f8d..e9ea702 100644 --- a/src/i18n/locales/ja/core.json +++ b/src/i18n/locales/ja/core.json @@ -328,9 +328,11 @@ "previous": "前の" }, "payment_notification": "支払い通知", + "payment": "お支払い", "poll_embed": "投票埋め込み", "port": "ポート", "price": "価格", + "publish": "出版物", "q_apps": { "about": "このq-appについて", "q_mail": "Qメール", @@ -384,4 +386,4 @@ }, "website": "Webサイト", "welcome": "いらっしゃいませ" -} \ No newline at end of file +} diff --git a/src/i18n/locales/ru/core.json b/src/i18n/locales/ru/core.json index 86e3c96..6024759 100644 --- a/src/i18n/locales/ru/core.json +++ b/src/i18n/locales/ru/core.json @@ -328,9 +328,11 @@ "previous": "предыдущий" }, "payment_notification": "уведомление о платеже", + "payment": "плата", "poll_embed": "Опрос встроен", "port": "порт", "price": "цена", + "publish": "публикация", "q_apps": { "about": "об этом Q-App", "q_mail": "Q-Mail", @@ -384,4 +386,4 @@ }, "website": "веб -сайт", "welcome": "добро пожаловать" -} \ No newline at end of file +} diff --git a/src/i18n/locales/zh/core.json b/src/i18n/locales/zh/core.json index a306263..0d6f46e 100644 --- a/src/i18n/locales/zh/core.json +++ b/src/i18n/locales/zh/core.json @@ -328,6 +328,8 @@ "previous": "以前的" }, "payment_notification": "付款通知", + "payment": "付款", + "publish": "出版刊物", "poll_embed": "嵌入民意测验", "port": "港口", "price": "价格", @@ -384,4 +386,4 @@ }, "website": "网站", "welcome": "欢迎" -} \ No newline at end of file +} From a00d0aaadde6947c7affe341c6e29be988821ac3 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 25 May 2025 00:25:45 +0200 Subject: [PATCH 499/717] Refine --- src/i18n/locales/zh/core.json | 86 +++++++++++++++++------------------ 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/src/i18n/locales/zh/core.json b/src/i18n/locales/zh/core.json index 0d6f46e..b5f2a6e 100644 --- a/src/i18n/locales/zh/core.json +++ b/src/i18n/locales/zh/core.json @@ -27,7 +27,7 @@ "copy_link": "复制链接", "create_apps": "创建应用程序", "create_file": "创建文件", - "create_transaction": "在Qortal区块链上创建交易", + "create_transaction": "在QORTal区块链上创建交易", "create_thread": "创建线程", "decline": "衰退", "decrypt": "解密", @@ -39,8 +39,8 @@ "enable_dev_mode": "启用开发模式", "enter_name": "输入名称", "export": "出口", - "get_qort": "获取Qort", - "get_qort_trade": "在Q-trade获取Qort", + "get_qort": "获取QORT", + "get_qort_trade": "在Q-trade获取QORT", "hide": "隐藏", "import": "进口", "import_theme": "导入主题", @@ -81,16 +81,16 @@ "select_category": "选择类别", "select_name_app": "选择名称/应用", "send": "发送", - "send_qort": "发送Qort", + "send_qort": "发送QORT", "set_avatar": "设置化身", "show": "展示", "show_poll": "显示民意调查", "start_minting": "开始铸造", "start_typing": "开始在这里输入...", - "trade_qort": "贸易Qort", - "transfer_qort": "转移Qort", - "unpin": "Uncin", - "unpin_app": "Unpin App", + "trade_qort": "贸易QORT", + "transfer_qort": "转移QORT", + "unpin": "取消固定", + "unpin_app": "取消固定应用程序", "unpin_from_dashboard": "从仪表板上锁定", "update": "更新", "update_app": "更新您的应用程序", @@ -123,7 +123,7 @@ "peers": "连接的同行", "version": "核心版本" }, - "current_language": "current language: {{ language }}", + "current_language": "当前语言:{{ language }}", "dev": "开发", "dev_mode": "开发模式", "domain": "领域", @@ -169,7 +169,7 @@ }, "member": "成员", "member_other": "成员", - "message_us": "如果您需要4个Qort开始聊天而无需任何限制", + "message_us": "如果您需要4个QORT开始聊天而无需任何限制", "message": { "error": { "address_not_found": "找不到您的地址", @@ -181,7 +181,7 @@ "encrypt_app": "无法加密应用程序。应用未发布'", "fetch_app": "无法获取应用", "fetch_publish": "无法获取发布", - "file_too_large": "file {{ filename }} is too large. Max size allowed is {{ size }} MB.", + "file_too_large": "文件{{ filename }}太大。 允许的最大大小为{{size}}MB。", "generic": "发生错误", "initiate_download": "无法启动下载", "invalid_amount": "无效的金额", @@ -193,10 +193,10 @@ "invalid_theme_format": "无效的主题格式", "invalid_zip": "无效拉链", "message_loading": "错误加载消息。", - "message_size": "your message size is of {{ size }} bytes out of a maximum of {{ maximum }}", + "message_size": "您的消息大小为{{size}}字节,超出{{maximum}}的最大值", "minting_account_add": "无法添加薄荷帐户", "minting_account_remove": "无法删除铸造帐户", - "missing_fields": "missing: {{ fields }}", + "missing_fields": "失踪:{{ fields }}", "navigation_timeout": "导航超时", "network_generic": "网络错误", "password_not_matching": "密码字段不匹配!", @@ -212,13 +212,13 @@ }, "generic": { "already_voted": "您已经投票了。", - "avatar_size": "{{ size }} KB max. for GIFS", - "benefits_qort": "Qort的好处", + "avatar_size": "{大小}KB最大。 对于GIF", + "benefits_qort": "QORT的好处", "building": "建筑", "building_app": "建筑应用", "created_by": "created by {{ owner }}", - "buy_order_request": "the Application
    {{hostname}}
    is requesting {{count}} buy order", - "buy_order_request_other": "the Application
    {{hostname}}
    is requesting {{count}} buy orders", + "buy_order_request": "应用程序
    {{hostname}}
    正在请求{{count}}购买订单", + "buy_order_request_other": "应用程序
    {{hostname}}
    正在请求{{count}}购买订单", "devmode_local_node": "请使用您的本地节点进行开发模式!注销并使用本地节点。", "downloading": "下载", "downloading_decrypting_app": "下载和解密私人应用程序。", @@ -226,22 +226,22 @@ "editing_message": "编辑消息", "encrypted": "加密", "encrypted_not": "没有加密", - "fee_qort": "fee: {{ message }} QORT", + "fee_qort": "费用:{讯息}QORT", "fetching_data": "获取应用程序数据", - "foreign_fee": "foreign fee: {{ message }}", - "get_qort_trade_portal": "使用Qortal的交叉链贸易门户网站获取Qort", - "minimal_qort_balance": "having at least {{ quantity }} QORT in your balance (4 qort balance for chat, 1.25 for name, 0.75 for some transactions)", + "foreign_fee": "国外费用:{{ message }}", + "get_qort_trade_portal": "使用QORTal的交叉链贸易门户网站获取QORT", + "minimal_qort_balance": "在您的余额中至少有{{quantity}}字(聊天4个QORT余额,姓名1.25,某些交易0.75)", "mentioned": "提及", "message_with_image": "此消息已经有一个图像", - "most_recent_payment": "{{ count }} most recent payment", - "name_available": "{{ name }} is available", + "most_recent_payment": "{{ count }}最近一次付款", + "name_available": "{{ name }}可用", "name_benefits": "名称的好处", "name_checking": "检查名称是否已经存在", "name_preview": "您需要一个使用预览的名称", - "name_publish": "您需要一个Qortal名称才能发布", + "name_publish": "您需要一个QORTal名称才能发布", "name_rate": "您需要一个名称来评估。", - "name_registration": "your balance is {{ balance }} QORT. A name registration requires a {{ fee }} QORT fee", - "name_unavailable": "{{ name }} is unavailable", + "name_registration": "您的余额值{{balance}}。 注册名称需要{{fee}}QORT费用", + "name_unavailable": "{{ name }}不可用", "no_data_image": "没有图像数据", "no_description": "没有描述", "no_messages": "没有消息", @@ -255,14 +255,14 @@ "overwrite_qdn": "覆盖为QDN", "password_confirm": "请确认密码", "password_enter": "请输入密码", - "payment_request": "the Application
    {{hostname}}
    is requesting a payment", + "payment_request": "应用程序
    {{hostname}}
    正在请求付款", "people_reaction": "people who reacted with {{ reaction }}", "processing_transaction": "正在处理交易,请等待...", - "publish_data": "将数据发布到Qortal:从应用到视频的任何内容。完全分散!", + "publish_data": "将数据发布到QORTal:从应用到视频的任何内容。完全分散!", "publishing": "出版...请等待。", "qdn": "使用QDN保存", "rating": "rating for {{ service }} {{ name }}", - "register_name": "您需要一个注册的Qortal名称来将固定的应用程序保存到QDN。", + "register_name": "您需要一个注册的QORTal名称来将固定的应用程序保存到QDN。", "replied_to": "replied to {{ person }}", "revert_default": "还原为默认值", "revert_qdn": "还原为QDN", @@ -285,17 +285,17 @@ "logout": "您确定要注销吗?", "new_user": "您是新用户吗?", "delete_chat_image": "您想删除以前的聊天图片吗?", - "perform_transaction": "would you like to perform a {{action}} transaction?", + "perform_transaction": "您想执行{{action}}事务吗?", "provide_thread": "请提供线程标题", "publish_app": "您想发布此应用吗?", "publish_avatar": "您想发布一个化身吗?", "publish_qdn": "您想将您的设置发布到QDN(加密)吗?", "overwrite_changes": "该应用程序无法下载您现有的QDN固定固定应用程序。您想覆盖这些更改吗?", - "rate_app": "would you like to rate this app a rating of {{ rate }}?. It will create a POLL tx.", + "rate_app": "你想评价这个应用程序的评级{{rate}}?. 它将创建一个POLL事务。", "register_name": "您想注册这个名字吗?", "reset_pinned": "不喜欢您当前的本地更改吗?您想重置默认的固定应用吗?", "reset_qdn": "不喜欢您当前的本地更改吗?您想重置保存的QDN固定应用吗?", - "transfer_qort": "would you like to transfer {{ amount }} QORT" + "transfer_qort": "你想转{{ amount }}个QORT吗?" }, "status": { "minting": "(铸造)", @@ -316,7 +316,7 @@ "minting_status": "铸造状态", "name": "姓名", "name_app": "名称/应用", - "new_post_in": "new post in {{ title }}", + "new_post_in": "新职位 {{ title }}", "none": "没有任何", "note": "笔记", "option": "选项", @@ -337,8 +337,8 @@ "about": "关于这个Q-App", "q_mail": "Q邮件", "q_manager": "Q-Manager", - "q_sandbox": "q-sandbox", - "q_wallets": "Q-WALLETS" + "q_sandbox": "Q-Sandbox", + "q_wallets": "Q-Wallet" }, "receiver": "接收者", "sender": "发件人", @@ -362,12 +362,12 @@ "thread_other": "线程", "thread_title": "线程标题", "time": { - "day_one": "{{count}} day", - "day_other": "{{count}} days", - "hour_one": "{{count}} hour", - "hour_other": "{{count}} hours", - "minute_one": "{{count}} minute", - "minute_other": "{{count}} minutes", + "day_one": "{{count}} 日", + "day_other": "{{count}} 天数", + "hour_one": "{{count}} 时间", + "hour_other": "{{count}} 工作时数", + "minute_one": "{{count}} 分钟", + "minute_other": "{{count}} 分钟", "time": "时间" }, "title": "标题", @@ -376,11 +376,11 @@ "url": "URL", "user_lookup": "用户查找", "vote": "投票", - "vote_other": "{{ count }} votes", + "vote_other": "{{ count }} 投票", "zip": "拉链", "wallet": { "litecoin": "莱特币钱包", - "qortal": "Qortal钱包", + "qortal": "QORTal钱包", "wallet": "钱包", "wallet_other": "钱包" }, From 56b09c4294f95d33ca96f8953e51fb73020615ed Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 25 May 2025 01:14:24 +0200 Subject: [PATCH 500/717] Add new key --- src/App.tsx | 55 ++++++++++++++++++++++++++--- src/components/Apps/AppViewer.tsx | 10 ++++-- src/components/Chat/GroupAvatar.tsx | 26 +++++++++++++- src/components/Group/Group.tsx | 12 +++++-- src/components/MainAvatar.tsx | 26 +++++++++++++- src/hooks/useModal.tsx | 1 - src/i18n/locales/de/tutorial.json | 4 +-- src/i18n/locales/en/auth.json | 10 +++--- src/i18n/locales/en/tutorial.json | 2 +- src/i18n/locales/es/auth.json | 1 + src/i18n/locales/fr/auth.json | 1 + src/i18n/locales/fr/tutorial.json | 2 +- src/i18n/locales/it/auth.json | 4 ++- src/i18n/locales/it/tutorial.json | 2 +- src/i18n/locales/ja/auth.json | 10 +++--- src/i18n/locales/ja/tutorial.json | 4 +-- src/i18n/locales/ru/auth.json | 10 +++--- src/i18n/locales/ru/tutorial.json | 2 +- src/i18n/locales/zh/auth.json | 4 ++- src/i18n/locales/zh/tutorial.json | 4 +-- 20 files changed, 154 insertions(+), 36 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 5c9e8a5..9c5c0ab 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3422,8 +3422,15 @@ function App() { aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" > - - {'Important Info'} + + {t('tutorial:important_info', { + postProcess: 'capitalizeAll', + })} @@ -3448,18 +3455,45 @@ function App() { aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" > - + {t('core:action.logout', { postProcess: 'capitalizeAll' })} - + {messageUnsavedChanges.message} - + )} {showScrollDownButton && !showScrollButton && ( From b742524b7e0f8f1c8d38ad9bac47902ea5dcb0df Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 26 May 2025 22:11:20 +0200 Subject: [PATCH 521/717] Set px --- src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 026209a..5138080 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3095,7 +3095,7 @@ function App() { } /> - + {t('auth:wallet.password_confirmation', { From 8ad0c07cca9f175ff6f8857e4f75f6afefa8a84f Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 26 May 2025 22:27:27 +0200 Subject: [PATCH 522/717] Refactor translation --- src/i18n/locales/it/group.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/locales/it/group.json b/src/i18n/locales/it/group.json index 12f7829..fe345c4 100644 --- a/src/i18n/locales/it/group.json +++ b/src/i18n/locales/it/group.json @@ -23,7 +23,7 @@ "remove_admin": "rimuovi da amministratore", "remove_minting_account": "rimuovi l'account di minting", "return_to_thread": "torna ai thread", - "scroll_bottom": "scorri sul fondo", + "scroll_bottom": "scorri in fondo", "scroll_unread_messages": "scendi ai messaggi non letti", "select_group": "seleziona un gruppo", "visit_q_mintership": "visita Q-Mintership" From b097a4ae3ba41c7af76fe0ac1abd83f665983254 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 26 May 2025 22:27:36 +0200 Subject: [PATCH 523/717] Sort --- src/components/Apps/AppInfo.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index 180dac4..753c737 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -127,12 +127,13 @@ export const AppInfo = ({ app, myName }) => { + Date: Mon, 26 May 2025 22:28:24 +0200 Subject: [PATCH 524/717] Set cbackground for hat message editing --- src/components/Chat/ChatGroup.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 3d2f067..f0cfaae 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -1185,7 +1185,7 @@ export const ChatGroup = ({ {(!!secretKey || isPrivate === false) && (
    Date: Mon, 26 May 2025 22:28:34 +0200 Subject: [PATCH 525/717] Increase font --- src/components/Chat/ChatOptions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Chat/ChatOptions.tsx b/src/components/Chat/ChatOptions.tsx index 09a2914..59124a0 100644 --- a/src/components/Chat/ChatOptions.tsx +++ b/src/components/Chat/ChatOptions.tsx @@ -280,7 +280,7 @@ export const ChatOptions = ({ {mentionList?.length === 0 && ( Date: Mon, 26 May 2025 23:26:32 +0200 Subject: [PATCH 526/717] Theme and translations --- src/components/Apps/Apps-styles.tsx | 13 ++++--------- src/components/Apps/AppsHomeDesktop.tsx | 3 ++- src/components/Apps/SortablePinnedApps.tsx | 17 +++++++++++++++-- src/components/Apps/TabComponent.tsx | 2 +- src/components/Drawer/DrawerUserLookup.tsx | 4 ++++ src/components/UserLookup.tsx/UserLookup.tsx | 3 +++ src/i18n/locales/de/core.json | 6 ++++-- src/i18n/locales/en/core.json | 2 ++ src/i18n/locales/es/core.json | 2 ++ src/i18n/locales/fr/core.json | 2 ++ src/i18n/locales/it/core.json | 6 ++++-- src/i18n/locales/it/tutorial.json | 4 ++-- src/i18n/locales/ja/core.json | 2 ++ src/i18n/locales/ru/core.json | 2 ++ src/i18n/locales/zh/core.json | 2 ++ 15 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/components/Apps/Apps-styles.tsx b/src/components/Apps/Apps-styles.tsx index 549282b..55ed470 100644 --- a/src/components/Apps/Apps-styles.tsx +++ b/src/components/Apps/Apps-styles.tsx @@ -3,25 +3,20 @@ import { styled } from '@mui/system'; export const AppsParent = styled(Box)(({ theme }) => ({ alignItems: 'center', + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, display: 'flex', flexDirection: 'column', height: '100%', + msOverflowStyle: 'none', // Hides scrollbar in IE and Edge overflow: 'auto', + scrollbarWidth: 'none', // Hides the scrollbar in Firefox width: '100%', // For WebKit-based browsers (Chrome, Safari, etc.) '::-webkit-scrollbar': { width: '0px', // Set the width to 0 to hide the scrollbar height: '0px', // Set the height to 0 for horizontal scrollbar }, - - // For Firefox - scrollbarWidth: 'none', // Hides the scrollbar in Firefox - - // Optional for better cross-browser consistency - msOverflowStyle: 'none', // Hides scrollbar in IE and Edge - - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, })); export const AppsContainer = styled(Box)(({ theme }) => ({ diff --git a/src/components/Apps/AppsHomeDesktop.tsx b/src/components/Apps/AppsHomeDesktop.tsx index 6a7d923..4bb7fa1 100644 --- a/src/components/Apps/AppsHomeDesktop.tsx +++ b/src/components/Apps/AppsHomeDesktop.tsx @@ -97,6 +97,8 @@ export const AppsHomeDesktop = ({ placeholder="qortal://" sx={{ width: '100%', + backgroundColor: theme.palette.background.surface, + borderRadius: '7px', color: theme.palette.text.primary, '& .MuiInput-input::placeholder': { color: theme.palette.text.secondary, @@ -110,7 +112,6 @@ export const AppsHomeDesktop = ({ '&:focus': { outline: 'none', }, - // Add any additional styles for the input here }} onKeyDown={(e) => { if (e.key === 'Enter' && qortalUrl) { diff --git a/src/components/Apps/SortablePinnedApps.tsx b/src/components/Apps/SortablePinnedApps.tsx index aef692d..edb4fae 100644 --- a/src/components/Apps/SortablePinnedApps.tsx +++ b/src/components/Apps/SortablePinnedApps.tsx @@ -27,6 +27,7 @@ import { ContextMenuPinnedApps } from '../ContextMenuPinnedApps'; import LockIcon from '@mui/icons-material/Lock'; import { useHandlePrivateApps } from '../../hooks/useHandlePrivateApps'; import { useAtom, useSetAtom } from 'jotai'; +import { useTranslation } from 'react-i18next'; const SortableItem = ({ id, name, app, isDesktop }) => { const { openApp } = useHandlePrivateApps(); @@ -46,6 +47,14 @@ const SortableItem = ({ id, name, app, isDesktop }) => { transition, }; + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); + return ( { width: '31px', height: 'auto', }} - // src={LogoSelected} alt="center-icon" /> @@ -120,7 +128,12 @@ const SortableItem = ({ id, name, app, isDesktop }) => { {app?.isPrivate ? ( - {`${app?.privateAppProperties?.appName || 'Private'}`} + {`${ + app?.privateAppProperties?.appName || + t('core:app_private', { + postProcess: 'capitalizeFirstChar', + }) + }`} ) : ( {app?.metadata?.title || app?.name} diff --git a/src/components/Apps/TabComponent.tsx b/src/components/Apps/TabComponent.tsx index a7584c0..6aca3a9 100644 --- a/src/components/Apps/TabComponent.tsx +++ b/src/components/Apps/TabComponent.tsx @@ -34,8 +34,8 @@ const TabComponent = ({ isSelected, app }) => { diff --git a/src/components/Drawer/DrawerUserLookup.tsx b/src/components/Drawer/DrawerUserLookup.tsx index 9f8427a..962142e 100644 --- a/src/components/Drawer/DrawerUserLookup.tsx +++ b/src/components/Drawer/DrawerUserLookup.tsx @@ -1,3 +1,4 @@ +import { useTheme } from '@mui/material'; import Box from '@mui/material/Box'; import Drawer from '@mui/material/Drawer'; @@ -6,6 +7,8 @@ export const DrawerUserLookup = ({ open, setOpen, children }) => { setOpen(newOpen); }; + const theme = useTheme(); + return (
    { hideBackdrop={true} open={open} onClose={toggleDrawer(false)} + sx={{ color: theme.palette.text.primary }} > { }} id="controllable-states-demo" loading={isLoading} + noOptionsText={t('core:option_no', { + postProcess: 'capitalizeFirstChar', + })} options={options} sx={{ width: 300 }} renderInput={(params) => ( diff --git a/src/i18n/locales/de/core.json b/src/i18n/locales/de/core.json index ec4e25e..0d9840c 100644 --- a/src/i18n/locales/de/core.json +++ b/src/i18n/locales/de/core.json @@ -105,8 +105,9 @@ "api": "API", "app": "app", "app_other": "apps", - "app_name": "app -Name", - "app_service_type": "app -Service -Typ", + "app_name": "aname der App", + "app_private": "privat", + "app_service_type": "app-Diensttyp", "apps_dashboard": "apps Dashboard", "apps_official": "offizielle Apps", "attachment": "anhang", @@ -319,6 +320,7 @@ "none": "keiner", "note": "notiz", "option": "option", + "option_no": "keine Optionen", "option_other": "optionen", "page": { "last": "zuletzt", diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 0f38ee5..aa6f209 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -109,6 +109,7 @@ "app": "app", "app_other": "apps", "app_name": "app name", + "app_private": "private", "app_service_type": "app service type", "apps_dashboard": "apps Dashboard", "apps_official": "official Apps", @@ -322,6 +323,7 @@ "none": "none", "note": "note", "option": "option", + "option_no": "no options", "option_other": "options", "page": { "last": "last", diff --git a/src/i18n/locales/es/core.json b/src/i18n/locales/es/core.json index f4d5995..d56577d 100644 --- a/src/i18n/locales/es/core.json +++ b/src/i18n/locales/es/core.json @@ -106,6 +106,7 @@ "app": "aplicación", "app_other": "aplicaciones", "app_name": "nombre de la aplicación", + "app_private": "privada", "app_service_type": "tipo de servicio de aplicaciones", "apps_dashboard": "panel de aplicaciones", "apps_official": "aplicaciones oficiales", @@ -319,6 +320,7 @@ "none": "ninguno", "note": "nota", "option": "opción", + "option_no": "sin opción", "option_other": "opción", "page": { "last": "último", diff --git a/src/i18n/locales/fr/core.json b/src/i18n/locales/fr/core.json index 8f94a3d..0dcca9d 100644 --- a/src/i18n/locales/fr/core.json +++ b/src/i18n/locales/fr/core.json @@ -107,6 +107,7 @@ "app": "appliquer", "app_other": "applications", "app_name": "nom de l'application", + "app_private": "privé", "app_service_type": "type de service d'application", "apps_dashboard": "tableau de bord Apps", "apps_official": "applications officielles", @@ -320,6 +321,7 @@ "none": "aucun", "note": "note", "option": "option", + "option_no": "aucune option", "option_other": "options", "page": { "last": "dernier", diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index cca7b1f..b737add 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -27,7 +27,7 @@ "copy_link": "copia link", "create_apps": "crea app", "create_file": "crea file", - "create_transaction": "crea transazioni sulla blockchain Qortal", + "create_transaction": "creare transazioni sulla blockchain Qortal", "create_thread": "crea thread", "decline": "rifiuta", "decrypt": "decripta", @@ -109,8 +109,9 @@ "app": "app", "app_other": "app", "app_name": "nome app", + "app_private": "privata", "app_service_type": "tipo di servizio app", - "apps_dashboard": "dashboard di app", + "apps_dashboard": "dashboard delle app", "apps_official": "app ufficiali", "attachment": "allegato", "balance": "bilancia:", @@ -322,6 +323,7 @@ "none": "nessuno", "note": "nota", "option": "opzione", + "option_no": "nessuna opzione", "option_other": "opzioni", "page": { "last": "scorso", diff --git a/src/i18n/locales/it/tutorial.json b/src/i18n/locales/it/tutorial.json index 96fc539..cc3ced5 100644 --- a/src/i18n/locales/it/tutorial.json +++ b/src/i18n/locales/it/tutorial.json @@ -6,11 +6,11 @@ "account_creation": "creazione dell'account", "important_info": "informazioni importanti", "apps": { - "dashboard": "1. Dashboard di app", + "dashboard": "1. Dashboard delle app", "navigation": "2. Navigazione delle app" }, "initial": { - "recommended_qort_qty": "avere almeno {{ quantity }} QORT nel tuo wallet", + "recommended_qort_qty": "avere almeno {{ quantity }} QORT nel proprio wallet", "explore": "esplora", "general_chat": "chat generale", "getting_started": "come iniziare", diff --git a/src/i18n/locales/ja/core.json b/src/i18n/locales/ja/core.json index 8da57d7..9194451 100644 --- a/src/i18n/locales/ja/core.json +++ b/src/i18n/locales/ja/core.json @@ -106,6 +106,7 @@ "app": "アプリ", "app_other": "アプリ", "app_name": "アプリ名", + "app_private": "プライベート", "app_service_type": "アプリサービスタイプ", "apps_dashboard": "アプリダッシュボード", "apps_official": "公式アプリ", @@ -319,6 +320,7 @@ "none": "なし", "note": "注記", "option": "オプション", + "option_no": "オプションなし", "option_other": "オプション", "page": { "last": "最後", diff --git a/src/i18n/locales/ru/core.json b/src/i18n/locales/ru/core.json index c9f0db1..d5e4f61 100644 --- a/src/i18n/locales/ru/core.json +++ b/src/i18n/locales/ru/core.json @@ -106,6 +106,7 @@ "app": "приложение", "app_other": "приложения", "app_name": "Название приложения", + "app_private": "частное", "app_service_type": "Тип службы приложений", "apps_dashboard": "приложения панель панели", "apps_official": "официальные приложения", @@ -320,6 +321,7 @@ "none": "никто", "note": "примечание", "option": "вариант", + "option_no": "выбора нет", "option_other": "параметры", "page": { "last": "последний", diff --git a/src/i18n/locales/zh/core.json b/src/i18n/locales/zh/core.json index 1b66a19..58ac531 100644 --- a/src/i18n/locales/zh/core.json +++ b/src/i18n/locales/zh/core.json @@ -106,6 +106,7 @@ "app": "应用程序", "app_other": "应用", "app_name": "应用名称", + "app_private": "私人应用程序", "app_service_type": "应用服务类型", "apps_dashboard": "应用仪表板", "apps_official": "官方应用程序", @@ -319,6 +320,7 @@ "none": "没有任何", "note": "笔记", "option": "选项", + "option_no": "没有选择", "option_other": "选项", "page": { "last": "最后的", From 8e6aea86ce388b10c8397b6de558cb9b250292b9 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Tue, 27 May 2025 08:10:37 +0200 Subject: [PATCH 527/717] Rename file --- .../Desktop/{DesktopSideBar.tsx => DesktopLeftSideBar.tsx} | 2 +- src/components/Group/Group.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/components/Desktop/{DesktopSideBar.tsx => DesktopLeftSideBar.tsx} (98%) diff --git a/src/components/Desktop/DesktopSideBar.tsx b/src/components/Desktop/DesktopLeftSideBar.tsx similarity index 98% rename from src/components/Desktop/DesktopSideBar.tsx rename to src/components/Desktop/DesktopLeftSideBar.tsx index 13f04b1..75f4f76 100644 --- a/src/components/Desktop/DesktopSideBar.tsx +++ b/src/components/Desktop/DesktopLeftSideBar.tsx @@ -1,7 +1,7 @@ import { Box, ButtonBase, useTheme } from '@mui/material'; import { HomeIcon } from '../../assets/Icons/HomeIcon'; import { Save } from '../Save/Save'; -import { IconWrapper } from '../Desktop/DesktopFooter'; +import { IconWrapper } from './DesktopFooter'; import { enabledDevModeAtom } from '../../atoms/global'; import { AppsIcon } from '../../assets/Icons/AppsIcon'; import ThemeSelector from '../Theme/ThemeSelector'; diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index ca48e2b..7d1370d 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -49,7 +49,7 @@ import { IconWrapper } from '../Desktop/DesktopFooter'; import { DesktopHeader } from '../Desktop/DesktopHeader'; import { AppsDesktop } from '../Apps/AppsDesktop'; import { AppsDevMode } from '../Apps/AppsDevMode'; -import { DesktopSideBar } from '../Desktop/DesktopSideBar'; +import { DesktopSideBar } from '../Desktop/DesktopLeftSideBar'; import { HubsIcon } from '../../assets/Icons/HubsIcon'; import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; import { formatEmailDate } from './QMailMessages'; From 62838bcaf93acc2af13208708932fc22dba2025f Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Tue, 27 May 2025 08:40:50 +0200 Subject: [PATCH 528/717] Refactor deprecated property --- src/components/Group/AddGroup.tsx | 4 +++- src/components/Group/ManageMembers.tsx | 4 +++- src/components/Group/Settings.tsx | 4 +++- src/components/UserLookup.tsx/UserLookup.tsx | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 3815d0d..cc3e487 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -223,7 +223,9 @@ export const AddGroup = ({ address, open, setOpen }) => { fullScreen open={open} onClose={handleClose} - TransitionComponent={Transition} + slots={{ + transition: Transition, + }} > { fullScreen open={open} onClose={handleClose} - TransitionComponent={Transition} + slots={{ + transition: Transition, + }} > diff --git a/src/components/UserLookup.tsx/UserLookup.tsx b/src/components/UserLookup.tsx/UserLookup.tsx index a805180..b58d616 100644 --- a/src/components/UserLookup.tsx/UserLookup.tsx +++ b/src/components/UserLookup.tsx/UserLookup.tsx @@ -29,7 +29,6 @@ import AccountCircleIcon from '@mui/icons-material/AccountCircle'; import { Spacer } from '../../common/Spacer'; import { formatTimestamp } from '../../utils/time'; import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen'; -import SearchIcon from '@mui/icons-material/Search'; import { executeEvent, subscribeToEvent, @@ -65,6 +64,7 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => { const [isLoadingUser, setIsLoadingUser] = useState(false); const [isLoadingPayments, setIsLoadingPayments] = useState(false); const [payments, setPayments] = useState([]); + const lookupFunc = useCallback( async (messageAddressOrName) => { try { @@ -481,6 +481,7 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => { /> )} + {!isLoadingPayments && addressInfo && ( Date: Tue, 27 May 2025 08:46:42 +0200 Subject: [PATCH 529/717] Refactor values --- src/i18n/locales/de/core.json | 4 ++-- src/i18n/locales/it/core.json | 12 ++++++------ src/i18n/locales/ru/core.json | 4 ++-- src/i18n/locales/zh/core.json | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/i18n/locales/de/core.json b/src/i18n/locales/de/core.json index 0d9840c..8e4691a 100644 --- a/src/i18n/locales/de/core.json +++ b/src/i18n/locales/de/core.json @@ -336,8 +336,8 @@ "about": "über diese Q-App", "q_mail": "Q-Mail", "q_manager": "Q-Manager", - "q_sandbox": "q-sandbox", - "q_wallets": "q-wallets" + "q_sandbox": "Q-Sandbox", + "q_wallets": "Q-Wallets" }, "receiver": "empfänger", "sender": "absender", diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index b737add..95751db 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -14,7 +14,7 @@ "change": "modifica", "change_avatar": "cambia Avatar", "change_file": "modifica file", - "change_language": "cambia il linguaggio", + "change_language": "cambia la lingua", "choose": "scegli", "choose_file": "scegli il file", "choose_image": "scegli l'immagine", @@ -339,10 +339,10 @@ "publish": "pubblicazione", "q_apps": { "about": "su questo Q-app", - "q_mail": "Q-mail", - "q_manager": "Q-manager", - "q_sandbox": "Q-sandbox", - "q_wallets": "Q-wallet" + "q_mail": "Q-Mail", + "q_manager": "Q-Manager", + "q_sandbox": "Q-Sandbox", + "q_wallets": "Q-Wallets" }, "receiver": "ricevitore", "sender": "mittente", @@ -358,7 +358,7 @@ "dark": "scuro", "dark_mode": "modalità scura", "default": "tema di default", - "light": "chiara", + "light": "chiaro", "light_mode": "modalità chiara", "manager": "manager tema", "name": "nome tema" diff --git a/src/i18n/locales/ru/core.json b/src/i18n/locales/ru/core.json index d5e4f61..927cec1 100644 --- a/src/i18n/locales/ru/core.json +++ b/src/i18n/locales/ru/core.json @@ -339,8 +339,8 @@ "about": "об этом Q-App", "q_mail": "Q-Mail", "q_manager": "Q-Manager", - "q_sandbox": "Q-SANDBOX", - "q_wallets": "Q-Wallet" + "q_sandbox": "Q-Sandbox", + "q_wallets": "Q-Wallets" }, "receiver": "приемник", "sender": "отправитель", diff --git a/src/i18n/locales/zh/core.json b/src/i18n/locales/zh/core.json index 58ac531..51168b8 100644 --- a/src/i18n/locales/zh/core.json +++ b/src/i18n/locales/zh/core.json @@ -337,9 +337,9 @@ "q_apps": { "about": "关于这个Q-App", "q_mail": "Q邮件", - "q_manager": "Q-Manager", - "q_sandbox": "Q-Sandbox", - "q_wallets": "Q-Wallet" + "q_manager": "Q-经理", + "q_sandbox": "Q-沙箱", + "q_wallets": "Q-钱包" }, "receiver": "接收者", "sender": "发件人", From 0f857acd8dbf42ad5a34b58b27b40b1599b8b1a7 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Tue, 27 May 2025 08:47:01 +0200 Subject: [PATCH 530/717] Refactor --- src/components/Group/Settings.tsx | 3 ++- src/encryption/bcryptworker.worker.js | 19 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index 9508ebc..92df026 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -154,7 +154,7 @@ export const Settings = ({ open, setOpen, rawWallet }) => { useEffect(() => { getUserSettings(); - }, []); + }); return ( @@ -243,6 +243,7 @@ const ExportPrivateKey = ({ rawWallet }) => { const [isOpen, setIsOpen] = useState(false); const { setOpenSnackGlobal, setInfoSnackCustom } = useContext(QORTAL_APP_CONTEXT); + const theme = useTheme(); const { t } = useTranslation([ 'auth', 'core', diff --git a/src/encryption/bcryptworker.worker.js b/src/encryption/bcryptworker.worker.js index d6d048a..f324c3f 100644 --- a/src/encryption/bcryptworker.worker.js +++ b/src/encryption/bcryptworker.worker.js @@ -1,12 +1,11 @@ -import bcrypt from 'bcryptjs' - +import bcrypt from 'bcryptjs'; self.onmessage = function (e) { - const { hashBase64, salt } = e.data; - try { - const result = bcrypt.hashSync(hashBase64, salt); - self.postMessage({ result }); - } catch (error) { - self.postMessage({ error: error.message }); - } -}; \ No newline at end of file + const { hashBase64, salt } = e.data; + try { + const result = bcrypt.hashSync(hashBase64, salt); + self.postMessage({ result }); + } catch (error) { + self.postMessage({ error: error.message }); + } +}; From 441e4c02310208d06123635d40a5600dad6d58c0 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Tue, 27 May 2025 09:02:58 +0200 Subject: [PATCH 531/717] Refactor deprecated MUI component --- src/components/Theme/ThemeManager.tsx | 39 +++++++++++++++------------ 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/components/Theme/ThemeManager.tsx b/src/components/Theme/ThemeManager.tsx index 1bfee78..f254a74 100644 --- a/src/components/Theme/ThemeManager.tsx +++ b/src/components/Theme/ThemeManager.tsx @@ -16,6 +16,7 @@ import { Tab, ListItemButton, useTheme, + ListItem, } from '@mui/material'; import { Sketch } from '@uiw/react-color'; import DeleteIcon from '@mui/icons-material/Delete'; @@ -269,24 +270,28 @@ export default function ThemeManager() { - - {theme.id !== 'default' && ( - <> - exportTheme(theme)}> - + + {theme.id !== 'default' && ( + <> + exportTheme(theme)}> + + + handleEditTheme(theme.id)}> + + + handleDeleteTheme(theme.id)}> + + + + )} + handleApplyTheme(theme)}> + - handleEditTheme(theme.id)}> - - - handleDeleteTheme(theme.id)}> - - - - )} - handleApplyTheme(theme)}> - - - + + } + > ))} From a416013b7a4827c8fb3ae256f9360d183d246850 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Tue, 27 May 2025 09:03:05 +0200 Subject: [PATCH 532/717] Update --- src/i18n/locales/it/core.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 95751db..1144455 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -316,7 +316,7 @@ "voted": "votato con successo. Si prega di attendere un paio di minuti affinché la rete propaghi le modifiche." } }, - "minting_status": "stato di minting", + "minting_status": "stato minting", "name": "nome", "name_app": "nome/app", "new_post_in": "nuovo post in {{ title }}", From e4223ce418201f1368e82f27a1b949f74d23cacd Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Tue, 27 May 2025 09:04:16 +0200 Subject: [PATCH 533/717] Remove unused import --- src/components/Theme/ThemeManager.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Theme/ThemeManager.tsx b/src/components/Theme/ThemeManager.tsx index f254a74..f596ccc 100644 --- a/src/components/Theme/ThemeManager.tsx +++ b/src/components/Theme/ThemeManager.tsx @@ -10,7 +10,6 @@ import { DialogActions, List, ListItemText, - ListItemSecondaryAction, TextField, Tabs, Tab, @@ -190,6 +189,7 @@ export default function ThemeManager() { {label} + Date: Tue, 27 May 2025 09:14:33 +0200 Subject: [PATCH 534/717] Uniform layout of Minting to Settings --- src/components/Group/Settings.tsx | 2 +- src/components/Minting/Minting.tsx | 58 +++++++++++------------------- 2 files changed, 22 insertions(+), 38 deletions(-) diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index 92df026..2c87334 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -175,8 +175,8 @@ export const Settings = ({ open, setOpen, rawWallet }) => { { }, }} > - - {t('group:message.generic.manage_minting', { - postProcess: 'capitalizeAll', - })} - + + + + {t('group:message.generic.manage_minting', { + postProcess: 'capitalizeFirstChar', + })} + - setIsOpenMinting(false)} - aria-label={t('core:action.close', { - postProcess: 'capitalizeFirstChar', - })} - > - - + setIsOpenMinting(false)} + aria-label={t('core:action.close', { + postProcess: 'capitalizeFirstChar', + })} + > + + + + { )} - - - - Date: Tue, 27 May 2025 19:00:26 +0200 Subject: [PATCH 535/717] Import CSS --- src/components/Theme/ThemeManager.tsx | 1 + src/{components/Theme => styles}/themeManager.css | 0 2 files changed, 1 insertion(+) rename src/{components/Theme => styles}/themeManager.css (100%) diff --git a/src/components/Theme/ThemeManager.tsx b/src/components/Theme/ThemeManager.tsx index f596ccc..d5e3087 100644 --- a/src/components/Theme/ThemeManager.tsx +++ b/src/components/Theme/ThemeManager.tsx @@ -31,6 +31,7 @@ import FileDownloadIcon from '@mui/icons-material/FileDownload'; import { saveFileToDiskGeneric } from '../../utils/generateWallet/generateWallet'; import { handleImportClick } from '../../utils/fileReading'; import { useTranslation } from 'react-i18next'; +import '../../styles/themeManager.css'; const uid = new ShortUniqueId({ length: 8 }); diff --git a/src/components/Theme/themeManager.css b/src/styles/themeManager.css similarity index 100% rename from src/components/Theme/themeManager.css rename to src/styles/themeManager.css From c9f6f8328c02cf2214c97671043520238f91e14b Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Tue, 27 May 2025 22:47:21 +0200 Subject: [PATCH 536/717] Restore box for replied messages --- src/components/Chat/MessageItem.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index 3151fc5..df6180c 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -340,6 +340,15 @@ export const MessageItem = memo( scrollToItem(replyIndex); }} > + + )} + Date: Tue, 27 May 2025 22:56:53 +0200 Subject: [PATCH 537/717] Update deprecated property --- src/components/Chat/MessageItem.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index df6180c..4d2a653 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -465,11 +465,12 @@ export const MessageItem = memo( vertical: 'bottom', horizontal: 'center', }} - PaperProps={{ - // TODO: deprecated - style: { - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, + slotProps={{ + paper: { + style: { + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + }, }, }} > @@ -595,6 +596,7 @@ export const MessageItem = memo( })} )} + Date: Tue, 27 May 2025 23:50:23 +0200 Subject: [PATCH 538/717] Revert backgroud palette --- src/components/Chat/ChatGroup.tsx | 3 +-- src/components/Group/Settings.tsx | 1 + src/styles/theme-dark.ts | 6 +++--- src/styles/theme-light.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index f0cfaae..612a7c8 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -58,7 +58,6 @@ const uidImages = new ShortUniqueId({ length: 12 }); export const ChatGroup = ({ selectedGroup, secretKey, - setSecretKey, getSecretKey, myAddress, handleNewEncryptionNotification, @@ -1185,7 +1184,7 @@ export const ChatGroup = ({ {(!!secretKey || isPrivate === false) && (
    { })} /> )} + {isEnabledDevMode && } diff --git a/src/styles/theme-dark.ts b/src/styles/theme-dark.ts index cfa5952..a519cd6 100644 --- a/src/styles/theme-dark.ts +++ b/src/styles/theme-dark.ts @@ -6,7 +6,7 @@ export const darkThemeOptions: ThemeOptions = { palette: { mode: 'dark', primary: { - main: 'rgba(0, 133, 255, 1)', + main: 'rgb(100, 155, 240)', dark: 'rgb(45, 92, 201)', light: 'rgb(130, 185, 255)', }, @@ -14,9 +14,9 @@ export const darkThemeOptions: ThemeOptions = { main: 'rgb(69, 173, 255)', }, background: { - default: 'rgba(6, 10, 30, 1)', + default: 'rgb(49, 51, 56)', + surface: 'rgb(58, 60, 65)', paper: 'rgb(62, 64, 68)', - surface: 'rgb(113, 113, 114)', }, text: { primary: 'rgb(255, 255, 255)', diff --git a/src/styles/theme-light.ts b/src/styles/theme-light.ts index 4f6a1b1..3cfbdae 100644 --- a/src/styles/theme-light.ts +++ b/src/styles/theme-light.ts @@ -15,8 +15,8 @@ export const lightThemeOptions: ThemeOptions = { }, background: { default: 'rgba(250, 250, 250, 1)', - paper: 'rgb(220, 220, 220)', // darker card background surface: 'rgb(240, 240, 240)', // optional middle gray for replies, side panels + paper: 'rgb(220, 220, 220)', // darker card background }, text: { primary: 'rgba(0, 0, 0, 0.87)', // 87% black (slightly softened) From 25695c87b01e127ee45b148cf93383259b250687 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 28 May 2025 18:54:52 +0300 Subject: [PATCH 539/717] fixes --- src/App.tsx | 20 +++++++++++++++++++- src/background/background.ts | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 5138080..fb60be6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -766,6 +766,24 @@ function App() { balanceSetInterval(); }); }; + + const refetchUserInfo = () => { + window + .sendMessage('userInfo') + .then((response) => { + if (response && !response.error) { + setUserInfo(response); + } + }) + .catch((error) => { + console.error('Failed to get user info:', error); + }); + }; + + const getBalanceAndUserInfoFunc = () => { + getBalanceFunc(); + refetchUserInfo(); + }; const getLtcBalanceFunc = () => { setLtcBalanceLoading(true); window @@ -1502,7 +1520,7 @@ function App() { Date: Wed, 28 May 2025 20:21:47 +0200 Subject: [PATCH 540/717] Add catch error --- src/components/Group/AddGroup.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index cc3e487..3e94755 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -40,6 +40,7 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useTranslation } from 'react-i18next'; import { useSetAtom } from 'jotai'; import { txListAtom } from '../../atoms/global'; +import { ErrorRounded } from '@mui/icons-material'; export const Label = styled('label')` display: block; @@ -123,13 +124,17 @@ export const AddGroup = ({ address, open, setOpen }) => { const fee = await getFee('CREATE_GROUP'); - await show({ - message: t('core:message.question.perform_transaction', { - action: 'CREATE_GROUP', - postProcess: 'capitalizeFirstChar', - }), - publishFee: fee.fee + ' QORT', - }); + try { + await show({ + message: t('core:message.question.perform_transaction', { + action: 'CREATE_GROUP', + postProcess: 'capitalizeFirstChar', + }), + publishFee: fee.fee + ' QORT', + }); + } catch (error) { + console.log(error); + } await new Promise((res, rej) => { window From 735bb08081f491d7afe71a5d049ae8949e7ddde2 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 28 May 2025 22:20:24 +0200 Subject: [PATCH 541/717] Adapt width to language --- src/components/Desktop/DesktopFooter.tsx | 3 +++ src/components/Desktop/DesktopLeftSideBar.tsx | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/Desktop/DesktopFooter.tsx b/src/components/Desktop/DesktopFooter.tsx index 3441cf6..23bd474 100644 --- a/src/components/Desktop/DesktopFooter.tsx +++ b/src/components/Desktop/DesktopFooter.tsx @@ -43,6 +43,9 @@ export const IconWrapper = ({ fontFamily: 'Inter', fontSize: '12px', fontWeight: 500, + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', }} > {label} diff --git a/src/components/Desktop/DesktopLeftSideBar.tsx b/src/components/Desktop/DesktopLeftSideBar.tsx index 75f4f76..99e570b 100644 --- a/src/components/Desktop/DesktopLeftSideBar.tsx +++ b/src/components/Desktop/DesktopLeftSideBar.tsx @@ -46,7 +46,7 @@ export const DesktopSideBar = ({ flexDirection: 'column', gap: '25px', height: '100vh', - width: '60px', + width: 'auto', // must adapt to the choosen language }} > Date: Wed, 28 May 2025 22:24:03 +0200 Subject: [PATCH 542/717] Refactor deprecated property --- src/components/Apps/AppsNavBarDesktop.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Apps/AppsNavBarDesktop.tsx b/src/components/Apps/AppsNavBarDesktop.tsx index bf24a8d..2eeecc6 100644 --- a/src/components/Apps/AppsNavBarDesktop.tsx +++ b/src/components/Apps/AppsNavBarDesktop.tsx @@ -186,6 +186,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => { > + { anchorEl={anchorEl} open={open} onClose={handleClose} - MenuListProps={{ - 'aria-labelledby': 'basic-button', - }} anchorOrigin={{ vertical: 'bottom', horizontal: 'center', @@ -281,6 +279,9 @@ export const AppsNavBarDesktop = ({ disableBack }) => { horizontal: 'center', }} slotProps={{ + list: { + 'aria-labelledby': 'basic-button', + }, paper: { sx: { backgroundColor: theme.palette.background.default, From 808a9de1e4e83aa3e9dbc11408de118af4bdeeab Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 28 May 2025 22:30:33 +0200 Subject: [PATCH 543/717] Add translation and refactor deprecated property --- src/components/Apps/AppsPrivate.tsx | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index 355d868..3fe611b 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -281,7 +281,11 @@ export const AppsPrivate = ({ myName }) => { - Private + + {t('core:app_private', { + postProcess: 'capitalizeFirstChar', + })} + @@ -306,10 +310,12 @@ export const AppsPrivate = ({ myName }) => { }} maxWidth="md" fullWidth={true} - PaperProps={{ - style: { - backgroundColor: theme.palette.background.paper, - boxShadow: 'none', + slotProps={{ + paper: { + style: { + backgroundColor: theme.palette.background.paper, + boxShadow: 'none', + }, }, }} > @@ -354,6 +360,7 @@ export const AppsPrivate = ({ myName }) => { /> + {valueTabPrivateApp === 0 && ( <> @@ -369,6 +376,7 @@ export const AppsPrivate = ({ myName }) => { postProcess: 'capitalizeFirstChar', })} + From 3a77600f6a41d951b70111bf21e0415939dea503 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 07:50:37 +0200 Subject: [PATCH 562/717] Refactor --- src/i18n/locales/it/core.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 213a415..cccba97 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -114,7 +114,7 @@ "apps_dashboard": "dashboard delle app", "apps_official": "app ufficiali", "attachment": "allegato", - "balance": "bilancia:", + "balance": "bilancio:", "basic_tabs_example": "esempio di schede base", "category": "categoria", "category_other": "categorie", From f445bcd5b14b2b39ad49f4d2de02cafb2dc359d3 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 08:07:36 +0200 Subject: [PATCH 563/717] Set key for list in direct messages --- src/components/Group/Group.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 641132b..105f258 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -1726,6 +1726,7 @@ export const Group = ({ > {directs.map((direct: any) => ( Date: Fri, 30 May 2025 08:16:31 +0200 Subject: [PATCH 564/717] Refactor property names --- src/assets/Icons/NavCloseTab.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/assets/Icons/NavCloseTab.tsx b/src/assets/Icons/NavCloseTab.tsx index 6062175..b8f295a 100644 --- a/src/assets/Icons/NavCloseTab.tsx +++ b/src/assets/Icons/NavCloseTab.tsx @@ -30,14 +30,14 @@ export const NavCloseTab: React.FC = ({ From 5215e7ad5bb0c56fc4ec0c35d6b53594a0116a3b Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 08:16:49 +0200 Subject: [PATCH 565/717] Sort properties --- src/components/Apps/AppsDesktop.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/Apps/AppsDesktop.tsx b/src/components/Apps/AppsDesktop.tsx index 98dfe6d..37d7648 100644 --- a/src/components/Apps/AppsDesktop.tsx +++ b/src/components/Apps/AppsDesktop.tsx @@ -524,12 +524,12 @@ export const AppsDesktop = ({ } return ( ); })} @@ -539,20 +539,20 @@ export const AppsDesktop = ({ From 9727711b04d52402ce77f5496d28016ede6fe0dc Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 08:17:05 +0200 Subject: [PATCH 566/717] Set src value or undefined --- src/components/Apps/AppViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 38cfe71..98a0b8a 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -278,7 +278,7 @@ export const AppViewer = forwardRef( width: '100%', }} id="browser-iframe" - src={defaultUrl} + src={defaultUrl || undefined} sandbox="allow-scripts allow-same-origin allow-forms allow-downloads allow-modals" allow="fullscreen; clipboard-read; clipboard-write" > From d45e9557b34ab189edfbaa0052327049dc0b7214 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 08:57:53 +0200 Subject: [PATCH 567/717] Refactor selectedTab --- src/components/Apps/AppsNavBarDesktop.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/components/Apps/AppsNavBarDesktop.tsx b/src/components/Apps/AppsNavBarDesktop.tsx index 2eeecc6..74e0ca0 100644 --- a/src/components/Apps/AppsNavBarDesktop.tsx +++ b/src/components/Apps/AppsNavBarDesktop.tsx @@ -67,7 +67,7 @@ export function saveToLocalStorage(key, subKey, newValue) { export const AppsNavBarDesktop = ({ disableBack }) => { const [tabs, setTabs] = useState([]); - const [selectedTab, setSelectedTab] = useState(null); + const [selectedTab, setSelectedTab] = useState(0); const [navigationController, setNavigationController] = useAtom( navigationControllerAtom ); @@ -125,7 +125,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => { const setTabsToNav = (e) => { const { tabs, selectedTab, isNewTabWindow } = e.detail?.data; setTabs([...tabs]); - setSelectedTab(!selectedTab ? null : { ...selectedTab }); + setSelectedTab(!selectedTab ? 0 : { ...selectedTab }); setIsNewTabWindow(isNewTabWindow); }; @@ -190,9 +190,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => { { maxHeight: `275px`, // Ensure the tabs container fits within the available space overflow: 'hidden', // Prevents overflow on small screens }} + value={false} > {tabs?.map((tab) => ( { > { - setSelectedTab(null); + setSelectedTab(0); executeEvent('newTabWindow', {}); }} > From 2c9eee8404e483f8d5d619927d2c1672bada033e Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 08:58:07 +0200 Subject: [PATCH 568/717] Sort --- src/components/Apps/AppViewerContainer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Apps/AppViewerContainer.tsx b/src/components/Apps/AppViewerContainer.tsx index 38c017b..ae05788 100644 --- a/src/components/Apps/AppViewerContainer.tsx +++ b/src/components/Apps/AppViewerContainer.tsx @@ -40,11 +40,11 @@ const AppViewerContainer = forwardRef( }} > ); From 0b0f5e0c073121f70a78e3a7cd7d2cf7e6b51b3b Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 08:58:17 +0200 Subject: [PATCH 569/717] Refactor CustomButtonAccept --- src/App.tsx | 8 ++++---- src/components/GlobalActions/JoinGroup.tsx | 8 ++++---- src/styles/App-styles.ts | 23 +++++++++++----------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index b898a14..92e0965 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3848,8 +3848,8 @@ function App() { }} > { disabled={isInGroup} > { ({ })); interface CustomButtonProps { - bgColor?: string; - color?: string; + customBgColor?: string; + customColor?: string; } -export const CustomButtonAccept = styled(Box)( - ({ bgColor, color, theme }) => ({ +export const CustomButtonAccept = styled(Box)((props) => { + const { customBgColor, customColor, theme } = props; + return { alignItems: 'center', - backgroundColor: bgColor || theme.palette.background.default, + backgroundColor: customBgColor || theme.palette.background.default, borderColor: theme.palette.background.paper, borderRadius: 5, borderStyle: 'solid', borderWidth: '0.5px', boxSizing: 'border-box', - color: color || theme.palette.background.default, + color: customColor || theme.palette.background.default, cursor: 'pointer', display: 'inline-flex', filter: 'drop-shadow(1px 4px 10.5px rgba(0,0,0,0.3))', @@ -163,16 +164,16 @@ export const CustomButtonAccept = styled(Box)( width: 'fit-content', '&:hover': { opacity: 1, - backgroundColor: bgColor || theme.palette.background.default, - color: color || '#fff', + backgroundColor: customBgColor || theme.palette.background.default, + color: customColor || '#fff', svg: { path: { - fill: color || '#fff', + fill: customColor || '#fff', }, }, }, - }) -); + }; +}); export const CustomInput = styled(TextField)(({ theme }) => ({ backgroundColor: theme.palette.background.default, From be5c3af318967c2b70dd056b9c34605604e221dd Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 09:12:00 +0200 Subject: [PATCH 570/717] Add comment (temporary) --- src/components/Apps/AppViewer.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 98a0b8a..9c82ebd 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -36,6 +36,11 @@ export const AppViewer = forwardRef( if (app?.isPreview) return; if (isDevMode) { setUrl(app?.url + `?theme=${themeMode}&lang=${currentLang}`); + console.log( + 'AAAAAAAAAAA---->' + + app.url + + `&theme=${themeMode}&lang=${currentLang}` + ); // TODO remove comment return; } let hasQueryParam = false; @@ -52,8 +57,14 @@ export const AppViewer = forwardRef( if (app?.isPreview && app?.url) { resetHistory(); setUrl(app.url + `&theme=${themeMode}&lang=${currentLang}`); + console.log( + 'AAAAAAAAAAA---->' + + app.url + + `&theme=${themeMode}&lang=${currentLang}` + ); // TODO remove comment } }, [app?.url, app?.isPreview]); + const defaultUrl = useMemo(() => { return url; }, [url, isDevMode]); @@ -68,6 +79,12 @@ export const AppViewer = forwardRef( app?.url + `?time=${Date.now()}&theme=${themeMode}&lang=${currentLang}` ); + + console.log( + 'AAAAAAAAAAA---->' + + app?.url + + `?time=${Date.now()}&theme=${themeMode}&lang=${currentLang}` + ); // TODO remove comment } return; } @@ -89,6 +106,7 @@ export const AppViewer = forwardRef( if (!iframe) return; try { + console.log('THEME_CHANGED---->' + iframe.src); // TODO remove comment const targetOrigin = new URL(iframe.src).origin; iframe.contentWindow?.postMessage( { action: 'THEME_CHANGED', theme: themeMode, requestedHandler: 'UI' }, @@ -104,6 +122,7 @@ export const AppViewer = forwardRef( if (!iframe) return; try { + console.log('LANGUAGE_CHANGED---->' + iframe.src); // TODO remove comment const targetOrigin = new URL(iframe.src).origin; iframe.contentWindow?.postMessage( { From 1f5a604932b1bd1bcee8acae2fd99f965c204c35 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 09:22:07 +0200 Subject: [PATCH 571/717] Add types --- src/components/Apps/AppViewer.tsx | 9 ++- src/components/Apps/AppViewerContainer.tsx | 74 ++++++++++++---------- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 9c82ebd..606f80a 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -7,7 +7,14 @@ import { useQortalMessageListener } from '../../hooks/useQortalMessageListener'; import { useThemeContext } from '../Theme/ThemeContext'; import { useTranslation } from 'react-i18next'; -export const AppViewer = forwardRef( +type AppViewerProps = { + app: any; + hide: boolean; + isDevMode: boolean; + skipAuth?: boolean; +}; + +export const AppViewer = forwardRef( ({ app, hide, isDevMode, skipAuth }, iframeRef) => { const { window: frameWindow } = useFrame(); const { path, history, changeCurrentIndex, resetHistory } = diff --git a/src/components/Apps/AppViewerContainer.tsx b/src/components/Apps/AppViewerContainer.tsx index ae05788..e26eb4a 100644 --- a/src/components/Apps/AppViewerContainer.tsx +++ b/src/components/Apps/AppViewerContainer.tsx @@ -2,15 +2,26 @@ import { forwardRef } from 'react'; import { AppViewer } from './AppViewer'; import Frame from 'react-frame-component'; -const AppViewerContainer = forwardRef( - ({ app, isSelected, hide, isDevMode, customHeight, skipAuth }, ref) => { - return ( - - - - } - style={{ - border: 'none', - height: customHeight || '100vh', - left: (!isSelected || hide) && '-200vw', - overflow: 'hidden', - position: (!isSelected || hide) && 'fixed', - width: '100%', - }} - > - - - ); - } -); + + + } + style={{ + border: 'none', + height: customHeight || '100vh', + left: (!isSelected || hide) && '-200vw', + overflow: 'hidden', + position: (!isSelected || hide) && 'fixed', + width: '100%', + }} + > + + + ); +}); export default AppViewerContainer; From 1de84262b1fbcefe6f8fecc5f5cf1de0d5067c94 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 09:31:27 +0200 Subject: [PATCH 572/717] Add optional --- src/components/Apps/AppsNavBarDesktop.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Apps/AppsNavBarDesktop.tsx b/src/components/Apps/AppsNavBarDesktop.tsx index 74e0ca0..70aeade 100644 --- a/src/components/Apps/AppsNavBarDesktop.tsx +++ b/src/components/Apps/AppsNavBarDesktop.tsx @@ -204,7 +204,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => { > {tabs?.map((tab) => ( Date: Fri, 30 May 2025 14:03:19 +0300 Subject: [PATCH 573/717] fixes --- src/components/Apps/AppViewer.tsx | 4 ++-- src/components/Apps/AppsDevModeNavBar.tsx | 8 +------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 38cfe71..33ade79 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -86,7 +86,7 @@ export const AppViewer = forwardRef( useEffect(() => { const iframe = iframeRef?.current; - if (!iframe) return; + if (!iframe || !iframe?.src) return; try { const targetOrigin = new URL(iframe.src).origin; @@ -101,7 +101,7 @@ export const AppViewer = forwardRef( useEffect(() => { const iframe = iframeRef?.current; - if (!iframe) return; + if (!iframe || !iframe?.src) return; try { const targetOrigin = new URL(iframe.src).origin; diff --git a/src/components/Apps/AppsDevModeNavBar.tsx b/src/components/Apps/AppsDevModeNavBar.tsx index 1ce7d76..548f1d2 100644 --- a/src/components/Apps/AppsDevModeNavBar.tsx +++ b/src/components/Apps/AppsDevModeNavBar.tsx @@ -32,13 +32,7 @@ export const AppsDevModeNavBar = () => { 'tutorial', ]); const theme = useTheme(); - const { t } = useTranslation([ - 'auth', - 'core', - 'group', - 'question', - 'tutorial', - ]); + const [isNewTabWindow, setIsNewTabWindow] = useState(false); const tabsRef = useRef(null); From 72f8f8ddcde0225d1598650166c0dda7013dc5c8 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 13:34:33 +0200 Subject: [PATCH 574/717] Remove comments --- src/components/Apps/AppViewer.tsx | 18 ------------------ src/components/Apps/AppViewerContainer.tsx | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 606f80a..0cdad98 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -43,11 +43,6 @@ export const AppViewer = forwardRef( if (app?.isPreview) return; if (isDevMode) { setUrl(app?.url + `?theme=${themeMode}&lang=${currentLang}`); - console.log( - 'AAAAAAAAAAA---->' + - app.url + - `&theme=${themeMode}&lang=${currentLang}` - ); // TODO remove comment return; } let hasQueryParam = false; @@ -64,11 +59,6 @@ export const AppViewer = forwardRef( if (app?.isPreview && app?.url) { resetHistory(); setUrl(app.url + `&theme=${themeMode}&lang=${currentLang}`); - console.log( - 'AAAAAAAAAAA---->' + - app.url + - `&theme=${themeMode}&lang=${currentLang}` - ); // TODO remove comment } }, [app?.url, app?.isPreview]); @@ -86,12 +76,6 @@ export const AppViewer = forwardRef( app?.url + `?time=${Date.now()}&theme=${themeMode}&lang=${currentLang}` ); - - console.log( - 'AAAAAAAAAAA---->' + - app?.url + - `?time=${Date.now()}&theme=${themeMode}&lang=${currentLang}` - ); // TODO remove comment } return; } @@ -113,7 +97,6 @@ export const AppViewer = forwardRef( if (!iframe) return; try { - console.log('THEME_CHANGED---->' + iframe.src); // TODO remove comment const targetOrigin = new URL(iframe.src).origin; iframe.contentWindow?.postMessage( { action: 'THEME_CHANGED', theme: themeMode, requestedHandler: 'UI' }, @@ -129,7 +112,6 @@ export const AppViewer = forwardRef( if (!iframe) return; try { - console.log('LANGUAGE_CHANGED---->' + iframe.src); // TODO remove comment const targetOrigin = new URL(iframe.src).origin; iframe.contentWindow?.postMessage( { diff --git a/src/components/Apps/AppViewerContainer.tsx b/src/components/Apps/AppViewerContainer.tsx index e26eb4a..5f6e30d 100644 --- a/src/components/Apps/AppViewerContainer.tsx +++ b/src/components/Apps/AppViewerContainer.tsx @@ -3,7 +3,7 @@ import { AppViewer } from './AppViewer'; import Frame from 'react-frame-component'; type AppViewerContainerProps = { - app: any; // Replace `any` with the correct type of `tab` if available + app: any; isSelected: boolean; hide: boolean; isDevMode: boolean; From f5294d6ea103c27dd91c240149e0b7f9dde57109 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 13:40:37 +0200 Subject: [PATCH 575/717] Remove undefined --- src/components/Apps/AppViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 0cdad98..75b468b 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -286,7 +286,7 @@ export const AppViewer = forwardRef( width: '100%', }} id="browser-iframe" - src={defaultUrl || undefined} + src={defaultUrl} sandbox="allow-scripts allow-same-origin allow-forms allow-downloads allow-modals" allow="fullscreen; clipboard-read; clipboard-write" > From b26c6ea7db20c587cf8a01e03247bd3cab7d0666 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 13:53:46 +0200 Subject: [PATCH 576/717] Add refresh action as key --- src/components/Apps/AppsNavBarDesktop.tsx | 5 ++-- src/i18n/locales/en/core.json | 1 + src/i18n/locales/es/core.json | 29 ++++++++++++----------- src/i18n/locales/fr/core.json | 1 + src/i18n/locales/it/core.json | 1 + src/i18n/locales/ja/core.json | 1 + src/i18n/locales/ru/core.json | 1 + src/i18n/locales/zh/core.json | 1 + 8 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/components/Apps/AppsNavBarDesktop.tsx b/src/components/Apps/AppsNavBarDesktop.tsx index 70aeade..b26391f 100644 --- a/src/components/Apps/AppsNavBarDesktop.tsx +++ b/src/components/Apps/AppsNavBarDesktop.tsx @@ -407,7 +407,6 @@ export const AppsNavBarDesktop = ({ disableBack }) => { tabId: selectedTab?.tabId, }); } - handleClose(); }} > @@ -433,7 +432,9 @@ export const AppsNavBarDesktop = ({ disableBack }) => { color: theme.palette.text.primary, }, }} - primary="Refresh" + primary={t('core:action.refresh', { + postProcess: 'capitalizeFirstChar', + })} /> diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index aa6f209..6695e9c 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -68,6 +68,7 @@ "publish": "publish", "publish_app": "publish your app", "publish_comment": "publish comment", + "refresh": "refresh", "register_name": "register name", "remove": "remove", "remove_reaction": "remove reaction", diff --git a/src/i18n/locales/es/core.json b/src/i18n/locales/es/core.json index d56577d..33544ab 100644 --- a/src/i18n/locales/es/core.json +++ b/src/i18n/locales/es/core.json @@ -68,21 +68,22 @@ "publish_app": "publica tu aplicación", "publish_comment": "publicar comentario", "register_name": "nombre de registro", - "remove": "eliminar", - "remove_reaction": "eliminar la reacción", + "refresh": "refresca", + "remove": "elimina", + "remove_reaction": "elimina la reacción", "return_apps_dashboard": "volver al tablero de aplicaciones", - "save": "ahorrar", - "save_disk": "guardar en el disco", - "search": "buscar", - "search_apps": "buscar aplicaciones", - "search_groups": "buscar grupos", - "search_chat_text": "search Chat Text", - "select_app_type": "seleccionar el tipo de aplicación", - "select_category": "seleccionar categoría", - "select_name_app": "seleccionar nombre/aplicación", - "send": "enviar", - "send_qort": "enviar Qort", - "set_avatar": "establecer avatar", + "save": "ahorra", + "save_disk": "guarda en el disco", + "search": "busca", + "search_apps": "busca aplicaciones", + "search_groups": "busca grupos", + "search_chat_text": "busca Chat Text", + "select_app_type": "selecciona el tipo de aplicación", + "select_category": "selecciona categoría", + "select_name_app": "selecciona nombre/aplicación", + "send": "envia", + "send_qort": "envia Qort", + "set_avatar": "establece avatar", "show": "espectáculo", "show_poll": "encuesta", "start_minting": "empiece a acuñar", diff --git a/src/i18n/locales/fr/core.json b/src/i18n/locales/fr/core.json index 0dcca9d..d1452f7 100644 --- a/src/i18n/locales/fr/core.json +++ b/src/i18n/locales/fr/core.json @@ -67,6 +67,7 @@ "publish": "publier", "publish_app": "publiez votre application", "publish_comment": "publier un commentaire", + "refresh": "rafraîche", "register_name": "nom de registre", "remove": "retirer", "remove_reaction": "éliminer la réaction", diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index cccba97..9a6af16 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -68,6 +68,7 @@ "publish": "pubblica", "publish_app": "pubblica la tua app", "publish_comment": "pubblica un commento", + "refresh": "aggiorna", "register_name": "registra nome", "remove": "rimuovi", "remove_reaction": "rimuovi la reazione", diff --git a/src/i18n/locales/ja/core.json b/src/i18n/locales/ja/core.json index 9194451..570210d 100644 --- a/src/i18n/locales/ja/core.json +++ b/src/i18n/locales/ja/core.json @@ -67,6 +67,7 @@ "publish": "公開", "publish_app": "アプリを公開します", "publish_comment": "コメントを公開します", + "refresh": "リフレッシュ", "register_name": "登録名", "remove": "取り除く", "remove_reaction": "反応を取り除きます", diff --git a/src/i18n/locales/ru/core.json b/src/i18n/locales/ru/core.json index 927cec1..f93831d 100644 --- a/src/i18n/locales/ru/core.json +++ b/src/i18n/locales/ru/core.json @@ -67,6 +67,7 @@ "publish": "публиковать", "publish_app": "Публикуйте свое приложение", "publish_comment": "Публикуйте комментарий", + "refresh": "освежить", "register_name": "зарегистрировать имя", "remove": "удалять", "remove_reaction": "удалить реакцию", diff --git a/src/i18n/locales/zh/core.json b/src/i18n/locales/zh/core.json index 51168b8..3622901 100644 --- a/src/i18n/locales/zh/core.json +++ b/src/i18n/locales/zh/core.json @@ -67,6 +67,7 @@ "publish": "发布", "publish_app": "发布您的应用", "publish_comment": "发布评论", + "refresh": "刷新", "register_name": "登记名称", "remove": "消除", "remove_reaction": "删除反应", From 945315cfe19f551bd4941a92430ee7423712896b Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 19:01:52 +0200 Subject: [PATCH 577/717] Check all file --- src/i18n/locales/it/group.json | 82 +++++++++++++++++----------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/i18n/locales/it/group.json b/src/i18n/locales/it/group.json index 1b4aeb4..12f1d5e 100644 --- a/src/i18n/locales/it/group.json +++ b/src/i18n/locales/it/group.json @@ -2,21 +2,21 @@ "action": { "add_promotion": "aggiungi promozione", "ban": "escludi membro del gruppo", - "cancel_ban": "annulla divieto", + "cancel_ban": "annulla esclusione", "copy_private_key": "copia chiave privata", "create_group": "crea gruppo", "disable_push_notifications": "disabilita tutte le notifiche push", - "export_password": "password di esportazione", + "export_password": "esporta la password", "export_private_key": "esporta una chiave privata", - "find_group": "trova il gruppo", - "join_group": "unisciti al gruppo", - "kick_member": "togli membro dal gruppo", + "find_group": "trova gruppo", + "join_group": "entra nel gruppo", + "kick_member": "allontana membro dal gruppo", "invite_member": "invita membro", "leave_group": "lascia il gruppo", "load_members": "carica i membri con i nomi", - "make_admin": "rendere amministratore", + "make_admin": "rendi amministratore", "manage_members": "gestisci i membri", - "promote_group": "promuovi il tuo gruppo ai non membri", + "promote_group": "promuovi il gruppo ai non membri", "publish_announcement": "pubblica annuncio", "publish_avatar": "pubblica avatar", "refetch_page": "ricarica pagina", @@ -41,7 +41,7 @@ "id": "gruppo ID", "invites": "inviti di gruppo", "group": "gruppo", - "group_name": "group: {{ name }}", + "group_name": "gruppo: {{ name }}", "group_other": "gruppi", "groups_admin": "gruppi in cui sei un amministratore", "management": "gestione del gruppo", @@ -54,12 +54,12 @@ "public": "gruppo pubblico", "type": "tipo di gruppo" }, - "invitation_expiry": "tempo di scadenza dell'invito", + "invitation_expiry": "scadenza dell'invito", "invitees_list": "elenco degli inviti", - "join_link": "unisciti al link di gruppo", - "join_requests": "unisciti alle richieste", + "join_link": "link per entrare nel gruppo", + "join_requests": "richieste di adesione", "last_message": "ultimo messaggio", - "last_message_date": "ultimo messaggio: {{date }}", + "last_message_date": "ultimo messaggio: {{ date }}", "latest_mails": "ultimi Q-Mail", "message": { "generic": { @@ -69,27 +69,27 @@ "already_in_group": "sei già in questo gruppo!", "block_delay_minimum": "ritardo minimo del blocco per le approvazioni delle transazioni di gruppo", "block_delay_maximum": "ritardo massimo del blocco per le approvazioni delle transazioni di gruppo", - "closed_group": "questo è un gruppo chiuso/privato, quindi dovrai attendere fino a quando un amministratore accetta la tua richiesta", + "closed_group": "questo è un gruppo chiuso/privato, quindi occorre attendere fino a quando un amministratore accetta la richiesta", "descrypt_wallet": "decrittazione del wallet ...", "encryption_key": "la prima chiave di crittografia comune del gruppo è in fase di creazione. Si prega di attendere qualche minuto per essere recuperato dalla rete. Controllo ogni 2 minuti ...", "group_announcement": "annunci di gruppo", "group_approval_threshold": "soglia di approvazione del gruppo (numero / percentuale di amministratori che devono approvare una transazione)", "group_encrypted": "gruppo crittografato", - "group_invited_you": "{{group}} has invited you", - "group_key_created": "primo tasto di gruppo creato.", - "group_member_list_changed": "l'elenco dei membri del gruppo è cambiato. Si prega di rivivere nuovamente la chiave segreta.", + "group_invited_you": "{{group}} ti ha invitato", + "group_key_created": "creata la prima chiave di gruppo.", + "group_member_list_changed": "l'elenco dei membri del gruppo è cambiato. Si prega di ricriptare nuovamente la chiave segreta.", "group_no_secret_key": "non esiste una chiave segreta di gruppo. Potresti essere il primo amministratore a pubblicarne una!", - "group_secret_key_no_owner": "l'ultima chiave segreta del gruppo è stata pubblicata da un non proprietario. Come proprietario del gruppo si prega di ricriptare la chiave per sicurezza.", + "group_secret_key_no_owner": "l'ultima chiave segreta del gruppo è stata pubblicata da un non proprietario. Per sicurezza come proprietario del gruppo si prega di ricriptare la chiave.", "invalid_content": "contenuto non valido, mittente o timestamp nei dati di reazione", - "invalid_data": "contenuto di caricamento degli errori: dati non validi", + "invalid_data": "errore di caricamento del contenuto: dati non validi", "latest_promotion": "verrà mostrata solo l'ultima promozione della settimana per il tuo gruppo.", - "loading_members": "caricamento dell'elenco dei membri con nomi ... Attendi.", + "loading_members": "caricamento dell'elenco dei membri con nomi ... Attendere.", "max_chars": "max 200 caratteri. Commissione", "manage_minting": "gestisci il minting", "minter_group": "al momento non fai parte del gruppo Minter", "mintership_app": "visita l'app Q-Mintership per chiedere di diventare un minter", "minting_account": "account di minting:", - "minting_keys_per_node": "sono ammessi solo 2 chiavi di minting per nodo. Rimuovine una se si desidera fare minting con questo account.", + "minting_keys_per_node": "sono ammessi solo 2 chiavi di minting per nodo. Rimuoverne una se si desidera fare minting con questo account.", "minting_keys_per_node_different": "sono ammessi solo 2 chiavi di minting per nodo. Rimuovi uno se desideri aggiungere un account diverso.", "next_level": "blocchi mancanti al livello successivo:", "node_minting": "questo nodo sta coniando:", @@ -98,11 +98,11 @@ "no_announcement": "nessun annuncio", "no_display": "niente da visualizzare", "no_selection": "nessun gruppo selezionato", - "not_part_group": "non fai parte del gruppo crittografato di membri. Attendi che un amministratore ricifri le chiavi.", + "not_part_group": "non fai parte del gruppo crittografato di membri. Attendere che un amministratore ricripti le chiavi.", "only_encrypted": "verranno visualizzati solo messaggi non crittografati.", "only_private_groups": "verranno mostrati solo gruppi privati", - "pending_join_requests": "{{ group }} ha {{ count }} richieste pendenti di join", - "private_key_copied": "copiata a chiave privata", + "pending_join_requests": "{{ group }} ha {{ count }} richieste pendenti di adesione", + "private_key_copied": "chiave privata copiata", "provide_message": "si prega di fornire un primo messaggio al thread", "secure_place": "mantieni la chiave privata in un luogo sicuro. Non condividerla!", "setting_group": "impostazione gruppo. Attendere, per favore." @@ -113,10 +113,10 @@ "description_required": "si prega di fornire una descrizione", "group_info": "impossibile accedere alle informazioni del gruppo", "group_join": "impossibile aderire al gruppo", - "group_promotion": "errore che pubblica la promozione. Per favore riprova", + "group_promotion": "errore di pubblicazione della promozione. Per favore riprovare", "group_secret_key": "impossibile ottenere la chiave segreta del gruppo", "name_required": "si prega di fornire un nome", - "notify_admins": "prova a avvisare un amministratore dall'elenco degli amministratori di seguito:", + "notify_admins": "prova a notificare un amministratore dall'elenco degli amministratori seguente:", "qortals_required": "occorrono almeno {{ quantity }} QORT per inviare un messaggio", "timeout_reward": "timeout in attesa di conferma della condivisione della ricompensa", "thread_id": "impossibile individuare il thread ID", @@ -124,40 +124,40 @@ "unable_minting": "impossibile iniziare a coniare" }, "success": { - "group_ban": "membro escluso con successo dal gruppo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", - "group_creation": "gruppo creato correttamente. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "group_ban": "membro escluso con successo dal gruppo. Potrebbero essere necessari un paio di minuti per propagare le modifiche.", + "group_creation": "gruppo creato correttamente. Potrebbero essere necessari un paio di minuti per propagare le modifiche.", "group_creation_name": "creato il gruppo {{group_name}}: attendere la conferma", - "group_creation_label": "creato il grupp {{name}}: successo!", - "group_invite": "invitato con successo {{invitee}}. Potrebbero essere necessari un paio di minuti per propagare le modifiche", - "group_join": "richiesto con successo di unirsi al gruppo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "group_creation_label": "creato il gruppo {{name}}: successo!", + "group_invite": "invitato con successo {{invitee}}. Potrebbero essere necessari un paio di minuti per propagare le modifiche.", + "group_join": "richiesto con successo di unirsi al gruppo. Potrebbero essere necessari un paio di minuti per propagare le modifiche.", "group_join_name": "adesione al gruppo {{group_name}}: attendere la conferma", "group_join_label": "adesione al gruppo {{name}}: success!", "group_join_request": "richiesta di adesione al gruppo {{group_name}}: attendere la conferma", "group_join_outcome": "richiesta di adesione al gruppo {{group_name}}: successo!", - "group_kick": "il membro è stato escludo dal gruppo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", - "group_leave": "richiesto con successo di lasciare il gruppo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "group_kick": "il membro è stato escludo dal gruppo. Potrebbero essere necessari un paio di minuti per propagare le modifiche.", + "group_leave": "richiesto con successo l'abbandono del gruppo. Potrebbero essere necessari un paio di minuti per propagare le modifiche.", "group_leave_name": "abbandonato il gruppo {{group_name}}: attendere la conferma", - "group_leave_label": "abbandonato il gruppo {{name}}: success!", - "group_member_admin": "il membro è ora amministratore. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "group_leave_label": "abbandonato il gruppo {{name}}: successo!", + "group_member_admin": "il membro è ora amministratore. Potrebbero essere necessari un paio di minuti per propagare le modifiche.", "group_promotion": "promozione pubblicata con successo. Potrebbero essere necessari un paio di minuti per la promozione", - "group_remove_member": "rimosso con successo il membro come amministratore. Potrebbero essere necessari un paio di minuti per propagare le modifiche", - "invitation_cancellation": "invito annullato con successo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", - "invitation_request": "richiesta di join accettata: in attesa di conferma", + "group_remove_member": "rimosso con successo il membro come amministratore. Potrebbero essere necessari un paio di minuti per propagare le modifiche.", + "invitation_cancellation": "invito annullato con successo. Potrebbero essere necessari un paio di minuti per propagare le modifiche.", + "invitation_request": "richiesta di adesione accettata: in attesa di conferma", "loading_threads": "caricamento dei thread ... Attendi.", "post_creation": "post creato correttamente. Potrebbe essere necessario del tempo per la propagazione della pubblicazione", "published_secret_key": "pubblicata la secret key per il gruppo {{ group_id }}: attendere la conferma", "published_secret_key_label": "pubblicata la secret key per il gruppo {{ group_id }}: successo!", - "registered_name": "registrato con successo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "registered_name": "registrato con successo. Potrebbero essere necessari un paio di minuti per propagare le modifiche.", "registered_name_label": "nome registrato: in attesa di conferma. Questo potrebbe richiedere un paio di minuti.", "registered_name_success": "nome registrato: successo!", "rewardshare_add": "aggiungi ricompensa: in attesa di conferma", "rewardshare_add_label": "aggiungi ricompensa: successo!", - "rewardshare_creation": "confermare la creazione di ricompensa sulla catena. Si prega di essere paziente, potrebbe richiedere fino a 90 secondi.", + "rewardshare_creation": "confermare la creazione di ricompensa sulla catena. Si prega di pazientare, potrebbe richiedere fino a 90 secondi.", "rewardshare_confirmed": "ricompensa confermata. Fare clic su Avanti.", "rewardshare_remove": "rimuovi la ricompensa: in attesa di conferma", "rewardshare_remove_label": "rimuovi la ricompensa: successo!", - "thread_creation": "thread creato correttamente. Potrebbe essere necessario del tempo per la propagazione della pubblicazione", - "unbanned_user": "utente riammesso con successo. Potrebbero essere necessari un paio di minuti per propagare le modifiche", + "thread_creation": "thread creato correttamente. Potrebbe essere necessario del tempo per la propagazione della pubblicazione.", + "unbanned_user": "utente riammesso con successo. Potrebbero essere necessari un paio di minuti per propagare le modifiche.", "user_joined": "l'utente si è unito con successo!" } }, From 6320e37d91de08602ebfde3da26f4f3c2995d058 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 19:02:03 +0200 Subject: [PATCH 578/717] Some refactoring --- src/i18n/locales/de/group.json | 2 +- src/i18n/locales/es/group.json | 2 +- src/i18n/locales/fr/group.json | 2 +- src/i18n/locales/it/core.json | 4 ++-- src/i18n/locales/ja/group.json | 2 +- src/i18n/locales/ru/group.json | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/i18n/locales/de/group.json b/src/i18n/locales/de/group.json index 32d3c1f..f41eedd 100644 --- a/src/i18n/locales/de/group.json +++ b/src/i18n/locales/de/group.json @@ -57,7 +57,7 @@ "invitation_expiry": "einladungszeit", "invitees_list": "lädt die Liste ein", "join_link": "gruppenverbindung beibringen", - "join_requests": "schließen Sie Anfragen an", + "join_requests": "join-Anfragen", "last_message": "letzte Nachricht", "last_message_date": "last message: {{date }}", "latest_mails": "letzte Q-Mails", diff --git a/src/i18n/locales/es/group.json b/src/i18n/locales/es/group.json index 437f0f4..10a8056 100644 --- a/src/i18n/locales/es/group.json +++ b/src/i18n/locales/es/group.json @@ -57,7 +57,7 @@ "invitation_expiry": "tiempo de vencimiento de la invitación", "invitees_list": "lista de invitados", "join_link": "unir el enlace grupal", - "join_requests": "unir las solicitudes", + "join_requests": "solicitudes de membresía", "last_message": "último mensaje", "last_message_date": "last message: {{date }}", "latest_mails": "Últimos correo electrónico", diff --git a/src/i18n/locales/fr/group.json b/src/i18n/locales/fr/group.json index d31c026..cb82fa4 100644 --- a/src/i18n/locales/fr/group.json +++ b/src/i18n/locales/fr/group.json @@ -57,7 +57,7 @@ "invitation_expiry": "temps d'expiration de l'invitation", "invitees_list": "liste des invités", "join_link": "rejoignez le lien de groupe", - "join_requests": "joindre les demandes", + "join_requests": "demandes d'adhésion", "last_message": "dernier message", "last_message_date": "last message: {{date }}", "latest_mails": "dernier Q-Mails", diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 9a6af16..21e4c7f 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -72,7 +72,7 @@ "register_name": "registra nome", "remove": "rimuovi", "remove_reaction": "rimuovi la reazione", - "return_apps_dashboard": "torna alla dashboard di app", + "return_apps_dashboard": "torna alla dashboard delle app", "save": "salva", "save_disk": "salva su disco", "search": "ricerca", @@ -133,7 +133,7 @@ "dev_mode": "modalità Dev", "domain": "dominio", "ui": { - "version": "versione dell'interfaccia utente" + "version": "versione UI" }, "count": { "none": "nessuno", diff --git a/src/i18n/locales/ja/group.json b/src/i18n/locales/ja/group.json index a1e2dc0..909f9e8 100644 --- a/src/i18n/locales/ja/group.json +++ b/src/i18n/locales/ja/group.json @@ -57,7 +57,7 @@ "invitation_expiry": "招待状の有効期限", "invitees_list": "招待リスト", "join_link": "グループリンクに参加します", - "join_requests": "リクエストに参加します", + "join_requests": "参加リクエスト", "last_message": "最後のメッセージ", "last_message_date": "last message: {{date }}", "latest_mails": "最新のQメール", diff --git a/src/i18n/locales/ru/group.json b/src/i18n/locales/ru/group.json index 18f61f7..2fe9e5f 100644 --- a/src/i18n/locales/ru/group.json +++ b/src/i18n/locales/ru/group.json @@ -57,7 +57,7 @@ "invitation_expiry": "Приглашение время истечения", "invitees_list": "Список приглашений", "join_link": "Присоединяйтесь к групповой ссылке", - "join_requests": "присоединиться к запросам", + "join_requests": "запросы на присоединение", "last_message": "Последнее сообщение", "last_message_date": "last message: {{date }}", "latest_mails": "Последние Q-Mails", From 07e67ada740ecc39aaba96c00e9b0c739ec74bef Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 19:11:20 +0200 Subject: [PATCH 579/717] Check all file --- src/i18n/locales/it/core.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 21e4c7f..eac7aec 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -152,7 +152,7 @@ "general_settings": "impostazioni generali", "home": "casa", "identifier": "identificativo", - "image_embed": "immagine incorporare", + "image_embed": "immagine incorporata", "last_height": "ultima altezza", "level": "livello", "library": "libreria", @@ -185,13 +185,13 @@ "encrypt_app": "impossibile crittografare. App non pubblicata.", "fetch_app": "impossibile recuperare l'app.", "fetch_publish": "impossibile recuperare la pubblicazione.", - "file_too_large": "file {{ filename }} is too large. Max size allowed is {{ size }} MB.", + "file_too_large": "il file {{ filename }} è troppo grande. La massima dimensione può essere di {{ size }} MB.", "generic": "si è verificato un errore", "initiate_download": "impossibile avviare il download.", "invalid_amount": "importo non valido", "invalid_base64": "dati Base64 non validi", "invalid_embed_link": "collegamento incorporato non valido", - "invalid_image_embed_link_name": "link incorporato dell'immagine non valido. Param mancante.", + "invalid_image_embed_link_name": "link incorporato dell'immagine non valido. Parametro mancante.", "invalid_poll_embed_link_name": "link incorporato del sondaggio non valido. Nome mancante.", "invalid_signature": "firma non valida", "invalid_theme_format": "formato tema non valido", @@ -200,7 +200,7 @@ "message_size": "la dimensione del messaggio è di {{ size }} byte su un massimo di {{maximum }}", "minting_account_add": "impossibile aggiungere l'account di minting.", "minting_account_remove": "impossibile rimuovere l'account di minting.", - "missing_fields": "missing: {{ fields }}", + "missing_fields": "mancano i campi: {{ fields }}", "navigation_timeout": "timeout di navigazione", "network_generic": "errore di rete", "password_not_matching": "i campi della password non corrispondono!", @@ -223,9 +223,9 @@ "created_by": "creato da {{ owner }}", "buy_order_request": "l'applicazione
    {{hostname}}
    sta effettuando {{count}} ordine d'acquisto.", "buy_order_request_other": "l'applicazione
    {{hostname}}
    sta effettuando {{count}} ordini d'acquisto.", - "devmode_local_node": "si prega di utilizzare il tuo nodo locale per la modalità Dev! Logout e usa il nodo locale.", + "devmode_local_node": "si prega di utilizzare il tuo nodo locale per la modalità Dev! Scollegati ed usa il nodo locale.", "downloading": "download", - "downloading_decrypting_app": "download e decritting di app private.", + "downloading_decrypting_app": "download e decriptazione dell'app privata.", "edited": "modificato", "editing_message": "messaggio di modifica", "encrypted": "crittografato", @@ -245,7 +245,7 @@ "name_publish": "hai bisogno di un nome Qortal per pubblicare", "name_rate": "hai bisogno di un nome da valutare.", "name_registration": "il tuo saldo è {{ balance }} QORT. La registrazione di un nome richiede una commissione di {{ fee }} QORT", - "name_unavailable": "{{ name }} is unavailable", + "name_unavailable": "{{ name }} non disponibile", "no_data_image": "nessun dato per l'immagine", "no_description": "nessuna descrizione", "no_messages": "nessun messaggio", @@ -293,12 +293,12 @@ "provide_thread": "si prega di fornire un titolo al thread", "publish_app": "vorresti pubblicare questa app?", "publish_avatar": "vorresti pubblicare un avatar?", - "publish_qdn": "vorresti pubblicare le tue impostazioni su QDN (crittografato)?", + "publish_qdn": "vorresti pubblicare le impostazioni su QDN (crittografato)?", "overwrite_changes": "l'app non è stata in grado di scaricare le app bloccate a QDN esistenti. Vorresti sovrascrivere quei cambiamenti?", "rate_app": "vorresti dare il voto {{ rate }} a quest'app?. Questo creerà una transazione POLL.", "register_name": "vorresti registrare questo nome?", - "reset_pinned": "non ti piacciono le tue attuali modifiche locali? Vorresti ripristinare le app bloccate predefinite?", - "reset_qdn": "non ti piacciono le tue attuali modifiche locali? Vorresti ripristinare le app QDN salvate?", + "reset_pinned": "non ti piacciono le attuali modifiche locali? Vorresti ripristinare le app bloccate predefinite?", + "reset_qdn": "non ti piacciono le attuali modifiche locali? Vorresti ripristinare le app QDN salvate?", "transfer_qort": "vuoi trasferire {{ amount }} QORT?" }, "status": { @@ -308,7 +308,7 @@ "synchronizing": "sincronizzazione" }, "success": { - "order_submitted": "il tuo ordine di acquisto è stato inviato", + "order_submitted": "l'ordine di acquisto è stato inviato", "published": "pubblicato con successo. Si prega di attendere un paio di minuti affinché la rete propaghi le modifiche.", "published_qdn": "pubblicato con successo su QDN", "rated_app": "valutato con successo. Si prega di attendere un paio di minuti affinché la rete propaghi le modifiche.", From 0bfcd296138f80e654e8fff0f13513d8ee104e1f Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 19:22:53 +0200 Subject: [PATCH 580/717] Update --- src/i18n/locales/it/core.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index eac7aec..59243c5 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -274,7 +274,7 @@ "secure_ownership": "sicura proprietà dei dati pubblicati con il tuo nome. Puoi anche vendere il tuo nome, insieme ai tuoi dati a una terza parte.", "select_file": "seleziona un file", "select_image": "seleziona un'immagine per un logo", - "select_zip": "seleziona il file .zip contenente contenuto statico:", + "select_zip": "seleziona il file .zip contenente il contenuto statico:", "sending": "invio ...", "settings": "si utilizza il modo di esportazione/importazione per salvare le impostazioni.", "space_for_admins": "mi dispiace, questo spazio è solo per gli amministratori.", From 622fccf3166ca497da3d73825361542ed4db1951 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 19:30:30 +0200 Subject: [PATCH 581/717] Revert to setting null --- src/components/Apps/AppsNavBarDesktop.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Apps/AppsNavBarDesktop.tsx b/src/components/Apps/AppsNavBarDesktop.tsx index b26391f..0b0006c 100644 --- a/src/components/Apps/AppsNavBarDesktop.tsx +++ b/src/components/Apps/AppsNavBarDesktop.tsx @@ -67,7 +67,7 @@ export function saveToLocalStorage(key, subKey, newValue) { export const AppsNavBarDesktop = ({ disableBack }) => { const [tabs, setTabs] = useState([]); - const [selectedTab, setSelectedTab] = useState(0); + const [selectedTab, setSelectedTab] = useState(null); const [navigationController, setNavigationController] = useAtom( navigationControllerAtom ); @@ -125,7 +125,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => { const setTabsToNav = (e) => { const { tabs, selectedTab, isNewTabWindow } = e.detail?.data; setTabs([...tabs]); - setSelectedTab(!selectedTab ? 0 : { ...selectedTab }); + setSelectedTab(!selectedTab ? null : { ...selectedTab }); setIsNewTabWindow(isNewTabWindow); }; From 95ec67950853642aab4b01ed21f6cf188696bd43 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 30 May 2025 19:31:34 +0200 Subject: [PATCH 582/717] Revert to setting null --- src/components/Apps/AppsNavBarDesktop.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Apps/AppsNavBarDesktop.tsx b/src/components/Apps/AppsNavBarDesktop.tsx index 0b0006c..9835628 100644 --- a/src/components/Apps/AppsNavBarDesktop.tsx +++ b/src/components/Apps/AppsNavBarDesktop.tsx @@ -236,7 +236,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => { > { - setSelectedTab(0); + setSelectedTab(null); executeEvent('newTabWindow', {}); }} > From 47d9e57db037bff2954ab4cf93a58895347c47a4 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 2 Jun 2025 21:25:11 +0200 Subject: [PATCH 583/717] Apply theme color to mode icon --- src/components/Theme/ThemeSelector.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/Theme/ThemeSelector.tsx b/src/components/Theme/ThemeSelector.tsx index 1d269a7..18fa5cc 100644 --- a/src/components/Theme/ThemeSelector.tsx +++ b/src/components/Theme/ThemeSelector.tsx @@ -1,5 +1,5 @@ import { useThemeContext } from './ThemeContext'; -import { Box, IconButton, Tooltip } from '@mui/material'; +import { Box, IconButton, Tooltip, useTheme } from '@mui/material'; import LightModeIcon from '@mui/icons-material/LightMode'; import DarkModeIcon from '@mui/icons-material/DarkMode'; import { useTranslation } from 'react-i18next'; @@ -15,6 +15,7 @@ const ThemeSelector = () => { ]); const { themeMode, toggleTheme } = useThemeContext(); const selectorRef = useRef(null); + const theme = useTheme(); return ( @@ -29,7 +30,12 @@ const ThemeSelector = () => { }) } > - + {themeMode === 'dark' ? : } From 9bd352577b63408a34b978c80f262b3314cc7751 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 4 Jun 2025 08:36:44 +0200 Subject: [PATCH 584/717] Some improvements --- src/i18n/locales/it/auth.json | 2 +- src/i18n/locales/it/core.json | 4 ++-- src/i18n/locales/it/group.json | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/i18n/locales/it/auth.json b/src/i18n/locales/it/auth.json index df1139f..2d20e5c 100644 --- a/src/i18n/locales/it/auth.json +++ b/src/i18n/locales/it/auth.json @@ -117,7 +117,7 @@ "digital_id": "il tuo wallet è come il tuo ID digitale su Qortal ed e verrà usato per accedere a Qortal. Contiene il tuo indirizzo pubblico e il nome Qortal che alla fine sceglierai. Ogni transazione che fai è collegata al tuo ID, nel quale potrai gestire tutte le tue criptovalute Qort e altre criptovalute negoziabili su Qortal.", "existing_account": "hai già un account Qortal? Inserisci qui la tua frase di backup segreta per accedervi. Questa frase è uno dei modi per recuperare il tuo account.", "key_encrypt_admin": "questa chiave crittografa i contenuti relativi ad amministratore. Solo gli amministratori vedranno il contenuto crittografato.", - "key_encrypt_group": "questa chiave crittografa i contenuti relativi al gruppo. Questo al momento è l'unico modo usato. Tutti i membri del gruppo saranno in grado di vedere i contenuti crittografati con questa chiave.", + "key_encrypt_group": "questa chiave crittografa i contenuti relativi al gruppo. Per ora questo è l'unico modo usato. Tutti i membri del gruppo saranno in grado di vedere i contenuti crittografati con questa chiave.", "new_account": "la creazione di un account consiste nella creazione di un wallet e di un ID digitale per iniziare a utilizzare Qortal. Una volta creato l'account, potrai iniziare a fare cose come ottenere dei Qort, acquistare un nome e un Avatar, pubblicare video e blog e molto altro.", "new_users": "i nuovi utenti iniziano qui!", "safe_place": "salva il tuo account in un posto da ricordare!", diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 59243c5..9484f7c 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -252,9 +252,9 @@ "no_minting_details": "impossibile visualizzare i dettagli di minire sul gateway", "no_notifications": "nessuna nuova notifica", "no_payments": "nessun pagamento", - "no_pinned_changes": "attualmente non hai modifiche alle tue app bloccate", + "no_pinned_changes": "per ora non ci sono modifiche alle app bloccate", "no_results": "nessun risultato", - "one_app_per_name": "nota: attualmente, sono consentiti solo un'app e un sito Web per nome.", + "one_app_per_name": "nota: per adesso sono consentiti solo un'app e un sito Web per nome.", "opened": "aperto", "overwrite_qdn": "sovrascrivi a QDN", "password_confirm": "si prega di confermare una password", diff --git a/src/i18n/locales/it/group.json b/src/i18n/locales/it/group.json index 12f1d5e..7357c94 100644 --- a/src/i18n/locales/it/group.json +++ b/src/i18n/locales/it/group.json @@ -77,28 +77,28 @@ "group_encrypted": "gruppo crittografato", "group_invited_you": "{{group}} ti ha invitato", "group_key_created": "creata la prima chiave di gruppo.", - "group_member_list_changed": "l'elenco dei membri del gruppo è cambiato. Si prega di ricriptare nuovamente la chiave segreta.", + "group_member_list_changed": "l'elenco dei membri del gruppo è cambiato. Si prega di recriptare nuovamente la chiave segreta.", "group_no_secret_key": "non esiste una chiave segreta di gruppo. Potresti essere il primo amministratore a pubblicarne una!", - "group_secret_key_no_owner": "l'ultima chiave segreta del gruppo è stata pubblicata da un non proprietario. Per sicurezza come proprietario del gruppo si prega di ricriptare la chiave.", + "group_secret_key_no_owner": "l'ultima chiave segreta del gruppo è stata pubblicata da un non proprietario. Per sicurezza, come proprietario del gruppo, si prega di recriptare la chiave.", "invalid_content": "contenuto non valido, mittente o timestamp nei dati di reazione", "invalid_data": "errore di caricamento del contenuto: dati non validi", "latest_promotion": "verrà mostrata solo l'ultima promozione della settimana per il tuo gruppo.", "loading_members": "caricamento dell'elenco dei membri con nomi ... Attendere.", "max_chars": "max 200 caratteri. Commissione", "manage_minting": "gestisci il minting", - "minter_group": "al momento non fai parte del gruppo Minter", + "minter_group": "per ora non fai parte del gruppo Minter", "mintership_app": "visita l'app Q-Mintership per chiedere di diventare un minter", "minting_account": "account di minting:", - "minting_keys_per_node": "sono ammessi solo 2 chiavi di minting per nodo. Rimuoverne una se si desidera fare minting con questo account.", - "minting_keys_per_node_different": "sono ammessi solo 2 chiavi di minting per nodo. Rimuovi uno se desideri aggiungere un account diverso.", + "minting_keys_per_node": "sono ammesse solo 2 chiavi di minting per nodo. Rimuoverne una se si desidera fare minting con questo account.", + "minting_keys_per_node_different": "sono ammesse solo 2 chiavi di minting per nodo. Rimuovine una se desideri aggiungere un account diverso.", "next_level": "blocchi mancanti al livello successivo:", "node_minting": "questo nodo sta coniando:", "node_minting_account": "account minting del nodo", - "node_minting_key": "attualmente hai una chiave di minting per questo account collegata al nodo", + "node_minting_key": "hai una chiave di minting per questo account collegata al nodo", "no_announcement": "nessun annuncio", "no_display": "niente da visualizzare", "no_selection": "nessun gruppo selezionato", - "not_part_group": "non fai parte del gruppo crittografato di membri. Attendere che un amministratore ricripti le chiavi.", + "not_part_group": "non fai parte del gruppo crittografato di membri. Attendere che un amministratore recripti le chiavi.", "only_encrypted": "verranno visualizzati solo messaggi non crittografati.", "only_private_groups": "verranno mostrati solo gruppi privati", "pending_join_requests": "{{ group }} ha {{ count }} richieste pendenti di adesione", From 957cbe9ef9d742f6d828df53a746e6fe8d94a005 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 4 Jun 2025 09:13:16 +0200 Subject: [PATCH 585/717] Refactor translation --- src/i18n/locales/es/auth.json | 2 +- src/i18n/locales/fr/auth.json | 2 +- src/i18n/locales/it/auth.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/i18n/locales/es/auth.json b/src/i18n/locales/es/auth.json index 5105809..d426e35 100644 --- a/src/i18n/locales/es/auth.json +++ b/src/i18n/locales/es/auth.json @@ -117,7 +117,7 @@ "digital_id": "su billetera es como su ID digital en Qortal, y es cómo iniciará sesión en la interfaz de usuario de Qortal. Sostiene su dirección pública y el nombre Qortal que eventualmente elegirá. Cada transacción que realice está vinculada a su ID, y aquí es donde administra todos sus Qort y otras criptomonedas comercializables en Qortal.", "existing_account": "¿Ya tienes una cuenta Qortal? Ingrese su frase de copia de seguridad secreta aquí para acceder a ella. Esta frase es una de las formas de recuperar su cuenta.", "key_encrypt_admin": "esta clave es cifrar el contenido relacionado con el administrador. Solo los administradores verían contenido encriptado con él.", - "key_encrypt_group": "esta clave es cifrar contenido relacionado con el grupo. Este es el único utilizado en esta interfaz de usuario a partir de ahora. Todos los miembros del grupo podrán ver contenido encriptado con esta clave.", + "key_encrypt_group": "esta clave es cifrar contenido relacionado con el grupo. Esta es el única utilizada en esta interfaz de usuario a partir de ahora. Todos los miembros del grupo podrán ver contenido encriptado con esta clave.", "new_account": "crear una cuenta significa crear una nueva billetera e ID digital para comenzar a usar Qortal. Una vez que haya hecho su cuenta, puede comenzar a hacer cosas como obtener algo de Qort, comprar un nombre y avatar, publicar videos y blogs, y mucho más.", "new_users": "¡Los nuevos usuarios comienzan aquí!", "safe_place": "¡Guarde su cuenta en un lugar donde la recordará!", diff --git a/src/i18n/locales/fr/auth.json b/src/i18n/locales/fr/auth.json index 1b91356..11b0e85 100644 --- a/src/i18n/locales/fr/auth.json +++ b/src/i18n/locales/fr/auth.json @@ -117,7 +117,7 @@ "digital_id": "votre portefeuille est comme votre ID numérique sur Qortal, et c'est comment vous vous connectez à l'interface utilisateur Qortal. Il détient votre adresse publique et le nom Qortal que vous allez éventuellement choisir. Chaque transaction que vous effectuez est liée à votre identifiant, et c'est là que vous gérez tous vos Qort et autres crypto-monnaies négociables sur Qortal.", "existing_account": "vous avez déjà un compte Qortal? Entrez ici votre phrase de sauvegarde secrète pour y accéder. Cette phrase est l'une des façons de récupérer votre compte.", "key_encrypt_admin": "cette clé est de crypter le contenu lié à l'administrateur. Seuls les administrateurs verraient du contenu chiffré avec.", - "key_encrypt_group": "cette clé est de chiffrer le contenu lié au groupe. C'est le seul utilisé dans cette interface utilisateur pour l'instant. Tous les membres du groupe pourront voir du contenu crypté avec cette clé.", + "key_encrypt_group": "cette clé est de chiffrer le contenu lié au groupe. C'est la seule utilisée dans cette interface utilisateur pour l'instant. Tous les membres du groupe pourront voir du contenu crypté avec cette clé.", "new_account": "la création d'un compte signifie créer un nouvel portefeuille et un nouvel ID numérique pour commencer à utiliser Qortal. Une fois que vous avez créé votre compte, vous pouvez commencer à faire des choses comme obtenir du Qort, acheter un nom et un avatar, publier des vidéos et des blogs, et bien plus encore.", "new_users": "les nouveaux utilisateurs commencent ici!", "safe_place": "enregistrez votre compte dans un endroit où vous vous en souviendrez!", diff --git a/src/i18n/locales/it/auth.json b/src/i18n/locales/it/auth.json index 2d20e5c..6c464e5 100644 --- a/src/i18n/locales/it/auth.json +++ b/src/i18n/locales/it/auth.json @@ -117,7 +117,7 @@ "digital_id": "il tuo wallet è come il tuo ID digitale su Qortal ed e verrà usato per accedere a Qortal. Contiene il tuo indirizzo pubblico e il nome Qortal che alla fine sceglierai. Ogni transazione che fai è collegata al tuo ID, nel quale potrai gestire tutte le tue criptovalute Qort e altre criptovalute negoziabili su Qortal.", "existing_account": "hai già un account Qortal? Inserisci qui la tua frase di backup segreta per accedervi. Questa frase è uno dei modi per recuperare il tuo account.", "key_encrypt_admin": "questa chiave crittografa i contenuti relativi ad amministratore. Solo gli amministratori vedranno il contenuto crittografato.", - "key_encrypt_group": "questa chiave crittografa i contenuti relativi al gruppo. Per ora questo è l'unico modo usato. Tutti i membri del gruppo saranno in grado di vedere i contenuti crittografati con questa chiave.", + "key_encrypt_group": "questa chiave crittografa i contenuti relativi al gruppo. Per ora questa è l'unica utilizzata in quest'interfaccia grafica. Tutti i membri del gruppo saranno in grado di vedere i contenuti crittografati con questa chiave.", "new_account": "la creazione di un account consiste nella creazione di un wallet e di un ID digitale per iniziare a utilizzare Qortal. Una volta creato l'account, potrai iniziare a fare cose come ottenere dei Qort, acquistare un nome e un Avatar, pubblicare video e blog e molto altro.", "new_users": "i nuovi utenti iniziano qui!", "safe_place": "salva il tuo account in un posto da ricordare!", From 4ddd2cd589ef115f003c7003026b51ee27d57c1b Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 4 Jun 2025 09:15:46 +0200 Subject: [PATCH 586/717] Update --- src/i18n/locales/it/auth.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/locales/it/auth.json b/src/i18n/locales/it/auth.json index 6c464e5..18055c7 100644 --- a/src/i18n/locales/it/auth.json +++ b/src/i18n/locales/it/auth.json @@ -95,7 +95,7 @@ "your_accounts": "i tuoi conti salvati" }, "success": { - "reencrypted_secret_key": "chiave segreta recriptata con successo. Potrebbero essere necessari un paio di minuti per propagare le modifiche. Aggiorna il gruppo in 5 minuti." + "reencrypted_secret_key": "chiave segreta recriptata con successo. Potrebbero essere necessari un paio di minuti per propagare le modifiche. Aggiorna il gruppo fra 5 minuti." } }, "node": { @@ -122,7 +122,7 @@ "new_users": "i nuovi utenti iniziano qui!", "safe_place": "salva il tuo account in un posto da ricordare!", "view_seedphrase": "se si desidera visualizzare la seed phrase, fai clic sulla parola \"seed phrase\" in questo testo. Le seed phrase vengono utilizzate per generare la chiave privata per il tuo account Qortal. Per la sicurezza per impostazione predefinita, le seed phrase non vengono visualizzate se non specificamente scelte.", - "wallet_secure": "mantieni al sicuro il tuo file di wallet." + "wallet_secure": "mantieni al sicuro il tuo file del wallet." }, "wallet": { "password_confirmation": "conferma la password del wallet", From 89507cd037616fe7b3308b9fc4af0be58240c4ea Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 4 Jun 2025 14:33:58 +0300 Subject: [PATCH 587/717] change support text and link --- src/components/NewUsersCTA.tsx | 9 ++--- src/constants/constants.ts | 3 ++ src/i18n/locales/de/core.json | 2 +- src/i18n/locales/de/question.json | 4 ++- src/i18n/locales/en/core.json | 2 +- src/i18n/locales/en/question.json | 4 ++- src/i18n/locales/es/core.json | 2 +- src/i18n/locales/es/question.json | 4 ++- src/i18n/locales/fr/core.json | 2 +- src/i18n/locales/fr/question.json | 4 ++- src/i18n/locales/it/core.json | 2 +- src/i18n/locales/it/question.json | 4 ++- src/i18n/locales/ja/core.json | 2 +- src/i18n/locales/ja/question.json | 4 ++- src/i18n/locales/ru/core.json | 2 +- src/i18n/locales/ru/question.json | 4 ++- src/i18n/locales/zh/core.json | 2 +- src/i18n/locales/zh/question.json | 4 ++- src/qortal/get.ts | 57 ++++++++++++++++++++++++++++++- 19 files changed, 94 insertions(+), 23 deletions(-) diff --git a/src/components/NewUsersCTA.tsx b/src/components/NewUsersCTA.tsx index d444717..0618ce5 100644 --- a/src/components/NewUsersCTA.tsx +++ b/src/components/NewUsersCTA.tsx @@ -70,17 +70,14 @@ export const NewUsersCTA = ({ balance }) => { onClick={() => { if (window?.electronAPI?.openExternal) { window.electronAPI.openExternal( - 'https://link.qortal.dev/telegram-invite' + 'https://link.qortal.dev/support' ); } else { - window.open( - 'https://link.qortal.dev/telegram-invite', - '_blank' - ); + window.open('https://link.qortal.dev/support', '_blank'); } }} > - Telegram + Nextcloud MAX_SIZE_PUBLISH) { + throw new Error( + i18n.t('question:message.error.max_size_publish', { + size: 2, + postProcess: 'capitalizeFirstChar', + }) + ); + } + + if (file && file.size > MAX_SIZE_PUBLIC_NODE) { + const isPublicNode = await isRunningGateway(); + if (isPublicNode) { + throw new Error( + i18n.t('question:message.error.max_size_publish_public', { + size: 500, + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + // Fill tags dynamically while maintaining backward compatibility for (let i = 0; i < 5; i++) { result[`tag${i + 1}`] = tags[i] || data[`tag${i + 1}`] || undefined; @@ -1542,6 +1567,36 @@ export const publishMultipleQDNResources = async ( }) ); } + const isPublicNode = await isRunningGateway(); + if (isPublicNode) { + const hasOversizedFilePublicNode = resources.some((resource) => { + const file = resource?.file; + return file instanceof File && file.size > MAX_SIZE_PUBLIC_NODE; + }); + + if (hasOversizedFilePublicNode) { + throw new Error( + i18n.t('question:message.error.max_size_publish_public', { + size: 500, + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + + const hasOversizedFile = resources.some((resource) => { + const file = resource?.file; + return file instanceof File && file.size > MAX_SIZE_PUBLISH; + }); + + if (hasOversizedFile) { + throw new Error( + i18n.t('question:message.error.max_size_publish', { + size: 2, + postProcess: 'capitalizeFirstChar', + }) + ); + } const encrypt = data?.encrypt; From e0eb53530e0dc26035e7be5faf8ef2ec1d2d5b50 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 4 Jun 2025 14:33:58 +0300 Subject: [PATCH 588/717] change support text and link --- src/components/NewUsersCTA.tsx | 9 ++--- src/constants/constants.ts | 3 ++ src/i18n/locales/de/core.json | 2 +- src/i18n/locales/de/question.json | 4 ++- src/i18n/locales/en/core.json | 2 +- src/i18n/locales/en/question.json | 4 ++- src/i18n/locales/es/core.json | 2 +- src/i18n/locales/es/question.json | 4 ++- src/i18n/locales/fr/core.json | 2 +- src/i18n/locales/fr/question.json | 4 ++- src/i18n/locales/it/core.json | 2 +- src/i18n/locales/it/question.json | 4 ++- src/i18n/locales/ja/core.json | 2 +- src/i18n/locales/ja/question.json | 4 ++- src/i18n/locales/ru/core.json | 2 +- src/i18n/locales/ru/question.json | 4 ++- src/i18n/locales/zh/core.json | 2 +- src/i18n/locales/zh/question.json | 4 ++- src/qortal/get.ts | 57 ++++++++++++++++++++++++++++++- 19 files changed, 94 insertions(+), 23 deletions(-) diff --git a/src/components/NewUsersCTA.tsx b/src/components/NewUsersCTA.tsx index d444717..0618ce5 100644 --- a/src/components/NewUsersCTA.tsx +++ b/src/components/NewUsersCTA.tsx @@ -70,17 +70,14 @@ export const NewUsersCTA = ({ balance }) => { onClick={() => { if (window?.electronAPI?.openExternal) { window.electronAPI.openExternal( - 'https://link.qortal.dev/telegram-invite' + 'https://link.qortal.dev/support' ); } else { - window.open( - 'https://link.qortal.dev/telegram-invite', - '_blank' - ); + window.open('https://link.qortal.dev/support', '_blank'); } }} > - Telegram + Nextcloud MAX_SIZE_PUBLISH) { + throw new Error( + i18n.t('question:message.error.max_size_publish', { + size: 2, + postProcess: 'capitalizeFirstChar', + }) + ); + } + + if (file && file.size > MAX_SIZE_PUBLIC_NODE) { + const isPublicNode = await isRunningGateway(); + if (isPublicNode) { + throw new Error( + i18n.t('question:message.error.max_size_publish_public', { + size: 500, + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + // Fill tags dynamically while maintaining backward compatibility for (let i = 0; i < 5; i++) { result[`tag${i + 1}`] = tags[i] || data[`tag${i + 1}`] || undefined; @@ -1542,6 +1567,36 @@ export const publishMultipleQDNResources = async ( }) ); } + const isPublicNode = await isRunningGateway(); + if (isPublicNode) { + const hasOversizedFilePublicNode = resources.some((resource) => { + const file = resource?.file; + return file instanceof File && file.size > MAX_SIZE_PUBLIC_NODE; + }); + + if (hasOversizedFilePublicNode) { + throw new Error( + i18n.t('question:message.error.max_size_publish_public', { + size: 500, + postProcess: 'capitalizeFirstChar', + }) + ); + } + } + + const hasOversizedFile = resources.some((resource) => { + const file = resource?.file; + return file instanceof File && file.size > MAX_SIZE_PUBLISH; + }); + + if (hasOversizedFile) { + throw new Error( + i18n.t('question:message.error.max_size_publish', { + size: 2, + postProcess: 'capitalizeFirstChar', + }) + ); + } const encrypt = data?.encrypt; From 170de54eb674a30b5e0a4b5c2e4a7b54d53edac8 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 4 Jun 2025 19:28:40 +0200 Subject: [PATCH 589/717] Use const instead of let, remove unused vars --- src/qdn/encryption/group-encryption.ts | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/qdn/encryption/group-encryption.ts b/src/qdn/encryption/group-encryption.ts index 6f018ba..ebead90 100644 --- a/src/qdn/encryption/group-encryption.ts +++ b/src/qdn/encryption/group-encryption.ts @@ -77,7 +77,7 @@ export const encryptDataGroup = ({ userPublicKey, customSymmetricKey, }: any) => { - let combinedPublicKeys = [...publicKeys, userPublicKey]; + const combinedPublicKeys = [...publicKeys, userPublicKey]; const decodedPrivateKey = Base58.decode(privateKey); const publicKeysDuplicateFree = [...new Set(combinedPublicKeys)]; const Uint8ArrayData = base64ToUint8Array(data64); @@ -114,7 +114,7 @@ export const encryptDataGroup = ({ const keyNonce = new Uint8Array(24); crypto.getRandomValues(keyNonce); // Encrypt the symmetric key for each recipient. - let encryptedKeys = []; + const encryptedKeys = []; publicKeysDuplicateFree.forEach((recipientPublicKey) => { const publicKeyUnit8Array = Base58.decode(recipientPublicKey); const convertedPrivateKey = ed2curve.convertSecretKey(decodedPrivateKey); @@ -153,7 +153,7 @@ export const encryptDataGroup = ({ encryptedKeysSize += key.length; }); combinedDataSize += encryptedKeysSize; - let combinedData = new Uint8Array(combinedDataSize); + const combinedData = new Uint8Array(combinedDataSize); combinedData.set(strUint8Array); combinedData.set(nonce, strUint8Array.length); combinedData.set(keyNonce, strUint8Array.length + nonce.length); @@ -244,9 +244,6 @@ export const encryptSingle = async ({ encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey); encryptedDataBase64 = uint8ArrayToBase64(encryptedData); - // Convert the nonce to base64 - const nonceBase64 = uint8ArrayToBase64(nonce); - // Concatenate the highest key, type number, nonce, and encrypted data (new format) const highestKeyStr = highestKey.toString().padStart(10, '0'); // Fixed length of 10 digits @@ -281,7 +278,7 @@ export const encryptSingle = async ({ }; export const decodeBase64ForUIChatMessages = (messages) => { - let msgs = []; + const msgs = []; for (const msg of messages) { try { const decoded = atob(msg?.data); @@ -326,7 +323,7 @@ export const decryptSingle = async ({ const secretKeyEntry = secretKeyObject[highestKey]; - let typeNumberStr, nonceBase64, encryptedDataBase64; + let nonceBase64, encryptedDataBase64; // Determine if typeNumber exists by checking if the next 3 characters after keyStr are digits const possibleTypeNumberStr = decodeForNumber.slice(10, 13); @@ -370,7 +367,6 @@ export const decryptSingle = async ({ return uint8ArrayToBase64(decryptedBytes); } // New format: Extract type number and nonce - typeNumberStr = possibleTypeNumberStr; // Extract type number nonceBase64 = decodeForNumber.slice(13, 45); // Extract nonce (next 32 characters after type number) encryptedDataBase64 = decodeForNumber.slice(45); // The remaining part is the encrypted data } else { @@ -424,14 +420,9 @@ export const decryptGroupEncryptionWithSharingKey = async ({ // Extract the shared keyNonce const keyNonceStartPosition = nonceEndPosition; const keyNonceEndPosition = keyNonceStartPosition + 24; // Nonce is 24 bytes - const keyNonce = allCombined.slice( - keyNonceStartPosition, - keyNonceEndPosition - ); // Extract the sender's public key const senderPublicKeyStartPosition = keyNonceEndPosition; const senderPublicKeyEndPosition = senderPublicKeyStartPosition + 32; // Public keys are 32 bytes - // Calculate count first const countStartPosition = allCombined.length - 4; // 4 bytes before the end, since count is stored in Uint32 (4 bytes) const countArray = allCombined.slice( @@ -662,7 +653,6 @@ export function decryptDeprecatedSingle(uint8Array, publicKey, privateKey) { const str = 'qortalEncryptedData'; const strEncoder = new TextEncoder(); const strUint8Array = strEncoder.encode(str); - const strData = combinedData.slice(0, strUint8Array.length); const nonce = combinedData.slice( strUint8Array.length, strUint8Array.length + 24 From 7f7f76d40e899d9f3c2bba793b5555696cc74ff5 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 4 Jun 2025 19:35:52 +0200 Subject: [PATCH 590/717] Set default src as null --- src/components/Embeds/ImageEmbed.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Embeds/ImageEmbed.tsx b/src/components/Embeds/ImageEmbed.tsx index ffa288e..5440862 100644 --- a/src/components/Embeds/ImageEmbed.tsx +++ b/src/components/Embeds/ImageEmbed.tsx @@ -197,7 +197,7 @@ export const ImageCard = ({ ); }; -export function ImageViewer({ src, alt = '' }) { +export function ImageViewer({ src = null, alt = '' }) { const [isFullscreen, setIsFullscreen] = useState(false); const handleOpenFullscreen = () => setIsFullscreen(true); From 6f698d2e498fb61fad036b49e68595d61da653a4 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 5 Jun 2025 01:53:49 +0300 Subject: [PATCH 591/717] fix chat edits with image --- src/components/Chat/ChatGroup.tsx | 68 ++++++++++++++++------------ src/components/Chat/ChatList.tsx | 21 ++------- src/components/Chat/MessageItem.tsx | 33 ++++++++++++-- src/components/Embeds/ImageEmbed.tsx | 17 ++++++- src/i18n/locales/de/core.json | 1 + src/i18n/locales/en/core.json | 1 + src/i18n/locales/es/core.json | 1 + src/i18n/locales/fr/core.json | 1 + src/i18n/locales/it/core.json | 1 + src/i18n/locales/ja/core.json | 1 + src/i18n/locales/ru/core.json | 1 + src/i18n/locales/zh/core.json | 1 + 12 files changed, 97 insertions(+), 50 deletions(-) diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index be0f7c4..a0efe00 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -299,17 +299,15 @@ export const ChatGroup = ({ const formatted = combineUIAndExtensionMsgs .filter((rawItem) => !rawItem?.chatReference) .map((item) => { - const message = ( -

    - {t('group:message.generic.group_key_created', { - postProcess: 'capitalizeFirstChar', - })} -

    - ); const additionalFields = item?.data === 'NDAwMQ==' // TODO put magic string somewhere in a file ? { - text: message, + text: `

    ${t( + 'group:message.generic.group_key_created', + { + postProcess: 'capitalizeFirstChar', + } + )}

    `, } : {}; return { @@ -450,17 +448,15 @@ export const ChatGroup = ({ const formatted = combineUIAndExtensionMsgs .filter((rawItem) => !rawItem?.chatReference) .map((item) => { - const message = ( -

    - {t('group:message.generic.group_key_created', { - postProcess: 'capitalizeFirstChar', - })} -

    - ); const additionalFields = item?.data === 'NDAwMQ==' ? { - text: message, + text: `

    ${t( + 'group:message.generic.group_key_created', + { + postProcess: 'capitalizeFirstChar', + } + )}

    `, } : {}; const divide = @@ -818,13 +814,28 @@ export const ChatGroup = ({ ); pauseAllQueues(); if (editorRef.current) { - const htmlContent = editorRef.current.getHTML(); - - if (!htmlContent?.trim() || htmlContent?.trim() === '

    ') return; + let htmlContent = editorRef.current.getHTML(); + const deleteImage = + onEditMessage && isDeleteImage && messageHasImage(onEditMessage); + const hasImage = + chatImagesToSave?.length > 0 || onEditMessage?.images?.length > 0; + if ( + (!htmlContent?.trim() || htmlContent?.trim() === '

    ') && + !hasImage && + !deleteImage + ) + return; + if (htmlContent?.trim() === '

    ') { + htmlContent = null; + } setIsSending(true); const message = - isPrivate === false ? editorRef.current.getJSON() : htmlContent; + isPrivate === false + ? !htmlContent + ? '

    ' + : editorRef.current.getJSON() + : htmlContent; const secretKeyObject = await getSecretKey(false, true); let repliedTo = replyMessage?.signature; @@ -849,8 +860,6 @@ export const ChatGroup = ({ } const imagesToPublish: ImageToPublish[] = []; - const deleteImage = - onEditMessage && isDeleteImage && messageHasImage(onEditMessage); if (deleteImage) { const fee = await getFee('ARBITRARY'); @@ -931,7 +940,6 @@ export const ChatGroup = ({ [isPrivate ? 'message' : 'messageText']: message, version: 3, }; - const message64: any = await objectToBase64(objectMessage); const encryptSingle = @@ -1042,11 +1050,15 @@ export const ChatGroup = ({ const onEdit = useCallback((message) => { setOnEditMessage(message); setReplyMessage(null); - editorRef.current - .chain() - .focus() - .setContent(message?.messageText || message?.text) - .run(); + try { + editorRef.current + .chain() + .focus() + .setContent(message?.messageText || message?.text || '

    ') + .run(); + } catch (error) { + console.error(error); + } }, []); const handleReaction = useCallback( diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 305674c..841cfd7 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -280,31 +280,18 @@ export const ChatList = ({ reactions = chatReferences[message.signature]?.reactions || null; - if ( - chatReferences[message.signature]?.edit?.message && - message?.text - ) { + if (chatReferences[message.signature]?.edit) { message.text = chatReferences[message.signature]?.edit?.message; - message.isEdit = true; - message.editTimestamp = - chatReferences[message.signature]?.edit?.timestamp; - } - if ( - chatReferences[message.signature]?.edit?.messageText && - message?.messageText - ) { message.messageText = chatReferences[message.signature]?.edit?.messageText; + message.images = + chatReferences[message.signature]?.edit?.images; + message.isEdit = true; message.editTimestamp = chatReferences[message.signature]?.edit?.timestamp; } - if (chatReferences[message.signature]?.edit?.images) { - message.images = - chatReferences[message.signature]?.edit?.images; - message.isEdit = true; - } } // Check if message is updating diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index b16b07f..bf6fe6c 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -47,6 +47,7 @@ import level8Img from '../../assets/badges/level-8.png'; import level9Img from '../../assets/badges/level-9.png'; import level10Img from '../../assets/badges/level-10.png'; import { Embed } from '../Embeds/Embed'; +import CommentsDisabledIcon from '@mui/icons-material/CommentsDisabled'; import { buildImageEmbedLink, isHtmlString, @@ -186,6 +187,13 @@ export const MessageItem = memo( 'tutorial', ]); + const hasNoMessage = + (!message.decryptedData?.data?.message || + message.decryptedData?.data?.message === '

    ') && + (message?.images || [])?.length === 0 && + (!message?.messageText || message?.messageText === '

    ') && + (!message?.text || message?.text === '

    '); + return ( <> {message?.divide && ( @@ -390,15 +398,33 @@ export const MessageItem = memo( )} - {htmlText && } + {htmlText && !hasNoMessage && ( + + )} {message?.decryptedData?.type === 'notification' ? ( - ) : ( + ) : hasNoMessage ? null : ( )} + {hasNoMessage && ( + + + + {t('core:message.generic.no_message', { + postProcess: 'capitalizeFirstChar', + })} + + + )} {message?.images && messageHasImage(message) && ( )} @@ -637,6 +663,7 @@ export const ReplyPreview = ({ message, isEdit = false }) => { ]); const replyMessageText = useMemo(() => { + if (!message?.messageText) return null; const isHtml = isHtmlString(message?.messageText); if (isHtml) return message?.messageText; return generateHTML(message?.messageText, [ @@ -692,7 +719,7 @@ export const ReplyPreview = ({ message, isEdit = false }) => { )} - {message?.messageText && ( + {message?.replyMessageText && ( )} diff --git a/src/components/Embeds/ImageEmbed.tsx b/src/components/Embeds/ImageEmbed.tsx index ffa288e..53bedc5 100644 --- a/src/components/Embeds/ImageEmbed.tsx +++ b/src/components/Embeds/ImageEmbed.tsx @@ -52,6 +52,8 @@ export const ImageCard = ({ backgroundColor: theme.palette.background.default, height: height, transition: 'height 0.6s ease-in-out', + display: 'flex', + flexDirection: 'column', }} > - - + + @@ -212,6 +224,7 @@ export function ImageViewer({ src, alt = '' }) { display: 'flex', justifyContent: 'center', maxWidth: '100%', // Prevent horizontal overflow + height: '100%', }} onClick={handleOpenFullscreen} > diff --git a/src/i18n/locales/de/core.json b/src/i18n/locales/de/core.json index 420894d..cc3a4e6 100644 --- a/src/i18n/locales/de/core.json +++ b/src/i18n/locales/de/core.json @@ -245,6 +245,7 @@ "no_data_image": "Keine Daten für das Bild", "no_description": "Keine Beschreibung", "no_messages": "Keine Nachrichten", + "no_message": "keine nachricht", "no_minting_details": "müngungsdetails auf dem Gateway können nicht angezeigt werden", "no_notifications": "Keine neuen Benachrichtigungen", "no_payments": "Keine Zahlungen", diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 49a97e8..a5ca275 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -249,6 +249,7 @@ "no_data_image": "no data for image", "no_description": "no description", "no_messages": "no messages", + "no_message": "no message", "no_minting_details": "cannot view minting details on the gateway", "no_notifications": "no new notifications", "no_payments": "no payments", diff --git a/src/i18n/locales/es/core.json b/src/i18n/locales/es/core.json index f37b46f..39e7e7c 100644 --- a/src/i18n/locales/es/core.json +++ b/src/i18n/locales/es/core.json @@ -246,6 +246,7 @@ "no_data_image": "no hay datos para la imagen", "no_description": "sin descripción", "no_messages": "sin mensajes", + "no_message": "sin mensaje", "no_minting_details": "no se puede ver los detalles de acuñado en la puerta de enlace", "no_notifications": "no hay nuevas notificaciones", "no_payments": "sin pagos", diff --git a/src/i18n/locales/fr/core.json b/src/i18n/locales/fr/core.json index a6d0fa1..76c028b 100644 --- a/src/i18n/locales/fr/core.json +++ b/src/i18n/locales/fr/core.json @@ -247,6 +247,7 @@ "no_data_image": "aucune donnée pour l'image", "no_description": "aucune description", "no_messages": "pas de messages", + "no_message": "aucun message", "no_minting_details": "impossible d'afficher les détails de la passerelle sur la passerelle", "no_notifications": "pas de nouvelles notifications", "no_payments": "aucun paiement", diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 41d1547..0b1846f 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -249,6 +249,7 @@ "no_data_image": "nessun dato per l'immagine", "no_description": "nessuna descrizione", "no_messages": "nessun messaggio", + "no_message": "nessun messaggio", "no_minting_details": "impossibile visualizzare i dettagli di minire sul gateway", "no_notifications": "nessuna nuova notifica", "no_payments": "nessun pagamento", diff --git a/src/i18n/locales/ja/core.json b/src/i18n/locales/ja/core.json index b2b2ae0..d39a89b 100644 --- a/src/i18n/locales/ja/core.json +++ b/src/i18n/locales/ja/core.json @@ -246,6 +246,7 @@ "no_data_image": "画像のデータはありません", "no_description": "説明なし", "no_messages": "メッセージはありません", + "no_message": "メッセージなし", "no_minting_details": "ゲートウェイでミントの詳細を表示できません", "no_notifications": "新しい通知はありません", "no_payments": "支払いなし", diff --git a/src/i18n/locales/ru/core.json b/src/i18n/locales/ru/core.json index e90fb33..aed5ce5 100644 --- a/src/i18n/locales/ru/core.json +++ b/src/i18n/locales/ru/core.json @@ -247,6 +247,7 @@ "no_data_image": "Нет данных для изображения", "no_description": "Нет описания", "no_messages": "Нет сообщений", + "no_message": "нет сообщения", "no_minting_details": "Не могу просматривать детали маттинга на шлюзе", "no_notifications": "Нет новых уведомлений", "no_payments": "Нет платежей", diff --git a/src/i18n/locales/zh/core.json b/src/i18n/locales/zh/core.json index 5208c36..74f2e74 100644 --- a/src/i18n/locales/zh/core.json +++ b/src/i18n/locales/zh/core.json @@ -246,6 +246,7 @@ "no_data_image": "没有图像数据", "no_description": "没有描述", "no_messages": "没有消息", + "no_message": "没有消息", "no_minting_details": "无法在网关上查看薄荷细节", "no_notifications": "没有新的通知", "no_payments": "无付款", From b8d87e4935a9f40273e471e7bb385bcce1fb59b7 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 5 Jun 2025 16:11:33 +0300 Subject: [PATCH 592/717] fix --- src/components/Chat/MessageItem.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index bf6fe6c..a50d312 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -719,9 +719,7 @@ export const ReplyPreview = ({ message, isEdit = false }) => { )} - {message?.replyMessageText && ( - - )} + {replyMessageText && } {message?.decryptedData?.type === 'notification' ? ( From 294914affb193c86a0b77ddfc2e48e717a076727 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 5 Jun 2025 18:10:58 +0300 Subject: [PATCH 593/717] change scroll down style --- src/components/Chat/ChatList.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 841cfd7..6000fab 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -420,14 +420,14 @@ export const ChatList = ({ - - )} - - {htmlText && } - - {message?.decryptedData?.type === 'notification' ? ( - - ) : ( - - )} - {message?.images && messageHasImage(message) && ( - + )} - - {reactions && - Object.keys(reactions).map((reaction) => { - const numberOfReactions = reactions[reaction]?.length; - if (numberOfReactions === 0) return null; - return ( - { - event.stopPropagation(); // Prevent event bubbling - setAnchorEl(event.currentTarget); - setSelectedReaction(reaction); - }} - > -
    - {reaction} -
    {' '} - {numberOfReactions > 1 && ( - - {numberOfReactions} - - )} -
    - ); - })} -
    - - {selectedReaction && ( - { - setAnchorEl(null); - setSelectedReaction(null); + {message?.isNotEncrypted && isPrivate && ( + - - - {t('core:message.generic.people_reaction', { - reaction: selectedReaction, - postProcess: 'capitalizeFirstChar', - })} - - - - {reactions[selectedReaction]?.map((reactionItem) => ( - - - - ))} - - - - - + /> )} - - {message?.isNotEncrypted && isPrivate && ( - - )} - - {isUpdating ? ( - - {message?.status === 'failed-permanent' - ? t('core:message.error.update_failed', { - postProcess: 'capitalizeFirstChar', - }) - : t('core:message.generic.updating', { - postProcess: 'capitalizeFirstChar', - })} - - ) : isTemp ? ( - - {message?.status === 'failed-permanent' - ? t('core:message.error.send_failed', { - postProcess: 'capitalizeFirstChar', - }) - : t('core:message.generic.sending', { - postProcess: 'capitalizeFirstChar', - })} - - ) : ( - <> - {message?.isEdit && ( - - {t('core:message.generic.edited', { - postProcess: 'capitalizeFirstChar', - })} - - )} - + {isUpdating ? ( + + {message?.status === 'failed-permanent' + ? t('core:message.error.update_failed', { + postProcess: 'capitalizeFirstChar', + }) + : t('core:message.generic.updating', { + postProcess: 'capitalizeFirstChar', + })} + + ) : isTemp ? ( + + {message?.status === 'failed-permanent' + ? t('core:message.error.send_failed', { + postProcess: 'capitalizeFirstChar', + }) + : t('core:message.generic.sending', { + postProcess: 'capitalizeFirstChar', + })} + + ) : ( + <> + {message?.isEdit && ( - {formatTimestamp(message.timestamp)} + {t('core:message.generic.edited', { + postProcess: 'capitalizeFirstChar', + })} - - )} - + )} + + + {formatTimestamp(message.timestamp)} + + + )}
    -
    - - - ); - } -); +
    +
    + + + ); +}; + +const MemoizedMessageItem = memo(MessageItemComponent); +MemoizedMessageItem.displayName = 'MessageItem'; // It ensures React DevTools shows MessageItem as the name (instead of "Anonymous" or "Memo") + +export const MessageItem = MemoizedMessageItem; export const ReplyPreview = ({ message, isEdit = false }) => { const theme = useTheme(); From e6f2015bd4e72c6cf69beaa092616c79ade9ae64 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 6 Jun 2025 06:33:24 +0200 Subject: [PATCH 600/717] Fix typo --- src/i18n/locales/it/core.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 9bff9a5..b9c0190 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -249,7 +249,7 @@ "no_data_image": "nessun dato per l'immagine", "no_description": "nessuna descrizione", "no_messages": "nessun messaggio", - "no_minting_details": "impossibile visualizzare i dettagli di minire sul gateway", + "no_minting_details": "impossibile visualizzare i dettagli di minting sul gateway", "no_notifications": "nessuna nuova notifica", "no_payments": "nessun pagamento", "no_pinned_changes": "per ora non ci sono modifiche alle app bloccate", From d24e2ac3bc6a707f074c9b27bbeeeddef68fad08 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 6 Jun 2025 06:55:32 +0200 Subject: [PATCH 601/717] Add type for Reaction --- src/components/Chat/ChatList.tsx | 11 ++++++++++- src/components/Chat/MessageItem.tsx | 11 ++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 305674c..3d8f84d 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -7,6 +7,15 @@ import { ChatOptions } from './ChatOptions'; import ErrorBoundary from '../../common/ErrorBoundary'; import { useTranslation } from 'react-i18next'; +type ReactionItem = { + sender: string; + senderName?: string; +}; + +export type ReactionsMap = { + [reactionType: string]: ReactionItem[]; +}; + export const ChatList = ({ initialMessages, myAddress, @@ -236,7 +245,7 @@ export const ChatList = ({ let message = messages[index] || null; // Safeguard against undefined let replyIndex = -1; let reply = null; - let reactions = null; + let reactions: ReactionsMap | null = null; let isUpdating = false; try { diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index c129408..4d412fd 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -53,6 +53,7 @@ import { messageHasImage, } from '../../utils/chat'; import { useTranslation } from 'react-i18next'; +import { ReactionsMap } from './ChatList'; const getBadgeImg = (level) => { switch (level?.toString()) { @@ -105,14 +106,14 @@ type MessageItemProps = { isShowingAsReply?: boolean; isTemp: boolean; isUpdating: boolean; - lastSignature: any; - message: any; - myAddress: any; + lastSignature: string; + message: string; + myAddress: string; onEdit: (messageId: string) => void; onReply: (messageId: string) => void; onSeen: () => void; - reactions: any; // could be null, or type it more strictly - reply: any; // same here + reactions: ReactionsMap | null; + reply: string | null; replyIndex: number; scrollToItem: (index: number) => void; }; From 656cbbde0e1b2d93fbd7948741201478f127d696 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 6 Jun 2025 07:08:31 +0200 Subject: [PATCH 602/717] Add TextStyle and refactor --- src/components/Chat/ChatList.tsx | 10 +++++----- src/components/Chat/ChatOptions.tsx | 8 ++------ src/components/Chat/TipTap.tsx | 2 +- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 3d8f84d..0651245 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -466,13 +466,13 @@ export const ChatList = ({ {enableMentions && (hasSecretKey || isPrivate === false) && ( )} diff --git a/src/components/Chat/ChatOptions.tsx b/src/components/Chat/ChatOptions.tsx index 2d2c352..ef1a4b8 100644 --- a/src/components/Chat/ChatOptions.tsx +++ b/src/components/Chat/ChatOptions.tsx @@ -25,7 +25,6 @@ import { AppsSearchRight, } from '../Apps/Apps-styles'; import IconClearInput from '../../assets/svgs/ClearInput.svg'; -import { CellMeasurerCache } from 'react-virtualized'; import { getBaseApiReact } from '../../App'; import { MessageDisplay } from './MessageDisplay'; import { useVirtualizer } from '@tanstack/react-virtual'; @@ -36,6 +35,7 @@ import { generateHTML } from '@tiptap/react'; import ErrorBoundary from '../../common/ErrorBoundary'; import { useTranslation } from 'react-i18next'; import { isHtmlString } from '../../utils/chat'; +import TextStyle from '@tiptap/extension-text-style'; const extractTextFromHTML = (htmlString = '') => { return convert(htmlString, { @@ -43,11 +43,6 @@ const extractTextFromHTML = (htmlString = '') => { })?.toLowerCase(); }; -const cache = new CellMeasurerCache({ - fixedWidth: true, - defaultHeight: 50, -}); - export const ChatOptions = ({ messages: untransformedMessages, goToMessage, @@ -86,6 +81,7 @@ export const ChatOptions = ({ Underline, Highlight, Mention, + TextStyle, ]); return { ...item, diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 0baef0a..3ab63a1 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -361,8 +361,8 @@ const MenuBar = memo( ); const extensions = [ + TextStyle, Color.configure({ types: [TextStyle.name, ListItem.name] }), - TextStyle.configure({ types: [ListItem.name] }), StarterKit.configure({ bulletList: { keepMarks: true, From dded49145af691f899a027abbc05db71aa092391 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 6 Jun 2025 07:30:44 +0200 Subject: [PATCH 603/717] Remove unused function textMatcher --- src/components/Chat/TipTap.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 3ab63a1..270dc47 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -34,16 +34,6 @@ import { fileToBase64 } from '../../utils/fileReading/index.js'; import { useTranslation } from 'react-i18next'; import i18n from 'i18next'; -function textMatcher(doc, from) { - const textBeforeCursor = doc.textBetween(0, from, ' ', ' '); - const match = textBeforeCursor.match(/@[\w]*$/); // Match '@' followed by valid characters - if (!match) return null; - - const start = from - match[0].length; - const query = match[0]; - return { start, query }; -} - const MenuBar = memo( ({ setEditorRef, From 4c8c90a579d59a92c60e49008ba45cb1f296cf57 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 6 Jun 2025 07:55:22 +0200 Subject: [PATCH 604/717] Define type --- src/components/Chat/TipTap.tsx | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 270dc47..4c69504 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -1,5 +1,5 @@ import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; -import { EditorProvider, useCurrentEditor } from '@tiptap/react'; +import { Editor, EditorProvider, useCurrentEditor } from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import { Color } from '@tiptap/extension-color'; import ListItem from '@tiptap/extension-list-item'; @@ -373,11 +373,26 @@ const extensions = [ const content = ``; -export default ({ +type TiptapProps = { + setEditorRef: (editorInstance: Editor | null) => void; + onEnter: () => void | Promise; + disableEnter?: boolean; + isChat?: boolean; + maxHeightOffset?: number; + overrideMobile?: boolean; + customEditorHeight?: number | null; + setIsFocusedParent: React.Dispatch>; + isFocusedParent: boolean; + membersWithNames: unknown[]; + enableMentions?: boolean; + insertImage: (image: any) => void; +}; + +const Tiptap = ({ setEditorRef, onEnter, - disableEnter, - isChat, + disableEnter = false, + isChat = false, maxHeightOffset, setIsFocusedParent, isFocusedParent, @@ -386,7 +401,7 @@ export default ({ membersWithNames, enableMentions, insertImage, -}) => { +}: TiptapProps) => { const theme = useTheme(); const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useAtom( isDisabledEditorEnterAtom @@ -613,3 +628,5 @@ export default ({ ); }; + +export default Tiptap; From f0805e1da5ad9ea31d452bce2358de5d206060f5 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 6 Jun 2025 07:55:42 +0200 Subject: [PATCH 605/717] Sort properties and add spaces in the code --- src/components/Wallets.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/Wallets.tsx b/src/components/Wallets.tsx index 75a36ea..aef22b3 100644 --- a/src/components/Wallets.tsx +++ b/src/components/Wallets.tsx @@ -244,6 +244,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { )} )} + {wallets?.length > 0 && ( { postProcess: 'capitalizeFirstChar', })} + { if (!seedValue || !seedName || !password) return; onOk({ seedValue, seedName, password }); }} - autoFocus + variant="contained" > {t('core:action.add', { postProcess: 'capitalizeFirstChar', })} + Date: Fri, 6 Jun 2025 08:00:21 +0200 Subject: [PATCH 606/717] Add comment TODO --- src/background/background.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/background/background.ts b/src/background/background.ts index fce3d83..3c688ff 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -1281,7 +1281,6 @@ export async function addUserSettings({ keyValue }) { getData(`${address}-userSettings`) .then((storedData) => { storedData = storedData || {}; // Initialize if no data found - storedData[key] = value; // Update the key-value pair within stored data // Save updated structure back to localStorage @@ -3393,7 +3392,7 @@ const checkGroupList = async () => { .filter( (item) => item?.name !== 'extension-proxy' && - item?.address !== 'QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH' + item?.address !== 'QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH' // TODO put address in a specific file ) .sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0)); @@ -3447,6 +3446,7 @@ export const checkNewMessages = async () => { const latestMessage = responseData.filter( (pub) => pub?.name !== myName )[0]; + if (!latestMessage) { return; // continue to the next group } From 203f14799fd53d7251de448abd3c10144b47db3b Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 5 Jun 2025 01:53:49 +0300 Subject: [PATCH 607/717] fix chat edits with image --- src/components/Chat/ChatGroup.tsx | 68 ++++++++++++++++------------ src/components/Chat/ChatList.tsx | 21 ++------- src/components/Chat/MessageItem.tsx | 4 +- src/components/Embeds/ImageEmbed.tsx | 17 ++++++- src/i18n/locales/de/core.json | 1 + src/i18n/locales/en/core.json | 1 + src/i18n/locales/es/core.json | 1 + src/i18n/locales/fr/core.json | 1 + src/i18n/locales/it/core.json | 1 + src/i18n/locales/ja/core.json | 1 + src/i18n/locales/ru/core.json | 1 + src/i18n/locales/zh/core.json | 1 + 12 files changed, 70 insertions(+), 48 deletions(-) diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index be0f7c4..a0efe00 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -299,17 +299,15 @@ export const ChatGroup = ({ const formatted = combineUIAndExtensionMsgs .filter((rawItem) => !rawItem?.chatReference) .map((item) => { - const message = ( -

    - {t('group:message.generic.group_key_created', { - postProcess: 'capitalizeFirstChar', - })} -

    - ); const additionalFields = item?.data === 'NDAwMQ==' // TODO put magic string somewhere in a file ? { - text: message, + text: `

    ${t( + 'group:message.generic.group_key_created', + { + postProcess: 'capitalizeFirstChar', + } + )}

    `, } : {}; return { @@ -450,17 +448,15 @@ export const ChatGroup = ({ const formatted = combineUIAndExtensionMsgs .filter((rawItem) => !rawItem?.chatReference) .map((item) => { - const message = ( -

    - {t('group:message.generic.group_key_created', { - postProcess: 'capitalizeFirstChar', - })} -

    - ); const additionalFields = item?.data === 'NDAwMQ==' ? { - text: message, + text: `

    ${t( + 'group:message.generic.group_key_created', + { + postProcess: 'capitalizeFirstChar', + } + )}

    `, } : {}; const divide = @@ -818,13 +814,28 @@ export const ChatGroup = ({ ); pauseAllQueues(); if (editorRef.current) { - const htmlContent = editorRef.current.getHTML(); - - if (!htmlContent?.trim() || htmlContent?.trim() === '

    ') return; + let htmlContent = editorRef.current.getHTML(); + const deleteImage = + onEditMessage && isDeleteImage && messageHasImage(onEditMessage); + const hasImage = + chatImagesToSave?.length > 0 || onEditMessage?.images?.length > 0; + if ( + (!htmlContent?.trim() || htmlContent?.trim() === '

    ') && + !hasImage && + !deleteImage + ) + return; + if (htmlContent?.trim() === '

    ') { + htmlContent = null; + } setIsSending(true); const message = - isPrivate === false ? editorRef.current.getJSON() : htmlContent; + isPrivate === false + ? !htmlContent + ? '

    ' + : editorRef.current.getJSON() + : htmlContent; const secretKeyObject = await getSecretKey(false, true); let repliedTo = replyMessage?.signature; @@ -849,8 +860,6 @@ export const ChatGroup = ({ } const imagesToPublish: ImageToPublish[] = []; - const deleteImage = - onEditMessage && isDeleteImage && messageHasImage(onEditMessage); if (deleteImage) { const fee = await getFee('ARBITRARY'); @@ -931,7 +940,6 @@ export const ChatGroup = ({ [isPrivate ? 'message' : 'messageText']: message, version: 3, }; - const message64: any = await objectToBase64(objectMessage); const encryptSingle = @@ -1042,11 +1050,15 @@ export const ChatGroup = ({ const onEdit = useCallback((message) => { setOnEditMessage(message); setReplyMessage(null); - editorRef.current - .chain() - .focus() - .setContent(message?.messageText || message?.text) - .run(); + try { + editorRef.current + .chain() + .focus() + .setContent(message?.messageText || message?.text || '

    ') + .run(); + } catch (error) { + console.error(error); + } }, []); const handleReaction = useCallback( diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 0651245..e7897c2 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -289,31 +289,18 @@ export const ChatList = ({ reactions = chatReferences[message.signature]?.reactions || null; - if ( - chatReferences[message.signature]?.edit?.message && - message?.text - ) { + if (chatReferences[message.signature]?.edit) { message.text = chatReferences[message.signature]?.edit?.message; - message.isEdit = true; - message.editTimestamp = - chatReferences[message.signature]?.edit?.timestamp; - } - if ( - chatReferences[message.signature]?.edit?.messageText && - message?.messageText - ) { message.messageText = chatReferences[message.signature]?.edit?.messageText; + message.images = + chatReferences[message.signature]?.edit?.images; + message.isEdit = true; message.editTimestamp = chatReferences[message.signature]?.edit?.timestamp; } - if (chatReferences[message.signature]?.edit?.images) { - message.images = - chatReferences[message.signature]?.edit?.images; - message.isEdit = true; - } } // Check if message is updating diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index 4d412fd..aeb2628 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -47,6 +47,7 @@ import level8Img from '../../assets/badges/level-8.png'; import level9Img from '../../assets/badges/level-9.png'; import level10Img from '../../assets/badges/level-10.png'; import { Embed } from '../Embeds/Embed'; +import CommentsDisabledIcon from '@mui/icons-material/CommentsDisabled'; import { buildImageEmbedLink, isHtmlString, @@ -660,6 +661,7 @@ export const ReplyPreview = ({ message, isEdit = false }) => { ]); const replyMessageText = useMemo(() => { + if (!message?.messageText) return null; const isHtml = isHtmlString(message?.messageText); if (isHtml) return message?.messageText; return generateHTML(message?.messageText, [ @@ -715,7 +717,7 @@ export const ReplyPreview = ({ message, isEdit = false }) => {
    )} - {message?.messageText && ( + {message?.replyMessageText && ( )} diff --git a/src/components/Embeds/ImageEmbed.tsx b/src/components/Embeds/ImageEmbed.tsx index 13ec3e2..7402039 100644 --- a/src/components/Embeds/ImageEmbed.tsx +++ b/src/components/Embeds/ImageEmbed.tsx @@ -52,6 +52,8 @@ export const ImageCard = ({ backgroundColor: theme.palette.background.default, height: height, transition: 'height 0.6s ease-in-out', + display: 'flex', + flexDirection: 'column', }} > - - + + @@ -211,6 +223,7 @@ export function ImageViewer({ src = null, alt = '' }) { display: 'flex', justifyContent: 'center', maxWidth: '100%', // Prevent horizontal overflow + height: '100%', }} onClick={handleOpenFullscreen} > diff --git a/src/i18n/locales/de/core.json b/src/i18n/locales/de/core.json index 420894d..cc3a4e6 100644 --- a/src/i18n/locales/de/core.json +++ b/src/i18n/locales/de/core.json @@ -245,6 +245,7 @@ "no_data_image": "Keine Daten für das Bild", "no_description": "Keine Beschreibung", "no_messages": "Keine Nachrichten", + "no_message": "keine nachricht", "no_minting_details": "müngungsdetails auf dem Gateway können nicht angezeigt werden", "no_notifications": "Keine neuen Benachrichtigungen", "no_payments": "Keine Zahlungen", diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 49a97e8..a5ca275 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -249,6 +249,7 @@ "no_data_image": "no data for image", "no_description": "no description", "no_messages": "no messages", + "no_message": "no message", "no_minting_details": "cannot view minting details on the gateway", "no_notifications": "no new notifications", "no_payments": "no payments", diff --git a/src/i18n/locales/es/core.json b/src/i18n/locales/es/core.json index f37b46f..39e7e7c 100644 --- a/src/i18n/locales/es/core.json +++ b/src/i18n/locales/es/core.json @@ -246,6 +246,7 @@ "no_data_image": "no hay datos para la imagen", "no_description": "sin descripción", "no_messages": "sin mensajes", + "no_message": "sin mensaje", "no_minting_details": "no se puede ver los detalles de acuñado en la puerta de enlace", "no_notifications": "no hay nuevas notificaciones", "no_payments": "sin pagos", diff --git a/src/i18n/locales/fr/core.json b/src/i18n/locales/fr/core.json index a6d0fa1..76c028b 100644 --- a/src/i18n/locales/fr/core.json +++ b/src/i18n/locales/fr/core.json @@ -247,6 +247,7 @@ "no_data_image": "aucune donnée pour l'image", "no_description": "aucune description", "no_messages": "pas de messages", + "no_message": "aucun message", "no_minting_details": "impossible d'afficher les détails de la passerelle sur la passerelle", "no_notifications": "pas de nouvelles notifications", "no_payments": "aucun paiement", diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 9bff9a5..49794a4 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -249,6 +249,7 @@ "no_data_image": "nessun dato per l'immagine", "no_description": "nessuna descrizione", "no_messages": "nessun messaggio", + "no_message": "nessun messaggio", "no_minting_details": "impossibile visualizzare i dettagli di minire sul gateway", "no_notifications": "nessuna nuova notifica", "no_payments": "nessun pagamento", diff --git a/src/i18n/locales/ja/core.json b/src/i18n/locales/ja/core.json index b2b2ae0..d39a89b 100644 --- a/src/i18n/locales/ja/core.json +++ b/src/i18n/locales/ja/core.json @@ -246,6 +246,7 @@ "no_data_image": "画像のデータはありません", "no_description": "説明なし", "no_messages": "メッセージはありません", + "no_message": "メッセージなし", "no_minting_details": "ゲートウェイでミントの詳細を表示できません", "no_notifications": "新しい通知はありません", "no_payments": "支払いなし", diff --git a/src/i18n/locales/ru/core.json b/src/i18n/locales/ru/core.json index e90fb33..aed5ce5 100644 --- a/src/i18n/locales/ru/core.json +++ b/src/i18n/locales/ru/core.json @@ -247,6 +247,7 @@ "no_data_image": "Нет данных для изображения", "no_description": "Нет описания", "no_messages": "Нет сообщений", + "no_message": "нет сообщения", "no_minting_details": "Не могу просматривать детали маттинга на шлюзе", "no_notifications": "Нет новых уведомлений", "no_payments": "Нет платежей", diff --git a/src/i18n/locales/zh/core.json b/src/i18n/locales/zh/core.json index 5208c36..74f2e74 100644 --- a/src/i18n/locales/zh/core.json +++ b/src/i18n/locales/zh/core.json @@ -246,6 +246,7 @@ "no_data_image": "没有图像数据", "no_description": "没有描述", "no_messages": "没有消息", + "no_message": "没有消息", "no_minting_details": "无法在网关上查看薄荷细节", "no_notifications": "没有新的通知", "no_payments": "无付款", From 2915766a26973adf1bfc151ba84c227313cc46ea Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 5 Jun 2025 16:11:33 +0300 Subject: [PATCH 608/717] fix --- src/components/Chat/MessageItem.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index aeb2628..f1cfb40 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -717,9 +717,7 @@ export const ReplyPreview = ({ message, isEdit = false }) => {
    )} - {message?.replyMessageText && ( - - )} + {replyMessageText && } {message?.decryptedData?.type === 'notification' ? ( From f6c6480fe8f6d001f4bcac67a4644d1c4a546c02 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 5 Jun 2025 18:10:58 +0300 Subject: [PATCH 609/717] change scroll down style --- src/components/Chat/ChatList.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index e7897c2..f5cb3f6 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -429,14 +429,14 @@ export const ChatList = ({
    {' '} +
    {numberOfReactions > 1 && ( - {' '} {errorMsg} - {' '} + )} diff --git a/src/components/Embeds/ImageEmbed.tsx b/src/components/Embeds/ImageEmbed.tsx index 53bedc5..487fe25 100644 --- a/src/components/Embeds/ImageEmbed.tsx +++ b/src/components/Embeds/ImageEmbed.tsx @@ -177,7 +177,6 @@ export const ImageCard = ({ justifyContent: 'center', }} > - {' '} - {' '} - {`${poll?.votes?.totalVotes} ${ - poll?.votes?.totalVotes === 1 ? ' vote' : ' votes' - }`} + {poll?.votes?.totalVotes}{' '} + {poll?.votes?.totalVotes === 1 ? ' vote' : ' votes'} diff --git a/src/components/GeneralNotifications.tsx b/src/components/GeneralNotifications.tsx index 53e85a8..4164214 100644 --- a/src/components/GeneralNotifications.tsx +++ b/src/components/GeneralNotifications.tsx @@ -157,7 +157,7 @@ export const GeneralNotifications = ({ address }) => { sx={{ color: theme.palette.text.primary, }} - />{' '} + /> {formatDate(latestTx?.timestamp)} diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index e0a7b99..6bca8c2 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -412,7 +412,7 @@ export const BlockedUsersModal = () => { sx={{ color: theme.palette.text.primary, }} - />{' '} + /> {t('auth:message.generic.choose_block', { postProcess: 'capitalizeFirstChar', diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index f4091f2..607280e 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -2124,7 +2124,6 @@ export const Group = ({ width: '100%', }} > - {' '} {t('group:message.generic.encryption_key', { postProcess: 'capitalizeFirstChar', diff --git a/src/components/Group/HomeDesktop.tsx b/src/components/Group/HomeDesktop.tsx index f148be1..bcfff93 100644 --- a/src/components/Group/HomeDesktop.tsx +++ b/src/components/Group/HomeDesktop.tsx @@ -209,7 +209,7 @@ export const HomeDesktop = ({ sx={{ ccolor: theme.palette.text.primary, }} - />{' '} + /> {' '} + diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index 3d2b7cc..49a3761 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -307,8 +307,10 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { {t('core:message.generic.settings', { postProcess: 'capitalizeFirstChar', })} - {' '} + + + - - {mintingAccounts?.length > 1 && ( - {t('group:message.generic.minting_keys_per_node', { + {t('auth:account.account_one', { + postProcess: 'capitalizeFirstChar', + })} + : {handleNames(accountInfo?.address)} + + + + {t('core:level', { + postProcess: 'capitalizeFirstChar', + })} + : {accountInfo?.level} + + + + {t('group:message.generic.next_level', { + postProcess: 'capitalizeFirstChar', + })}{' '} + {_levelUpBlocks()} + + + + {t('group:message.generic.node_minting', { + postProcess: 'capitalizeFirstChar', + })}{' '} + {nodeInfos?.isMintingPossible?.toString()} + + + + + + {isPartOfMintingGroup && !accountIsMinting && ( + + + + {mintingAccounts?.length > 1 && ( + + {t('group:message.generic.minting_keys_per_node', { + postProcess: 'capitalizeFirstChar', + })} + + )} + + )} + + + + {mintingAccounts?.length > 0 && ( + + {t('group:message.generic.node_minting_account', { postProcess: 'capitalizeFirstChar', })} )} - - )} - - - - {mintingAccounts?.length > 0 && ( - - {t('group:message.generic.node_minting_account', { - postProcess: 'capitalizeFirstChar', - })} - - )} - - {accountIsMinting && ( - - - {t('group:message.generic.node_minting_key', { - postProcess: 'capitalizeFirstChar', - })} - - - )} + {accountIsMinting && ( + + + {t('group:message.generic.node_minting_key', { + postProcess: 'capitalizeFirstChar', + })} + + + )} - + - {mintingAccounts?.map((acct) => ( - - - {t('group:message.generic.minting_account', { - postProcess: 'capitalizeFirstChar', - })}{' '} - {handleNames(acct?.mintingAccount)} - + {mintingAccounts?.map((acct) => ( + + + {t('group:message.generic.minting_account', { + postProcess: 'capitalizeFirstChar', + })}{' '} + {handleNames(acct?.mintingAccount)} + - + + + + + + ))} + + {mintingAccounts?.length > 1 && ( + + {t('group:message.generic.minting_keys_per_node_different', { + postProcess: 'capitalizeFirstChar', + })} + + )} + + + + + {!isPartOfMintingGroup && ( + + + + {t('group:message.generic.minter_group', { + postProcess: 'capitalizeFirstChar', + })} + + + + {t('group:message.generic.mintership_app', { + postProcess: 'capitalizeFirstChar', + })} + + + + + + + + )} + + {showWaitDialog && ( + + { - removeMintingAccount(acct.publicKey, acct); - }} - variant="contained" - > - {t('group:action.remove_minting_account', { - postProcess: 'capitalizeFirstChar', - })} - + }} + > + {isShowNext + ? t('core:message.generic.confirmed', { + postProcess: 'capitalizeFirstChar', + }) + : t('core:message.generic.wait', { + postProcess: 'capitalizeFirstChar', + })} + - + + {!isShowNext && ( + + {t('group:message.success.rewardshare_creation', { + postProcess: 'capitalizeFirstChar', + })} + + )} - - - ))} + {isShowNext && ( + + {t('group:message.success.rewardshare_confirmed', { + postProcess: 'capitalizeFirstChar', + })} + + )} + - {mintingAccounts?.length > 1 && ( - - {t('group:message.generic.minting_keys_per_node_different', { - postProcess: 'capitalizeFirstChar', - })} - - )} - - - - - {!isPartOfMintingGroup && ( - - - - {t('group:message.generic.minter_group', { - postProcess: 'capitalizeFirstChar', - })} - - - - {t('group:message.generic.mintership_app', { - postProcess: 'capitalizeFirstChar', - })} - - - - - - - - )} - - - - General Minting Details - - - - - Blockchain Statistics - - - - - - - - - - - - Minting Account Details - - - - - - - - - - - With a 24/7 Minting you will reach level 5 in{' '} - 117.58 days! - - - - - - - Minting Rewards Info - - - - - - - - - {/* */} - - - - - {showWaitDialog && ( - - - {isShowNext - ? t('core:message.generic.confirmed', { - postProcess: 'capitalizeFirstChar', - }) - : t('core:message.generic.wait', { - postProcess: 'capitalizeFirstChar', - })} - - - - {!isShowNext && ( - - {t('group:message.success.rewardshare_creation', { - postProcess: 'capitalizeFirstChar', - })} - - )} - - {isShowNext && ( - - {t('group:message.success.rewardshare_confirmed', { - postProcess: 'capitalizeFirstChar', - })} - - )} - - - - - - - )} - + + + + + )} + + + )} { if (isLoading) return null; return ( -
    + {wallets?.length === 0 || !wallets ? ( <> @@ -469,7 +469,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { -
    + ); }; From 57fed7846db78beace269857013a0cc3c60435d9 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 7 Jun 2025 00:33:33 +0200 Subject: [PATCH 650/717] Add new translation --- src/App.tsx | 12 +++++++----- src/i18n/locales/de/core.json | 11 ++++++----- src/i18n/locales/es/core.json | 2 +- src/i18n/locales/fr/core.json | 2 +- src/i18n/locales/zh/core.json | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 979a561..6ffbf21 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3978,11 +3978,13 @@ function App() { )} {isOpenMinting && ( - + // + + )} {!isAuthenticated && ( diff --git a/src/i18n/locales/de/core.json b/src/i18n/locales/de/core.json index ad2c602..6411ac9 100644 --- a/src/i18n/locales/de/core.json +++ b/src/i18n/locales/de/core.json @@ -216,9 +216,10 @@ "generic": { "already_voted": "Sie haben bereits gestimmt.", "avatar_size": "{{ size }} KB max. for GIFS", - "benefits_qort": "Vorteile von Qort", - "building": "Gebäude", - "building_app": "App -App", + "benefits_qort": "vorteile von Qort", + "building": "gebäude", + "building_app": "app -App", + "confirmed": "bestätigen", "created_by": "created by {{ owner }}", "buy_order_request": "the Application
    {{hostname}}
    is requesting {{count}} buy order", "buy_order_request_other": "the Application
    {{hostname}}
    is requesting {{count}} buy orders", @@ -229,8 +230,8 @@ "editing_message": "Bearbeitungsnachricht", "encrypted": "verschlüsselt", "encrypted_not": "nicht verschlüsselt", - "fee_qort": "fee: {{ message }} QORT", - "fetching_data": "App -Daten abrufen", + "fee_qort": "Gebühr: {{ message }} QORT", + "fetching_data": "app -Daten abrufen", "foreign_fee": "foreign fee: {{ message }}", "get_qort_trade_portal": "Holen Sie sich QORT mit dem CrossChain -Handelsportal von Qortal", "minimal_qort_balance": "having at least {{ quantity }} QORT in your balance (4 qort balance for chat, 1.25 for name, 0.75 for some transactions)", diff --git a/src/i18n/locales/es/core.json b/src/i18n/locales/es/core.json index 23927cf..f4cae5a 100644 --- a/src/i18n/locales/es/core.json +++ b/src/i18n/locales/es/core.json @@ -394,4 +394,4 @@ }, "website": "sitio web", "welcome": "bienvenido" -} \ No newline at end of file +} diff --git a/src/i18n/locales/fr/core.json b/src/i18n/locales/fr/core.json index 8ddf369..7f6fd37 100644 --- a/src/i18n/locales/fr/core.json +++ b/src/i18n/locales/fr/core.json @@ -394,4 +394,4 @@ }, "website": "site web", "welcome": "accueillir" -} \ No newline at end of file +} diff --git a/src/i18n/locales/zh/core.json b/src/i18n/locales/zh/core.json index c26611a..f241237 100644 --- a/src/i18n/locales/zh/core.json +++ b/src/i18n/locales/zh/core.json @@ -394,4 +394,4 @@ }, "website": "网站", "welcome": "欢迎" -} \ No newline at end of file +} From e82b9dab8c63eb66a9232f072582b9776d64df9c Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 7 Jun 2025 17:39:25 +0200 Subject: [PATCH 651/717] Translate core json file --- src/i18n/locales/de/core.json | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/i18n/locales/de/core.json b/src/i18n/locales/de/core.json index 6411ac9..ad2c602 100644 --- a/src/i18n/locales/de/core.json +++ b/src/i18n/locales/de/core.json @@ -216,10 +216,9 @@ "generic": { "already_voted": "Sie haben bereits gestimmt.", "avatar_size": "{{ size }} KB max. for GIFS", - "benefits_qort": "vorteile von Qort", - "building": "gebäude", - "building_app": "app -App", - "confirmed": "bestätigen", + "benefits_qort": "Vorteile von Qort", + "building": "Gebäude", + "building_app": "App -App", "created_by": "created by {{ owner }}", "buy_order_request": "the Application
    {{hostname}}
    is requesting {{count}} buy order", "buy_order_request_other": "the Application
    {{hostname}}
    is requesting {{count}} buy orders", @@ -230,8 +229,8 @@ "editing_message": "Bearbeitungsnachricht", "encrypted": "verschlüsselt", "encrypted_not": "nicht verschlüsselt", - "fee_qort": "Gebühr: {{ message }} QORT", - "fetching_data": "app -Daten abrufen", + "fee_qort": "fee: {{ message }} QORT", + "fetching_data": "App -Daten abrufen", "foreign_fee": "foreign fee: {{ message }}", "get_qort_trade_portal": "Holen Sie sich QORT mit dem CrossChain -Handelsportal von Qortal", "minimal_qort_balance": "having at least {{ quantity }} QORT in your balance (4 qort balance for chat, 1.25 for name, 0.75 for some transactions)", From 4be989346510fefb377550d9ff387a948a9f6fea Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 12 Jun 2025 09:07:07 +0200 Subject: [PATCH 652/717] Use Box instead of div --- src/App.tsx | 13 +++++-------- src/components/Chat/ChatDirect.tsx | 12 ++++++------ src/components/Chat/ChatList.tsx | 24 ++++++++++++------------ src/components/Group/GroupList.tsx | 4 ++-- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 6ffbf21..381b41b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -143,7 +143,6 @@ import { CopyIcon } from './assets/Icons/CopyIcon.tsx'; import { SuccessIcon } from './assets/Icons/SuccessIcon.tsx'; import { useAtom, useSetAtom } from 'jotai'; import { useResetAtom } from 'jotai/utils'; -import MintingDetailsPage from './components/Minting/Minting-new.tsx'; type extStates = | 'authenticated' @@ -3978,13 +3977,11 @@ function App() { )} {isOpenMinting && ( - // - - + )} {!isAuthenticated && ( diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index 6f95eee..5b5ac1f 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -546,7 +546,7 @@ export const ChatDirect = ({ }, []); return ( -
    -
    -
    )} -
    + -
    + -
    + ); }; diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index f5cb3f6..e029776 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -206,7 +206,7 @@ export const ChatList = ({ width: '100%', }} > -
    -
    -
    -
    -
    + ); } return ( -
    -
    + ); })} -
    -
    -
    + + + {showScrollButton && (
    + {enableMentions && (hasSecretKey || isPrivate === false) && ( -
    ))} -
    + Date: Thu, 12 Jun 2025 09:41:32 +0200 Subject: [PATCH 653/717] Remove tooltip --- src/components/Language/LanguageSelector.tsx | 25 +++++++------------ src/components/Theme/ThemeSelector.tsx | 26 ++++++-------------- 2 files changed, 16 insertions(+), 35 deletions(-) diff --git a/src/components/Language/LanguageSelector.tsx b/src/components/Language/LanguageSelector.tsx index a51c715..e15727b 100644 --- a/src/components/Language/LanguageSelector.tsx +++ b/src/components/Language/LanguageSelector.tsx @@ -30,25 +30,18 @@ const LanguageSelector = () => { return ( {!showSelect && ( - setShowSelect(true)} + style={{ + fontSize: '1.3rem', + }} + aria-label={t('core:current_language', { + language: name, postProcess: 'capitalizeFirstChar', })} > - - + {flag} + )} {showSelect && ( diff --git a/src/components/Theme/ThemeSelector.tsx b/src/components/Theme/ThemeSelector.tsx index 18fa5cc..6b6f880 100644 --- a/src/components/Theme/ThemeSelector.tsx +++ b/src/components/Theme/ThemeSelector.tsx @@ -19,26 +19,14 @@ const ThemeSelector = () => { return ( - - - {themeMode === 'dark' ? : } - - + {themeMode === 'dark' ? : } + ); }; From 89cc4981f8a8bdc00ee1b722fb77356ec3cbd0d0 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 12 Jun 2025 10:13:16 +0200 Subject: [PATCH 654/717] Lighter paper --- src/styles/theme-dark.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/theme-dark.ts b/src/styles/theme-dark.ts index a519cd6..e64654c 100644 --- a/src/styles/theme-dark.ts +++ b/src/styles/theme-dark.ts @@ -16,7 +16,7 @@ export const darkThemeOptions: ThemeOptions = { background: { default: 'rgb(49, 51, 56)', surface: 'rgb(58, 60, 65)', - paper: 'rgb(62, 64, 68)', + paper: 'rgb(77, 80, 85)', }, text: { primary: 'rgb(255, 255, 255)', From 1abda4a27832592be55bd4fb758968dc30be078c Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 12 Jun 2025 10:14:04 +0200 Subject: [PATCH 655/717] Refactor style for background --- src/components/Chat/ChatList.tsx | 15 +++++++-------- src/components/Chat/ChatOptions.tsx | 1 + src/components/Chat/MessageItem.tsx | 6 +++--- src/components/Group/Group.tsx | 8 ++++---- src/components/Group/GroupList.tsx | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index e029776..dc018d9 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -217,7 +217,6 @@ export const ChatList = ({ > scrollToBottom()} - style={{ + sx={{ backgroundColor: theme.palette.other.unread, border: 'none', borderRadius: '20px', @@ -428,15 +427,15 @@ export const ChatList = ({ {showScrollDownButton && !showScrollButton && ( + + + + )} + + + {isLoadingPayments && ( + + + + )} + + {!isLoadingPayments && addressInfo && ( + + + {t('core:message.generic.most_recent_payment', { + count: 20, + postProcess: 'capitalizeFirstChar', + })} + + + + + {!isLoadingPayments && payments?.length === 0 && ( - {t('core:balance', { + {t('core:message.generic.no_payments', { postProcess: 'capitalizeFirstChar', })} - - {addressInfo?.balance} + )} - - - - - - - )} - - - - {isLoadingPayments && ( - - - - )} - - {!isLoadingPayments && addressInfo && ( - - - {t('core:message.generic.most_recent_payment', { - count: 20, - postProcess: 'capitalizeFirstChar', - })} - - - - - {!isLoadingPayments && payments?.length === 0 && ( - - - {t('core:message.generic.no_payments', { - postProcess: 'capitalizeFirstChar', - })} - - - )} - - - - - - {t('core:sender', { postProcess: 'capitalizeFirstChar' })} - - - {t('core:receiver', { - postProcess: 'capitalizeFirstChar', - })} - - - {t('core:amount', { postProcess: 'capitalizeFirstChar' })} - - - {t('core:time.time', { - postProcess: 'capitalizeFirstChar', - })} - - - - - - {payments.map((payment, index) => ( - +
    + + - - {t('auth:action.copy_address', { - postProcess: 'capitalizeFirstChar', - })} - - } - placement="bottom" - arrow - sx={{ fontSize: '24' }} - slotProps={{ - tooltip: { - sx: { - color: theme.palette.text.primary, - backgroundColor: - theme.palette.background.default, - }, - }, - arrow: { - sx: { - color: theme.palette.text.primary, - }, - }, - }} - > - { - navigator.clipboard.writeText( - payment?.creatorAddress - ); - }} - > - {formatAddress(payment?.creatorAddress)} - - + {t('core:sender', { + postProcess: 'capitalizeFirstChar', + })} - - - {t('auth:action.copy_address', { - postProcess: 'capitalizeFirstChar', - })} - - } - placement="bottom" - arrow - sx={{ fontSize: '24' }} - slotProps={{ - tooltip: { - sx: { - color: theme.palette.text.primary, - backgroundColor: - theme.palette.background.default, - }, - }, - arrow: { - sx: { - color: theme.palette.text.primary, - }, - }, - }} - > - { - navigator.clipboard.writeText(payment?.recipient); - }} - > - {formatAddress(payment?.recipient)} - - + {t('core:receiver', { + postProcess: 'capitalizeFirstChar', + })} - - {payment?.amount} - - {formatTimestamp(payment?.timestamp)} + {t('core:amount', { + postProcess: 'capitalizeFirstChar', + })} + + + {t('core:time.time', { + postProcess: 'capitalizeFirstChar', + })} - ))} - -
    -
    - )} + + + + {payments.map((payment, index) => ( + + + + {t('auth:action.copy_address', { + postProcess: 'capitalizeFirstChar', + })} + + } + placement="bottom" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: theme.palette.text.primary, + backgroundColor: + theme.palette.background.default, + }, + }, + arrow: { + sx: { + color: theme.palette.text.primary, + }, + }, + }} + > + { + navigator.clipboard.writeText( + payment?.creatorAddress + ); + }} + > + {formatAddress(payment?.creatorAddress)} + + + + + + + {t('auth:action.copy_address', { + postProcess: 'capitalizeFirstChar', + })} + + } + placement="bottom" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: theme.palette.text.primary, + backgroundColor: + theme.palette.background.default, + }, + }, + arrow: { + sx: { + color: theme.palette.text.primary, + }, + }, + }} + > + { + navigator.clipboard.writeText( + payment?.recipient + ); + }} + > + {formatAddress(payment?.recipient)} + + + + + {payment?.amount} + + + {formatTimestamp(payment?.timestamp)} + + + ))} + + + + )} +
    -
    + ); }; From 970959c285fcbb1bcb86341b33986cc208ebb84d Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 12 Jun 2025 20:24:33 +0200 Subject: [PATCH 659/717] Remove import --- src/components/Group/AddGroup.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 2ada6a4..90ff47a 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -40,7 +40,6 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useTranslation } from 'react-i18next'; import { useSetAtom } from 'jotai'; import { txListAtom } from '../../atoms/global'; -import { ErrorRounded } from '@mui/icons-material'; export const Label = styled('label')` display: block; From 274c8d10487eb044b246eda3b8dad54b96ba2afa Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 12 Jun 2025 22:30:39 +0200 Subject: [PATCH 660/717] Add translation --- src/i18n/locales/de/core.json | 1 + src/i18n/locales/en/core.json | 1 + src/i18n/locales/es/core.json | 1 + src/i18n/locales/fr/core.json | 1 + src/i18n/locales/it/core.json | 1 + src/i18n/locales/ja/core.json | 3 ++- src/i18n/locales/ru/core.json | 1 + src/i18n/locales/zh/core.json | 1 + 8 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/i18n/locales/de/core.json b/src/i18n/locales/de/core.json index ad2c602..a4f9ba1 100644 --- a/src/i18n/locales/de/core.json +++ b/src/i18n/locales/de/core.json @@ -255,6 +255,7 @@ "no_pinned_changes": "Sie haben derzeit keine Änderungen an Ihren angestellten Apps", "no_results": "Keine Ergebnisse", "one_app_per_name": "Hinweis: Derzeit ist pro Namen nur eine App und Website zulässig.", + "ongoing_transactions": "laufende Transaktionen", "opened": "geöffnet", "overwrite_qdn": "überschreibt zu QDN", "password_confirm": "Bitte bestätigen Sie ein Passwort", diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 1d39e57..e5667ae 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -256,6 +256,7 @@ "no_pinned_changes": "you currently do not have any changes to your pinned apps", "no_results": "no results", "one_app_per_name": "note: Currently, only one App and Website is allowed per Name.", + "ongoing_transactions": "ongoing transactions", "opened": "opened", "overwrite_qdn": "overwrite to QDN", "password_confirm": "please confirm a password", diff --git a/src/i18n/locales/es/core.json b/src/i18n/locales/es/core.json index f4cae5a..fa73deb 100644 --- a/src/i18n/locales/es/core.json +++ b/src/i18n/locales/es/core.json @@ -256,6 +256,7 @@ "no_pinned_changes": "Actualmente no tiene ningún cambio en sus aplicaciones fijadas", "no_results": "Sin resultados", "one_app_per_name": "Nota: Actualmente, solo se permite una aplicación y un sitio web por nombre.", + "ongoing_transactions": "transacciones en curso", "opened": "abierto", "overwrite_qdn": "sobrescribir a QDN", "password_confirm": "Confirme una contraseña", diff --git a/src/i18n/locales/fr/core.json b/src/i18n/locales/fr/core.json index 7f6fd37..a89df31 100644 --- a/src/i18n/locales/fr/core.json +++ b/src/i18n/locales/fr/core.json @@ -256,6 +256,7 @@ "no_pinned_changes": "Vous n'avez actuellement aucune modification à vos applications épinglées", "no_results": "Aucun résultat", "one_app_per_name": "Remarque: Actuellement, une seule application et site Web est autorisée par nom.", + "ongoing_transactions": "transactions en cours", "opened": "ouvert", "overwrite_qdn": "Écraser à QDN", "password_confirm": "Veuillez confirmer un mot de passe", diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 8d47b2c..a620917 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -255,6 +255,7 @@ "no_pinned_changes": "per ora non ci sono modifiche alle app bloccate", "no_results": "nessun risultato", "one_app_per_name": "nota: per adesso sono consentiti solo un'app e un sito Web per nome.", + "ongoing_transactions": "transazioni in corso", "opened": "aperto", "overwrite_qdn": "sovrascrivi a QDN", "password_confirm": "si prega di confermare una password", diff --git a/src/i18n/locales/ja/core.json b/src/i18n/locales/ja/core.json index 1a997b4..0e2bdbe 100644 --- a/src/i18n/locales/ja/core.json +++ b/src/i18n/locales/ja/core.json @@ -256,6 +256,7 @@ "no_pinned_changes": "現在、ピン留めアプリに変更がありません", "no_results": "結果はありません", "one_app_per_name": "注:現在、名前ごとに1つのアプリとWebサイトのみが許可されています。", + "ongoing_transactions": "継続中の取引", "opened": "オープン", "overwrite_qdn": "QDNに上書きします", "password_confirm": "パスワードを確認してください", @@ -394,4 +395,4 @@ }, "website": "Webサイト", "welcome": "いらっしゃいませ" -} \ No newline at end of file +} diff --git a/src/i18n/locales/ru/core.json b/src/i18n/locales/ru/core.json index 36ea098..33cf0db 100644 --- a/src/i18n/locales/ru/core.json +++ b/src/i18n/locales/ru/core.json @@ -256,6 +256,7 @@ "no_pinned_changes": "В настоящее время у вас нет никаких изменений в ваших приложениях", "no_results": "Нет результатов", "one_app_per_name": "ПРИМЕЧАНИЕ. В настоящее время только одно приложение и веб -сайт разрешены для имени.", + "ongoing_transactions": "текущие операции", "opened": "открыл", "overwrite_qdn": "перезаписать в QDN", "password_confirm": "Пожалуйста, подтвердите пароль", diff --git a/src/i18n/locales/zh/core.json b/src/i18n/locales/zh/core.json index f241237..6ed13ee 100644 --- a/src/i18n/locales/zh/core.json +++ b/src/i18n/locales/zh/core.json @@ -256,6 +256,7 @@ "no_pinned_changes": "您目前对固定应用程序没有任何更改", "no_results": "没有结果", "one_app_per_name": "注意:目前,每个名称只允许一个应用程序和网站。", + "ongoing_transactions": "正在进行的交易", "opened": "打开", "overwrite_qdn": "覆盖为QDN", "password_confirm": "请确认密码", From df49f327f12399c340db4fec0304287b25f006f2 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 12 Jun 2025 22:30:59 +0200 Subject: [PATCH 661/717] Move transition in a separate file --- src/common/Transitions.tsx | 12 + src/components/Apps/AppsDevModeNavBar.tsx | 2 +- src/components/Apps/AppsNavBarDesktop.tsx | 2 +- src/components/Apps/AppsPrivate.tsx | 2 +- src/components/Group/AddGroup.tsx | 24 +- src/components/Group/ManageMembers.tsx | 2 +- src/components/Group/Settings.tsx | 12 +- src/components/Minting/Minting.tsx | 774 +++++++++++---------- src/components/TaskManager/TaskManager.tsx | 16 +- src/components/Tutorials/Tutorials.tsx | 2 +- 10 files changed, 445 insertions(+), 403 deletions(-) create mode 100644 src/common/Transitions.tsx diff --git a/src/common/Transitions.tsx b/src/common/Transitions.tsx new file mode 100644 index 0000000..bf917f8 --- /dev/null +++ b/src/common/Transitions.tsx @@ -0,0 +1,12 @@ +import { Slide } from '@mui/material'; +import { TransitionProps } from '@mui/material/transitions'; +import { forwardRef, ReactElement, Ref } from 'react'; + +export const TransitionUp = forwardRef(function Transition( + props: TransitionProps & { + children: ReactElement; + }, + ref: Ref +) { + return ; +}); diff --git a/src/components/Apps/AppsDevModeNavBar.tsx b/src/components/Apps/AppsDevModeNavBar.tsx index add58e9..6a39c16 100644 --- a/src/components/Apps/AppsDevModeNavBar.tsx +++ b/src/components/Apps/AppsDevModeNavBar.tsx @@ -111,7 +111,7 @@ export const AppsDevModeNavBar = () => { variant="scrollable" // Make tabs scrollable scrollButtons={true} sx={{ - '& .MuiTabs-indicator': { + '&.MuiTabs-indicator': { backgroundColor: theme.palette.text.primary, }, maxHeight: `275px`, // Ensure the tabs container fits within the available space diff --git a/src/components/Apps/AppsNavBarDesktop.tsx b/src/components/Apps/AppsNavBarDesktop.tsx index 68c72dc..ce358ab 100644 --- a/src/components/Apps/AppsNavBarDesktop.tsx +++ b/src/components/Apps/AppsNavBarDesktop.tsx @@ -193,7 +193,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => { variant="scrollable" // Make tabs scrollable scrollButtons={true} sx={{ - '& .MuiTabs-indicator': { + '&.MuiTabs-indicator': { backgroundColor: theme.palette.background.default, }, maxHeight: `275px`, // Ensure the tabs container fits within the available space diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index bb1e153..5567785 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -357,7 +357,7 @@ export const AppsPrivate = ({ myName, myAddress }) => { variant={'fullWidth'} scrollButtons="auto" sx={{ - '& .MuiTabs-indicator': { + '&.MuiTabs-indicator': { backgroundColor: theme.palette.background.default, }, }} diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 90ff47a..dae956d 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -1,8 +1,5 @@ import { - forwardRef, Fragment, - ReactElement, - Ref, SyntheticEvent, useContext, useEffect, @@ -17,8 +14,6 @@ import Typography from '@mui/material/Typography'; import CloseIcon from '@mui/icons-material/Close'; import ExpandLess from '@mui/icons-material/ExpandLess'; import ExpandMore from '@mui/icons-material/ExpandMore'; -import Slide from '@mui/material/Slide'; -import { TransitionProps } from '@mui/material/transitions'; import { Box, Collapse, @@ -40,6 +35,7 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useTranslation } from 'react-i18next'; import { useSetAtom } from 'jotai'; import { txListAtom } from '../../atoms/global'; +import { TransitionUp } from '../../common/Transitions.tsx'; export const Label = styled('label')` display: block; @@ -49,15 +45,6 @@ export const Label = styled('label')` margin-bottom: 4px; `; -const Transition = forwardRef(function Transition( - props: TransitionProps & { - children: ReactElement; - }, - ref: Ref -) { - return ; -}); - export const AddGroup = ({ address, open, setOpen }) => { const { show } = useContext(QORTAL_APP_CONTEXT); const setTxList = useSetAtom(txListAtom); @@ -228,13 +215,12 @@ export const AddGroup = ({ address, open, setOpen }) => { open={open} onClose={handleClose} slots={{ - transition: Transition, + transition: TransitionUp, }} > @@ -281,7 +267,7 @@ export const AddGroup = ({ address, open, setOpen }) => { scrollButtons="auto" allowScrollButtonsMobile sx={{ - '& .MuiTabs-indicator': { + '&.MuiTabs-indicator': { backgroundColor: theme.palette.background.default, }, }} @@ -421,10 +407,10 @@ export const AddGroup = ({ address, open, setOpen }) => { setOpenAdvance((prev) => !prev)} > diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index 998e145..8e58c21 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -255,7 +255,7 @@ export const ManageMembers = ({ scrollButtons="auto" // Show scroll buttons automatically allowScrollButtonsMobile // Show scroll buttons on mobile as well sx={{ - '& .MuiTabs-indicator': { + '&.MuiTabs-indicator': { backgroundColor: theme.palette.background.default, }, maxWidth: '100%', // Ensure the tabs container fits within the available space diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index 905dd2d..e94c957 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -40,6 +40,7 @@ import { walletVersion } from '../../background/background.ts'; import Base58 from '../../encryption/Base58.ts'; import { QORTAL_APP_CONTEXT } from '../../App'; import { useTranslation } from 'react-i18next'; +import { TransitionUp } from '../../common/Transitions.tsx'; const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ padding: 8, @@ -74,15 +75,6 @@ const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ }, })); -const Transition = forwardRef(function Transition( - props: TransitionProps & { - children: ReactElement; - }, - ref: Ref -) { - return ; -}); - export const Settings = ({ open, setOpen, rawWallet }) => { const [checked, setChecked] = useState(false); const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); @@ -163,7 +155,7 @@ export const Settings = ({ open, setOpen, rawWallet }) => { open={open} onClose={handleClose} slots={{ - transition: Transition, + transition: TransitionUp, }} > diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index 687d06b..658467f 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -41,6 +41,7 @@ import { useModal } from '../../hooks/useModal.tsx'; import { useAtom, useSetAtom } from 'jotai'; import { memberGroupsAtom, txListAtom } from '../../atoms/global'; import { useTranslation } from 'react-i18next'; +import { TransitionUp } from '../../common/Transitions.tsx'; export const Minting = ({ setIsOpenMinting, myAddress, show }) => { const setTxList = useSetAtom(txListAtom); @@ -540,14 +541,8 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { maxWidth="lg" fullWidth fullScreen - sx={{ - '& .MuiDialog-paper': { - height: '100vh', - margin: 0, - maxWidth: '100%', - overflow: 'hidden', // Prevent scrollbars - width: '100%', - }, + slots={{ + transition: TransitionUp, }} > @@ -575,418 +570,465 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { - - - - - - - - - {valueMintingTab === 0 && ( - <> - + + - - - - Blockchain Statistics - - - - - - - - - - - - Minting Account Details - - - - - - - - - - - With a 24/7 Minting you will reach level 5 in{' '} - 117.58 days! - - - - - - - Minting Rewards Info - - - - - - - - - {/* */} - - - - - - )} - - {valueMintingTab === 1 && ( - <> - - {isLoading && ( - - - - )} - - + + + + + {valueMintingTab === 0 && ( + <> + - - {t('auth:account.account_one', { - postProcess: 'capitalizeFirstChar', - })} - : {handleNames(accountInfo?.address)} - - - - {t('core:level', { - postProcess: 'capitalizeFirstChar', - })} - : {accountInfo?.level} - - - - {t('group:message.generic.next_level', { - postProcess: 'capitalizeFirstChar', - })}{' '} - {_levelUpBlocks()} - - - - {t('group:message.generic.node_minting', { - postProcess: 'capitalizeFirstChar', - })}{' '} - {nodeInfos?.isMintingPossible?.toString()} - - - - - - {isPartOfMintingGroup && !accountIsMinting && ( - - - - {mintingAccounts?.length > 1 && ( - - {t('group:message.generic.minting_keys_per_node', { - postProcess: 'capitalizeFirstChar', - })} + + + + Blockchain Statistics - )} - - )} - + + + + + + - {mintingAccounts?.length > 0 && ( - - {t('group:message.generic.node_minting_account', { - postProcess: 'capitalizeFirstChar', - })} - - )} - + + Minting Account Details + + + + + + + + + + + With a 24/7 Minting you will reach level 5 in{' '} + 117.58 days! + + + + + + + Minting Rewards Info + + + + + + + + + {/* */} + + + + + + )} + + {valueMintingTab === 1 && ( + <> + - {accountIsMinting && ( + {isLoading && ( - - {t('group:message.generic.node_minting_key', { - postProcess: 'capitalizeFirstChar', - })} - + )} - - - {mintingAccounts?.map((acct) => ( - - - {t('group:message.generic.minting_account', { - postProcess: 'capitalizeFirstChar', - })}{' '} - {handleNames(acct?.mintingAccount)} - - - - - - - - - ))} - - {mintingAccounts?.length > 1 && ( - - {t('group:message.generic.minting_keys_per_node_different', { - postProcess: 'capitalizeFirstChar', - })} - - )} - - - - - {!isPartOfMintingGroup && ( + + {t('auth:account.account_one', { + postProcess: 'capitalizeFirstChar', + })} + : {handleNames(accountInfo?.address)} + + + + {t('core:level', { + postProcess: 'capitalizeFirstChar', + })} + : {accountInfo?.level} + + + + {t('group:message.generic.next_level', { + postProcess: 'capitalizeFirstChar', + })}{' '} + {_levelUpBlocks()} + + + + {t('group:message.generic.node_minting', { + postProcess: 'capitalizeFirstChar', + })}{' '} + {nodeInfos?.isMintingPossible?.toString()} + + + + + + {isPartOfMintingGroup && !accountIsMinting && ( - - {t('group:message.generic.minter_group', { - postProcess: 'capitalizeFirstChar', - })} - - - - {t('group:message.generic.mintership_app', { - postProcess: 'capitalizeFirstChar', - })} - - - - - - - )} - {showWaitDialog && ( - 1 && ( + + {t('group:message.generic.minting_keys_per_node', { + postProcess: 'capitalizeFirstChar', + })} + + )} + + )} + + + + {mintingAccounts?.length > 0 && ( + + {t('group:message.generic.node_minting_account', { + postProcess: 'capitalizeFirstChar', + })} + + )} + - + + {t('group:message.generic.node_minting_key', { + postProcess: 'capitalizeFirstChar', + })} + + + )} + + + + {mintingAccounts?.map((acct) => ( + + + {t('group:message.generic.minting_account', { + postProcess: 'capitalizeFirstChar', + })}{' '} + {handleNames(acct?.mintingAccount)} + + + + + + + + + ))} + + {mintingAccounts?.length > 1 && ( + + {t( + 'group:message.generic.minting_keys_per_node_different', + { + postProcess: 'capitalizeFirstChar', + } + )} + + )} + + + + + {!isPartOfMintingGroup && ( + - {isShowNext - ? t('core:message.generic.confirmed', { - postProcess: 'capitalizeFirstChar', - }) - : t('core:message.generic.wait', { - postProcess: 'capitalizeFirstChar', - })} - - - - {!isShowNext && ( - - {t('group:message.success.rewardshare_creation', { - postProcess: 'capitalizeFirstChar', - })} - - )} - - {isShowNext && ( - - {t('group:message.success.rewardshare_confirmed', { - postProcess: 'capitalizeFirstChar', - })} - - )} - - - - - - - )} - - - )} + + {t('group:message.generic.minter_group', { + postProcess: 'capitalizeFirstChar', + })} + + + + {t('group:message.generic.mintership_app', { + postProcess: 'capitalizeFirstChar', + })} + + + + + + + + )} + + {showWaitDialog && ( + + + {isShowNext + ? t('core:message.generic.confirmed', { + postProcess: 'capitalizeFirstChar', + }) + : t('core:message.generic.wait', { + postProcess: 'capitalizeFirstChar', + })} + + + + {!isShowNext && ( + + {t('group:message.success.rewardshare_creation', { + postProcess: 'capitalizeFirstChar', + })} + + )} + + {isShowNext && ( + + {t('group:message.success.rewardshare_confirmed', { + postProcess: 'capitalizeFirstChar', + })} + + )} + + + + + + + )} + + + )} + { const [memberGroups] = useAtom(memberGroupsAtom); - const [txList, setTxList] = useAtom(txListAtom); - const [open, setOpen] = React.useState(false); const intervals = useRef({}); const theme = useTheme(); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const handleClick = () => { setOpen((prev) => !prev); @@ -199,7 +205,11 @@ export const TaskManager = ({ getUserInfo }) => { )} - + {open ? : } diff --git a/src/components/Tutorials/Tutorials.tsx b/src/components/Tutorials/Tutorials.tsx index a32e57a..6b41586 100644 --- a/src/components/Tutorials/Tutorials.tsx +++ b/src/components/Tutorials/Tutorials.tsx @@ -41,7 +41,7 @@ export const Tutorials = () => { > Date: Thu, 12 Jun 2025 22:33:57 +0200 Subject: [PATCH 662/717] Improve translation --- src/i18n/locales/it/group.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/locales/it/group.json b/src/i18n/locales/it/group.json index 8047970..fa504c8 100644 --- a/src/i18n/locales/it/group.json +++ b/src/i18n/locales/it/group.json @@ -71,7 +71,7 @@ "block_delay_maximum": "ritardo massimo del blocco per le approvazioni delle transazioni di gruppo", "closed_group": "questo è un gruppo chiuso/privato, quindi occorre attendere fino a quando un amministratore accetta la richiesta", "descrypt_wallet": "decrittazione del wallet ...", - "encryption_key": "la prima chiave di crittografia comune del gruppo è in fase di creazione. Si prega di attendere qualche minuto per essere recuperato dalla rete. Controllo ogni 2 minuti ...", + "encryption_key": "la prima chiave di crittografia comune del gruppo è in fase di creazione. Si prega di attendere qualche minuto per il suo recupero dalla rete. Controllo ogni 2 minuti ...", "group_announcement": "annunci di gruppo", "group_approval_threshold": "soglia di approvazione del gruppo (numero / percentuale di amministratori che devono approvare una transazione)", "group_encrypted": "gruppo crittografato", From a7d476bb1c8cf606a245d616cd7cf7d060b1ddc1 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 12 Jun 2025 22:57:48 +0200 Subject: [PATCH 663/717] Fix wrong previouos merged conflict --- src/qortal/get.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/qortal/get.ts b/src/qortal/get.ts index 93bcb63..80acbb2 100644 --- a/src/qortal/get.ts +++ b/src/qortal/get.ts @@ -2463,7 +2463,6 @@ export const saveFile = async (data, sender, isFromExtension, snackMethods) => { const filename = data.filename; const blob = data.blob; - const mimeType = blob.type || data.mimeType; const resPermission = await getUserPermission( { text1: i18n.t('question:download_file', { @@ -2474,17 +2473,6 @@ export const saveFile = async (data, sender, isFromExtension, snackMethods) => { isFromExtension ); const { accepted } = resPermission; - if (!accepted) throw new Error('User declined to save file'); // TODO translate - showSaveFilePicker( - { - filename, - mimeType, - blob, - }, - snackMethods - ); - - return true; if (accepted) { const mimeType = blob.type || data.mimeType; From 44593893c1e9b9d901df0f1e55d5ce3934306448 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 12 Jun 2025 23:04:23 +0200 Subject: [PATCH 664/717] Improve style --- src/components/Chat/CreateCommonSecret.tsx | 2 +- src/components/ContextMenu.tsx | 2 +- src/components/Minting/Minting.tsx | 4 ++-- src/components/NotAuthenticated.tsx | 20 ++++++++++---------- src/components/RegisterName.tsx | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/components/Chat/CreateCommonSecret.tsx b/src/components/Chat/CreateCommonSecret.tsx index 0f04356..a8d8961 100644 --- a/src/components/Chat/CreateCommonSecret.tsx +++ b/src/components/Chat/CreateCommonSecret.tsx @@ -213,7 +213,7 @@ export const CreateCommonSecret = ({ return ( {
    ); -}; +}; // TODO translate diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index 658467f..afaba36 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -584,7 +584,7 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { sx={{ borderBottom: 1, borderColor: theme.palette.text.secondary }} > { }} > <> @@ -795,11 +795,11 @@ export const NotAuthenticated = ({ {mode === 'list' && ( @@ -894,10 +894,10 @@ export const NotAuthenticated = ({